Merge "Add fast mmio support"
diff --git a/Makefile.common b/Makefile.common
index 77fd367..f9d98af 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -479,6 +479,7 @@
     android/android-device.c \
     android/async-socket-connector.c \
     android/async-socket.c \
+    android/sdk-controller-socket.c \
     android/sensors-port.c \
     android/utils/timezone.c \
     android/camera/camera-format-converters.c \
diff --git a/android/async-io-common.h b/android/async-io-common.h
index 02714a7..02b2c96 100644
--- a/android/async-io-common.h
+++ b/android/async-io-common.h
@@ -44,6 +44,12 @@
     /* Asynchronous I/O has been cancelled (due to disconnect, for
      * instance).                                       (7) */
     ASIO_STATE_CANCELLED,
+    /* Asynchronous I/O is finished and is about to be discarder. This state is
+     * useful in case there is an association between an I/O and some client's
+     * component, that holds a reference associated with this I/O. When callback
+     * is invoked with this state, it means that it's safe to drop that extra
+     * reference associated with the I/O                (8) */
+    ASIO_STATE_FINISHED,
 } AsyncIOState;
 
 /* Enumerates actions to perform with an I/O on state transition.
diff --git a/android/async-socket-connector.c b/android/async-socket-connector.c
index 80ec5bb..836016c 100644
--- a/android/async-socket-connector.c
+++ b/android/async-socket-connector.c
@@ -32,6 +32,14 @@
 #define  D(...)    VERBOSE_PRINT(asconnector,__VA_ARGS__)
 #define  D_ACTIVE  VERBOSE_CHECK(asconnector)
 
+#define TRACE_ON    0
+
+#if TRACE_ON
+#define  T(...)    VERBOSE_PRINT(asconnector,__VA_ARGS__)
+#else
+#define  T(...)
+#endif
+
 /********************************************************************************
  *                             Internals
  *******************************************************************************/
@@ -57,6 +65,8 @@
     int             fd;
     /* Number of outstanding references to the connector. */
     int             ref_count;
+    /* Flags whether (1) or not (0) connector owns the looper. */
+    int             owns_looper;
 };
 
 /* Asynchronous I/O looper callback invoked by the connector.
@@ -81,23 +91,30 @@
 _async_socket_connector_free(AsyncSocketConnector* connector)
 {
     if (connector != NULL) {
-        D("%s: Connector is destroyed.", _asc_socket_string(connector));
+        T("ASC %s: Connector is destroying...", _asc_socket_string(connector));
+
+        /* Stop all activities. */
         if (asyncConnector_stop(connector->connector) == 0) {
             /* Connection was in progress. We need to destroy I/O descriptor for
              * that connection. */
-            D("%s: Stopped async connection in progress.",
+            D("ASC %s: Stopped async connection in progress.",
               _asc_socket_string(connector));
             loopIo_done(connector->connector_io);
         }
+
+        /* Free allocated resources. */
+        if (connector->looper != NULL) {
+            loopTimer_done(connector->connector_timer);
+            if (connector->owns_looper) {
+                looper_free(connector->looper);
+            }
+        }
+
         if (connector->fd >= 0) {
             socket_close(connector->fd);
         }
 
-        if (connector->looper != NULL) {
-            loopTimer_stop(connector->connector_timer);
-            loopTimer_done(connector->connector_timer);
-            looper_free(connector->looper);
-        }
+        T("ASC %s: Connector is destroyed", _asc_socket_string(connector));
 
         sock_address_done(&connector->address);
 
@@ -117,7 +134,7 @@
     /* Open socket. */
     connector->fd = socket_create_inet(SOCKET_STREAM);
     if (connector->fd < 0) {
-        D("%s: Unable to create socket: %d -> %s",
+        D("ASC %s: Unable to create socket: %d -> %s",
           _asc_socket_string(connector), errno, strerror(errno));
         return -1;
     }
@@ -125,6 +142,9 @@
     /* Prepare for async I/O on the connector. */
     socket_set_nonblock(connector->fd);
 
+    T("ASC %s: Connector socket is opened with FD = %d",
+      _asc_socket_string(connector), connector->fd);
+
     return 0;
 }
 
@@ -139,6 +159,8 @@
 {
     if (connector->fd >= 0) {
         socket_close(connector->fd);
+        T("ASC %s: Connector socket FD = %d is closed.",
+          _asc_socket_string(connector), connector->fd);
         connector->fd = -1;
     }
 }
@@ -177,15 +199,18 @@
             break;
 
         case ASYNC_NEED_MORE:
+            T("ASC %s: Waiting on connection to complete. Connector FD = %d",
+              _asc_socket_string(connector), connector->fd);
             return;
     }
 
     if (action == ASIO_ACTION_RETRY) {
-        D("%s: Retrying connection", _asc_socket_string(connector));
+        D("ASC %s: Retrying connection. Connector FD = %d",
+          _asc_socket_string(connector), connector->fd);
         loopTimer_startRelative(connector->connector_timer, connector->retry_to);
     } else if (action == ASIO_ACTION_ABORT) {
-        D("%s: AsyncSocketConnector client for socket '%s' has aborted connection",
-          __FUNCTION__, _asc_socket_string(connector));
+        D("ASC %s: Client has aborted connection. Connector FD = %d",
+          _asc_socket_string(connector), connector->fd);
     }
 }
 
@@ -206,8 +231,8 @@
         const AsyncStatus status = asyncConnector_run(connector->connector);
         _on_async_socket_connector_connecting(connector, status);
     } else {
-        D("%s: AsyncSocketConnector client for socket '%s' has aborted connection",
-          __FUNCTION__, _asc_socket_string(connector));
+        D("ASC %s: Client has aborted connection. Connector FD = %d",
+          _asc_socket_string(connector), connector->fd);
     }
 
     /* Release the connector after we're done with handing I/O. */
@@ -224,6 +249,9 @@
     AsyncStatus status;
     AsyncSocketConnector* const connector = (AsyncSocketConnector*)opaque;
 
+    T("ASC %s: Reconnect timer expired. Connector FD = %d",
+              _asc_socket_string(connector), connector->fd);
+
     /* Reference the connector while we're in callback. */
     async_socket_connector_reference(connector);
 
@@ -249,8 +277,8 @@
 
         _on_async_socket_connector_connecting(connector, status);
     } else {
-        D("%s: AsyncSocketConnector client for socket '%s' has aborted connection",
-          __FUNCTION__, _asc_socket_string(connector));
+        D("ASC %s: Client has aborted connection. Connector FD = %d",
+          _asc_socket_string(connector), connector->fd);
     }
 
     /* Release the connector after we're done with the callback. */
@@ -265,7 +293,8 @@
 async_socket_connector_new(const SockAddress* address,
                            int retry_to,
                            asc_event_cb cb,
-                           void* cb_opaque)
+                           void* cb_opaque,
+                           Looper* looper)
 {
     AsyncSocketConnector* connector;
 
@@ -292,19 +321,27 @@
     }
 
     /* Create a looper for asynchronous I/O. */
-    connector->looper = looper_newCore();
-    if (connector->looper == NULL) {
-        E("Unable to create I/O looper for AsyncSocketConnector for socket '%s'",
-          _asc_socket_string(connector));
-        cb(cb_opaque, connector, ASIO_STATE_FAILED);
-        _async_socket_connector_free(connector);
-        return NULL;
+    if (looper == NULL) {
+        connector->looper = looper_newCore();
+        if (connector->looper == NULL) {
+            E("Unable to create I/O looper for AsyncSocketConnector for socket '%s'",
+              _asc_socket_string(connector));
+            cb(cb_opaque, connector, ASIO_STATE_FAILED);
+            _async_socket_connector_free(connector);
+            return NULL;
+        }
+        connector->owns_looper = 1;
+    } else {
+        connector->looper = looper;
+        connector->owns_looper = 0;
     }
 
     /* Create a timer that will be used for connection retries. */
     loopTimer_init(connector->connector_timer, connector->looper,
                    _on_async_socket_connector_retry, connector);
 
+    T("ASC %s: New connector object", _asc_socket_string(connector));
+
     return connector;
 }
 
