Merge "Multi-touch emulation support"
diff --git a/Makefile.common b/Makefile.common
index 8b98df8..54ab704 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -442,7 +442,10 @@
     android/camera/camera-service.c \
     android/adb-server.c \
     android/adb-qemud.c \
-    android/snaphost-android.c
+    android/snaphost-android.c \
+    android/multitouch-screen.c \
+    android/multitouch-port.c \
+    android/utils/jpeg-compress.c
 
 $(call gen-hw-config-defs)
 
diff --git a/android/android-device.c b/android/android-device.c
index 37af0c6..498e304 100644
--- a/android/android-device.c
+++ b/android/android-device.c
@@ -581,35 +581,60 @@
         return -1;
     }
 
-    iolooper_add_read(_ads_io_looper(ads), ads->fd);
-    do {
-        int res = socket_recv(ads->fd, buf + recvd, bufsize - recvd);
-        if (res == 0) {
-            /* Disconnection. */
-            errno = ECONNRESET;
-            recvd = -1;
-            break;
-        }
+    /* XXX: This is a hack that implements a blocked line read on an async
+     * event socket! Redo this ASAP! */
+    if (ads->type == ADS_TYPE_EVENT) {
+        AndroidEventSocket* adsevent = (AndroidEventSocket*)ads;
+        asyncLineReader_init(&adsevent->alr, buf, bufsize, adsevent->io);
+        /* Default EOL for the line reader was '\n'. */
+        asyncLineReader_setEOL(&adsevent->alr, '\0');
+        AsyncStatus status = ASYNC_NEED_MORE;
 
-        if (res < 0) {
-            if (errno == EINTR) {
-                /* loop on EINTR */
-                continue;
+        while (status == ASYNC_NEED_MORE) {
+            status = asyncLineReader_read(&adsevent->alr);
+            if (status == ASYNC_COMPLETE) {
+                recvd = adsevent->alr.pos;
+                break;
+            } else if (status == ASYNC_ERROR) {
+                if (errno == ENOMEM) {
+                    recvd = adsevent->alr.pos;
+                } else {
+                    recvd = -1;
+                }
+                break;
+            }
+        }
+    } else {
+        iolooper_add_read(_ads_io_looper(ads), ads->fd);
+        do {
+            int res = socket_recv(ads->fd, buf + recvd, bufsize - recvd);
+            if (res == 0) {
+                /* Disconnection. */
+                errno = ECONNRESET;
+                recvd = -1;
+                break;
             }
 
-            if (errno == EWOULDBLOCK || errno == EAGAIN) {
-                res = iolooper_wait_absolute(_ads_io_looper(ads), ads->deadline);
-                if (res > 0) {
-                    /* Ready to read. */
+            if (res < 0) {
+                if (errno == EINTR) {
+                    /* loop on EINTR */
                     continue;
                 }
+
+                if (errno == EWOULDBLOCK || errno == EAGAIN) {
+                    res = iolooper_wait_absolute(_ads_io_looper(ads), ads->deadline);
+                    if (res > 0) {
+                        /* Ready to read. */
+                        continue;
+                    }
+                }
+                recvd = -1;
+                break;
             }
-            recvd = -1;
-            break;
-        }
-        recvd += res;
-    } while (recvd < bufsize);
-    iolooper_del_read(_ads_io_looper(ads), ads->fd);
+            recvd += res;
+        } while (recvd < bufsize);
+        iolooper_del_read(_ads_io_looper(ads), ads->fd);
+    }
 
     /* In case of an I/O failure we have to invoke failure callback. Note that we
      * report I/O failures only on registered sockets. */
@@ -1108,6 +1133,7 @@
         /* Continue reading data. */
         status = asyncLineReader_read(&adsevent->alr);
         if (status == ASYNC_COMPLETE) {
+            errno = 0;
             _on_event_received(adsevent);
         } else if (status == ASYNC_ERROR) {
             D("I/O failure while reading from channel '%s'@%d: %s",
@@ -1153,6 +1179,7 @@
                 return;
             } else if (sent == to_send->data_remaining) {
                 /* All data is sent. */
+                errno = 0;
                 adsevent->send_pending = to_send->next;
                 _async_send_buffer_complete(to_send, ATR_SUCCESS);
             } else {
@@ -1182,6 +1209,7 @@
     /* Complete event socket connection by identifying it as "event" socket with
      * the application. */
     res = _android_dev_socket_register(ads);
+
     if (res) {
         const int save_error = errno;
         _android_event_socket_disconnect(adsevent);
diff --git a/android/avd/hardware-properties.ini b/android/avd/hardware-properties.ini
index 005c63d..241bf54 100644
--- a/android/avd/hardware-properties.ini
+++ b/android/avd/hardware-properties.ini
@@ -53,6 +53,13 @@
 abstract    = Touch-screen support
 description = Whether there is a touch screen or not on the device.
 
+# Multi-touch screen support
+name        = hw.multiTouch
+type        = boolean
+default     = no
+abstract    = Multi-touch screen support
+description = Whether there is a multi-touch screen or not on the device.
+
 # Hardware main keys (back/home)
 name        = hw.mainKeys
 type        = boolean
diff --git a/android/cmdline-options.h b/android/cmdline-options.h
index 0cf358f..d490e40 100644
--- a/android/cmdline-options.h
+++ b/android/cmdline-options.h
@@ -159,6 +159,8 @@
 OPT_PARAM( fake_camera, "<mode>", "set fake camera emulation mode" )
 OPT_LIST( webcam, "name=<name>[,dir=<direction>]", "setup web camera emulation" )
 
+OPT_PARAM( screen, "<mode>", "set emulated screen mode" )
+
 #undef CFG_FLAG
 #undef CFG_PARAM
 #undef OPT_FLAG
diff --git a/android/help.c b/android/help.c
index 5f151ca..db46d21 100644
--- a/android/help.c
+++ b/android/help.c
@@ -1488,6 +1488,21 @@
     );
 }
 
+static void
+help_screen(stralloc_t* out)
+{
+    PRINTF(
+    "  Use -screen <mode> to set the emulated screen mode.\n"
+    "  Valid values for <mode> are:\n\n"
+
+    "     touch       -> emulate a touch screen\n"
+    "     multi-touch -> emulate a multi-touch screen\n"
+    "     off         -> disable touch and multi-touch screen emulation\n\n"
+
+    "  Default mode for screen emulation is 'touch'.\n\n"
+    );
+}
+
 #define  help_no_skin   NULL
 #define  help_netspeed  help_shaper
 #define  help_netdelay  help_shaper
diff --git a/android/main.c b/android/main.c
index b7421a1..2723834 100644
--- a/android/main.c
+++ b/android/main.c
@@ -1271,6 +1271,30 @@
         args[n++] = "socket,vlan=1,mcast=230.0.0.10:1234";
     }
 
+    /* Setup screen emulation */
+    if (opts->screen) {
+        if (!strcmp(opts->screen, "touch")) {
+            hw->hw_touchScreen = 1;
+            hw->hw_multiTouch = 0;
+        } else if (!strcmp(opts->screen, "multi-touch")) {
+            hw->hw_multiTouch = 1;
+            hw->hw_touchScreen = 0;
+        } else if (!strcmp(opts->screen, "off")) {
+            hw->hw_touchScreen = 0;
+            hw->hw_multiTouch = 0;
+        } else {
+            derror("Invalid value for -screen <mode> parameter: %s\n", opts->screen);
+            derror("Valid values are: touch, multi-touch, or off\n");
+            exit(1);
+        }
+    } else {
+        /* If both, touch and multitouch are set in hw.ini, choose multi-touch
+         * for screen emulation. */
+        if (hw->hw_touchScreen && hw->hw_multiTouch) {
+            hw->hw_touchScreen = 0;
+        }
+    }
+
     while(argc-- > 0) {
         args[n++] = *argv++;
     }
diff --git a/android/multitouch-port.c b/android/multitouch-port.c
new file mode 100644
index 0000000..c8e3ca5
--- /dev/null
+++ b/android/multitouch-port.c
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "qemu-common.h"
+#include "utils/panic.h"
+#include "android/hw-events.h"
+#include "android/charmap.h"
+#include "android/multitouch-screen.h"
+#include "android/multitouch-port.h"
+#include "android/globals.h"  /* for android_hw */
+#include "android/utils/misc.h"
+#include "android/utils/jpeg-compress.h"
+
+#define  E(...)    derror(__VA_ARGS__)
+#define  W(...)    dwarning(__VA_ARGS__)
+#define  D(...)    VERBOSE_PRINT(mtport,__VA_ARGS__)
+#define  D_ACTIVE  VERBOSE_CHECK(mtport)
+
+/* Query timeout in milliseconds. */
+#define MTSP_QUERY_TIMEOUT       3000
+#define MTSP_MAX_MSG             2048
+#define MTSP_MAX_EVENT           2048
+
+/* Multi-touch port descriptor. */
+struct AndroidMTSPort {
+    /* Caller identifier. */
+    void*           opaque;
+    /* Connected android device. */
+    AndroidDevice*  device;
+    /* Initialized JPEG compressor instance. */
+    AJPEGDesc*      jpeg_compressor;
+    /* Connection status: 1 connected, 0 - disconnected. */
+    int             is_connected;
+    /* Buffer where to receive multitouch messages. */
+    char            mts_msg[MTSP_MAX_MSG];
+    /* Buffer where to receive multitouch events. */
+    char            events[MTSP_MAX_EVENT];
+};
+
+/* Destroys and frees the descriptor. */
+static void
+_mts_port_free(AndroidMTSPort* mtsp)
+{
+    if (mtsp != NULL) {
+        if (mtsp->jpeg_compressor != NULL) {
+            jpeg_compressor_destroy(mtsp->jpeg_compressor);
+        }
+        if (mtsp->device != NULL) {
+            android_device_destroy(mtsp->device);
+        }
+        AFREE(mtsp);
+    }
+}
+
+/********************************************************************************
+ *                          Multi-touch action handlers
+ *******************************************************************************/
+
+/*
+ * Although there are a lot of similarities in the way the handlers below are
+ * implemented, for the sake of tracing / debugging it's better to have a
+ * separate handler for each distinctive action.
+ */
+
+/* First pointer down event handler. */
+static void
+_on_action_down(int tracking_id, int x, int y, int pressure)
+{
+    multitouch_update_pointer(MTES_DEVICE, tracking_id, x, y, pressure);
+}
+
+/* Last pointer up event handler. */
+static void
+_on_action_up(int tracking_id)
+{
+    multitouch_update_pointer(MTES_DEVICE, tracking_id, 0, 0, 0);
+}
+
+/* Pointer down event handler. */
+static void
+_on_action_pointer_down(int tracking_id, int x, int y, int pressure)
+{
+    multitouch_update_pointer(MTES_DEVICE, tracking_id, x, y, pressure);
+}
+
+/* Pointer up event handler. */
+static void
+_on_action_pointer_up(int tracking_id)
+{
+    multitouch_update_pointer(MTES_DEVICE, tracking_id, 0, 0, 0);
+}
+
+/* Pointer move event handler. */
+static void
+_on_action_move(int tracking_id, int x, int y, int pressure)
+{
+    multitouch_update_pointer(MTES_DEVICE, tracking_id, x, y, pressure);
+}
+
+/********************************************************************************
+ *                          Multi-touch event handlers
+ *******************************************************************************/
+
+/* Handles "pointer move" event. */
+static void
+_on_move(const char* param)
+{
+    const char* pid = param;
+    D(">>> MOVE: %s", param);
+    while (pid && *pid) {
+        int pid_val, x, y, pressure = 0;
+        if (!get_token_value_int(pid, "pid", &pid_val) &&
+            !get_token_value_int(pid, "x", &x) &&
+            !get_token_value_int(pid, "y", &y)) {
+            get_token_value_int(pid, "pressure", &pressure);
+            _on_action_move(pid_val, x, y, pressure);
+            pid = strstr(pid + 1, "pid");
+        } else {
+            break;
+        }
+    }
+}
+
+/* Handles "first pointer down" event. */
+static void
+_on_down(const char* param)
+{
+    int pid_val, x, y, pressure = 0;
+    D(">>> 1-ST DOWN: %s", param);
+    if (!get_token_value_int(param, "pid", &pid_val) &&
+        !get_token_value_int(param, "x", &x) &&
+        !get_token_value_int(param, "y", &y)) {
+        get_token_value_int(param, "pressure", &pressure);
+        _on_action_down(pid_val, x, y, pressure);
+    } else {
+        W("Invalid parameters '%s' for MTS 'down' event", param);
+    }
+}
+
+/* Handles "last pointer up" event. */
+static void
+_on_up(const char* param)
+{
+    int pid_val;
+    D(">>> LAST UP: %s", param);
+    if (!get_token_value_int(param, "pid", &pid_val)) {
+        _on_action_up(pid_val);
+    } else {
+        W("Invalid parameters '%s' for MTS 'up' event", param);
+    }
+}
+
+/* Handles "next pointer down" event. */
+static void
+_on_pdown(const char* param)
+{
+    int pid_val, x, y, pressure = 0;
+    D(">>> DOWN: %s", param);
+    if (!get_token_value_int(param, "pid", &pid_val) &&
+        !get_token_value_int(param, "x", &x) &&
+        !get_token_value_int(param, "y", &y)) {
+        get_token_value_int(param, "pressure", &pressure);
+        _on_action_pointer_down(pid_val, x, y, pressure);
+    } else {
+        W("Invalid parameters '%s' for MTS 'pointer down' event", param);
+    }
+}
+
+/* Handles "next pointer up" event. */
+static void
+_on_pup(const char* param)
+{
+    int pid_val;
+    D(">>> UP: %s", param);
+    if (!get_token_value_int(param, "pid", &pid_val)) {
+        _on_action_pointer_up(pid_val);
+    } else {
+        W("Invalid parameters '%s' for MTS 'up' event", param);
+    }
+}
+
+/********************************************************************************
+ *                      Device communication callbacks
+ *******************************************************************************/
+
+/* Main event handler.
+ * This routine is invoked when an event message has been received from the
+ * device.
+ */
+static void
+_on_event_received(void* opaque, AndroidDevice* ad, char* msg, int msgsize)
+{
+    char* action;
+    int res;
+    AndroidMTSPort* mtsp = (AndroidMTSPort*)opaque;
+
+    if (errno) {
+        D("Multi-touch notification has failed: %s", strerror(errno));
+        return;
+    }
+
+    /* Dispatch the event to an appropriate handler. */
+    res = get_token_value_alloc(msg, "action", &action);
+    if (!res) {
+        const char* param = strchr(msg, ' ');
+        if (param) {
+            param++;
+        }
+        if (!strcmp(action, "move")) {
+            _on_move(param);
+        } else if (!strcmp(action, "down")) {
+            _on_down(param);
+        } else if (!strcmp(action, "up")) {
+            _on_up(param);
+        } else if (!strcmp(action, "pdown")) {
+            _on_pdown(param);
+        } else if (!strcmp(action, "pup")) {
+            _on_pup(param);
+        } else {
+            D("Unknown multi-touch event action '%s'", action);
+        }
+        free(action);
+    }
+
+    /* Listen to the next event. */
+    android_device_listen(ad, mtsp->events, sizeof(mtsp->events),
+                          _on_event_received);
+}
+
+/* A callback that is invoked when android device is connected (i.e. both,
+ * command and event channels have been established).
+ * Param:
+ *  opaque - AndroidMTSPort instance.
+ *  ad - Android device used by this port.
+ *  failure - Connections status.
+ */
+static void
+_on_device_connected(void* opaque, AndroidDevice* ad, int failure)
+{
+    if (!failure) {
+        AndroidMTSPort* mtsp = (AndroidMTSPort*)opaque;
+        mtsp->is_connected = 1;
+        D("Multi-touch emulation has started");
+        android_device_listen(mtsp->device, mtsp->events, sizeof(mtsp->events),
+                              _on_event_received);
+        mts_port_start(mtsp);
+    }
+}
+
+/* Invoked when an I/O failure occurs on a socket.
+ * Note that this callback will not be invoked on connection failures.
+ * Param:
+ *  opaque - AndroidMTSPort instance.
+ *  ad - Android device instance
+ *  ads - Connection socket where failure has occured.
+ *  failure - Contains 'errno' indicating the reason for failure.
+ */
+static void
+_on_io_failure(void* opaque, AndroidDevice* ad, int failure)
+{
+    AndroidMTSPort* mtsp = (AndroidMTSPort*)opaque;
+    E("Multi-touch port got disconnected: %s", strerror(failure));
+    mtsp->is_connected = 0;
+    android_device_disconnect(ad);
+
+    /* Try to reconnect again. */
+    android_device_connect_async(ad, _on_device_connected);
+}
+
+/********************************************************************************
+ *                          MTS port API
+ *******************************************************************************/
+
+AndroidMTSPort*
+mts_port_create(void* opaque)
+{
+    AndroidMTSPort* mtsp;
+    int res;
+
+    ANEW0(mtsp);
+    mtsp->opaque = opaque;
+    mtsp->is_connected = 0;
+
+    /* Initialize default MTS descriptor. */
+    multitouch_init(mtsp);
+
+    /* Create JPEG compressor. Put "$BLOB:%09d\0" + MTFrameHeader header in front
+     * of the compressed data. this way we will have entire query ready to be
+     * transmitted to the device. */
+    mtsp->jpeg_compressor = jpeg_compressor_create(16 + sizeof(MTFrameHeader), 4096);
+
+    mtsp->device = android_device_init(mtsp, AD_MULTITOUCH_PORT, _on_io_failure);
+    if (mtsp->device == NULL) {
+        _mts_port_free(mtsp);
+        return NULL;
+    }
+
+    res = android_device_connect_async(mtsp->device, _on_device_connected);
+    if (res != 0) {
+        mts_port_destroy(mtsp);
+        return NULL;
+    }
+
+    return mtsp;
+}
+
+void
+mts_port_destroy(AndroidMTSPort* mtsp)
+{
+    _mts_port_free(mtsp);
+}
+
+int
+mts_port_is_connected(AndroidMTSPort* mtsp)
+{
+    return mtsp->is_connected;
+}
+
+int
+mts_port_start(AndroidMTSPort* mtsp)
+{
+    char qresp[MTSP_MAX_MSG];
+    char query[256];
+    AndroidHwConfig* config = android_hw;
+
+    /* Query the device to start capturing multi-touch events, also providing
+     * the device with width / height of the emulator's screen. This is required
+     * so device can properly adjust multi-touch event coordinates, and display
+     * emulator's framebuffer. */
+    snprintf(query, sizeof(query), "start:%dx%d",
+             config->hw_lcd_width, config->hw_lcd_height);
+    int res = android_device_query(mtsp->device, query, qresp, sizeof(qresp),
+                                   MTSP_QUERY_TIMEOUT);
+    if (!res) {
+        /* By protocol device should reply with its view dimensions. */
+        if (*qresp) {
+            int width, height;
+            if (sscanf(qresp, "%dx%d", &width, &height) == 2) {
+                multitouch_set_device_screen_size(width, height);
+                D("Multi-touch emulation has started. Device dims: %dx%d",
+                  width, height);
+            } else {
+                E("Unexpected reply to MTS 'start' query: %s", qresp);
+                android_device_query(mtsp->device, "stop", qresp, sizeof(qresp),
+                                     MTSP_QUERY_TIMEOUT);
+                res = -1;
+            }
+        } else {
+            E("MTS protocol error: no reply to query 'start'");
+            android_device_query(mtsp->device, "stop", qresp, sizeof(qresp),
+                                 MTSP_QUERY_TIMEOUT);
+            res = -1;
+        }
+    } else {
+        if (errno) {
+            D("Query 'start' failed on I/O: %s", strerror(errno));
+        } else {
+            D("Query 'start' failed on device: %s", qresp);
+        }
+    }
+    return res;
+}
+
+int
+mts_port_stop(AndroidMTSPort* mtsp)
+{
+    char qresp[MTSP_MAX_MSG];
+    const int res =
+        android_device_query(mtsp->device, "stop", qresp, sizeof(qresp),
+                             MTSP_QUERY_TIMEOUT);
+    if (res) {
+        if (errno) {
+            D("Query 'stop' failed on I/O: %s", strerror(errno));
+        } else {
+            D("Query 'stop' failed on device: %s", qresp);
+        }
+    }
+
+    return res;
+}
+
+/********************************************************************************
+ *                       Handling framebuffer updates
+ *******************************************************************************/
+
+/* Compresses a framebuffer region into JPEG image.
+ * Param:
+ *  mtsp - Multi-touch port descriptor with initialized JPEG compressor.
+ *  fmt Descriptor for framebuffer region to compress.
+ *  fb Beginning of the framebuffer.
+ *  jpeg_quality JPEG compression quality. A number from 1 to 100. Note that
+ *      value 10 provides pretty decent image for the purpose of multi-touch
+ *      emulation.
+ */
+static void
+_fb_compress(const AndroidMTSPort* mtsp,
+             const MTFrameHeader* fmt,
+             const uint8_t* fb,
+             int jpeg_quality)
+{
+    jpeg_compressor_compress_fb(mtsp->jpeg_compressor, fmt->x, fmt->y, fmt->w,
+                                fmt->h, fmt->bpp, fmt->bpl, fb, jpeg_quality);
+}
+
+int
+mts_port_send_frame(AndroidMTSPort* mtsp,
+                    MTFrameHeader* fmt,
+                    const uint8_t* fb,
+                    async_send_cb cb,
+                    void* cb_opaque)
+{
+    char* query;
+    int blob_size, off;
+
+    /* Make sure that port is connected. */
+    if (!mts_port_is_connected(mtsp)) {
+        return -1;
+    }
+
+    /* Compress framebuffer region. 10% quality seems to be sufficient. */
+    fmt->format = MTFB_JPEG;
+    _fb_compress(mtsp, fmt, fb, 10);
+
+    /* Total size of the blob: header + JPEG image. */
+    blob_size = sizeof(MTFrameHeader) +
+                jpeg_compressor_get_jpeg_size(mtsp->jpeg_compressor);
+
+    /* Query starts at the beginning of the buffer allocated by the compressor's
+     * destination manager. */
+    query = (char*)jpeg_compressor_get_buffer(mtsp->jpeg_compressor);
+
+    /* Build the $BLOB query to transfer to the device. */
+    snprintf(query, jpeg_compressor_get_header_size(mtsp->jpeg_compressor),
+             "$BLOB:%09d", blob_size);
+    off = strlen(query) + 1;
+
+    /* Copy framebuffer update header to the query. */
+    memcpy(query + off, fmt, sizeof(MTFrameHeader));
+
+    /* Zeroing the rectangle in the update header we indicate that it contains
+     * no updates. */
+    fmt->x = fmt->y = fmt->w = fmt->h = 0;
+
+    /* Initiate asynchronous transfer of the updated framebuffer rectangle. */
+    if (android_device_send_async(mtsp->device, query, off + blob_size, 0, cb, cb_opaque)) {
+        D("Unable to send query '%s': %s", query, strerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/android/multitouch-port.h b/android/multitouch-port.h
new file mode 100644
index 0000000..553617b
--- /dev/null
+++ b/android/multitouch-port.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_ANDROID_MULTITOUCH_PORT_H_
+#define ANDROID_ANDROID_MULTITOUCH_PORT_H_
+
+/*
+ * Encapsulates exchange protocol between the multi-touch screen emulator, and an
+ * application running on an Android device that provides touch events, and is
+ * connected to the host via USB.
+ */
+
+#include "android/android-device.h"
+
+/* TCP port reserved for multi-touch emulation. */
+#define AD_MULTITOUCH_PORT      1969
+
+/*
+ * Codes that define transmitted framebuffer format:
+ *
+ * NOTE: Application on the device side depends on these values. Any changes
+ * made here must be reflected in the app too. Application location is at
+ * 'sdk/apps/SdkController/SdkControllerMultitouch' root.
+ */
+
+/* Framebuffer is transmitted as JPEG. */
+#define MTFB_JPEG       1
+/* Framebuffer is transmitted as raw RGB565 bitmap. */
+#define MTFB_RGB565     2
+/* Framebuffer is transmitted as raw RGB888 bitmap. */
+#define MTFB_RGB888     3
+
+/* Framebuffer update descriptor.
+ * This descriptor is used to collect properties of the updated framebuffer
+ * region. This descriptor is also sent to the MT emulation application on the
+ * device, so it can properly redraw its screen.
+ *
+ * NOTE: Application on the device side depends on that structure. Any changes
+ * made here must be reflected in the app too. Application location is at
+ * 'sdk/apps/SdkController/SdkControllerMultitouch' root.
+ */
+typedef struct MTFrameHeader {
+    /* Size of the header. Must be always sizeof(MTFrameHeader). */
+    int         header_size;
+    /* Display width */
+    int         disp_width;
+    /* Display height */
+    int         disp_height;
+    /* x, y, w, and h define framebuffer region that has been updated. */
+    int         x;
+    int         y;
+    int         w;
+    int         h;
+    /* Bytes per line in the framebufer. */
+    int         bpl;
+    /* Bytes per pixel in the framebufer. */
+    int         bpp;
+    /* Defines format in which framebuffer is transmitted to the device. */
+    int         format;
+} MTFrameHeader;
+
+/* Declares multi-touch port descriptor. */
+typedef struct AndroidMTSPort AndroidMTSPort;
+
+/* Creates multi-touch port, and connects it to the device.
+ * Param:
+ *  opaque - An opaque pointer that is passed back to the callback routines.
+ * Return:
+ *  Initialized device descriptor on success, or NULL on failure. If failure is
+ *  returned from this routine, 'errno' indicates the reason for failure. If this
+ *  routine successeds, a connection is established with the sensor reading
+ *  application on the device.
+ */
+extern AndroidMTSPort* mts_port_create(void* opaque);
+
+/* Disconnects from the multi-touch port, and destroys the descriptor. */
+extern void mts_port_destroy(AndroidMTSPort* amtp);
+
+/* Checks if port is connected to a MT-emulating application on the device.
+ * Note that connection can go out and then be restored at any time after
+ * mts_port_create API succeeded.
+ */
+extern int mts_port_is_connected(AndroidMTSPort* amtp);
+
+/* Queries the connected application to start delivering multi-touch events.
+ * Param:
+ *  amtp - Android multi-touch port instance returned from mts_port_create.
+ * Return:
+ *  Zero on success, failure otherwise.
+ */
+extern int mts_port_start(AndroidMTSPort* amtp);
+
+/* Queries the connected application to stop delivering multi-touch events.
+ * Param:
+ *  amtp - Android multi-touch port instance returned from mts_port_create.
+ * Return:
+ *  Zero on success, failure otherwise.
+ */
+extern int mts_port_stop(AndroidMTSPort* amtp);
+
+/* Sends framebuffer update to the multi-touch emulation application, running on
+ * the android device.
+ * Param:
+ *  mtsp - Android multi-touch port instance returned from mts_port_create.
+ *  fmt - Framebuffer update descriptor.
+ *  fb - Beginning of the framebuffer.
+ *  cb - Callback to invoke when update has been transferred to the MT-emulating
+ *      application on the device.
+ *  cb_opaque - An opaque parameter to pass back to the 'cb' callback.
+ * Return:
+ *  0 on success, or != 0 on failure.
+ */
+extern int mts_port_send_frame(AndroidMTSPort* mtsp,
+                               MTFrameHeader* fmt,
+                               const uint8_t* fb,
+                               async_send_cb cb,
+                               void* cb_opaque);
+
+#endif  /* ANDROID_ANDROID_MULTITOUCH_PORT_H_ */
diff --git a/android/multitouch-screen.c b/android/multitouch-screen.c
new file mode 100644
index 0000000..e1a9a8a
--- /dev/null
+++ b/android/multitouch-screen.c
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "qemu-common.h"
+#include "user-events.h"
+#include "android/display-core.h"
+#include "android/hw-events.h"
+#include "android/charmap.h"
+#include "android/globals.h"  /* for android_hw */
+#include "android/utils/misc.h"
+#include "android/utils/debug.h"
+#include "android/multitouch-screen.h"
+
+#define  E(...)    derror(__VA_ARGS__)
+#define  W(...)    dwarning(__VA_ARGS__)
+#define  D(...)    VERBOSE_PRINT(mtscreen,__VA_ARGS__)
+#define  D_ACTIVE  VERBOSE_CHECK(mtscreen)
+
+/* Maximum number of pointers, supported by multi-touch emulation. */
+#define MTS_POINTERS_NUM    10
+/* Signals that pointer is not tracked (or is "up"). */
+#define MTS_POINTER_UP      -1
+/* Special tracking ID for a mouse pointer. */
+#define MTS_POINTER_MOUSE   -2
+
+/* Describes state of a multi-touch pointer  */
+typedef struct MTSPointerState {
+    /* Tracking ID assigned to the pointer by an app emulating multi-touch. */
+    int tracking_id;
+    /* X - coordinate of the tracked pointer. */
+    int x;
+    /* Y - coordinate of the tracked pointer. */
+    int y;
+    /* Current pressure value. */
+    int pressure;
+} MTSPointerState;
+
+/* Describes state of an emulated multi-touch screen. */
+typedef struct MTSState {
+    /* Multi-touch port connected to the device. */
+    AndroidMTSPort* mtsp;
+    /* Emulator's display state. */
+    DisplayState*   ds;
+    /* Screen width of the device that emulates multi-touch. */
+    int             device_width;
+    /* Screen height of the device that emulates multi-touch. */
+    int             device_height;
+    /* Number of tracked pointers. */
+    int             tracked_ptr_num;
+    /* Index in the 'tracked_pointers' array of the last pointer for which
+     * ABS_MT_SLOT was sent. -1 indicates that no slot selection has been made
+     * yet. */
+    int             current_slot;
+    /* Accumulator for ABS_TOUCH_MAJOR value. */
+    int             touch_major;
+    /* Array of multi-touch pointers. */
+    MTSPointerState tracked_pointers[MTS_POINTERS_NUM];
+    /* Header collecting framebuffer information and updates. */
+    MTFrameHeader   fb_header;
+    /* Boolean value indicating if framebuffer updates are currently being
+     * transferred to the application running on the device. */
+    int             fb_transfer_in_progress;
+} MTSState;
+
+/* Default multi-touch screen descriptor */
+static MTSState _MTSState;
+
+/* Pushes event to the event device. */
+static void
+_push_event(int type, int code, int value)
+{
+    user_event_generic(type, code, value);
+}
+
+/* Gets an index in the MTS's tracking pointers array MTS for the given
+ * tracking id.
+ * Return:
+ *  Index of a matching entry in the MTS's tracking pointers array, or -1 if
+ *  matching entry was not found.
+ */
+static int
+_mtsstate_get_pointer_index(const MTSState* mts_state, int tracking_id)
+{
+    int index;
+    for (index = 0; index < MTS_POINTERS_NUM; index++) {
+        if (mts_state->tracked_pointers[index].tracking_id == tracking_id) {
+            return index;
+        }
+    }
+    return -1;
+}
+
+/* Gets an index of the first untracking pointer in the MTS's tracking pointers
+ * array.
+ * Return:
+ *  An index of the first untracking pointer, or -1 if all pointers are tracked.
+ */
+static int
+_mtsstate_get_available_pointer_index(const MTSState* mts_state)
+{
+    return _mtsstate_get_pointer_index(mts_state, MTS_POINTER_UP);
+}
+
+/* Handles a "pointer down" event
+ * Param:
+ *  mts_state - MTS state descriptor.
+ *  tracking_id - Tracking ID of the "downed" pointer.
+ *  x, y - "Downed" pointer coordinates,
+ *  pressure - Pressure value for the pointer.
+ */
+static void
+_mts_pointer_down(MTSState* mts_state, int tracking_id, int x, int y, int pressure)
+{
+    /* Get first available slot for the new pointer. */
+    const int slot_index = _mtsstate_get_available_pointer_index(mts_state);
+
+    /* Make sure there is a place for the pointer. */
+    if (slot_index >= 0) {
+        /* Initialize pointer's entry. */
+        mts_state->tracked_ptr_num++;
+        mts_state->tracked_pointers[slot_index].tracking_id = tracking_id;
+        mts_state->tracked_pointers[slot_index].x = x;
+        mts_state->tracked_pointers[slot_index].y = y;
+        mts_state->tracked_pointers[slot_index].pressure = pressure;
+
+        /* Send events indicating a "pointer down" to the EventHub */
+        /* Make sure that correct slot is selected. */
+        if (slot_index != mts_state->current_slot) {
+            _push_event(EV_ABS, ABS_MT_SLOT, slot_index);
+        }
+        _push_event(EV_ABS, ABS_MT_TRACKING_ID, slot_index);
+        _push_event(EV_ABS, ABS_MT_TOUCH_MAJOR, ++mts_state->touch_major);
+        _push_event(EV_ABS, ABS_MT_PRESSURE, pressure);
+        _push_event(EV_ABS, ABS_MT_POSITION_X, x);
+        _push_event(EV_ABS, ABS_MT_POSITION_Y, y);
+        _push_event(EV_SYN, SYN_REPORT, 0);
+        mts_state->current_slot = slot_index;
+    } else {
+        D("MTS pointer count is exceeded.");
+        return;
+    }
+}
+
+/* Handles a "pointer up" event
+ * Param:
+ *  mts_state - MTS state descriptor.
+ *  slot_index - Pointer's index in the MTS's array of tracked pointers.
+ */
+static void
+_mts_pointer_up(MTSState* mts_state, int slot_index)
+{
+    /* Make sure that correct slot is selected. */
+    if (slot_index != mts_state->current_slot) {
+        _push_event(EV_ABS, ABS_MT_SLOT, slot_index);
+    }
+
+    /* Send event indicating "pointer up" to the EventHub. */
+    _push_event(EV_ABS, ABS_MT_TRACKING_ID, -1);
+    _push_event(EV_SYN, SYN_REPORT, 0);
+
+    /* Update MTS descriptor, removing the tracked pointer. */
+    mts_state->tracked_pointers[slot_index].tracking_id = MTS_POINTER_UP;
+    mts_state->tracked_pointers[slot_index].x = 0;
+    mts_state->tracked_pointers[slot_index].y = 0;
+    mts_state->tracked_pointers[slot_index].pressure = 0;
+
+    /* Since current slot is no longer tracked, make sure we will do a "select"
+     * next time we send events to the EventHub. */
+    mts_state->current_slot = -1;
+    mts_state->tracked_ptr_num--;
+    assert(mts_state->tracked_ptr_num >= 0);
+}
+
+/* Handles a "pointer move" event
+ * Param:
+ *  mts_state - MTS state descriptor.
+ *  slot_index - Pointer's index in the MTS's array of tracked pointers.
+ *  x, y - New pointer coordinates,
+ *  pressure - Pressure value for the pointer.
+ */
+static void
+_mts_pointer_move(MTSState* mts_state, int slot_index, int x, int y, int pressure)
+{
+    MTSPointerState* ptr_state = &mts_state->tracked_pointers[slot_index];
+
+    /* Make sure that coordinates have really changed. */
+    if (ptr_state->x == x && ptr_state->y == y) {
+        /* Coordinates didn't change. Bail out. */
+        return;
+    }
+
+    /* Make sure that the right slot is selected. */
+    if (slot_index != mts_state->current_slot) {
+        _push_event(EV_ABS, ABS_MT_SLOT, slot_index);
+        mts_state->current_slot = slot_index;
+    }
+
+    /* Push the changes down. */
+    if (ptr_state->pressure != pressure && pressure != 0) {
+        _push_event(EV_ABS, ABS_MT_PRESSURE, pressure);
+        ptr_state->pressure = pressure;
+    }
+    if (ptr_state->x != x) {
+        _push_event(EV_ABS, ABS_MT_POSITION_X, x);
+        ptr_state->x = x;
+    }
+    if (ptr_state->y != y) {
+        _push_event(EV_ABS, ABS_MT_POSITION_Y, y);
+        ptr_state->y = y;
+    }
+    _push_event(EV_SYN, SYN_REPORT, 0);
+}
+
+/********************************************************************************
+ *                       Multi-touch API
+ *******************************************************************************/
+
+/* Callback that is invoked when framebuffer update has been transmitted to the
+ * device. */
+static void
+_on_fb_sent(void* opaque, ATResult res, void* data, int size, int sent)
+{
+    MTSState* const mts_state = (MTSState*)opaque;
+
+    /* Lets see if we have accumulated more changes while transmission has been
+     * in progress. */
+    if (mts_state->fb_header.w && mts_state->fb_header.h) {
+        /* Send accumulated updates. */
+        if (mts_port_send_frame(mts_state->mtsp, &mts_state->fb_header,
+                                mts_state->ds->surface->data, _on_fb_sent,
+                                mts_state)) {
+            mts_state->fb_transfer_in_progress = 0;
+        }
+    } else {
+        /* Framebuffer transfer is completed, and no more updates are pending. */
+        mts_state->fb_transfer_in_progress = 0;
+    }
+}
+
+/* A callback invoked on framebuffer updates.
+ * Param:
+ *  opaque - MTSState instance.
+ *  x, y, w, h - Defines an updated rectangle inside the framebuffer.
+ */
+static void
+_mt_fb_update(void* opaque, int x, int y, int w, int h)
+{
+    MTSState* const mts_state = (MTSState*)opaque;
+    const DisplaySurface* const surface = mts_state->ds->surface;
+
+    if (mts_state->fb_header.w == 0 && mts_state->fb_header.h == 0) {
+        /* First update after previous one has been transmitted to the device. */
+        mts_state->fb_header.x = x;
+        mts_state->fb_header.y = y;
+        mts_state->fb_header.w = w;
+        mts_state->fb_header.h = h;
+    } else {
+        /*
+         * Accumulate framebuffer changes in the header.
+         */
+
+        /* "right" and "bottom" coordinates of the current update. */
+        int right = mts_state->fb_header.x + mts_state->fb_header.w;
+        int bottom = mts_state->fb_header.y + mts_state->fb_header.h;
+
+        /* "right" and "bottom" coordinates of the new update. */
+        const int new_right = x + w;
+        const int new_bottom = y + h;
+
+        /* Accumulate changed rectangle coordinates in the header. */
+        if (mts_state->fb_header.x > x) {
+            mts_state->fb_header.x = x;
+        }
+        if (mts_state->fb_header.y > y) {
+            mts_state->fb_header.y = y;
+        }
+        if (right < new_right) {
+            right = new_right;
+        }
+        if (bottom < new_bottom) {
+            bottom = new_bottom;
+        }
+        mts_state->fb_header.w = right - mts_state->fb_header.x;
+        mts_state->fb_header.h = bottom - mts_state->fb_header.y;
+    }
+
+    /* TODO: Looks like general framebuffer properties can change on the fly.
+     * Find a callback that can catch that. For now, just copy FB properties
+     * over in every FB update. */
+    mts_state->fb_header.bpp = surface->pf.bytes_per_pixel;
+    mts_state->fb_header.bpl = surface->linesize;
+    mts_state->fb_header.disp_width = surface->width;
+    mts_state->fb_header.disp_height = surface->height;
+
+    /* We will send updates to the device only after previous transmission is
+     * completed. */
+    if (!mts_state->fb_transfer_in_progress) {
+        mts_state->fb_transfer_in_progress = 1;
+        if (mts_port_send_frame(mts_state->mtsp, &mts_state->fb_header,
+                                surface->data, _on_fb_sent, mts_state)) {
+            mts_state->fb_transfer_in_progress = 0;
+        }
+    }
+}
+
+void
+multitouch_init(AndroidMTSPort* mtsp)
+{
+    /* Multi-touch service initialization flag. */
+    static int _is_mt_initialized = 0;
+
+    if (!_is_mt_initialized) {
+        MTSState* const mts_state = &_MTSState;
+        DisplayState* const ds = get_displaystate();
+        DisplayUpdateListener* dul;
+        int index;
+
+        /*
+         * Initialize the descriptor.
+         */
+
+        memset(mts_state, 0, sizeof(MTSState));
+        mts_state->tracked_ptr_num = 0;
+        mts_state->current_slot = -1;
+        for (index = 0; index < MTS_POINTERS_NUM; index++) {
+            mts_state->tracked_pointers[index].tracking_id = MTS_POINTER_UP;
+        }
+        mts_state->device_width = android_hw->hw_lcd_width;
+        mts_state->device_height = android_hw->hw_lcd_height;
+        mts_state->mtsp = mtsp;
+        mts_state->fb_header.header_size = sizeof(MTFrameHeader);
+        mts_state->fb_transfer_in_progress = 0;
+
+        /*
+         * Set framebuffer update listener.
+         */
+
+        ANEW0(dul);
+        dul->opaque = &_MTSState;
+        dul->dpy_update = _mt_fb_update;
+
+        /* Initialize framebuffer information in the screen descriptor. */
+        mts_state->ds = ds;
+        mts_state->fb_header.disp_width = ds->surface->width;
+        mts_state->fb_header.disp_height = ds->surface->height;
+        mts_state->fb_header.x = mts_state->fb_header.y = 0;
+        mts_state->fb_header.w = mts_state->fb_header.h = 0;
+        mts_state->fb_header.bpp = ds->surface->pf.bytes_per_pixel;
+        mts_state->fb_header.bpl = ds->surface->linesize;
+        mts_state->fb_transfer_in_progress = 0;
+
+        register_displayupdatelistener(ds, dul);
+
+        _is_mt_initialized = 1;
+    }
+}
+
+void
+multitouch_update_pointer(MTESource source,
+                          int tracking_id,
+                          int x,
+                          int y,
+                          int pressure)
+{
+    MTSState* const mts_state = &_MTSState;
+
+    /* Assign a fixed tracking ID to the mouse pointer. */
+    if (source == MTES_MOUSE) {
+        tracking_id = MTS_POINTER_MOUSE;
+    }
+
+    /* Find the tracked pointer for the tracking ID. */
+    const int slot_index = _mtsstate_get_pointer_index(mts_state, tracking_id);
+    if (slot_index < 0) {
+        /* This is the first time the pointer is seen. Must be "pressed",
+         * otherwise it's "hoovering", which we don't support yet. */
+        if (pressure == 0) {
+            if (tracking_id != MTS_POINTER_MOUSE) {
+                D("Unexpected MTS pointer update for tracking id: %d",
+                   tracking_id);
+            }
+            return;
+        }
+
+        /* This is a "pointer down" event */
+        _mts_pointer_down(mts_state, tracking_id, x, y, pressure);
+    } else if (pressure == 0) {
+        /* This is a "pointer up" event */
+        _mts_pointer_up(mts_state, slot_index);
+    } else {
+        /* This is a "pointer move" event */
+        _mts_pointer_move(mts_state, slot_index, x, y, pressure);
+    }
+}
+
+int
+multitouch_get_max_slot()
+{
+    return MTS_POINTERS_NUM - 1;
+}
+
+void
+multitouch_set_device_screen_size(int width, int height)
+{
+    MTSState* const mts_state = &_MTSState;
+
+    mts_state->device_width = width;
+    mts_state->device_height = height;
+}
diff --git a/android/multitouch-screen.h b/android/multitouch-screen.h
new file mode 100644
index 0000000..9288073
--- /dev/null
+++ b/android/multitouch-screen.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MULTITOUCH_SCREEN_H_
+#define ANDROID_MULTITOUCH_SCREEN_H_
+
+#include "android/multitouch-port.h"
+
+/*
+ * Encapsulates functionality of multi-touch screen. Main task of this component
+ * is to report touch events to the emulated system via event device (see
+ * hw/goldfish_events_device.c) The source of touch events can be a mouse, or an
+ * actual android device that is used for multi-touch emulation. Note that since
+ * we need to simultaneousely support a mouse and a device as event source, we
+ * need to know which one has sent us a touch event. This is important for proper
+ * tracking of pointer IDs when multitouch is in play.
+ */
+
+/* Defines a source of multi-touch event. This is used to properly track
+ * pointer IDs.
+ */
+typedef enum MTESource {
+    /* The event is associated with a mouse. */
+    MTES_MOUSE,
+    /* The event is associated with an actual android device. */
+    MTES_DEVICE,
+} MTESource;
+
+/* Initializes MTSState instance.
+ * Param:
+ *  mtsp - Instance of the multi-touch port connected to the device.
+ */
+extern void multitouch_init(AndroidMTSPort* mtsp);
+
+/* Handles a MT pointer event.
+ * Param:
+ *  source - Identifies the source of the event (mouse or a device).
+ *  tracking_id - Tracking ID of the pointer.
+ *  x, y - Pointer coordinates,
+ *  pressure - Pressure value for the pointer.
+ */
+extern void multitouch_update_pointer(MTESource source,
+                                      int tracking_id,
+                                      int x,
+                                      int y,
+                                      int pressure);
+
+/* Gets maximum slot index available for the multi-touch emulation. */
+extern int multitouch_get_max_slot();
+
+/* Saves screen size reported by the device that emulates multi-touch. */
+extern void multitouch_set_device_screen_size(int width, int height);
+
+#endif  /* ANDROID_MULTITOUCH_SCREEN_H_ */
+
diff --git a/android/qemulator.c b/android/qemulator.c
index 35587ff..cd97510 100644
--- a/android/qemulator.c
+++ b/android/qemulator.c
@@ -81,7 +81,8 @@
 
         qemulator_set_title(emulator);
 
-        skin_window_enable_touch ( emulator->window, android_hw->hw_touchScreen != 0 );
+        skin_window_enable_touch ( emulator->window, android_hw->hw_touchScreen != 0 ||
+                                                     android_hw->hw_multiTouch != 0);
         skin_window_enable_dpad  ( emulator->window, android_hw->hw_dPad != 0 );
         skin_window_enable_qwerty( emulator->window, android_hw->hw_keyboard != 0 );
         skin_window_enable_trackball( emulator->window, android_hw->hw_trackBall != 0 );
diff --git a/android/utils/debug.h b/android/utils/debug.h
index ec3094d..e97ec43 100644
--- a/android/utils/debug.h
+++ b/android/utils/debug.h
@@ -38,6 +38,8 @@
     _VERBOSE_TAG(camera,       "camera") \
     _VERBOSE_TAG(adevice,      "android device connected via port forwarding") \
     _VERBOSE_TAG(sensors_port, "sensors emulator connected to android device") \
+    _VERBOSE_TAG(mtport,       "multi-touch emulator connected to android device") \
+    _VERBOSE_TAG(mtscreen,     "multi-touch screen emulation") \
     _VERBOSE_TAG(gles,         "hardware OpenGLES emulation") \
     _VERBOSE_TAG(adbserver,    "ADB server") \
     _VERBOSE_TAG(adbclient,    "ADB QEMU client") \
diff --git a/android/utils/jpeg-compress.c b/android/utils/jpeg-compress.c
new file mode 100644
index 0000000..2b42a00
--- /dev/null
+++ b/android/utils/jpeg-compress.c
@@ -0,0 +1,197 @@
+/* Copyright (C) 2011 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+#include <stdint.h>
+#include "jinclude.h"
+#include "jpeglib.h"
+#include "jpeg-compress.h"
+#include "panic.h"
+
+/* Implements JPEG destination manager's init_destination routine. */
+static void _on_init_destination(j_compress_ptr cinfo);
+/* Implements JPEG destination manager's empty_output_buffer routine. */
+static boolean _on_empty_output_buffer(j_compress_ptr cinfo);
+/* Implements JPEG destination manager's term_destination routine. */
+static void _on_term_destination(j_compress_ptr cinfo);
+
+/* JPEG compression descriptor. */
+struct AJPEGDesc {
+    /* Common JPEG compression destination manager header. */
+    struct jpeg_destination_mgr     common;
+    /* Buffer where to save compressed output. */
+    uint8_t*                        jpeg_buf;
+    /* Byte size of the 'jpeg_buf' */
+    int                             size;
+    /* Chunk size to increment the 'jpeg_buf' with on each allocation request. */
+    int                             chunk_size;
+    /* Size of the header to put in front of the compressed data. */
+    int                             header_size;
+};
+
+/********************************************************************************
+ *                      jpeglib callbacks.
+ *******************************************************************************/
+
+/* Implements JPEG destination manager's init_destination routine. */
+static void
+_on_init_destination(j_compress_ptr cinfo)
+{
+    AJPEGDesc* const dst = (AJPEGDesc*)cinfo->dest;
+    if (dst->jpeg_buf == NULL) {
+        /* This is the first time our destination manager is initialized.
+         * Allocate minimal buffer. */
+        dst->size = dst->chunk_size;
+        dst->jpeg_buf = malloc(dst->size);
+        if (dst->jpeg_buf == NULL) {
+            APANIC("Unable to allocate %d bytes for JPEG compression", dst->size);
+        }
+    }
+    /* Initialize common header with entire destination buffer. */
+    dst->common.next_output_byte = dst->jpeg_buf + dst->header_size;
+    dst->common.free_in_buffer = dst->size - dst->header_size;
+}
+
+/* Implements JPEG destination manager's empty_output_buffer routine.
+ * Name is a bit misleading here. This routine is called by the compressor when
+ * output buffer doesn't have enough free space to contain the next chunk of the
+ * compressed data. So, here we should reallocate the output buffer, rather than
+ * "empty" it.
+ */
+static boolean
+_on_empty_output_buffer(j_compress_ptr cinfo)
+{
+    AJPEGDesc* const dst = (AJPEGDesc*)cinfo->dest;
+    /* Save already compressed data size. */
+    const int accumulated = jpeg_compressor_get_jpeg_size(dst);
+
+    /* Reallocate output buffer. */
+    dst->size += dst->chunk_size;
+    dst->jpeg_buf = realloc(dst->jpeg_buf, dst->size);
+    if (dst->jpeg_buf == NULL) {
+        APANIC("Unable to allocate %d bytes for JPEG compression", dst->size);
+    }
+
+    /* Update common header. */
+    dst->common.next_output_byte = dst->jpeg_buf + accumulated + dst->header_size;
+    dst->common.free_in_buffer = dst->size - accumulated - dst->header_size;
+
+    return TRUE;
+}
+
+/* Implements JPEG destination manager's term_destination routine.
+ * We don't do anything here. All the cleanup will be performed when the user
+ * calls jpeg_compressor_destroy. */
+static void
+_on_term_destination(j_compress_ptr cinfo)
+{
+}
+
+/********************************************************************************
+ *                      JPEG compressor API.
+ *******************************************************************************/
+
+AJPEGDesc*
+jpeg_compressor_create(int header_size, int chunk_size)
+{
+    AJPEGDesc* dsc = (AJPEGDesc*)malloc(sizeof(AJPEGDesc));
+    if (dsc == NULL) {
+        APANIC("Unable to allocate JPEG compression descriptor.");
+    }
+
+    dsc->common.next_output_byte    = NULL;
+    dsc->common.free_in_buffer      = 0;
+    dsc->common.init_destination    = _on_init_destination;
+    dsc->common.empty_output_buffer = _on_empty_output_buffer;
+    dsc->common.term_destination    = _on_term_destination;
+    dsc->jpeg_buf                   = NULL;
+    dsc->size                       = 0;
+    dsc->chunk_size                 = chunk_size;
+    dsc->header_size                = header_size;
+    return dsc;
+}
+
+void
+jpeg_compressor_destroy(AJPEGDesc* dsc)
+{
+    if (dsc != NULL) {
+        if (dsc->jpeg_buf != NULL) {
+            free(dsc->jpeg_buf);
+        }
+        free(dsc);
+    }
+}
+
+int
+jpeg_compressor_get_jpeg_size(const AJPEGDesc* dsc)
+{
+    return (dsc->jpeg_buf == NULL) ? 0 :
+        (uint8_t*)dsc->common.next_output_byte - dsc->jpeg_buf - dsc->header_size;
+}
+
+void*
+jpeg_compressor_get_buffer(const AJPEGDesc* dsc)
+{
+     return dsc->jpeg_buf;
+}
+
+int
+jpeg_compressor_get_header_size(const AJPEGDesc* dsc)
+{
+     return dsc->header_size;
+}
+
+void
+jpeg_compressor_compress_fb(AJPEGDesc* dsc,
+                            int x, int y, int w, int h,
+                            int bpp, int bpl,
+                            const uint8_t* fb,
+                            int jpeg_quality){
+    struct jpeg_compress_struct cinfo = {0};
+    struct jpeg_error_mgr err_mgr;
+
+    /*
+     * Initialize compressin information structure, and start compression
+     */
+
+    cinfo.err = jpeg_std_error(&err_mgr);
+    jpeg_create_compress(&cinfo);
+    cinfo.dest = &dsc->common;
+    cinfo.image_width = w;
+    cinfo.image_height = h;
+
+    /* Decode framebuffer's pixel format. There can be only three:
+     * - RGB565,
+     * - RGBA8888,
+     * - RGBX8888 */
+    if (bpp == 2) {
+        /* This is RGB565 - most commonly used pixel format for framebuffer. */
+        cinfo.input_components = 2;
+        cinfo.in_color_space = JCS_RGB_565;
+    } else {
+        /* RGBA8888, or RGBX8888 - makes no difference here. */
+        cinfo.input_components = 4;
+        cinfo.in_color_space = JCS_RGBA_8888;
+    }
+    jpeg_set_defaults(&cinfo);
+    jpeg_set_quality(&cinfo, jpeg_quality, TRUE);
+    jpeg_start_compress(&cinfo, TRUE);
+
+    /* Line by line compress the region. */
+    while (cinfo.next_scanline < cinfo.image_height) {
+        JSAMPROW rgb = (JSAMPROW)(fb + (cinfo.next_scanline + y) * bpl + x * bpp);
+        jpeg_write_scanlines(&cinfo, (JSAMPARRAY)&rgb, 1);
+    }
+
+    /* Complete the compression. */
+    jpeg_finish_compress(&cinfo);
+    jpeg_destroy_compress(&cinfo);
+}
diff --git a/android/utils/jpeg-compress.h b/android/utils/jpeg-compress.h
new file mode 100644
index 0000000..4e0e61a
--- /dev/null
+++ b/android/utils/jpeg-compress.h
@@ -0,0 +1,93 @@
+/* Copyright (C) 2011 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_UTILS_JPEG_COMPRESS_H
+#define _ANDROID_UTILS_JPEG_COMPRESS_H
+
+/*
+ * Contains declaration of utility routines that compress an RGB bitmap into
+ * a JPEG image.
+ *
+ * NOTE: This code uses a jpeglib library located in distrib/jpeg-6b. It's a
+ * 3-rd party library that uses its own type definitions that are different from
+ * the ones that are use elsewhere in the emulator code. For instance, in the
+ * emulator built for Windows, sizeof(bool) = 1, while in the jpeglib sizeof(bool) = 4.
+ * So, to simplify dealing with these issues, all the code that uses jpeglib should
+ * be compiled separately, and should include only headers that are used to compile
+ * jpeglib.
+ */
+
+
+/* Declares descriptor for a JPEG compression. */
+typedef struct AJPEGDesc AJPEGDesc;
+
+/* Creates a descriptor that will be used for compression.
+ * Param:
+ *  header_size - Number of bytes to allocate for a custom header that should
+ *      preceed the actual JPEG buffer. This is useful when sending JPEG
+ *      somewhere else along with some extra data about the compressed image.
+ *  cunk_size - Number of bytes to increment the compressed buffer with each time
+ *      compressor requests more memory.
+ * Return:
+ *  Initialized compression descriptor.
+ */
+extern AJPEGDesc* jpeg_compressor_create(int header_size, int chunk_size);
+
+/* Destroys compressor descriptor.
+ * Param:
+ *  dsc - Compressin descriptor, obtained with jpeg_compressor_create.
+ */
+extern void jpeg_compressor_destroy(AJPEGDesc* dsc);
+
+/* Returns compressed data size.
+ * Param:
+ *  dsc - Compression descriptor, obtained with jpeg_compressor_create.
+ * Return:
+ *  Compressed data size.
+ */
+extern int jpeg_compressor_get_jpeg_size(const AJPEGDesc* dsc);
+
+/* Returns compressed buffer.
+ * Param:
+ *  dsc - Compression descriptor, obtained with jpeg_compressor_create.
+ * Return:
+ *  Compressed buffer. NOTE: if 'header_size' parameter passed to the jpeg_compressor_create
+ *  for this descriptor was not zero, this routine returns a pointer to the custom
+ *  header. Compressed data follows immediately after that header.
+ */
+extern void* jpeg_compressor_get_buffer(const AJPEGDesc* dsc);
+
+/* Returns size of the custom header placed before the compressed data.
+ * Param:
+ *  dsc - Compression descriptor, obtained with jpeg_compressor_create.
+ * Return:
+ *  Size of the custom header placed before the compressed data.
+ */
+extern int jpeg_compressor_get_header_size(const AJPEGDesc* dsc);
+
+/* Compresses a framebuffer region into JPEG image.
+ * Param:
+ *  dsc - Compression descriptor, obtained with jpeg_compressor_create.
+ *  x, y, w, h - Coordinates and sizes of framebuffer region to compress.
+ *  bpp - Number of bytes per pixel in the framebuffer.
+ *  bpl - Number of bytes per line in the framebuffer.
+ *  fb - Beginning of the framebuffer.
+ *  jpeg_quality JPEG compression quality. A number from 1 to 100. Note that
+ *      value 10 provides pretty decent image for the purpose of multi-touch
+ *      emulation.
+ */
+extern void jpeg_compressor_compress_fb(AJPEGDesc* dsc,
+                                        int x, int y, int w, int h,
+                                        int bpp, int bpl,
+                                        const uint8_t* fb,
+                                        int jpeg_quality);
+
+#endif  /* _ANDROID_UTILS_JPEG_COMPRESS_H */
diff --git a/hw/goldfish_events_device.c b/hw/goldfish_events_device.c
index 3c33b46..83e9fdf 100644
--- a/hw/goldfish_events_device.c
+++ b/hw/goldfish_events_device.c
@@ -13,6 +13,7 @@
 #include "android/hw-events.h"
 #include "android/charmap.h"
 #include "android/globals.h"  /* for android_hw */
+#include "android/multitouch-screen.h"
 #include "irq.h"
 #include "user-events.h"
 #include "console.h"
@@ -68,6 +69,18 @@
     size_t abs_info_count;
 } events_state;
 
+/* An entry in the array of ABS_XXX values */
+typedef struct ABSEntry {
+    /* Minimum ABS_XXX value. */
+    uint32_t    min;
+    /* Maximum ABS_XXX value. */
+    uint32_t    max;
+    /* 'fuzz;, and 'flat' ABS_XXX values are always zero here. */
+    uint32_t    fuzz;
+    uint32_t    flat;
+} ABSEntry;
+
+
 /* modify this each time you change the events_device structure. you
  * will also need to upadte events_state_load and events_state_save
  */
@@ -258,20 +271,27 @@
      * in android/skin/trackball.c and android/skin/window.c
      */
     if (dz == 0) {
-        enqueue_event(s, EV_ABS, ABS_X, dx);
-        enqueue_event(s, EV_ABS, ABS_Y, dy);
-        enqueue_event(s, EV_ABS, ABS_Z, dz);
-        enqueue_event(s, EV_KEY, BTN_TOUCH, buttons_state&1);
+        if (android_hw->hw_multiTouch) {
+            /* Convert mouse event into multi-touch event */
+            multitouch_update_pointer(MTES_MOUSE, 0, dx, dy,
+                                      (buttons_state & 1) ? 0x81 : 0);
+        } else if (android_hw->hw_touchScreen) {
+            enqueue_event(s, EV_ABS, ABS_X, dx);
+            enqueue_event(s, EV_ABS, ABS_Y, dy);
+            enqueue_event(s, EV_ABS, ABS_Z, dz);
+            enqueue_event(s, EV_KEY, BTN_TOUCH, buttons_state&1);
+            enqueue_event(s, EV_SYN, 0, 0);
+        }
     } else {
         enqueue_event(s, EV_REL, REL_X, dx);
         enqueue_event(s, EV_REL, REL_Y, dy);
+        enqueue_event(s, EV_SYN, 0, 0);
     }
-    enqueue_event(s, EV_SYN, 0, 0);
 }
 
 static void  events_put_generic(void*  opaque, int  type, int  code, int  value)
 {
-   events_state *s = (events_state *) opaque;
+    events_state *s = (events_state *) opaque;
 
     enqueue_event(s, type, code, value);
 }
@@ -431,13 +451,13 @@
      *
      * EV_ABS events are sent when the touchscreen is pressed
      */
-    if (config->hw_touchScreen) {
-        int32_t*  values;
+    if (config->hw_touchScreen || config->hw_multiTouch) {
+        ABSEntry* abs_values;
 
         events_set_bit (s, EV_SYN, EV_ABS );
         events_set_bits(s, EV_ABS, ABS_X, ABS_Z);
         /* Allocate the absinfo to report the min/max bounds for each
-         * absolute dimension. The array must contain 3 tuples
+         * absolute dimension. The array must contain 3, or ABS_MAX tuples
          * of (min,max,fuzz,flat) 32-bit values.
          *
          * min and max are the bounds
@@ -448,28 +468,34 @@
          * There is no need to save/restore this array in a snapshot
          * since the values only depend on the hardware configuration.
          */
-        s->abs_info_count = 3*4;
-        s->abs_info = values = malloc(sizeof(uint32_t)*s->abs_info_count);
+        s->abs_info_count = config->hw_multiTouch ? ABS_MAX * 4 : 3 * 4;
+        const int abs_size = sizeof(uint32_t) * s->abs_info_count;
+        s->abs_info = malloc(abs_size);
+        memset(s->abs_info, 0, abs_size);
+        abs_values = (ABSEntry*)s->abs_info;
 
-        /* ABS_X min/max/fuzz/flat */
-        values[0] = 0;
-        values[1] = config->hw_lcd_width-1;
-        values[2] = 0;
-        values[3] = 0;
-        values   += 4;
+        abs_values[ABS_X].max = config->hw_lcd_width-1;
+        abs_values[ABS_Y].max = config->hw_lcd_height-1;
+        abs_values[ABS_Z].max = 1;
 
-        /* ABS_Y */
-        values[0] = 0;
-        values[1] = config->hw_lcd_height-1;
-        values[2] = 0;
-        values[3] = 0;
-        values   += 4;
+        if (config->hw_multiTouch) {
+            /*
+             * Setup multitouch.
+             */
+            events_set_bit(s, EV_ABS, ABS_MT_SLOT);
+            events_set_bit(s, EV_ABS, ABS_MT_POSITION_X);
+            events_set_bit(s, EV_ABS, ABS_MT_POSITION_Y);
+            events_set_bit(s, EV_ABS, ABS_MT_TRACKING_ID);
+            events_set_bit(s, EV_ABS, ABS_MT_TOUCH_MAJOR);
+            events_set_bit(s, EV_ABS, ABS_MT_PRESSURE);
 
-        /* ABS_Z */
-        values[0] = 0;
-        values[1] = 1;
-        values[2] = 0;
-        values[3] = 0;
+            abs_values[ABS_MT_SLOT].max = multitouch_get_max_slot();
+            abs_values[ABS_MT_TRACKING_ID].max = abs_values[ABS_MT_SLOT].max + 1;
+            abs_values[ABS_MT_POSITION_X].max = abs_values[ABS_X].max;
+            abs_values[ABS_MT_POSITION_Y].max = abs_values[ABS_Y].max;
+            abs_values[ABS_MT_TOUCH_MAJOR].max = 0x7fffffff; // TODO: Make it less random
+            abs_values[ABS_MT_PRESSURE].max = 0x100; // TODO: Make it less random
+        }
     }
 
     /* configure EV_SW array
diff --git a/linux_keycodes.h b/linux_keycodes.h
index 3875028..195797e 100644
--- a/linux_keycodes.h
+++ b/linux_keycodes.h
@@ -449,4 +449,60 @@
 #define KEY_MIN_INTERESTING	KEY_MUTE
 #define KEY_MAX			0x1ff
 
+#ifndef ABS_MT_SLOT
+#define ABS_MT_SLOT             0x2f    /* MT slot being modified */
+#endif
+#ifndef ABS_MT_TOUCH_MAJOR
+#define ABS_MT_TOUCH_MAJOR      0x30    /* Major axis of touching ellipse */
+#endif
+#ifndef ABS_MT_TOUCH_MINOR
+#define ABS_MT_TOUCH_MINOR      0x31    /* Minor axis (omit if circular) */
+#endif
+#ifndef ABS_MT_WIDTH_MAJOR
+#define ABS_MT_WIDTH_MAJOR      0x32    /* Major axis of approaching ellipse */
+#endif
+#ifndef ABS_MT_WIDTH_MINOR
+#define ABS_MT_WIDTH_MINOR      0x33    /* Minor axis (omit if circular) */
+#endif
+#ifndef ABS_MT_ORIENTATION
+#define ABS_MT_ORIENTATION      0x34    /* Ellipse orientation */
+#endif
+#ifndef ABS_MT_POSITION_X
+#define ABS_MT_POSITION_X       0x35    /* Center X ellipse position */
+#endif
+#ifndef ABS_MT_POSITION_Y
+#define ABS_MT_POSITION_Y       0x36    /* Center Y ellipse position */
+#endif
+#ifndef ABS_MT_TOOL_TYPE
+#define ABS_MT_TOOL_TYPE        0x37    /* Type of touching device */
+#endif
+#ifndef ABS_MT_BLOB_ID
+#define ABS_MT_BLOB_ID          0x38    /* Group a set of packets as a blob */
+#endif
+#ifndef ABS_MT_TRACKING_ID
+#define ABS_MT_TRACKING_ID      0x39    /* Unique ID of initiated contact */
+#endif
+#ifndef ABS_MT_PRESSURE
+#define ABS_MT_PRESSURE         0x3a    /* Pressure on contact area */
+#endif
+#ifndef ABS_MT_DISTANCE
+#define ABS_MT_DISTANCE         0x3b    /* Contact hover distance */
+#endif
+#ifndef ABS_MAX
+#define ABS_MAX                 0x3f
+#endif
+
+#ifndef SYN_REPORT
+#define SYN_REPORT              0
+#endif
+#ifndef SYN_CONFIG
+#define SYN_CONFIG              1
+#endif
+#ifndef SYN_MT_REPORT
+#define SYN_MT_REPORT           2
+#endif
+#ifndef SYN_DROPPED
+#define SYN_DROPPED             3
+#endif
+
 #endif
diff --git a/vl-android.c b/vl-android.c
index 46f869a..171d763 100644
--- a/vl-android.c
+++ b/vl-android.c
@@ -55,6 +55,7 @@
 #include "android/hw-pipe-net.h"
 #include "android/hw-qemud.h"
 #include "android/camera/camera-service.h"
+#include "android/multitouch-port.h"
 #include "android/charmap.h"
 #include "android/globals.h"
 #include "android/utils/bufprint.h"
@@ -4258,6 +4259,11 @@
                       initrd_filename,
                       cpu_model);
 
+        /* Initialize multi-touch emulation. */
+        if (android_hw->hw_multiTouch) {
+            mts_port_create(NULL);
+        }
+
         stralloc_reset(kernel_params);
         stralloc_reset(kernel_config);
     }