@@ -334,13 +371,16 @@
 {
     AsyncStatus status;
 
+    T("ASC %s: Handling connect request. Connector FD = %d",
+      _asc_socket_string(connector), connector->fd);
+
     if (_async_socket_connector_open_socket(connector) == 0) {
         const AsyncIOAction action =
             connector->on_connected_cb(connector->on_connected_cb_opaque,
                                        connector, ASIO_STATE_STARTED);
         if (action == ASIO_ACTION_ABORT) {
-            D("%s: AsyncSocketConnector client for socket '%s' has aborted connection",
-              __FUNCTION__, _asc_socket_string(connector));
+            D("ASC %s: Client has aborted connection. Connector FD = %d",
+              _asc_socket_string(connector), connector->fd);
             return;
         } else {
             loopIo_init(connector->connector_io, connector->looper,
@@ -363,5 +403,8 @@
     if (fd >= 0) {
         connector->fd = -1;
     }
+
+    T("ASC %s: Client has pulled connector FD %d", _asc_socket_string(connector), fd);
+
     return fd;
 }
diff --git a/android/async-socket-connector.h b/android/async-socket-connector.h
index 0769f56..bedc2df 100644
--- a/android/async-socket-connector.h
+++ b/android/async-socket-connector.h
@@ -89,6 +89,8 @@
  *  retry_to - Timeout to retry a failed connection attempt in milliseconds.
  *  cb, cb_opaque - Callback to invoke on connection events. This callback is
  *      required, and must not be NULL.
+ *  looper - An optional (can be NULL) I/O looper to use for connection I/O. If
+ *      this parameter is NULL, the connector will create its own looper.
  * Return:
  *  Initialized AsyncSocketConnector instance. Note that AsyncSocketConnector
  *  instance returned from this routine will be destroyed by the connector itself,
@@ -99,7 +101,8 @@
 extern AsyncSocketConnector* async_socket_connector_new(const SockAddress* address,
                                                         int retry_to,
                                                         asc_event_cb cb,
-                                                        void* cb_opaque);
+                                                        void* cb_opaque,
+                                                        Looper* looper);
 
 /* References AsyncSocketConnector object.
  * Param:
diff --git a/android/async-socket.c b/android/async-socket.c
index a2577c9..5e2ae29 100644
--- a/android/async-socket.c
+++ b/android/async-socket.c
@@ -33,6 +33,14 @@
 #define  D(...)    VERBOSE_PRINT(asyncsocket,__VA_ARGS__)
 #define  D_ACTIVE  VERBOSE_CHECK(asyncsocket)
 
+#define TRACE_ON    0
+
+#if TRACE_ON
+#define  T(...)    VERBOSE_PRINT(asyncsocket,__VA_ARGS__)
+#else
+#define  T(...)
+#endif
+
 /********************************************************************************
  *                  Asynchronous Socket internal API declarations
  *******************************************************************************/
@@ -153,6 +161,9 @@
     /* Reference socket that is holding this I/O. */
     async_socket_reference(as);
 
+    T("ASocket %s: %s I/O descriptor %p is created for %d bytes of data",
+      _async_socket_string(as), is_io_read ? "READ" : "WRITE", asio, len);
+
     return asio;
 }
 
@@ -162,7 +173,9 @@
 {
     AsyncSocket* const as = asio->as;
 
-    loopTimer_stop(asio->timer);
+    T("ASocket %s: %s I/O descriptor %p is destroyed.",
+      _async_socket_string(as), asio->is_io_read ? "READ" : "WRITE", asio);
+
     loopTimer_done(asio->timer);
 
     /* Try to recycle it first, and free the memory if recycler is full. */
@@ -178,6 +191,14 @@
     async_socket_release(as);
 }
 
+/* An I/O has been finished and its descriptor is about to be discarded. */
+static void
+_async_socket_io_finished(AsyncSocketIO* asio)
+{
+    /* Notify the client of the I/O that I/O is finished. */
+    asio->on_io(asio->io_opaque, asio, ASIO_STATE_FINISHED);
+}
+
 int
 async_socket_io_reference(AsyncSocketIO* asio)
 {
@@ -192,6 +213,7 @@
     assert(asio->ref_count > 0);
     asio->ref_count--;
     if (asio->ref_count == 0) {
+        _async_socket_io_finished(asio);
         /* Last reference has been dropped. Destroy this object. */
         _async_socket_io_free(asio);
         return 0;
@@ -253,7 +275,7 @@
     AsyncSocketIO* const asio = (AsyncSocketIO*)opaque;
     AsyncSocket* const as = asio->as;
 
-    D("%s: %s I/O (deadline = %lld) timed out at %lld",
+    D("ASocket %s: %s I/O with deadline %lld has timed out at %lld",
       _async_socket_string(as), asio->is_io_read ? "READ" : "WRITE",
       asio->deadline, async_socket_deadline(as, 0));
 
@@ -361,6 +383,8 @@
     int                 reconnect_to;
     /* Number of outstanding references to the socket. */
     int                 ref_count;
+    /* Flags whether (1) or not (0) socket owns the looper. */
+    int                 owns_looper;
 };
 
 static const char*
@@ -523,8 +547,11 @@
 static AsyncIOAction
 _async_socket_complete_io(AsyncSocket* as, AsyncSocketIO* asio)
 {
+    T("ASocket %s: %s I/O %p is completed.",
+      _async_socket_string(as), asio->is_io_read ? "READ" : "WRITE", asio);
+
     /* Stop the timer. */
-    loopTimer_stop(asio->timer);
+    async_socket_io_cancel_time_out(asio);
 
     return asio->on_io(asio->io_opaque, asio, ASIO_STATE_SUCCEEDED);
 }
@@ -539,6 +566,10 @@
 static AsyncIOAction
 _async_socket_io_timed_out(AsyncSocket* as, AsyncSocketIO* asio)
 {
+    T("ASocket %s: %s I/O %p with deadline %lld has timed out at %lld",
+      _async_socket_string(as), asio->is_io_read ? "READ" : "WRITE", asio,
+      asio->deadline, async_socket_deadline(as, 0));
+
     /* Report to the client. */
     const AsyncIOAction action = asio->on_io(asio->io_opaque, asio,
                                              ASIO_STATE_TIMED_OUT);
@@ -565,8 +596,11 @@
 static AsyncIOAction
 _async_socket_cancel_io(AsyncSocket* as, AsyncSocketIO* asio)
 {
+    T("ASocket %s: %s I/O %p is cancelled.",
+      _async_socket_string(as), asio->is_io_read ? "READ" : "WRITE", asio);
+
     /* Stop the timer. */
-    loopTimer_stop(asio->timer);
+    async_socket_io_cancel_time_out(asio);
 
     return asio->on_io(asio->io_opaque, asio, ASIO_STATE_CANCELLED);
 }
@@ -582,8 +616,12 @@
 static AsyncIOAction
 _async_socket_io_failure(AsyncSocket* as, AsyncSocketIO* asio, int failure)
 {
+    T("ASocket %s: %s I/O %p has failed: %d -> %s",
+      _async_socket_string(as), asio->is_io_read ? "READ" : "WRITE", asio,
+      failure, strerror(failure));
+
     /* Stop the timer. */
-    loopTimer_stop(asio->timer);
+    async_socket_io_cancel_time_out(asio);
 
     errno = failure;
     return asio->on_io(asio->io_opaque, asio, ASIO_STATE_FAILED);
@@ -647,6 +685,8 @@
     if (as->fd >= 0) {
         loopIo_done(as->io);
         socket_close(as->fd);
+        T("ASocket %s: Socket handle %d is closed.",
+          _async_socket_string(as), as->fd);
         as->fd = -1;
     }
 }
@@ -659,7 +699,7 @@
 _async_socket_free(AsyncSocket* as)
 {
     if (as != NULL) {
-        D("AsyncSocket '%s' is destroyed", _async_socket_string(as));
+        T("ASocket %s: Socket descriptor is destroyed.", _async_socket_string(as));
 
         /* Close socket. */
         _async_socket_close_socket(as);
@@ -667,7 +707,9 @@
         /* Free allocated resources. */
         if (as->looper != NULL) {
             loopTimer_done(as->reconnect_timer);
-            looper_free(as->looper);
+            if (as->owns_looper) {
+                looper_free(as->looper);
+            }
         }
         sock_address_done(&as->address);
         AFREE(as);
@@ -682,6 +724,8 @@
 static void
 _async_socket_reconnect(AsyncSocket* as, int to)
 {
+    T("ASocket %s: reconnecting in %dms...", _async_socket_string(as), to);
+
     /* Make sure that no I/O is active, and socket is closed before we
      * reconnect. */
     _async_socket_cancel_all_io(as);
@@ -705,8 +749,7 @@
     const int save_errno = errno;
     AsyncIOAction action = ASIO_ACTION_ABORT;
 
-    D("Async socket '%s' is disconnected. Error %d -> %s",
-      _async_socket_string(as), errno, strerror(errno));
+    D("ASocket %s: Disconnected.", _async_socket_string(as));
 
     /* Cancel all the I/O on this socket. */
     _async_socket_cancel_all_io(as);
@@ -732,7 +775,7 @@
 static AsyncIOAction
 _on_async_socket_failure(AsyncSocket* as, AsyncSocketIO* asio)
 {
-    D("Async socket '%s' %s I/O failure: %d -> %s",
+    D("ASocket %s: %s I/O failure: %d -> %s",
       _async_socket_string(as), asio->is_io_read ? "READ" : "WRITE",
       errno, strerror(errno));
 
@@ -755,8 +798,7 @@
     /* Get current reader. */
     AsyncSocketIO* const asr = as->readers_head;
     if (asr == NULL) {
-        D("No async socket reader available on IO_READ for '%s'",
-          _async_socket_string(as));
+        D("ASocket %s: No reader is available.", _async_socket_string(as));
         loopIo_dontWantRead(as->io);
         return 0;
     }
@@ -772,8 +814,7 @@
     }
     action = asr->on_io(asr->io_opaque, asr, asr->state);
     if (action == ASIO_ACTION_ABORT) {
-        D("Read is aborted by the client of async socket '%s'",
-          _async_socket_string(as));
+        D("ASocket %s: Read is aborted by the client.", _async_socket_string(as));
         /* Move on to the next reader. */
         _async_socket_advance_reader(as);
         /* Lets see if there are still active readers, and enable, or disable
@@ -813,9 +854,8 @@
 
         /* An I/O error. */
         action = _on_async_socket_failure(as, asr);
-        if (action == ASIO_ACTION_ABORT) {
-            D("Read is aborted on failure by the client of async socket '%s'",
-              _async_socket_string(as));
+        if (action != ASIO_ACTION_RETRY) {
+            D("ASocket %s: Read is aborted on failure.", _async_socket_string(as));
             /* Move on to the next reader. */
             _async_socket_advance_reader(as);
             /* Lets see if there are still active readers, and enable, or disable
@@ -868,8 +908,7 @@
     /* Get current writer. */
     AsyncSocketIO* const asw = as->writers_head;
     if (asw == NULL) {
-        D("No async socket writer available on IO_WRITE for '%s'",
-          _async_socket_string(as));
+        D("ASocket %s: No writer is available.", _async_socket_string(as));
         loopIo_dontWantWrite(as->io);
         return 0;
     }
@@ -885,8 +924,7 @@
     }
     action = asw->on_io(asw->io_opaque, asw, asw->state);
     if (action == ASIO_ACTION_ABORT) {
-        D("Write is aborted by the client of async socket '%s'",
-          _async_socket_string(as));
+        D("ASocket %s: Write is aborted by the client.", _async_socket_string(as));
         /* Move on to the next writer. */
         _async_socket_advance_writer(as);
         /* Lets see if there are still active writers, and enable, or disable
@@ -926,9 +964,8 @@
 
         /* An I/O error. */
         action = _on_async_socket_failure(as, asw);
-        if (action == ASIO_ACTION_ABORT) {
-            D("Write is aborted on failure by the client of async socket '%s'",
-              _async_socket_string(as));
+        if (action != ASIO_ACTION_RETRY) {
+            D("ASocket %s: Write is aborted on failure.", _async_socket_string(as));
             /* Move on to the next writer. */
             _async_socket_advance_writer(as);
             /* Lets see if there are still active writers, and enable, or disable
@@ -1028,8 +1065,8 @@
     if (event == ASIO_STATE_SUCCEEDED && action != ASIO_ACTION_DONE) {
         /* For whatever reason the client didn't want to keep this connection.
          * Close it. */
-        D("Connection is discarded by a client of async socket '%s'",
-           _async_socket_string(as));
+        D("ASocket %s: Connection is discarded by the client.",
+          _async_socket_string(as));
         _async_socket_close_socket(as);
     }
 
@@ -1066,7 +1103,8 @@
 async_socket_new(int port,
                  int reconnect_to,
                  on_as_connection_cb client_cb,
-                 void* client_opaque)
+                 void* client_opaque,
+                 Looper* looper)
 {
     AsyncSocket* as;
 
@@ -1084,16 +1122,25 @@
     as->reconnect_to = reconnect_to;
     as->ref_count = 1;
     sock_address_init_inet(&as->address, SOCK_ADDRESS_INET_LOOPBACK, port);
-    as->looper = looper_newCore();
-    if (as->looper == NULL) {
-        E("Unable to create I/O looper for async socket '%s'",
-          _async_socket_string(as));
-        client_cb(client_opaque, as, ASIO_STATE_FAILED);
-        _async_socket_free(as);
-        return NULL;
+    if (looper == NULL) {
+        as->looper = looper_newCore();
+        if (as->looper == NULL) {
+            E("Unable to create I/O looper for async socket '%s'",
+              _async_socket_string(as));
+            client_cb(client_opaque, as, ASIO_STATE_FAILED);
+            _async_socket_free(as);
+            return NULL;
+        }
+        as->owns_looper = 1;
+    } else {
+        as->looper = looper;
+        as->owns_looper = 0;
     }
+
     loopTimer_init(as->reconnect_timer, as->looper, _on_async_socket_reconnect, as);
 
+    T("ASocket %s: Descriptor is created.", _async_socket_string(as));
+
     return as;
 }
 
@@ -1122,8 +1169,12 @@
 void
 async_socket_connect(AsyncSocket* as, int retry_to)
 {
+    T("ASocket %s: Handling connection request for %dms...",
+      _async_socket_string(as), retry_to);
+
     AsyncSocketConnector* const connector =
-        async_socket_connector_new(&as->address, retry_to, _on_connector_events, as);
+        async_socket_connector_new(&as->address, retry_to, _on_connector_events,
+                                   as, as->looper);
     if (connector != NULL) {
         async_socket_connector_connect(connector);
     } else {
@@ -1134,6 +1185,8 @@
 void
 async_socket_disconnect(AsyncSocket* as)
 {
+    T("ASocket %s: Handling disconnection request...", _async_socket_string(as));
+
     if (as != NULL) {
         _async_socket_cancel_all_io(as);
         _async_socket_close_socket(as);
@@ -1143,9 +1196,12 @@
 void
 async_socket_reconnect(AsyncSocket* as, int retry_to)
 {
+    T("ASocket %s: Handling reconnection request for %dms...",
+      _async_socket_string(as), retry_to);
+
     _async_socket_cancel_all_io(as);
     _async_socket_close_socket(as);
-    async_socket_connect(as, retry_to);
+    _async_socket_reconnect(as, retry_to);
 }
 
 void
@@ -1155,6 +1211,9 @@
                       void* reader_opaque,
                       Duration deadline)
 {
+    T("ASocket %s: Handling read for %d bytes with deadline %lld...",
+      _async_socket_string(as), len, deadline);
+
     AsyncSocketIO* const asr =
         _async_socket_reader_new(as, buffer, len, reader_cb, reader_opaque,
                                  deadline);
@@ -1188,6 +1247,9 @@
                        void* writer_opaque,
                        Duration deadline)
 {
+    T("ASocket %s: Handling write for %d bytes with deadline %lld...",
+      _async_socket_string(as), len, deadline);
+
     AsyncSocketIO* const asw =
         _async_socket_writer_new(as, buffer, len, writer_cb, writer_opaque,
                                  deadline);
diff --git a/android/async-socket.h b/android/async-socket.h
index 661fae5..503907b 100644
--- a/android/async-socket.h
+++ b/android/async-socket.h
@@ -137,13 +137,16 @@
  *  reconnect_to - Timeout before trying to reconnect after disconnection.
  *  connect_cb - Client callback to monitor connection state (must not be NULL).
  *  client_opaque - An opaque pointer to associate with the socket client.
+ *  looper - An optional (can be NULL) I/O looper to use for socket I/O. If
+ *      this parameter is NULL, the socket will create its own looper.
  * Return:
  *  Initialized AsyncSocket instance on success, or NULL on failure.
  */
 extern AsyncSocket* async_socket_new(int port,
                                      int reconnect_to,
                                      on_as_connection_cb connect_cb,
-                                     void* client_opaque);
+                                     void* client_opaque,
+                                     Looper* looper);
 
 /* References AsyncSocket object.
  * Param:
diff --git a/android/sdk-controller-socket.c b/android/sdk-controller-socket.c
new file mode 100644
index 0000000..a300169
--- /dev/null
+++ b/android/sdk-controller-socket.c
@@ -0,0 +1,1691 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+/*
+ * Encapsulates exchange protocol between the emulator, and an Android device
+ * that is connected to the host via USB. The communication is established over
+ * a TCP port forwarding, enabled by ADB.
+ */
+
+#include "qemu-common.h"
+#include "android/async-utils.h"
+#include "android/utils/debug.h"
+#include "android/async-socket-connector.h"
+#include "android/async-socket.h"
+#include "android/sdk-controller-socket.h"
+#include "utils/panic.h"
+#include "iolooper.h"
+
+#define  E(...)    derror(__VA_ARGS__)
+#define  W(...)    dwarning(__VA_ARGS__)
+#define  D(...)    VERBOSE_PRINT(sdkctlsocket,__VA_ARGS__)
+#define  D_ACTIVE  VERBOSE_CHECK(sdkctlsocket)
+
+#define TRACE_ON    1
+
+#if TRACE_ON
+#define  T(...)    VERBOSE_PRINT(sdkctlsocket,__VA_ARGS__)
+#else
+#define  T(...)
+#endif
+
+/* Recycling memory descriptor. */
+typedef struct SDKCtlRecycled SDKCtlRecycled;
+struct SDKCtlRecycled {
+    union {
+        /* Next recycled descriptor (while listed in recycler). */
+        SDKCtlRecycled* next;
+        /* Allocated memory size (while outside of the recycler). */
+        uint32_t        size;
+    };
+};
+
+/********************************************************************************
+ *                      SDKCtlPacket declarations
+ *******************************************************************************/
+
+/*
+ * Types of the packets of data sent via SDK controller socket.
+ */
+
+/* The packet is a message. */
+#define SDKCTL_PACKET_MESSAGE           1
+/* The packet is a query. */
+#define SDKCTL_PACKET_QUERY             2
+/* The packet is a response to a query. */
+#define SDKCTL_PACKET_QUERY_RESPONSE    3
+
+/* Data packet descriptor.
+ *
+ * All packets, sent and received via SDK controller socket begin with this
+ * header, with packet data immediately following this header.
+ */
+typedef struct SDKCtlPacketHeader {
+    /* Total size of the data to transfer with this packet, including this
+     * header. The transferring data should immediatelly follow this header. */
+    int     size;
+    /* Encodes packet type. See SDKCTL_PACKET_XXX for the list of packet types
+     * used by SDK controller. */
+    int     type;
+} SDKCtlPacketHeader;
+
+/* Packet descriptor, allocated by this API for data packets to be sent to SDK
+ * controller service on the device.
+ *
+ * When packet descriptors are allocated by this API, they are allocated large
+ * enough to contain this header, and packet data to send to the service,
+ * immediately following this descriptor.
+ */
+struct SDKCtlPacket {
+    /* Supports recycling. Don't put anything in front: recycler expects this
+     * to be the first field in recyclable descriptor. */
+    SDKCtlRecycled      recycling;
+
+    /* Next packet in the list of packets to send. */
+    SDKCtlPacket*       next;
+    /* SDK controller socket that transmits this packet. */
+    SDKCtlSocket*       sdkctl;
+    /* Number of outstanding references to the packet. */
+    int                 ref_count;
+
+    /* Common packet header. Packet data immediately follows this header, so it
+     * must be last field in SDKCtlPacket descriptor. */
+    SDKCtlPacketHeader  header;
+};
+
+/********************************************************************************
+ *                      SDKCtlQuery declarations
+ *******************************************************************************/
+
+/*
+ * Types of queries sent via SDK controller socket.
+ */
+
+/* Handshake query.
+ * This query is sent to SDK controller service as part of the connection
+ * protocol implementation.
+ */
+#define SDKCTL_QUERY_HANDSHAKE          -1
+
+/* Query packet descriptor.
+ *
+ * All queries, sent and received via SDK controller socket begin with this
+ * header, with query data immediately following this header.
+ */
+typedef struct SDKCtlQueryHeader {
+    /* Data packet header for this query. */
+    SDKCtlPacketHeader  packet;
+    /* A unique query identifier. This ID is used to track the query in the
+     * asynchronous environment in whcih SDK controller socket operates. */
+    int                 query_id;
+    /* Query type. See SDKCTL_QUERY_XXX for the list of query types used by SDK
+     * controller. */
+    int                 query_type;
+} SDKCtlQueryHeader;
+
+/* Query descriptor, allocated by this API for queries to be sent to SDK
+ * controller service on the device.
+ *
+ * When query descriptors are allocated by this API, they are allocated large
+ * enough to contain this header, and query data to send to the service,
+ * immediately following this descriptor.
+ */
+struct SDKCtlQuery {
+    /* Supports recycling. Don't put anything in front: recycler expects this
+     * to be the first field in recyclable descriptor. */
+    SDKCtlRecycled          recycling;
+
+    /* Next query in the list of active, or recycled queries. */
+    SDKCtlQuery*            next;
+    /* A timer to run time out on this query after it has been sent. */
+    LoopTimer               timer[1];
+    /* Absolute time for this query's deadline. This is the value that query's
+     * timer is set for after query has been transmitted to the service. */
+    Duration                deadline;
+    /* SDK controller socket that owns the query. */
+    SDKCtlSocket*           sdkctl;
+    /* A callback to invoke on query state changes. */
+    on_sdkctl_query_cb      query_cb;
+    /* An opaque pointer associated with this query. */
+    void*                   query_opaque;
+    /* Points to an address of a buffer where to save query response. */
+    void**                  response_buffer;
+    /* Points to a variable containing size of the response buffer (on the way in),
+     * or actual query response size (when query is completed). */
+    uint32_t*               response_size;
+    /* Internal response buffer, allocated if query creator didn't provide its
+     * own. This field is valid only if response_buffer field is NULL, or is
+     * pointing to this field. */
+    void*                   internal_resp_buffer;
+    /* Internal response buffer size This field is valid only if response_size
+     * field is NULL, or is pointing to this field. */
+    uint32_t                internal_resp_size;
+    /* Number of outstanding references to the query. */
+    int                     ref_count;
+
+    /* Common packet header. Query data immediately follows this header, so it
+     * must be last field in SDKCtlQuery descriptor. */
+    SDKCtlQueryHeader       header;
+};
+
+/* Query reply descriptor.
+ *
+ * All replies to a query, sent and received via SDK controller socket begin with
+ * this header, with query reply data immediately following this header.
+ */
+typedef struct SDKCtlQueryReplyHeader {
+    /* Data packet header for this reply. */
+    SDKCtlPacketHeader  packet;
+
+    /* An identifier for the query that is addressed with this reply. */
+    int                 query_id;
+} SDKCtlQueryReplyHeader;
+
+/********************************************************************************
+ *                      SDK Control Socket declarations
+ *******************************************************************************/
+
+/* Enumerates SDKCtlSocket states. */
+typedef enum SDKCtlSocketState {
+    /* Socket is disconnected from SDK controller. */
+    SDKCTL_SOCKET_DISCONNECTED,
+    /* Connection to SDK controller is in progress. */
+    SDKCTL_SOCKET_CONNECTING,
+    /* Socket is connected to an SDK controller service. */
+    SDKCTL_SOCKET_CONNECTED
+} SDKCtlSocketState;
+
+/* Enumerates SDKCtlSocket I/O dispatcher states. */
+typedef enum SDKCtlIODispatcherState {
+    /* I/O dispatcher expects a packet header. */
+    SDKCTL_IODISP_EXPECT_HEADER,
+    /* I/O dispatcher expects packet data. */
+    SDKCTL_IODISP_EXPECT_DATA,
+    /* I/O dispatcher expects query response header. */
+    SDKCTL_IODISP_EXPECT_QUERY_REPLY_HEADER,
+    /* I/O dispatcher expects query response data. */
+    SDKCTL_IODISP_EXPECT_QUERY_REPLY_DATA,
+} SDKCtlIODispatcherState;
+
+/* SDKCtlSocket I/O dispatcher descriptor. */
+typedef struct SDKCtlIODispatcher {
+    /* SDKCtlSocket instance for this dispatcher. */
+    SDKCtlSocket*               sdkctl;
+    /* Dispatcher state. */
+    SDKCtlIODispatcherState     state;
+    /* Unites all types of headers used in SDK controller data exchange. */
+    union {
+        /* Common packet header. */
+        SDKCtlPacketHeader      header;
+        /* Header for a query packet. */
+        SDKCtlQueryHeader       query_header;
+        /* Header for a query response packet. */
+        SDKCtlQueryReplyHeader  query_reply_header;
+    };
+    /* Descriptor of a packet packet received from SDK controller. */
+    SDKCtlPacket*               packet;
+    /* A query for which a reply is currently being received. */
+    SDKCtlQuery*                current_query;
+} SDKCtlIODispatcher;
+
+/* SDK controller socket descriptor. */
+struct SDKCtlSocket {
+    /* SDK controller socket state */
+    SDKCtlSocketState           state;
+    /* I/O dispatcher for the socket. */
+    SDKCtlIODispatcher          io_dispatcher;
+    /* Asynchronous socket connected to SDK Controller on the device. */
+    AsyncSocket*                as;
+    /* Client callback that monitors this socket connection. */
+    on_sdkctl_connection_cb     on_connection;
+    /* A callback to invoke when handshake message is received from the
+     * SDK controller. */
+    on_sdkctl_handshake_cb      on_handshake;
+    /* A callback to invoke when a message is received from the SDK controller. */
+    on_sdkctl_message_cb        on_message;
+    /* An opaque pointer associated with this socket. */
+    void*                       opaque;
+    /* Name of an SDK controller service this socket is connected to. */
+    char*                       service_name;
+    /* I/O looper for timers. */
+    Looper*                     looper;
+    /* Head of the active query list. */
+    SDKCtlQuery*                query_head;
+    /* Tail of the active query list. */
+    SDKCtlQuery*                query_tail;
+    /* Query ID generator that gets incremented for each new query. */
+    int                         next_query_id;
+    /* Timeout before trying to reconnect after disconnection. */
+    int                         reconnect_to;
+    /* Number of outstanding references to this descriptor. */
+    int                         ref_count;
+    /* Head of the recycled memory */
+    SDKCtlRecycled*             recycler;
+    /* Recyclable block size. */
+    uint32_t                    recycler_block_size;
+    /* Maximum number of blocks to recycle. */
+    int                         recycler_max;
+    /* Number of blocs in the recycler. */
+    int                         recycler_count;
+};
+
+/********************************************************************************
+ *                      SDKCtlSocket recycling management
+ *******************************************************************************/
+
+/* Gets a recycled block for a given SDKCtlSocket, or allocates new memory
+ * block. */
+static void*
+_sdkctl_socket_alloc_recycler(SDKCtlSocket* sdkctl, uint32_t size)
+{
+    SDKCtlRecycled* block = NULL;
+
+    if (sdkctl->recycler != NULL && size <= sdkctl->recycler_block_size) {
+        /* There are blocks in the recycler, and requested size fits. */
+        block = sdkctl->recycler;
+        sdkctl->recycler = block->next;
+        block->size = sdkctl->recycler_block_size;
+        sdkctl->recycler_count--;
+    } else if (size <= sdkctl->recycler_block_size) {
+        /* There are no blocks in the recycler, but requested size fits. */
+        block = malloc(sdkctl->recycler_block_size);
+        if (block == NULL) {
+            APANIC("SDKCtl %s: Unable to allocate %d bytes block",
+                   sdkctl->service_name, sdkctl->recycler_block_size);
+        }
+        block->size = sdkctl->recycler_block_size;
+    } else {
+        /* Requested size doesn't fit the recycler. */
+        block = malloc(size);
+        if (block == NULL) {
+            APANIC("SDKCtl %s: Unable to allocate %d bytes block",
+                   sdkctl->service_name, size);
+        }
+        block->size = size;
+    }
+
+    return block;
+}
+
+/* Recycles, or frees a block of memory for a given SDKCtlSocket. */
+static void
+_sdkctl_socket_free_recycler(SDKCtlSocket* sdkctl, void* mem)
+{
+    SDKCtlRecycled* block = (SDKCtlRecycled*)mem;
+
+    if (sdkctl->recycler_count == sdkctl->recycler_max ||
+        block->size != sdkctl->recycler_block_size) {
+        /* Recycler is full, or block cannot be recycled. */
+        free(mem);
+        return;
+    }
+
+    block->next = sdkctl->recycler;
+    sdkctl->recycler = block;
+    sdkctl->recycler_count++;
+}
+
+/* Empties the recycler for a given SDKCtlSocket. */
+static void
+_sdkctl_socket_empty_recycler(SDKCtlSocket* sdkctl)
+{
+    SDKCtlRecycled* block = sdkctl->recycler;
+    while (block != NULL) {
+        void* to_free = block;
+        block = block->next;
+        free(to_free);
+    }
+    sdkctl->recycler = NULL;
+    sdkctl->recycler_count = 0;
+}
+
+/********************************************************************************
+ *                      SDKCtlSocket query list management
+ *******************************************************************************/
+
+/* Adds a query to the list of active queries.
+ * Param:
+ *  sdkctl - SDKCtlSocket instance for the query.
+ *  query - Query to add to the list.
+ */
+static void
+_sdkctl_socket_add_query(SDKCtlQuery* query)
+{
+    SDKCtlSocket* const sdkctl = query->sdkctl;
+    if (sdkctl->query_head == NULL) {
+        sdkctl->query_head = sdkctl->query_tail = query;
+    } else {
+        sdkctl->query_tail->next = query;
+        sdkctl->query_tail = query;
+    }
+
+    /* Keep the query referenced while it's in the list. */
+    sdkctl_query_reference(query);
+}
+
+/* Removes a query from the list of active queries.
+ * Param:
+ *  query - Query to remove from the list of active queries.
+ * Return:
+ *  Boolean: 1 if query has been removed, or 0 if query has not been found in the
+ *  list of active queries.
+ */
+static int
+_sdkctl_socket_remove_query(SDKCtlQuery* query)
+{
+    SDKCtlSocket* const sdkctl = query->sdkctl;
+    SDKCtlQuery* prev = NULL;
+    SDKCtlQuery* head = sdkctl->query_head;
+
+    /* Quick check: the query could be currently handled by dispatcher. */
+    if (sdkctl->io_dispatcher.current_query == query) {
+        /* Release the query from dispatcher. */
+        sdkctl_query_release(query);
+        sdkctl->io_dispatcher.current_query = NULL;
+        return 1;
+    }
+
+    /* Remove query from the list. */
+    while (head != NULL && query != head) {
+        prev = head;
+        head = head->next;
+    }
+    if (head != NULL) {
+        if (prev == NULL) {
+            /* Query is at the head of the list. */
+            assert(query == sdkctl->query_head);
+            sdkctl->query_head = query->next;
+        } else {
+            /* Query is in the middle / at the end of the list. */
+            assert(query != sdkctl->query_head);
+            prev->next = query->next;
+        }
+        if (sdkctl->query_tail == query) {
+            /* Query is at the tail of the list. */
+            assert(query->next == NULL);
+            sdkctl->query_tail = prev;
+        }
+        query->next = NULL;
+
+        /* Release query that is now removed from the list. Note that query
+         * passed to this routine should hold an extra reference, owned by the
+         * caller. */
+        sdkctl_query_release(query);
+        return 1;
+    } else {
+        D("%s: Query %p is not found in the list.", sdkctl->service_name, query);
+        return 0;
+    }
+}
+
+/* Removes a query (based on query ID) from the list of active queries.
+ * Param:
+ *  sdkctl - SDKCtlSocket instance that owns the query.
+ *  query_id - Identifies the query to remove.
+ * Return:
+ *  A query removed from the list of active queries, or NULL if query with the
+ *  given ID has not been found in the list.
+ */
+static SDKCtlQuery*
+_sdkctl_socket_remove_query_id(SDKCtlSocket* sdkctl, int query_id)
+{
+    SDKCtlQuery* prev = NULL;
+    SDKCtlQuery* head = sdkctl->query_head;
+
+    /* Quick check: the query could be currently handled by dispatcher. */
+    if (sdkctl->io_dispatcher.current_query != NULL &&
+        sdkctl->io_dispatcher.current_query->header.query_id == query_id) {
+        /* Release the query from dispatcher. */
+        SDKCtlQuery* const query = sdkctl->io_dispatcher.current_query;
+        sdkctl->io_dispatcher.current_query = NULL;
+        return query;
+    }
+
+    /* Remove query from the list. */
+    while (head != NULL && head->header.query_id != query_id) {
+        prev = head;
+        head = head->next;
+    }
+    if (head != NULL) {
+        /* Query is found in the list. */
+        SDKCtlQuery* const query = head;
+        if (prev == NULL) {
+            /* Query is at the head of the list. */
+            assert(query == sdkctl->query_head);
+            sdkctl->query_head = query->next;
+        } else {
+            /* Query is in the middle, or at the end of the list. */
+            assert(query != sdkctl->query_head);
+            prev->next = query->next;
+        }
+        if (sdkctl->query_tail == query) {
+            /* Query is at the tail of the list. */
+            assert(query->next == NULL);
+            sdkctl->query_tail = prev;
+        }
+        query->next = NULL;
+        return query;
+    } else {
+        D("%s: Query ID %d is not found in the list.",
+          sdkctl->service_name, query_id);
+        return NULL;
+    }
+}
+
+/* Pulls the first query from the list of active queries.
+ * Param:
+ *  sdkctl - SDKCtlSocket instance that owns the query.
+ * Return:
+ *  A query removed pulled from the list of active queries, or NULL if query
+ *  list is empty.
+ */
+static SDKCtlQuery*
+_sdkctl_socket_pull_first_query(SDKCtlSocket* sdkctl)
+{
+    SDKCtlQuery* const query = sdkctl->query_head;
+
+    if (query != NULL) {
+        sdkctl->query_head = query->next;
+        if (sdkctl->query_head == NULL) {
+            sdkctl->query_tail = NULL;
+        }
+    }
+    return query;
+}
+
+/* Generates new query ID for the given SDKCtl. */
+static int
+_sdkctl_socket_next_query_id(SDKCtlSocket* sdkctl)
+{
+    return ++sdkctl->next_query_id;
+}
+
+/********************************************************************************
+ *                      SDKCtlPacket implementation
+ *******************************************************************************/
+
+/* Alocates a packet. */
+static SDKCtlPacket*
+_sdkctl_packet_new(SDKCtlSocket* sdkctl, int size, int type)
+{
+    const uint32_t total_size = sizeof(SDKCtlPacket) + size;
+    SDKCtlPacket* const packet = _sdkctl_socket_alloc_recycler(sdkctl, total_size);
+
+    packet->sdkctl      = sdkctl;
+    packet->ref_count   = 1;
+    packet->header.size = size;
+    packet->header.type = type;
+
+    /* Refence SDKCTlSocket that owns this packet. */
+    sdkctl_socket_reference(sdkctl);
+
+    return packet;
+}
+
+/* Frees a packet. */
+static void
+_sdkctl_packet_free(SDKCtlPacket* packet)
+{
+    SDKCtlSocket* const sdkctl = packet->sdkctl;
+
+    /* Free allocated resources. */
+    _sdkctl_socket_free_recycler(packet->sdkctl, packet);
+
+    /* Release SDKCTlSocket that owned this packet. */
+    sdkctl_socket_release(sdkctl);
+}
+
+int
+sdkctl_packet_reference(SDKCtlPacket* packet)
+{
+    assert(packet->ref_count > 0);
+    packet->ref_count++;
+    return packet->ref_count;
+}
+
+int
+sdkctl_packet_release(SDKCtlPacket* packet)
+{
+    assert(packet->ref_count > 0);
+    packet->ref_count--;
+    if (packet->ref_count == 0) {
+        /* Last reference has been dropped. Destroy this object. */
+        _sdkctl_packet_free(packet);
+        return 0;
+    }
+    return packet->ref_count;
+}
+
+/********************************************************************************
+ *                    SDKCtlQuery implementation
+ *******************************************************************************/
+
+/* Frees query descriptor. */
+static void
+_sdkctl_query_free(SDKCtlQuery* query)
+{
+    if (query != NULL) {
+        SDKCtlSocket* const sdkctl = query->sdkctl;
+        T("SDKCtl %s: Query %p ID %d is freed.",
+          sdkctl->service_name, query, query->header.query_id);
+
+        /* Free allocated resources. */
+        if (query->internal_resp_buffer != NULL &&
+            (query->response_buffer == NULL ||
+             query->response_buffer == &query->internal_resp_buffer)) {
+            free(query->internal_resp_buffer);
+        }
+
+        loopTimer_done(query->timer);
+        _sdkctl_socket_free_recycler(sdkctl, query);
+
+        /* Release socket that owned this query. */
+        sdkctl_socket_release(sdkctl);
+    }
+}
+
+/* Cancels timeout for the query.
+ *
+ * For the simplicity of implementation, the dispatcher will cancel query timer
+ * when query response data begins to flow in. If we let the timer to expire at
+ * that stage, we will end up with data flowing in without real place to
+ * accomodate it.
+ */
+static void
+_sdkctl_query_cancel_timeout(SDKCtlQuery* query)
+{
+    loopTimer_stop(query->timer);
+
+    T("SDKCtl %s: Query %p ID %d deadline is cancelled.",
+      query->sdkctl->service_name, query, query->header.query_id);
+}
+
+/*
+ * Query I/O callbacks.
+ */
+
+/* Callback that is invoked by the I/O dispatcher when query is successfuly
+ * completed (i.e. response to the query is received).
+ */
+static void
+_on_sdkctl_query_completed(SDKCtlQuery* query)
+{
+    T("SDKCtl %s: Query %p ID %d is completed.",
+      query->sdkctl->service_name, query, query->header.query_id);
+
+    /* Cancel deadline, and inform the client about query completion. */
+    _sdkctl_query_cancel_timeout(query);
+    query->query_cb(query->query_opaque, query, ASIO_STATE_SUCCEEDED);
+}
+
+/* A callback that is invoked on query cancellation. */
+static void
+_on_sdkctl_query_cancelled(SDKCtlQuery* query)
+{
+    /*
+     * Query cancellation means that SDK controller is disconnected. In turn,
+     * this means that SDK controller socket will handle disconnection in its
+     * connection callback. So, at this point all we need to do here is to inform
+     * the client, and then unlist the query.
+     */
+
+    /* Cancel deadline, and inform the client about query cancellation. */
+    _sdkctl_query_cancel_timeout(query);
+    query->query_cb(query->query_opaque, query, ASIO_STATE_CANCELLED);
+}
+
+/* A timer callback that is invoked on query timeout.
+ * Param:
+ *  opaque - SDKCtlQuery instance.
+ */
+static void
+_on_skdctl_query_timeout(void* opaque)
+{
+    SDKCtlQuery* const query = (SDKCtlQuery*)opaque;
+
+    D("SDKCtl %s: Query %p ID %d with deadline %lld has timed out at %lld",
+      query->sdkctl->service_name, query, query->header.query_id,
+      query->deadline, async_socket_deadline(query->sdkctl->as, 0));
+
+    /* Reference the query while we're in this callback. */
+    sdkctl_query_reference(query);
+
+    /* Inform the client about deadline expiration. Note that client may
+     * extend the deadline, and retry the query. */
+    const AsyncIOAction action =
+        query->query_cb(query->query_opaque, query, ASIO_STATE_TIMED_OUT);
+
+    /* For actions other than retry we will destroy the query. */
+    if (action != ASIO_ACTION_RETRY) {
+        _sdkctl_socket_remove_query(query);
+    }
+
+    sdkctl_query_release(query);
+}
+
+/* A callback that is invoked when query has been sent to the SDK controller
+ * service. */
+static void
+_on_sdkctl_query_sent(SDKCtlQuery* query)
+{
+    T("SDKCtl %s: sent %d bytes of query %p ID %d of type %d",
+      query->sdkctl->service_name, query->header.packet.size, query,
+      query->header.query_id, query->header.query_type);
+
+    /* Inform the client about the event. */
+    query->query_cb(query->query_opaque, query, ASIO_STATE_CONTINUES);
+
+    /* Set a timer to expire at query's deadline, and let the response to come
+     * through the dispatcher loop. */
+    loopTimer_startAbsolute(query->timer, query->deadline);
+}
+
+/* An I/O callback invoked on query transmission.
+ * Param:
+ *  io_opaque SDKCtlQuery instance of the query that's being sent with this I/O.
+ *  asio - Write I/O descriptor.
+ *  status - I/O status.
+ */
+static AsyncIOAction
+_on_sdkctl_query_send_io(void* io_opaque,
+                         AsyncSocketIO* asio,
+                         AsyncIOState status)
+{
+    SDKCtlQuery* const query = (SDKCtlQuery*)io_opaque;
+    AsyncIOAction action = ASIO_ACTION_DONE;
+
+    /* Reference the query while we're in this callback. */
+    sdkctl_query_reference(query);
+
+    if (status == ASIO_STATE_SUCCEEDED) {
+        /* Query has been sent to the service. */
+        _on_sdkctl_query_sent(query);
+
+        sdkctl_query_release(query);
+
+        return ASIO_ACTION_DONE;
+    }
+
+    /* Lets see what's going on with query transmission. */
+    switch (status) {
+        case ASIO_STATE_CANCELLED:
+            T("SDKCtl %s: Query %p ID %d is cancelled in %s I/O.",
+              query->sdkctl->service_name, query, query->header.query_id,
+              async_socket_io_is_read(asio) ? "READ" : "WRITE");
+            /* Remove the query from the list of active queries. */
+            _sdkctl_socket_remove_query(query);
+            _on_sdkctl_query_cancelled(query);
+            break;
+
+        case ASIO_STATE_TIMED_OUT:
+            D("SDKCtl %s: Query %p ID %d with deadline %lld has timed out in %s I/O at %lld",
+              query->sdkctl->service_name, query, query->header.query_id,
+              query->deadline, async_socket_io_is_read(asio) ? "READ" : "WRITE",
+              async_socket_deadline(query->sdkctl->as, 0));
+            /* Invoke query's callback. */
+            action = query->query_cb(query->query_opaque, query, status);
+            /* For actions other than retry we need to stop the query. */
+            if (action != ASIO_ACTION_RETRY) {
+                _sdkctl_socket_remove_query(query);
+            }
+            break;
+
+        case ASIO_STATE_FAILED:
+            T("SDKCtl %s: Query %p ID %d failed in %s I/O: %d -> %s",
+              query->sdkctl->service_name, query, query->header.query_id,
+              async_socket_io_is_read(asio) ? "READ" : "WRITE",
+              errno, strerror(errno));
+            /* Invoke query's callback. Note that we will let the client to
+             * decide what to do on I/O failure. */
+            action = query->query_cb(query->query_opaque, query, status);
+            /* For actions other than retry we need to stop the query. */
+            if (action != ASIO_ACTION_RETRY) {
+                _sdkctl_socket_remove_query(query);
+            }
+            break;
+
+        case ASIO_STATE_FINISHED:
+            /* Time to disassociate with the I/O. */
+            sdkctl_query_release(query);
+            break;
+
+        default:
+            /* Transitional state. */
+            break;
+    }
+
+    sdkctl_query_release(query);
+
+    return action;
+}
+
+/********************************************************************************
+ *                    SDKCtlQuery public API implementation
+ ********************************************************************************/
+
+SDKCtlQuery*
+sdkctl_query_new(SDKCtlSocket* sdkctl, int query_type, uint32_t in_data_size)
+{
+    const uint32_t total_size = sizeof(SDKCtlQuery) + in_data_size;
+
+    SDKCtlQuery* const query = _sdkctl_socket_alloc_recycler(sdkctl, total_size);
+    query->next                 = NULL;
+    query->sdkctl               = sdkctl;
+    query->response_buffer      = NULL;
+    query->response_size        = NULL;
+    query->internal_resp_buffer = NULL;
+    query->internal_resp_size   = 0;
+    query->query_cb             = NULL;
+    query->query_opaque         = NULL;
+    query->deadline             = DURATION_INFINITE;
+    query->ref_count            = 1;
+    query->header.packet.size   = sizeof(SDKCtlQueryHeader) + in_data_size;
+    query->header.packet.type   = SDKCTL_PACKET_QUERY;
+    query->header.query_id      = _sdkctl_socket_next_query_id(sdkctl);
+    query->header.query_type    = query_type;
+
+    /* Initialize timer to fire up on query deadline expiration. */
+    loopTimer_init(query->timer, sdkctl->looper, _on_skdctl_query_timeout, query);
+
+    /* Reference socket that owns this query. */
+    sdkctl_socket_reference(sdkctl);
+
+    T("SDKCtl %s: Query %p ID %d type %d is created for %d bytes of data.",
+      query->sdkctl->service_name, query, query->header.query_id,
+      query_type, in_data_size);
+
+    return query;
+}
+
+SDKCtlQuery*
+sdkctl_query_new_ex(SDKCtlSocket* sdkctl,
+                    int query_type,
+                    uint32_t in_data_size,
+                    const void* in_data,
+                    void** response_buffer,
+                    uint32_t* response_size,
+                    on_sdkctl_query_cb query_cb,
+                    void* query_opaque)
+{
+    SDKCtlQuery* const query = sdkctl_query_new(sdkctl, query_type, in_data_size);
+
+    query->response_buffer  = response_buffer;
+    if (query->response_buffer == NULL) {
+        /* Creator didn't supply a buffer. Use internal one instead. */
+        query->response_buffer = &query->internal_resp_buffer;
+        query->internal_resp_buffer = NULL;
+    }
+    query->response_size    = response_size;
+    if (query->response_size == NULL) {
+        /* Creator didn't supply a buffer for response size. Use internal one
+         * instead. */
+        query->response_size = &query->internal_resp_size;
+        query->internal_resp_size = 0;
+    }
+    query->query_cb         = query_cb;
+    query->query_opaque     = query_opaque;
+    /* Init query's input buffer. */
+    if (in_data_size != 0 && in_data != NULL) {
+        memcpy(query + 1, in_data, in_data_size);
+    }
+
+    return query;
+}
+
+void
+sdkctl_query_send(SDKCtlQuery* query, int to)
+{
+    SDKCtlSocket* const sdkctl = query->sdkctl;
+
+    /* Initialize the deadline. */
+    query->deadline = async_socket_deadline(query->sdkctl->as, to);
+
+    /* List the query in the list of active queries. */
+    _sdkctl_socket_add_query(query);
+
+    /* Reference query associated with write I/O. */
+    sdkctl_query_reference(query);
+
+    /* Transmit the query to SDK controller. */
+    async_socket_write_abs(sdkctl->as, &query->header, query->header.packet.size,
+                           _on_sdkctl_query_send_io, query, query->deadline);
+
+    T("SDKCtl %s: Query %p ID %d type %d is sent with deadline at %lld",
+      query->sdkctl->service_name, query, query->header.query_id,
+      query->header.query_type, query->deadline);
+}
+
+SDKCtlQuery*
+sdkctl_query_build_and_send(SDKCtlSocket* sdkctl,
+                            int query_type,
+                            uint32_t in_data_size,
+                            const void* in_data,
+                            void** response_buffer,
+                            uint32_t* response_size,
+                            on_sdkctl_query_cb query_cb,
+                            void* query_opaque,
+                            int to)
+{
+    SDKCtlQuery* const query =
+        sdkctl_query_new_ex(sdkctl, query_type, in_data_size, in_data,
+                            response_buffer, response_size, query_cb,
+                            query_opaque);
+    sdkctl_query_send(query, to);
+    return query;
+}
+
+int
+sdkctl_query_reference(SDKCtlQuery* query)
+{
+    assert(query->ref_count > 0);
+    query->ref_count++;
+    return query->ref_count;
+}
+
+int
+sdkctl_query_release(SDKCtlQuery* query)
+{
+    assert(query->ref_count > 0);
+    query->ref_count--;
+    if (query->ref_count == 0) {
+        /* Last reference has been dropped. Destroy this object. */
+        _sdkctl_query_free(query);
+        return 0;
+    }
+    return query->ref_count;
+}
+
+/********************************************************************************
+ *                      SDKCtlPacket implementation
+ *******************************************************************************/
+
+/* A packet has been received from SDK controller. */
+static void
+_on_sdkctl_packet_received(SDKCtlSocket* sdkctl, SDKCtlPacket* packet)
+{
+    T("SDKCtl %s: Received packet size: %d, type: %d",
+      sdkctl->service_name, packet->header.size, packet->header.type);
+
+    /* Dispatch received packet to the client. */
+    sdkctl->on_message(sdkctl->opaque, sdkctl, packet, packet->header.type,
+                       packet + 1, packet->header.size - sizeof(SDKCtlPacketHeader));
+}
+
+/********************************************************************************
+ *                      SDKCtlIODispatcher implementation
+ *******************************************************************************/
+
+/* An I/O callback invoked when data gets received from the socket.
+ * Param:
+ *  io_opaque SDKCtlIODispatcher instance associated with the reader.
+ *  asio - Read I/O descriptor.
+ *  status - I/O status.
+ */
+static AsyncIOAction _on_sdkctl_io_dispatcher_io(void* io_opaque,
+                                                 AsyncSocketIO* asio,
+                                                 AsyncIOState status);
+
+/* Starts I/O dispatcher for SDK controller socket. */
+static void
+_sdkctl_io_dispatcher_start(SDKCtlSocket* sdkctl) {
+    SDKCtlIODispatcher* const dispatcher = &sdkctl->io_dispatcher;
+
+    dispatcher->state           = SDKCTL_IODISP_EXPECT_HEADER;
+    dispatcher->sdkctl          = sdkctl;
+    dispatcher->packet          = NULL;
+    dispatcher->current_query   = NULL;
+
+    /* Register a packet header reader with the socket. */
+    async_socket_read_rel(dispatcher->sdkctl->as, &dispatcher->header,
+                          sizeof(SDKCtlPacketHeader), _on_sdkctl_io_dispatcher_io,
+                          dispatcher, -1);
+}
+
+/* Resets I/O dispatcher for SDK controller socket. */
+static void
+_sdkctl_io_dispatcher_reset(SDKCtlSocket* sdkctl) {
+    SDKCtlIODispatcher* const dispatcher = &sdkctl->io_dispatcher;
+
+    /* Cancel current query. */
+    if (dispatcher->current_query != NULL) {
+        SDKCtlQuery* const query = dispatcher->current_query;
+        dispatcher->current_query = NULL;
+        _on_sdkctl_query_cancelled(query);
+        sdkctl_query_release(query);
+    }
+
+    /* Free packet data buffer. */
+    if (dispatcher->packet != NULL) {
+        sdkctl_packet_release(dispatcher->packet);
+        dispatcher->packet = NULL;
+    }
+
+    /* Reset dispatcher state. */
+    dispatcher->state = SDKCTL_IODISP_EXPECT_HEADER;
+}
+
+/*
+ * I/O dispatcher callbacks.
+ */
+
+/* A callback that is invoked when a failure occurred while dispatcher was
+ * reading data from the socket.
+ */
+static void
+_on_io_dispatcher_io_failure(SDKCtlIODispatcher* dispatcher,
+                             AsyncSocketIO* asio)
+{
+    SDKCtlSocket* const sdkctl = dispatcher->sdkctl;
+
+    D("SDKCtl %s: Dispatcher I/O failure: %d -> %s",
+      sdkctl->service_name, errno, strerror(errno));
+
+    /* We treat all I/O failures same way we treat disconnection. Just cancel
+     * everything, disconnect, and let the client to decide what to do next. */
+    sdkctl_socket_disconnect(sdkctl);
+
+    /* Report disconnection to the client, and let it restore connection in this
+     * callback. */
+    sdkctl->on_connection(sdkctl->opaque, sdkctl, ASIO_STATE_FAILED);
+}
+
+/* A callback that is invoked when dispatcher's reader has been cancelled. */
+static void
+_on_io_dispatcher_io_cancelled(SDKCtlIODispatcher* dispatcher,
+                               AsyncSocketIO* asio)
+{
+    T("SDKCtl %s: Dispatcher I/O cancelled.", dispatcher->sdkctl->service_name);
+
+    /* If we're in the middle of receiving query reply we need to cancel the
+     * query. */
+    if (dispatcher->current_query != NULL) {
+        SDKCtlQuery* const query = dispatcher->current_query;
+        dispatcher->current_query = NULL;
+        _on_sdkctl_query_cancelled(query);
+        sdkctl_query_release(query);
+    }
+
+    /* Discard packet data we've received so far. */
+    if (dispatcher->packet != NULL) {
+        sdkctl_packet_release(dispatcher->packet);
+        dispatcher->packet = NULL;
+    }
+}
+
+/* A generic packet header has been received by I/O dispatcher. */
+static AsyncIOAction
+_on_io_dispatcher_packet_header(SDKCtlIODispatcher* dispatcher,
+                                AsyncSocketIO* asio)
+{
+    SDKCtlSocket* const sdkctl = dispatcher->sdkctl;
+
+    T("SDKCtl %s: Packet header type %d, size %d is received.",
+      dispatcher->sdkctl->service_name, dispatcher->header.type,
+      dispatcher->header.size);
+
+    /* Here we have three choices for the packet, that define the rest of
+     * the data that follow it:
+     * - Regular packet,
+     * - Response to a query that has been sent to SDK controller,
+     * - A query from SDK controller.
+     * Update the state accordingly, and initiate reading of the
+     * remaining of the packet.
+     */
+     if (dispatcher->header.type == SDKCTL_PACKET_QUERY_RESPONSE) {
+        /* This is a response to the query. Before receiving response data we
+         * need to locate the relevant query, and use its response buffer to read
+         * the data. For that we need to obtain query ID firts. So, initiate
+         * reading of the remaining part of SDKCtlQueryReplyHeader. */
+        dispatcher->state = SDKCTL_IODISP_EXPECT_QUERY_REPLY_HEADER;
+        async_socket_read_rel(sdkctl->as, &dispatcher->query_reply_header.query_id,
+                              sizeof(SDKCtlQueryReplyHeader) - sizeof(SDKCtlPacketHeader),
+                             _on_sdkctl_io_dispatcher_io, dispatcher, -1);
+    } else {
+        /* For regular packets, as well as queries, we simply allocate buffer,
+         * that fits the entire packet, and read the remainder of the data in
+         * there. */
+        dispatcher->state = SDKCTL_IODISP_EXPECT_DATA;
+        dispatcher->packet =
+            _sdkctl_packet_new(sdkctl, dispatcher->header.size,
+                               dispatcher->header.type);
+        /* Initiate reading of the packet data. */
+        async_socket_read_rel(sdkctl->as, dispatcher->packet + 1,
+                              dispatcher->header.size - sizeof(SDKCtlPacketHeader),
+                              _on_sdkctl_io_dispatcher_io, dispatcher, -1);
+    }
+
+    return ASIO_ACTION_DONE;
+}
+
+/* A generic packet has been received by I/O dispatcher. */
+static AsyncIOAction
+_on_io_dispatcher_packet(SDKCtlIODispatcher* dispatcher, AsyncSocketIO* asio)
+{
+    SDKCtlSocket* const sdkctl = dispatcher->sdkctl;
+
+    T("SDKCtl %s: Packet type %d, size %d is received.",
+      dispatcher->sdkctl->service_name, dispatcher->header.type,
+      dispatcher->header.size);
+
+    _on_sdkctl_packet_received(sdkctl, dispatcher->packet);
+    sdkctl_packet_release(dispatcher->packet);
+    dispatcher->packet = NULL;
+
+    /* Get ready for the next I/O cycle. */
+    dispatcher->state = SDKCTL_IODISP_EXPECT_HEADER;
+    async_socket_read_rel(sdkctl->as, &dispatcher->header, sizeof(SDKCtlPacketHeader),
+                          _on_sdkctl_io_dispatcher_io, dispatcher, -1);
+    return ASIO_ACTION_DONE;
+}
+
+/* A query reply header has been received by I/O dispatcher. */
+static AsyncIOAction
+_on_io_dispatcher_query_reply_header(SDKCtlIODispatcher* dispatcher,
+                                     AsyncSocketIO* asio)
+{
+    SDKCtlSocket* const sdkctl = dispatcher->sdkctl;
+    SDKCtlQuery* query;
+
+    T("SDKCtl %s: Query reply header is received for query ID %d",
+      dispatcher->sdkctl->service_name, dispatcher->query_reply_header.query_id);
+
+    /* Pull the query out of the list of active queries. It's the dispatcher that
+     * owns this query now. */
+    dispatcher->current_query =
+        _sdkctl_socket_remove_query_id(sdkctl, dispatcher->query_reply_header.query_id);
+    query = dispatcher->current_query;
+
+    if (query == NULL) {
+        D("%s: Query #%d is not found by dispatcher",
+          dispatcher->sdkctl->service_name, dispatcher->query_reply_header.query_id);
+
+        /* Query is not found. Just read the remainder of reply up in the air,
+         * and then discard when it's over. */
+        dispatcher->state = SDKCTL_IODISP_EXPECT_QUERY_REPLY_DATA;
+        dispatcher->packet =
+            _sdkctl_packet_new(sdkctl, dispatcher->header.size,
+                               dispatcher->header.type);
+        /* Copy query reply info to the packet. */
+        memcpy(&dispatcher->packet->header, &dispatcher->query_reply_header,
+               sizeof(SDKCtlQueryReplyHeader));
+        async_socket_read_rel(sdkctl->as, &dispatcher->query_header + 1,
+                              dispatcher->header.size - sizeof(SDKCtlQueryReplyHeader),
+                             _on_sdkctl_io_dispatcher_io, dispatcher, -1);
+    } else {
+        /* Prepare to receive query reply. For the simplicity sake, cancel query
+         * time out, so it doesn't expire on us while we're in the middle of
+         * receiving query's reply. */
+        _sdkctl_query_cancel_timeout(query);
+
+        /* Adjust the reply buffer set for the query (if needed). */
+        const uint32_t query_data_size =
+            dispatcher->header.size - sizeof(SDKCtlQueryReplyHeader);
+        if (*query->response_size < query_data_size) {
+            *query->response_buffer = malloc(query_data_size);
+            if (*query->response_buffer == NULL) {
+                APANIC("%s: Unable to allocate %d bytes for query response",
+                       sdkctl->service_name, query_data_size);
+            }
+        }
+        /* Save the actual query response size. */
+        *query->response_size = query_data_size;
+
+        /* Start reading query response. */
+        dispatcher->state = SDKCTL_IODISP_EXPECT_QUERY_REPLY_DATA;
+        async_socket_read_rel(sdkctl->as, *query->response_buffer,
+                              *query->response_size, _on_sdkctl_io_dispatcher_io,
+                              dispatcher, -1);
+    }
+
+    return ASIO_ACTION_DONE;
+}
+
+/* A query reply header has been received by I/O dispatcher. */
+static AsyncIOAction
+_on_io_dispatcher_query_reply(SDKCtlIODispatcher* dispatcher, AsyncSocketIO* asio)
+{
+    SDKCtlSocket* const sdkctl = dispatcher->sdkctl;
+    SDKCtlQuery* const query = dispatcher->current_query;
+    dispatcher->current_query = NULL;
+
+    if (query != NULL) {
+        _ANDROID_ASSERT(query->header.query_id == dispatcher->query_reply_header.query_id,
+                        "SDKCtl %s: Query ID mismatch in I/O dispatcher",
+                        sdkctl->service_name);
+        T("SDKCtl %s: Query reply is received for query %p ID %d. Reply size is %d",
+          dispatcher->sdkctl->service_name, query, query->header.query_id,
+          *query->response_size);
+
+        /* Complete the query, and release it from the dispatcher. */
+        _on_sdkctl_query_completed(query);
+        sdkctl_query_release(query);
+    } else {
+        /* This was "read up in the air" for a cancelled query. Just discard the
+         * read data. */
+        if (dispatcher->packet != NULL) {
+            sdkctl_packet_release(dispatcher->packet);
+            dispatcher->packet = NULL;
+        }
+    }
+
+    /* Get ready for the next I/O cycle. */
+    dispatcher->state = SDKCTL_IODISP_EXPECT_HEADER;
+    async_socket_read_rel(sdkctl->as, &dispatcher->header, sizeof(SDKCtlPacketHeader),
+                          _on_sdkctl_io_dispatcher_io, dispatcher, -1);
+    return ASIO_ACTION_DONE;
+}
+
+/* An I/O callback invoked when data gets received from the socket.
+ * This is main I/O dispatcher loop.
+ * Param:
+ *  io_opaque SDKCtlIODispatcher instance associated with the reader.
+ *  asio - Read I/O descriptor.
+ *  status - I/O status.
+ */
+static AsyncIOAction
+_on_sdkctl_io_dispatcher_io(void* io_opaque,
+                            AsyncSocketIO* asio,
+                            AsyncIOState status)
+{
+    AsyncIOAction action = ASIO_ACTION_DONE;
+    SDKCtlIODispatcher* const dispatcher = (SDKCtlIODispatcher*)io_opaque;
+    SDKCtlSocket* const sdkctl = dispatcher->sdkctl;
+
+    /* Reference SDKCtlSocket while we're in this callback. */
+    sdkctl_socket_reference(sdkctl);
+
+    if (status != ASIO_STATE_SUCCEEDED) {
+        /* Something going on with I/O other than receiving data.. */
+        switch (status) {
+            case ASIO_STATE_STARTED:
+                /* Data has started flowing in. Cancel timeout on I/O that has
+                 * started, so we can complete the current state of the
+                 * dispatcher without interruptions other than I/O failures. */
+                async_socket_io_cancel_time_out(asio);
+                break;
+
+            case ASIO_STATE_FAILED:
+                /* I/O failure has occurred. Handle the failure. */
+                _on_io_dispatcher_io_failure(dispatcher, asio);
+                break;
+
+            case ASIO_STATE_TIMED_OUT:
+                 /* The way I/O dispatcher is implemented, this should never
+                  * happen, because dispatcher doesn't set I/O expiration time
+                  * when registering its readers. */
+                _ANDROID_ASSERT(0,
+                    "SDKCtl %s: We should never receive ASIO_STATE_TIMED_OUT in SDKCtl I/O dispatcher.",
+                    sdkctl->service_name);
+                break;
+
+            case ASIO_STATE_CANCELLED:
+                /* Cancellation means that we're in the middle of handling
+                 * disconnection. Sooner or later, this dispatcher will be reset,
+                 * so we don't really care about keeping its state at this point.
+                 */
+                _on_io_dispatcher_io_cancelled(dispatcher, asio);
+                break;
+
+            case ASIO_STATE_FINISHED:
+                break;
+
+            default:
+                _ANDROID_ASSERT(0, "SDKCtl %s: Unexpected I/O status %d in the dispatcher",
+                                sdkctl->service_name, status);
+                /* Handle this as protocol failure. */
+                errno = EINVAL;
+                _on_io_dispatcher_io_failure(dispatcher, asio);
+                action = ASIO_ACTION_ABORT;
+                break;
+        }
+
+        sdkctl_socket_release(sdkctl);
+
+        return action;
+    }
+
+    /* Requested data has been read. Handle the chunk depending on dispatcher's
+     * state. */
+    switch (dispatcher->state) {
+        case SDKCTL_IODISP_EXPECT_HEADER:
+            /* A generic packet header is received. */
+            action = _on_io_dispatcher_packet_header(dispatcher, asio);
+            break;
+
+        case SDKCTL_IODISP_EXPECT_QUERY_REPLY_HEADER:
+            /* Query reply header is received. */
+            action = _on_io_dispatcher_query_reply_header(dispatcher, asio);
+            break;
+
+        case SDKCTL_IODISP_EXPECT_QUERY_REPLY_DATA:
+            /* Query reply is received. Complete the query. */
+            action = _on_io_dispatcher_query_reply(dispatcher, asio);
+            break;
+
+        case SDKCTL_IODISP_EXPECT_DATA:
+            /* A generic packet is received. */
+            action = _on_io_dispatcher_packet(dispatcher, asio);
+            break;
+
+        default:
+            _ANDROID_ASSERT(0, "SDKCtl %s: Unexpected I/O dispacher state %d",
+                            sdkctl->service_name, dispatcher->state);
+            break;
+    }
+
+    sdkctl_socket_release(sdkctl);
+
+    return action;
+}
+
+/********************************************************************************
+ *                       SDKCtlSocket internals.
+ *******************************************************************************/
+
+/* Cancels all queries that is active on this socket. */
+static void
+_sdkctl_socket_cancel_all_queries(SDKCtlSocket* sdkctl)
+{
+    SDKCtlIODispatcher* const dispatcher = &sdkctl->io_dispatcher;
+    SDKCtlQuery* query;
+
+    /* Cancel query that is being completed in dispatcher. */
+    if (dispatcher->current_query != NULL) {
+        SDKCtlQuery* const query = dispatcher->current_query;
+        dispatcher->current_query = NULL;
+        _on_sdkctl_query_cancelled(query);
+        sdkctl_query_release(query);
+    }
+
+    /* One by one empty query list cancelling pulled queries. */
+    query = _sdkctl_socket_pull_first_query(sdkctl);
+    while (query != NULL) {
+        _sdkctl_query_cancel_timeout(query);
+        query->query_cb(query->query_opaque, query, ASIO_STATE_CANCELLED);
+        sdkctl_query_release(query);
+        query = _sdkctl_socket_pull_first_query(sdkctl);
+    }
+}
+
+/* Cancels all packets that is active on this socket. */
+static void
+_sdkctl_socket_cancel_all_packets(SDKCtlSocket* sdkctl)
+{
+}
+
+/* Cancels all I/O that is active on this socket. */
+static void
+_sdkctl_socket_cancel_all_io(SDKCtlSocket* sdkctl)
+{
+    /* Cancel all queries, and packets that are active for this I/O. */
+    _sdkctl_socket_cancel_all_queries(sdkctl);
+    _sdkctl_socket_cancel_all_packets(sdkctl);
+}
+
+/* Disconnects AsyncSocket for SDKCtlSocket. */
+static void
+_sdkctl_socket_disconnect_socket(SDKCtlSocket* sdkctl)
+{
+    if (sdkctl->as != NULL) {
+        /* Disconnect the socket. This will trigger I/O cancellation callbacks. */
+        async_socket_disconnect(sdkctl->as);
+
+        /* Cancel all I/O that is active on this socket. */
+        _sdkctl_socket_cancel_all_io(sdkctl);
+
+        /* Reset I/O dispatcher. */
+        _sdkctl_io_dispatcher_reset(sdkctl);
+    }
+
+    sdkctl->state = SDKCTL_SOCKET_DISCONNECTED;
+}
+
+/* Frees SDKCtlSocket instance. */
+static void
+_sdkctl_socket_free(SDKCtlSocket* sdkctl)
+{
+    if (sdkctl != NULL) {
+        /* Disconnect, and release the socket. */
+        if (sdkctl->as != NULL) {
+            async_socket_disconnect(sdkctl->as);
+            async_socket_release(sdkctl->as);
+        }
+
+        /* Free allocated resources. */
+        if (sdkctl->looper != NULL) {
+            looper_free(sdkctl->looper);
+        }
+        if (sdkctl->service_name != NULL) {
+            free(sdkctl->service_name);
+        }
+        _sdkctl_socket_empty_recycler(sdkctl);
+
+        AFREE(sdkctl);
+    }
+}
+
+/********************************************************************************
+ *                    SDK Control Socket connection callbacks.
+ *******************************************************************************/
+
+/* Initiates handshake query when SDK controller socket is connected. */
+static void _sdkctl_do_handshake(SDKCtlSocket* sdkctl);
+
+/* A socket connection is established.
+ * Here we will start I/O dispatcher, and will initiate a handshake with
+ * the SdkController service for this socket. */
+static AsyncIOAction
+_on_async_socket_connected(SDKCtlSocket* sdkctl)
+{
+    D("SDKCtl %s: Socket is connected.", sdkctl->service_name);
+
+    /* Notify the client that connection is established. */
+    const AsyncIOAction action =
+        sdkctl->on_connection(sdkctl->opaque, sdkctl, ASIO_STATE_SUCCEEDED);
+
+    if (action == ASIO_ACTION_DONE) {
+        /* Initialize, and start main I/O dispatcher. */
+        _sdkctl_io_dispatcher_start(sdkctl);
+
+        /* Initiate handshake. */
+        _sdkctl_do_handshake(sdkctl);
+
+        return action;
+    } else {
+        /* Client didn't like something about this connection. */
+        return action;
+    }
+}
+
+/* Handles lost connection with SdkController service. */
+static AsyncIOAction
+_on_async_socket_disconnected(SDKCtlSocket* sdkctl)
+{
+    D("SDKCtl %s: Socket has been disconnected.", sdkctl->service_name);
+
+    _sdkctl_socket_disconnect_socket(sdkctl);
+
+    AsyncIOAction action = sdkctl->on_connection(sdkctl->opaque, sdkctl,
+                                                 ASIO_STATE_FAILED);
+    if (action == ASIO_ACTION_DONE) {
+        /* Default action for disconnect is to reestablish the connection. */
+        action = ASIO_ACTION_RETRY;
+    }
+    if (action == ASIO_ACTION_RETRY) {
+        sdkctl->state = SDKCTL_SOCKET_CONNECTING;
+    }
+    return action;
+}
+
+/* An entry point for all socket connection events.
+ * Here we will dispatch connection events to appropriate handlers.
+ * Param:
+ *  client_opaque - SDKCtlSocket isntance.
+ */
+static AsyncIOAction
+_on_async_socket_connection(void* client_opaque,
+                            AsyncSocket* as,
+                            AsyncIOState status)
+{
+    AsyncIOAction action = ASIO_ACTION_DONE;
+    SDKCtlSocket* const sdkctl = (SDKCtlSocket*)client_opaque;
+
+    /* Reference the socket while in this callback. */
+    sdkctl_socket_reference(sdkctl);
+
+    switch (status) {
+        case ASIO_STATE_SUCCEEDED:
+            sdkctl->state = SDKCTL_SOCKET_CONNECTED;
+            _on_async_socket_connected(sdkctl);
+            break;
+
+        case ASIO_STATE_FAILED:
+            if (sdkctl->state == SDKCTL_SOCKET_CONNECTED) {
+                /* This is disconnection condition. */
+                action = _on_async_socket_disconnected(sdkctl);
+            } else {
+                /* An error has occurred while attempting to connect to socket.
+                 * Lets try again... */
+                action = ASIO_ACTION_RETRY;
+            }
+            break;
+
+        case ASIO_STATE_RETRYING:
+        default:
+            action = ASIO_ACTION_RETRY;
+            break;
+    }
+
+    sdkctl_socket_release(sdkctl);
+
+    return action;
+}
+
+/********************************************************************************
+ *                      SDK Control Socket public API
+ *******************************************************************************/
+
+SDKCtlSocket*
+sdkctl_socket_new(int reconnect_to,
+                  const char* service_name,
+                  on_sdkctl_connection_cb on_connection,
+                  on_sdkctl_handshake_cb on_handshake,
+                  on_sdkctl_message_cb on_message,
+                  void* opaque)
+{
+    SDKCtlSocket* sdkctl;
+    ANEW0(sdkctl);
+
+    sdkctl->state               = SDKCTL_SOCKET_DISCONNECTED;
+    sdkctl->opaque              = opaque;
+    sdkctl->service_name        = ASTRDUP(service_name);
+    sdkctl->on_connection       = on_connection;
+    sdkctl->on_handshake        = on_handshake;
+    sdkctl->on_message          = on_message;
+    sdkctl->reconnect_to        = reconnect_to;
+    sdkctl->as                  = NULL;
+    sdkctl->next_query_id       = 0;
+    sdkctl->query_head          = sdkctl->query_tail = NULL;
+    sdkctl->ref_count           = 1;
+    sdkctl->recycler            = NULL;
+    sdkctl->recycler_block_size = 0;
+    sdkctl->recycler_max        = 0;
+    sdkctl->recycler_count      = 0;
+
+    sdkctl->looper = looper_newCore();
+    if (sdkctl->looper == NULL) {
+        E("Unable to create I/O looper for SDKCtl socket '%s'",
+          service_name);
+        on_connection(opaque, sdkctl, ASIO_STATE_FAILED);
+        _sdkctl_socket_free(sdkctl);
+        return NULL;
+    }
+
+    return sdkctl;
+}
+
+int sdkctl_socket_reference(SDKCtlSocket* sdkctl)
+{
+    assert(sdkctl->ref_count > 0);
+    sdkctl->ref_count++;
+    return sdkctl->ref_count;
+}
+
+int
+sdkctl_socket_release(SDKCtlSocket* sdkctl)
+{
+    assert(sdkctl->ref_count > 0);
+    sdkctl->ref_count--;
+    if (sdkctl->ref_count == 0) {
+        /* Last reference has been dropped. Destroy this object. */
+        _sdkctl_socket_free(sdkctl);
+        return 0;
+    }
+    return sdkctl->ref_count;
+}
+
+void
+sdkctl_init_recycler(SDKCtlSocket* sdkctl,
+                     uint32_t data_size,
+                     int max_recycled_num)
+{
+    if (sdkctl->recycler != NULL) {
+        D("SDKCtl %s: Recycler is already initialized. Ignoring recycler init.",
+          sdkctl->service_name);
+        return;
+    }
+
+    /* SDKCtlQuery is max descriptor sizeof. */
+    data_size += sizeof(SDKCtlQuery);
+
+    sdkctl->recycler_block_size = data_size;
+    sdkctl->recycler_max        = max_recycled_num;
+    sdkctl->recycler_count      = 0;
+}
+
+void
+sdkctl_socket_connect(SDKCtlSocket* sdkctl, int port, int retry_to)
+{
+    T("SDKCtl %s: Handling connect request to port %d, retrying in %dms...",
+      sdkctl->service_name, port, retry_to);
+
+    sdkctl->state = SDKCTL_SOCKET_CONNECTING;
+    sdkctl->as = async_socket_new(port, sdkctl->reconnect_to,
+                                  _on_async_socket_connection, sdkctl,
+                                  sdkctl->looper);
+    if (sdkctl->as == NULL) {
+        E("Unable to allocate AsyncSocket for SDKCtl socket '%s'",
+           sdkctl->service_name);
+        sdkctl->on_connection(sdkctl->opaque, sdkctl, ASIO_STATE_FAILED);
+    } else {
+        async_socket_connect(sdkctl->as, retry_to);
+    }
+}
+
+void
+sdkctl_socket_reconnect(SDKCtlSocket* sdkctl, int port, int retry_to)
+{
+    T("SDKCtl %s: Handling reconnection request to port %d, retrying in %dms...",
+      sdkctl->service_name, port, retry_to);
+
+    _sdkctl_socket_disconnect_socket(sdkctl);
+
+    if (sdkctl->as == NULL) {
+        sdkctl_socket_connect(sdkctl, port, retry_to);
+    } else {
+        sdkctl->state = SDKCTL_SOCKET_CONNECTING;
+        async_socket_reconnect(sdkctl->as, retry_to);
+    }
+}
+
+void
+sdkctl_socket_disconnect(SDKCtlSocket* sdkctl)
+{
+    T("SDKCtl %s: Handling disconnect request.", sdkctl->service_name);
+
+    _sdkctl_socket_disconnect_socket(sdkctl);
+}
+
+
+/********************************************************************************
+ *                       Handshake query
+ *******************************************************************************/
+
+/* A callback that is ivoked on handshake I/O events. */
+static AsyncIOAction
+_on_handshake_io(void* query_opaque,
+                 SDKCtlQuery* query,
+                 AsyncIOState status)
+{
+    SDKCtlSocket* const sdkctl = (SDKCtlSocket*)query_opaque;
+
+    if (status == ASIO_STATE_SUCCEEDED) {
+        D("SDKCtl %s: %d bytes of handshake reply is received.",
+          sdkctl->service_name, *query->response_size);
+
+        /* Handshake is received. Inform the client. */
+        sdkctl->on_handshake(sdkctl->opaque, sdkctl, *query->response_buffer,
+                             *query->response_size, status);
+    } else {
+        /* Something is going on with the handshake... */
+        switch (status) {
+            case ASIO_STATE_FAILED:
+            case ASIO_STATE_TIMED_OUT:
+            case ASIO_STATE_CANCELLED:
+              D("SDKCtl %s: Handshake failed: I/O state %d. Error: %d -> %s",
+                sdkctl->service_name, status, errno, strerror(errno));
+                sdkctl->on_handshake(sdkctl->opaque, sdkctl,
+                                     *query->response_buffer,
+                                     *query->response_size, status);
+                break;
+
+            default:
+                break;
+        }
+    }
+    return ASIO_ACTION_DONE;
+}
+
+static AsyncIOAction
+_on_sdkctl_endianness_io(void* io_opaque,
+                         AsyncSocketIO* asio,
+                         AsyncIOState status) {
+    SDKCtlSocket* const sdkctl = (SDKCtlSocket*)io_opaque;
+
+    if (status == ASIO_STATE_SUCCEEDED) {
+        /* Now it's time to initiate handshake message. */
+        D("SDKCtl %s: Sending handshake query...", sdkctl->service_name);
+        SDKCtlQuery* query =
+            sdkctl_query_build_and_send(sdkctl, SDKCTL_QUERY_HANDSHAKE,
+                                        strlen(sdkctl->service_name),
+                                        sdkctl->service_name, NULL, NULL,
+                                        _on_handshake_io, sdkctl, 3000);
+        sdkctl_query_release(query);
+        return ASIO_ACTION_DONE;
+    } else {
+        /* Something is going on with the endianness... */
+        switch (status) {
+                case ASIO_STATE_FAILED:
+                case ASIO_STATE_TIMED_OUT:
+                case ASIO_STATE_CANCELLED:
+                  D("SDKCtl %s: endianness failed: I/O state %d. Error: %d -> %s",
+                    sdkctl->service_name, status, errno, strerror(errno));
+                    sdkctl->on_handshake(sdkctl->opaque, sdkctl, NULL, 0, status);
+                    break;
+
+                default:
+                    break;
+        }
+    }
+    return ASIO_ACTION_DONE;
+}
+
+static void
+_sdkctl_do_handshake(SDKCtlSocket* sdkctl)
+{
+#ifndef HOST_WORDS_BIGENDIAN
+static const char _host_end = 0;
+#else
+static const char _host_end = 1;
+#endif
+
+    D("SDKCtl %s: Sending endianness: %d...", sdkctl->service_name, _host_end);
+
+    /* Before we can send any structured data to the SDK controller we need to
+     * report endianness of the host. */
+    async_socket_write_rel(sdkctl->as, &_host_end, 1,
+                           _on_sdkctl_endianness_io, sdkctl, 3000);
+}
diff --git a/android/sdk-controller-socket.h b/android/sdk-controller-socket.h
new file mode 100644
index 0000000..03b12f4
--- /dev/null
+++ b/android/sdk-controller-socket.h
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2012 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_SDKCONTROL_SOCKET_H_
+#define ANDROID_SDKCONTROL_SOCKET_H_
+
+#include "android/async-socket.h"
+
+/*
+ * Contains declaration of an API that encapsulates communication protocol with
+ * SdkController running on an Android device.
+ *
+ * SdkController is used to provide:
+ *
+ * - Realistic sensor emulation.
+ * - Multi-touch emulation.
+ * - Open for other types of emulation.
+ *
+ * The idea behind this type of emulation is such that there is an actual
+ * Android device that is connected via USB to the host running the emulator.
+ * On the device there is an SdkController service running that enables
+ * communication between an Android application that gathers information required
+ * by the emulator, and transmits that info to the emulator.
+ *
+ * SdkController service on the device, and SDKCtlSocket API implemented here
+ * implement the exchange protocol between an Android application, and emulation
+ * engine running inside the emulator.
+ *
+ * In turn, the exchange protocol is implemented on top of asynchronous socket
+ * communication (abstracted in AsyncSocket protocol implemented in
+ * android/async-socket.*). It means that connection, and all data transfer
+ * (both, in, and out) are completely asynchronous, and results of each operation
+ * are reported through callbacks.
+ *
+ * Essentially, this entire API implements two types of protocols:
+ *
+ * - Connection protocol.
+ * - Data exchange protocol.
+ *
+ * 1. Connection protocol.
+ *
+ * Connecting to SdkController service on the attached device can be broken down
+ * into two phases:
+ * - Connecting to a TCP socket.
+ * - Sending a "handshake" query to the SdkController.
+ *
+ * 1.1. Connecting to the socket.
+ *
+ * TCP socket connection with SdkController is enabled by using adb port
+ * forwarding. SdkController is always listening to a local abstract socket named
+ * 'android.sdk.controller', so to enable connecting to it from the host, run
+ *
+ *   adb forward tcp:<port> localabstract: android.sdk.controller
+ *
+ * After that TCP socket for the requested port can be used to connect to
+ * SdkController, and connecting to it is no different than connecting to any
+ * socket server. Except for one thing: adb port forwarding is implemented in
+ * such a way, that socket_connect will always succeed, even if there is no
+ * server listening to that port on the other side of connection. Moreover,
+ * even socked_send will succeed in this case, so the only way to ensure that
+ * SdkController in deed is listening is to exchange a handshake with it:
+ * Fortunatelly, an attempt to read from forwarded TCP port on condition that
+ * there is no listener on the oher side will fail.
+ *
+ * 1.2. Handshake query.
+ *
+ * Handshake query is a special type of query that SDKCtlSocket sends to the
+ * SdkController upon successful socket connection. This query served two
+ * purposes:
+ * - Informs the SdkController about host endianness. This information is
+ *   important, because SdkController needs it in order to format its messages
+ *   with proper endianness.
+ * - Ensures that SdkController is in deed listening on the other side of the
+ *   connected socket.
+ *
+ * Connection with SdkController is considered to be successfuly established when
+ * SdkController responds to the handshake query, thus, completing the connection.
+ *
+ * 2. Data exchange protocol.
+ *
+ * As it was mentioned above, all data transfer in this API is completely
+ * asynchronous, and result of each data transfer is reported via callbacks.
+ * This also complicates destruction of data involved in exchange, since in an
+ * asynchronous environment it's hard to control the lifespan of an object, its
+ * owner, and who and when is responsible to free resources allocated for the
+ * transfer. To address this issue, all the descriptors that this API operates
+ * with are referenced on use / released after use, and get freed when reference
+ * counter for them drops to zero, indicating that there is no component that is
+ * interested in that particular descriptor.
+ *
+ * There are three types of data in the exchange protocol:
+ * - A packet - the simplest type of data that doesn't require any replies.
+ * - A query - A message that require a reply, and
+ * - A query reply - A message that delivers query reply.
+ */
+
+/* Declares SDK controller socket descriptor. */
+typedef struct SDKCtlSocket SDKCtlSocket;
+
+/* Declares SDK controller data packet descriptor. */
+typedef struct SDKCtlPacket SDKCtlPacket;
+
+/* Declares SDK controller query descriptor. */
+typedef struct SDKCtlQuery SDKCtlQuery;
+
+/* Defines client's callback set to monitor SDK controller socket connection.
+ *
+ * SDKCtlSocket will invoke this callback when connection to TCP port is
+ * established, but before handshake query is processed. The client should use
+ * on_sdkctl_handshake_cb to receive notification about an operational connection
+ * with SdkController.
+ *
+ * The main purpose of this callback for the client is to monitor connection
+ * state: in addition to TCP port connection, this callback will be invoked when
+ * connection with the port is lost.
+ *
+ * Param:
+ *  client_opaque - An opaque pointer associated with the client.
+ *  sdkctl - Initialized SDKCtlSocket instance.
+ *  status - Socket connection status.  Can be one of these:
+ *    - ASIO_STATE_SUCCEEDED : Socket is connected to the port.
+ *    - ASIO_STATE_FAILED    : Connection attempt has failed, or connection with
+ *                             the port is lost.
+ * Return:
+ *  One of the AsyncIOAction values.
+ */
+typedef AsyncIOAction (*on_sdkctl_connection_cb)(void* client_opaque,
+                                                 SDKCtlSocket* sdkctl,
+                                                 AsyncIOState status);
+
+/* Defines client's callback set to receive handshake reply from the SdkController
+ * service running on the device.
+ *
+ * Successful handshake means that connection between the client and SDK
+ * controller service has been established.
+ *
+ * Param:
+ *  client_opaque - An opaque pointer associated with the client.
+ *  sdkctl - Initialized SDKCtlSocket instance.
+ *  handshake - Handshake message received from the SDK controller service.
+ *  handshake_size - Size of the fandshake message received from the SDK
+ *      controller service.
+ *  status - Handshake status. Note that handshake, and handshake_size are valid
+ *      only if this parameter is set to ASIO_STATE_SUCCEEDED.
+ */
+typedef void (*on_sdkctl_handshake_cb)(void* client_opaque,
+                                       SDKCtlSocket* sdkctl,
+                                       void* handshake,
+                                       uint32_t handshake_size,
+                                       AsyncIOState status);
+
+/* Defines a message notification callback.
+ * Param:
+ *  client_opaque - An opaque pointer associated with the client.
+ *  sdkctl - Initialized SDKCtlSocket instance.
+ *  message - Descriptor for received message. Note that message descriptor will
+ *      be released upon exit from this callback (thus, could be freed along
+ *      with message data). If the client is interested in working with that
+ *      message after the callback returns, it should reference the message
+ *      descriptor in this callback.
+ *  msg_type - Message type.
+ *  msg_data, msg_size - Message data.
+ */
+typedef void (*on_sdkctl_message_cb)(void* client_opaque,
+                                     SDKCtlSocket* sdkctl,
+                                     SDKCtlPacket* message,
+                                     int msg_type,
+                                     void* msg_data,
+                                     int msg_size);
+
+/* Defines query completion callback.
+ * Param:
+ *  query_opaque - An opaque pointer associated with the query by the client.
+ *  query - Query descriptor.  Note that query descriptor will be released upon
+ *      exit from this callback (thus, could be freed along with query data). If
+ *      the client is interested in working with that query after the callback
+ *      returns, it should reference the query descriptor in this callback.
+ *  status - Query status. Can be one of these:
+ *    - ASIO_STATE_CONTINUES : Query data has been transmitted to the service,
+ *                             and query is now waiting for response.
+ *    - ASIO_STATE_SUCCEEDED : Query has been successfully completed.
+ *    - ASIO_STATE_FAILED    : Query has failed on an I/O.
+ *    - ASIO_STATE_TIMED_OUT : Deadline set for the query has expired.
+ *    - ASIO_STATE_CANCELLED : Query has been cancelled due to socket
+ *                             disconnection.
+ * Return:
+ *  One of the AsyncIOAction values.
+ */
+typedef AsyncIOAction (*on_sdkctl_query_cb)(void* query_opaque,
+                                            SDKCtlQuery* query,
+                                            AsyncIOState status);
+
+/********************************************************************************
+ *                         SDKCtlPacket API
+ ********************************************************************************/
+
+/* References SDKCtlPacket object.
+ * Param:
+ *  packet - Initialized SDKCtlPacket instance.
+ * Return:
+ *  Number of outstanding references to the object.
+ */
+extern int sdkctl_packet_reference(SDKCtlPacket* packet);
+
+/* Releases SDKCtlPacket object.
+ * Note that upon exiting from this routine the object might be destroyed, even
+ * if this routine returns value other than zero.
+ * Param:
+ *  packet - Initialized SDKCtlPacket instance.
+ * Return:
+ *  Number of outstanding references to the object.
+ */
+extern int sdkctl_packet_release(SDKCtlPacket* packet);
+
+/********************************************************************************
+ *                          SDKCtlQuery API
+ ********************************************************************************/
+
+/* Creates, and partially initializes query descriptor.
+ * Note that returned descriptor is referenced, and it must be eventually
+ * released with a call to sdkctl_query_release.
+ * Param:
+ *  sdkctl - SDKCtlSocket instance for the query.
+ *  query_type - Defines query type.
+ *  in_data_size Size of the query's input buffer (data to be sent with this
+ *      query). Note that buffer for query data will be allocated along with the
+ *      query descriptor. Use sdkctl_query_get_buffer_in to get address of data
+ *      buffer for this query.
+ * Return:
+ *  Referenced SDKCtlQuery descriptor.
+ */
+extern SDKCtlQuery* sdkctl_query_new(SDKCtlSocket* sdkctl,
+                                     int query_type,
+                                     uint32_t in_data_size);
+
+/* Creates, and fully initializes query descriptor.
+ * Note that returned descriptor is referenced, and it must be eventually
+ * released with a call to sdkctl_query_release.
+ * Param:
+ *  sdkctl - SDKCtlSocket instance for the query.
+ *  query_type - Defines query type.
+ *  in_data_size Size of the query's input buffer (data to be sent with this
+ *      query). Note that buffer for query data will be allocated along with the
+ *      query descriptor. Use sdkctl_query_get_buffer_in to get address of data
+ *      buffer for this query.
+ *  in_data - Data to initialize query's input buffer with.
+ *  response_buffer - Contains address of the buffer addressing preallocated
+ *      response buffer on the way in, and address of the buffer containing query
+ *      response on query completion. If this parameter is NULL, the API will
+ *      allocate its own query response buffer, which is going to be freed after
+ *      query completion.
+ *  response_size - Contains size of preallocated response buffer on the way in,
+ *      and size of the received response on query completion. Can be NULL.
+ *  query_cb - A callback to monitor query state.
+ *  query_opaque - An opaque pointer associated with the query.
+ * Return:
+ *  Referenced SDKCtlQuery descriptor.
+ */
+extern SDKCtlQuery* sdkctl_query_new_ex(SDKCtlSocket* sdkctl,
+                                        int query_type,
+                                        uint32_t in_data_size,
+                                        const void* in_data,
+                                        void** response_buffer,
+                                        uint32_t* response_size,
+                                        on_sdkctl_query_cb query_cb,
+                                        void* query_opaque);
+
+/* Sends query to SDK controller.
+ * Param:
+ *  query - Query to send. Note that this must be a fully initialized query
+ *      descriptor.
+ *  to - Milliseconds to allow for the query to complete. Negative value means
+ *  "forever".
+ */
+extern void sdkctl_query_send(SDKCtlQuery* query, int to);
+
+/* Creates, fully initializes query descriptor, and sends the query to SDK
+ * controller.
+ * Note that returned descriptor is referenced, and it must be eventually
+ * released with a call to sdkctl_query_release.
+ * Param:
+ *  sdkctl - SDKCtlSocket instance for the query.
+ *  query_type - Defines query type.
+ *  in_data_size Size of the query's input buffer (data to be sent with this
+ *      query). Note that buffer for query data will be allocated along with the
+ *      query descriptor. Use sdkctl_query_get_buffer_in to get address of data
+ *      buffer for this query.
+ *  in_data - Data to initialize query's input buffer with.
+ *  response_buffer - Contains address of the buffer addressing preallocated
+ *      response buffer on the way in, and address of the buffer containing query
+ *      response on query completion. If this parameter is NULL, the API will
+ *      allocate its own query response buffer, which is going to be freed after
+ *      query completion.
+ *  response_size - Contains size of preallocated response buffer on the way in,
+ *      and size of the received response on query completion. Can be NULL.
+ *  query_cb - A callback to monitor query state.
+ *  query_opaque - An opaque pointer associated with the query.
+ *  to - Milliseconds to allow for the query to complete. Negative value means
+ *  "forever".
+ * Return:
+ *  Referenced SDKCtlQuery descriptor for the query that has been sent.
+ */
+extern SDKCtlQuery* sdkctl_query_build_and_send(SDKCtlSocket* sdkctl,
+                                                int query_type,
+                                                uint32_t in_data_size,
+                                                const void* in_data,
+                                                void** response_buffer,
+                                                uint32_t* response_size,
+                                                on_sdkctl_query_cb query_cb,
+                                                void* query_opaque,
+                                                int to);
+
+/* References SDKCtlQuery object.
+ * Param:
+ *  query - Initialized SDKCtlQuery instance.
+ * Return:
+ *  Number of outstanding references to the object.
+ */
+extern int sdkctl_query_reference(SDKCtlQuery* query);
+
+/* Releases SDKCtlQuery object.
+ * Note that upon exit from this routine the object might be destroyed, even if
+ * this routine returns value other than zero.
+ * Param:
+ *  query - Initialized SDKCtlQuery instance.
+ * Return:
+ *  Number of outstanding references to the object.
+ */
+extern int sdkctl_query_release(SDKCtlQuery* query);
+
+/* Gets address of query's input data buffer (data to be send).
+ * Param:
+ *  query - Query to get data buffer for.
+ * Return:
+ *  Address of query's input data buffer.
+ */
+extern void* sdkctl_query_get_buffer_in(SDKCtlQuery* query);
+
+/********************************************************************************
+ *                          SDKCtlSocket API
+ ********************************************************************************/
+
+/* Creates an SDK controller socket descriptor.
+ * Param:
+ *  reconnect_to - Timeout before trying to reconnect, or retry connection
+ *      attempts after disconnection, or on connection failures.
+ *  service_name - Name of the SdkController service for this socket (such as
+ *      'sensors', 'milti-touch', etc.)
+ *  on_connection - A callback to invoke on socket connection events.
+ *  on_handshake - A callback to invoke on handshake events.
+ *  on_message - A callback to invoke when a message is received from the SDK
+ *      controller.
+ *  opaque - An opaque pointer to associate with the socket.
+ * Return:
+ *  Initialized SDKCtlSocket instance on success, or NULL on failure.
+ */
+extern SDKCtlSocket* sdkctl_socket_new(int reconnect_to,
+                                       const char* service_name,
+                                       on_sdkctl_connection_cb on_connection,
+                                       on_sdkctl_handshake_cb on_handshake,
+                                       on_sdkctl_message_cb on_message,
+                                       void* opaque);
+
+/* Improves throughput by recycling memory allocated for buffers transferred via
+ * this API.
+ *
+ * In many cases data exchanged between SDK controller sides are small, and,
+ * although may come quite often, are coming in a sequential manner. For instance,
+ * sensor service on the device may send us sensor updates every 20ms, one after
+ * another. For such data traffic it makes perfect sense to recycle memory
+ * allocated for the previous sensor update, rather than to free it just to
+ * reallocate same buffer in 20ms. This routine sets properties of the recycler
+ * for the given SDK controller connection. Recycling includes memory allocated
+ * for all types of data transferred in this API: packets, and queries.
+ * Param:
+ *  sdkctl - Initialized SDKCtlSocket instance.
+ *  data_size - Size of buffer to allocate for each data block.
+ *  max_recycled_num - Maximum number of allocated buffers to keep in the
+ *      recycler.
+ */
+extern void sdkctl_init_recycler(SDKCtlSocket* sdkctl,
+                                 uint32_t data_size,
+                                 int max_recycled_num);
+
+/* References SDKCtlSocket object.
+ * Param:
+ *  sdkctl - Initialized SDKCtlSocket instance.
+ * Return:
+ *  Number of outstanding references to the object.
+ */
+extern int sdkctl_socket_reference(SDKCtlSocket* sdkctl);
+
+/* Releases SDKCtlSocket object.
+ * Note that upon exit from this routine the object might be destroyed, even if
+ * this routine returns value other than zero.
+ * Param:
+ *  sdkctl - Initialized SDKCtlSocket instance.
+ * Return:
+ *  Number of outstanding references to the object.
+ */
+extern int sdkctl_socket_release(SDKCtlSocket* sdkctl);
+
+/* Asynchronously connects to SDK controller.
+ * Param:
+ *  sdkctl - Initialized SDKCtlSocket instance.
+ *  port - TCP port to connect the socket to.
+ *  retry_to - Number of milliseconds to wait before retrying a failed
+ *      connection attempt.
+ */
+extern void sdkctl_socket_connect(SDKCtlSocket* sdkctl, int port, int retry_to);
+
+/* Asynchronously reconnects to SDK controller.
+ * Param:
+ *  sdkctl - Initialized SDKCtlSocket instance.
+ *  port - TCP port to reconnect the socket to.
+ *  retry_to - Number of milliseconds to wait before reconnecting. Same timeout
+ *      will be used for retrying a failed connection attempt.
+ */
+extern void sdkctl_socket_reconnect(SDKCtlSocket* sdkctl, int port, int retry_to);
+
+/* Disconnects from SDK controller.
+ * Param:
+ *  sdkctl - Initialized SDKCtlSocket instance.
+ */
+extern void sdkctl_socket_disconnect(SDKCtlSocket* sdkctl);
+
+#endif  /* ANDROID_SDKCONTROL_SOCKET_H_ */
diff --git a/android/sensors-port.c b/android/sensors-port.c
index 9e9351d..8620783 100644
--- a/android/sensors-port.c
+++ b/android/sensors-port.c
@@ -173,6 +173,42 @@
  *                          Sensors port API
  *******************************************************************************/
 
+#include "android/sdk-controller-socket.h"
+
+static AsyncIOAction
+_on_sdkctl_connection(void* client_opaque, SDKCtlSocket* sdkctl, AsyncIOState status)
+{
+    if (status == ASIO_STATE_FAILED) {
+        sdkctl_socket_reconnect(sdkctl, 1970, 20);
+    }
+    return ASIO_ACTION_DONE;
+}
+
+void on_sdkctl_handshake(void* client_opaque,
+                         SDKCtlSocket* sdkctl,
+                         void* handshake,
+                         uint32_t handshake_size,
+                         AsyncIOState status)
+{
+    if (status == ASIO_STATE_SUCCEEDED) {
+        printf("---------- Handshake %d bytes received.\n", handshake_size);
+    } else {
+        printf("!!!!!!!!!! Handshake failed with status %d: %d -> %s\n",
+               status, errno, strerror(errno));
+        sdkctl_socket_reconnect(sdkctl, 1970, 20);
+    }
+}
+
+void on_sdkctl_message(void* client_opaque,
+                       SDKCtlSocket* sdkctl,
+                       SDKCtlPacket* message,
+                       int msg_type,
+                       void* msg_data,
+                       int msg_size)
+{
+    printf("########################################################\n");
+}
+
 AndroidSensorsPort*
 sensors_port_create(void* opaque)
 {
@@ -180,6 +216,12 @@
     char* wrk;
     int res;
 
+    SDKCtlSocket* sdkctl = sdkctl_socket_new(20, "test", _on_sdkctl_connection,
+                                             on_sdkctl_handshake, on_sdkctl_message,
+                                             NULL);
+//    sdkctl_init_recycler(sdkctl, 64, 8);
+    sdkctl_socket_connect(sdkctl, 1970, 20);
+
     ANEW0(asp);
     asp->opaque = opaque;
     asp->is_connected = 0;
diff --git a/android/utils/debug.h b/android/utils/debug.h
index d6fd60c..5aa52ba 100644
--- a/android/utils/debug.h
+++ b/android/utils/debug.h
@@ -46,6 +46,7 @@
     _VERBOSE_TAG(adb,          "ADB debugger") \
     _VERBOSE_TAG(asconnector,  "Asynchronous socket connector") \
     _VERBOSE_TAG(asyncsocket,  "Asynchronous socket") \
+    _VERBOSE_TAG(sdkctlsocket, "Socket tethering to SdkControl server") \
 
 #define  _VERBOSE_TAG(x,y)  VERBOSE_##x,
 typedef enum {