Use new SdkController communication protocol for emulation ports

android/sdk-control-socket.* has replaced android/android-device.* as the back-bone
of communicating with SDK controller on the device. The major differences are:

- New communication protocol uses just one (async) socket connection to communicate
  with the device (the old one used two sockets: one sync, and another - async).
- New communication protocol connects to one TCP port (1970 in this CL) for all emulation
  ports. Channel multiplexing is done by using port names, and assigning a separate socket
  for communication inside each separate port. The old protocol had separate TCP ports for
  each emulation ports (1968 for sensors, and 1969 for multi-touch)

Change-Id: I779fcbdfba2f9b4c433a9d76a567975708b00469
diff --git a/Makefile.common b/Makefile.common
index 20b6fa7..4382bd2 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -478,7 +478,6 @@
     android/hw-pipe-net.c \
     android/qemu-setup.c \
     android/snapshot.c \
-    android/android-device.c \
     android/async-socket-connector.c \
     android/async-socket.c \
     android/sdk-controller-socket.c \
diff --git a/android/android-device.c b/android/android-device.c
deleted file mode 100644
index 5f88108..0000000
--- a/android/android-device.c
+++ /dev/null
@@ -1,1526 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * 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 "android/android-device.h"
-#include "utils/panic.h"
-#include "iolooper.h"
-
-#define  E(...)    derror(__VA_ARGS__)
-#define  W(...)    dwarning(__VA_ARGS__)
-#define  D(...)    VERBOSE_PRINT(adevice,__VA_ARGS__)
-#define  D_ACTIVE  VERBOSE_CHECK(adevice)
-
-/********************************************************************************
- *                       Common android device socket
- *******************************************************************************/
-
-/* Milliseconds between retrying asynchronous connections to the device. */
-#define ADS_RETRY_CONNECTION_TIMEOUT 3000
-
-/* Socket type. */
-typedef enum ADSType {
-    /* Query socket. */
-    ADS_TYPE_QUERY = 0,
-    /* Events socket. */
-    ADS_TYPE_EVENT = 1
-} ADSType;
-
-/* Status of the socket. */
-typedef enum ADSStatus {
-    /* Socket is disconnected. */
-    ADS_DISCONNECTED,
-    /* Connection process has been started. */
-    ADS_CONNECTING,
-    /* Connection has been established. */
-    ADS_CONNECTED,
-    /* Socket has been registered with the server. */
-    ADS_REGISTERED,
-} ADSStatus;
-
-/* Identifies socket as a "query" socket with the server. */
-static const char* _ads_query_socket_id = "query";
-/* Identifies socket as an "event" socket with the server. */
-static const char* _ads_event_socket_id = "event";
-
-/* Android device socket descriptor. */
-typedef struct AndroidDevSocket AndroidDevSocket;
-
-/*
- * Callback routines.
- */
-
-/* Callback routine that is called when a socket is connected.
- * Param:
- *  opaque - Opaque pointer associated with the socket. Typicaly it's the same
- *      pointer that is associated with AndroidDevice instance.
- *  ads - Connection socket.
- *  failure - If zero, indicates that socket has been successuly connected. If a
- *      connection error has occured, this parameter contains the error code (as
- *      in 'errno).
- */
-typedef void (*ads_socket_connected_cb)(void* opaque,
-                                        struct AndroidDevSocket* ads,
-                                        int failure);
-
-/* Android device socket descriptor. */
-struct AndroidDevSocket {
-    /* Socket type. */
-    ADSType             type;
-    /* Socket status. */
-    ADSStatus           socket_status;
-    /* TCP address for the socket. */
-    SockAddress         address;
-    /* Android device descriptor that owns the socket. */
-    AndroidDevice*      ad;
-    /* Opaque pointer associated with the socket. Typicaly it's the same
-     * pointer that is associated with AndroidDevice instance.*/
-    void*               opaque;
-    /* Deadline for current I/O performed on the socket. */
-    Duration            deadline;
-    /* Socket's file descriptor. */
-    int                 fd;
-};
-
-/* Query socket descriptor. */
-typedef struct AndroidQuerySocket {
-    /* Common device socket. */
-    AndroidDevSocket    dev_socket;
-} AndroidQuerySocket;
-
-/* Describes data to send via an asynchronous socket. */
-typedef struct AsyncSendBuffer {
-    /* Next buffer to send. */
-    struct AsyncSendBuffer* next;
-    /* Callback to invoke when data transfer is completed. */
-    async_send_cb           complete_cb;
-    /* An opaque pointer to pass to the transfer completion callback. */
-    void*                   complete_opaque;
-    /* Data to send. */
-    uint8_t*                data;
-    /* Size of the entire data buffer. */
-    int                     data_size;
-    /* Remaining bytes to send. */
-    int                     data_remaining;
-    /* Boolean flag indicating whether to free data buffer upon completion. */
-    int                     free_on_completion;
-} AsyncSendBuffer;
-
-/* Event socket descriptor. */
-typedef struct AndroidEventSocket {
-    /* Common socket descriptor. */
-    AndroidDevSocket        dev_socket;
-    /* Asynchronous connector to the device. */
-    AsyncConnector          connector[1];
-    /* I/O port for asynchronous I/O on this socket. */
-    LoopIo                  io[1];
-    /* Asynchronous string reader. */
-    AsyncLineReader         alr;
-    /* Callback to call at the end of the asynchronous connection to this socket.
-     * Can be NULL. */
-    ads_socket_connected_cb on_connected;
-    /* Callback to call when an event is received on this socket. Can be NULL. */
-    event_cb                on_event;
-    /* Lists buffers that are pending to be sent. */
-    AsyncSendBuffer*        send_pending;
-} AndroidEventSocket;
-
-/* Android device descriptor. */
-struct AndroidDevice {
-    /* Query socket for the device. */
-    AndroidQuerySocket  query_socket;
-    /* Event socket for the device. */
-    AndroidEventSocket  event_socket;
-    /* An opaque pointer associated with this descriptor. */
-    void*               opaque;
-    /* I/O looper for synchronous I/O on the sockets for this device. */
-    IoLooper*           io_looper;
-    /* Timer that is used to retry asynchronous connections. */
-    LoopTimer           timer[1];
-    /* I/O looper for asynchronous I/O. */
-    Looper*             looper;
-    /* Callback to call when device is fully connected. */
-    device_connected_cb on_connected;
-    /* I/O failure callback .*/
-    io_failure_cb       on_io_failure;
-};
-
-/* Creates descriptor for a buffer to send asynchronously.
- * Param:
- *  data, size - Buffer to send.
- *  free_on_close - Boolean flag indicating whether to free data buffer upon
- *      completion.
- *  cb - Callback to invoke when data transfer is completed.
- *  opaque - An opaque pointer to pass to the transfer completion callback.
- */
-static AsyncSendBuffer*
-_async_send_buffer_create(void* data,
-                          int size,
-                          int free_on_close,
-                          async_send_cb cb,
-                          void* opaque)
-{
-    AsyncSendBuffer* desc = malloc(sizeof(AsyncSendBuffer));
-    if (desc == NULL) {
-        APANIC("Unable to allocate %d bytes for AsyncSendBuffer",
-              sizeof(AsyncSendBuffer));
-    }
-    desc->next = NULL;
-    desc->data = (uint8_t*)data;
-    desc->data_size = desc->data_remaining = size;
-    desc->free_on_completion = free_on_close;
-    desc->complete_cb = cb;
-    desc->complete_opaque = opaque;
-
-    return desc;
-}
-
-/* Completes data transfer for the given descriptor.
- * Note that this routine will free the descriptor.
- * Param:
- *  desc - Asynchronous data transfer descriptor. Will be freed upon the exit
- *      from this routine.
- *  res - Data transfer result.
- */
-static void
-_async_send_buffer_complete(AsyncSendBuffer* desc, ATResult res)
-{
-    /* Invoke completion callback (if present) */
-    if (desc->complete_cb) {
-        desc->complete_cb(desc->complete_opaque, res, desc->data, desc->data_size,
-                          desc->data_size - desc->data_remaining);
-    }
-
-    /* Free data buffer (if required) */
-    if (desc->free_on_completion) {
-        free(desc->data);
-    }
-
-    /* Free the descriptor itself. */
-    free(desc);
-}
-
-/********************************************************************************
- *                        Common socket declarations
- *******************************************************************************/
-
-/* Initializes common device socket.
- * Param:
- *  ads - Socket descriptor to initialize.
- *  opaque - An opaque pointer to associate with the socket. Typicaly it's the
- *      same pointer that is associated with AndroidDevice instance.
- *  ad - Android device descriptor that owns the socket.
- *  port - Socket's TCP port.
- *  type - Socket type (query, or event).
- */
-static int _android_dev_socket_init(AndroidDevSocket* ads,
-                                    void* opaque,
-                                    AndroidDevice* ad,
-                                    int port,
-                                    ADSType type);
-
-/* Destroys socket descriptor. */
-static void _android_dev_socket_destroy(AndroidDevSocket* ads);
-
-/* Callback that is ivoked from _android_dev_socket_connect when a file
- * descriptor has been created for a socket.
- * Param:
- *  ads - Socket descritor.
- *  opaque - An opaque pointer associated with the callback.
- */
-typedef void (*on_socked_fd_created)(AndroidDevSocket* ads, void* opaque);
-
-/* Synchronously connects to the socket, and registers it with the server.
- * Param:
- *  ads - Socket to connect. Must have 'deadline' field properly setup.
- *  cb, opaque - A callback to invoke (and opaque parameters to pass to the
- *      callback) when a file descriptor has been created for a socket. These
- *      parameters are optional and can be NULL.
- * Return:
- *  0 on success, -1 on failure with errno containing the reason for failure.
- */
-static int _android_dev_socket_connect(AndroidDevSocket* ads,
-                                       on_socked_fd_created cb,
-                                       void* opaque);
-
-/* Synchronously registers a connected socket with the server.
- * Param:
- *  ads - Socket to register. Must be connected, and must have 'deadline' field
- *      properly setup.
- * Return:
- *  0 on success, -1 on failure with errno containing the reason for failure.
- */
-static int _android_dev_socket_register(AndroidDevSocket* ads);
-
-/* Disconnects the socket (if it was connected) */
-static void _android_dev_socket_disconnect(AndroidDevSocket* ads);
-
-/* Synchronously sends data to the socket.
- * Param:
- *  ads - Socket to send the data to. Must be connected, and must have 'deadline'
- *      field properly setup.
- *  buff, buffsize - Buffer to send.
- * Return:
- *  Number of bytes sent on success, or -1 on failure with errno containing the
- *  reason for failure.
- */
-static int _android_dev_socket_send(AndroidDevSocket* ads,
-                                    const char* buff,
-                                    int buffsize);
-
-/* Synchronously receives data from the socket.
- * Param:
- *  ads - Socket to receive the data from. Must be connected, and must have
- *      'deadline' field properly setup.
- *  buff, buffsize - Buffer where to receive data.
- * Return:
- *  Number of bytes received on success, or -1 on failure with errno containing
- *  the reason for failure.
- */
-static int _android_dev_socket_recv(AndroidDevSocket* ads,
-                                    char* buf,
-                                    int bufsize);
-
-/* Synchronously reads zero-terminated string from the socket.
- * Param:
- *  ads - Socket to read the string from. Must be connected, and must have
- *      'deadline' field properly setup.
- *  str, strsize - Buffer where to read the string.
- * Return:
- *  Number of charactes read into the string buffer (including zero-terminator)
- *  on success, or -1 on failure with 'errno' containing the reason for failure.
- *  If this routine returns -1, and errno contains ENOMEM, this is an indicator
- *  that supplied string buffer was too small for the receiving string.
- */
-static int _android_dev_socket_read_string(AndroidDevSocket* ads,
-                                           char* str,
-                                           int strsize);
-
-/* Synchronously reads zero-terminated query response from the socket.
- * All queries respond with an 'ok', or 'ko' prefix, indicating a success, or
- * failure. Prefix can be followed by more query response data, separated from
- * the prefix with a ':' character. This routine helps separating prefix from the
- * data, by placing only the query response data into provided buffer. 'ko' or
- * 'ok' will be encoded in the return value.
- * Param:
- *  ads - Socket to read the response from. Must be connected, and must have
- *      'deadline' field properly setup.
- *  data, datasize - Buffer where to read the query response data.
- * Return:
- *  Number of charactes read into the data buffer (including zero-terminator) on
- *  success, or -1 on failure with errno containing the reason for failure.
- *  If the query has been completed with 'ko', this routine will return -1, with
- *  errno set to 0. If this routine returned -1, and errno is set to EINVAL, this
- *  indicates that reply string didn't match expected query reply format.
- */
-static int _android_dev_socket_read_response(AndroidDevSocket* ads,
-                                             char* str,
-                                             int strsize);
-
-/* Gets ID string for the channel. */
-AINLINED const char*
-_ads_id_str(AndroidDevSocket* ads)
-{
-    return (ads->type == ADS_TYPE_QUERY) ? _ads_query_socket_id :
-                                           _ads_event_socket_id;
-}
-
-/* Gets socket's TCP port. */
-AINLINED int
-_ads_port(AndroidDevSocket* ads)
-{
-    return sock_address_get_port(&ads->address);
-}
-
-/* Gets synchronous I/O looper for the socket. */
-AINLINED IoLooper*
-_ads_io_looper(AndroidDevSocket* ads)
-{
-    return ads->ad->io_looper;
-}
-
-/* Sets deadline on a socket operation, given relative timeout.
- * Param:
- *  ads - Socket descriptor to set deadline for.
- *  to - Relative timeout (in millisec) for the operation.
- *      AD_INFINITE_WAIT passed in this parameter means "no deadline".
- */
-AINLINED void
-_ads_set_deadline(AndroidDevSocket* ads, int to)
-{
-    ads->deadline = (to == AD_INFINITE_WAIT) ? DURATION_INFINITE :
-                                               iolooper_now() + to;
-}
-
-/********************************************************************************
- *                        Common socket implementation
- *******************************************************************************/
-
-static int
-_android_dev_socket_init(AndroidDevSocket* ads,
-                         void* opaque,
-                         AndroidDevice* ad,
-                         int port,
-                         ADSType type)
-{
-    ads->type = type;
-    ads->socket_status = ADS_DISCONNECTED;
-    ads->opaque = opaque;
-    ads->ad = ad;
-    ads->fd = -1;
-    sock_address_init_inet(&ads->address, SOCK_ADDRESS_INET_LOOPBACK, port);
-
-    return 0;
-}
-
-static void
-_android_dev_socket_destroy(AndroidDevSocket* ads)
-{
-    /* Make sure it's disconnected. */
-    _android_dev_socket_disconnect(ads);
-
-    /* Finalize socket address. */
-    sock_address_done(&ads->address);
-    memset(&ads->address, 0, sizeof(ads->address));
-}
-
-static int
-_android_dev_socket_connect(AndroidDevSocket* ads,
-                            on_socked_fd_created cb,
-                            void* opaque)
-{
-    int res;
-
-    /* Create communication socket. */
-    ads->fd = socket_create_inet(SOCKET_STREAM);
-    if (ads->fd < 0) {
-        D("Unable to create socket for channel '%s'@%d: %s",
-          _ads_id_str(ads), _ads_port(ads), strerror(errno));
-        return -1;
-    }
-    socket_set_nonblock(ads->fd);
-
-    /* Invoke FD creation callback (if required) */
-    if (cb != NULL) {
-        cb(ads, opaque);
-    }
-
-    /* Synchronously connect to it. */
-    ads->socket_status = ADS_CONNECTING;
-    iolooper_add_write(_ads_io_looper(ads), ads->fd);
-    res = socket_connect(ads->fd, &ads->address);
-    while (res < 0 && errno == EINTR) {
-        res = socket_connect(ads->fd, &ads->address);
-    }
-
-    if (res && (errno == EINPROGRESS || errno == EWOULDBLOCK || errno == EAGAIN)) {
-        /* Connection is delayed. Wait for it until timeout expires. */
-        res = iolooper_wait_absolute(_ads_io_looper(ads), ads->deadline);
-        if (res > 0) {
-            /* Pick up on possible connection error. */
-            errno = socket_get_error(ads->fd);
-            res = (errno == 0) ? 0 : -1;
-        } else {
-            res = -1;
-        }
-    }
-    iolooper_del_write(_ads_io_looper(ads), ads->fd);
-
-    if (res == 0) {
-        D("Channel '%s'@%d is connected", _ads_id_str(ads), _ads_port(ads));
-        /* Socket is connected. Now register it with the server. */
-        ads->socket_status = ADS_CONNECTED;
-        res = _android_dev_socket_register(ads);
-    } else {
-        D("Unable to connect channel '%s' to port %d: %s",
-          _ads_id_str(ads), _ads_port(ads), strerror(errno));
-    }
-
-    if (res) {
-        _android_dev_socket_disconnect(ads);
-    }
-
-    return res;
-}
-
-static int
-_android_dev_socket_register(AndroidDevSocket* ads)
-{
-    /* Make sure that socket is connected. */
-    if (ads->socket_status < ADS_CONNECTED) {
-        D("Attempt to register a disconnected channel '%s'@%d",
-          _ads_id_str(ads),  _ads_port(ads));
-        errno = ECONNRESET;
-        return -1;
-    }
-
-    /* Register this socket accordingly to its type. */
-    const char* reg_str = _ads_id_str(ads);
-    int res = _android_dev_socket_send(ads, reg_str, strlen(reg_str) + 1);
-    if (res > 0) {
-        /* Receive reply. Note that according to the protocol, the server should
-         * reply to channel registration with 'ok', or 'ko' (just like with queries),
-         * so we can use query reply reader here. */
-        char reply[256];
-        res = _android_dev_socket_read_response(ads, reply, sizeof(reply));
-        if (res >= 0) {
-            /* Socket is now registered. */
-            ads->socket_status = ADS_REGISTERED;
-            D("Channel '%s'@%d is registered", _ads_id_str(ads), _ads_port(ads));
-            res = 0;
-        } else {
-            if (errno == 0) {
-                /* 'ko' condition */
-                D("Device failed registration of channel '%s'@%d: %s",
-                  _ads_id_str(ads), _ads_port(ads), reply);
-                errno = EINVAL;
-            } else {
-                D("I/O failure while registering channel '%s'@%d: %s",
-                  _ads_id_str(ads), _ads_port(ads), strerror(errno));
-            }
-            res = -1;
-        }
-    } else {
-        D("Unable to send registration query for channel '%s'@%d: %s",
-          _ads_id_str(ads), _ads_port(ads), strerror(errno));
-        res = -1;
-    }
-
-    return res;
-}
-
-static void
-_android_dev_socket_disconnect(AndroidDevSocket* ads)
-{
-    /* Preserve errno */
-    const int save_error = errno;
-    if (ads->socket_status != ADS_DISCONNECTED) {
-        /* Reset I/O looper for this socket. */
-        iolooper_modify(_ads_io_looper(ads), ads->fd,
-                        IOLOOPER_READ | IOLOOPER_WRITE, 0);
-
-        /* Mark as disconnected. */
-        ads->socket_status = ADS_DISCONNECTED;
-
-        /* Close socket. */
-        if (ads->fd >= 0) {
-            socket_close(ads->fd);
-            ads->fd = -1;
-        }
-    }
-    errno = save_error;
-}
-
-static int
-_android_dev_socket_send(AndroidDevSocket* ads, const char* buff, int to_send)
-{
-    int sent = 0;
-
-    /* Make sure that socket is connected. */
-    if (ads->socket_status < ADS_CONNECTED) {
-        D("Attempt to send via disconnected channel '%s'@%d",
-          _ads_id_str(ads),  _ads_port(ads));
-        errno = ECONNRESET;
-        return -1;
-    }
-
-    iolooper_add_write(_ads_io_looper(ads), ads->fd);
-    do {
-        int res = socket_send(ads->fd, buff + sent, to_send - sent);
-        if (res == 0) {
-            /* Disconnection. */
-            errno = ECONNRESET;
-            sent = -1;
-            break;
-        }
-
-        if (res < 0) {
-            if (errno == EINTR) {
-                /* loop on EINTR */
-                continue;
-            }
-
-            if (errno == EWOULDBLOCK || errno == EAGAIN) {
-                res = iolooper_wait_absolute(_ads_io_looper(ads), ads->deadline);
-                if (res > 0) {
-                    /* Ready to write. */
-                    continue;
-                }
-            }
-            sent = -1;
-            break;
-        }
-        sent += res;
-    } while (sent < to_send);
-    iolooper_del_write(_ads_io_looper(ads), ads->fd);
-
-    /* In case of an I/O failure we have to invoke failure callback. Note that we
-     * report I/O failures only on registered sockets. */
-    if (sent < 0) {
-        D("I/O error while sending data via channel '%s'@%d: %s",
-          _ads_id_str(ads), _ads_port(ads), strerror(errno));
-
-        if (ads->ad->on_io_failure != NULL && ads->socket_status > ADS_CONNECTED) {
-            const char save_error = errno;
-            ads->ad->on_io_failure(ads->opaque, ads->ad, save_error);
-            errno = save_error;
-        }
-    }
-
-    return sent;
-}
-
-static int
-_android_dev_socket_recv(AndroidDevSocket* ads, char* buf, int bufsize)
-{
-    int recvd = 0;
-
-    /* Make sure that socket is connected. */
-    if (ads->socket_status < ADS_CONNECTED) {
-        D("Attempt to receive from disconnected channel '%s'@%d",
-          _ads_id_str(ads),  _ads_port(ads));
-        errno = ECONNRESET;
-        return -1;
-    }
-
-    iolooper_add_read(_ads_io_looper(ads), ads->fd);
-    do {
-        int res = socket_recv(ads->fd, buf + recvd, bufsize - recvd);
-        if (res == 0) {
-            /* Disconnection. */
-            errno = ECONNRESET;
-            recvd = -1;
-            break;
-        }
-
-        if (res < 0) {
-            if (errno == EINTR) {
-                /* loop on EINTR */
-                continue;
-            }
-
-            if (errno == EWOULDBLOCK || errno == EAGAIN) {
-                res = iolooper_wait_absolute(_ads_io_looper(ads), ads->deadline);
-                if (res > 0) {
-                    /* Ready to read. */
-                    continue;
-                }
-            }
-            recvd = -1;
-            break;
-        }
-        recvd += res;
-    } while (recvd < bufsize);
-    iolooper_del_read(_ads_io_looper(ads), ads->fd);
-
-    /* In case of an I/O failure we have to invoke failure callback. Note that we
-     * report I/O failures only on registered sockets. */
-    if (recvd < 0) {
-        D("I/O error while receiving from channel '%s'@%d: %s",
-          _ads_id_str(ads), _ads_port(ads), strerror(errno));
-
-         if (ads->ad->on_io_failure != NULL && ads->socket_status > ADS_CONNECTED) {
-            const char save_error = errno;
-            ads->ad->on_io_failure(ads->opaque, ads->ad, save_error);
-            errno = save_error;
-         }
-    }
-
-    return recvd;
-}
-
-static int
-_android_dev_socket_read_string(AndroidDevSocket* ads, char* str, int strsize)
-{
-    int n;
-
-    /* Char by char read from the socket, until zero-terminator is read. */
-    for (n = 0; n < strsize; n++) {
-        if (_android_dev_socket_recv(ads, str + n, 1) > 0) {
-            if (str[n] == '\0') {
-                /* Done. */
-                return n + 1;   /* Including zero-terminator. */
-            }
-        } else {
-            /* I/O error. */
-            return -1;
-        }
-    }
-
-    /* Buffer was too small. Report that by setting errno to ENOMEM. */
-    D("Buffer %d is too small to receive a string from channel '%s'@%d",
-       strsize, _ads_id_str(ads), _ads_port(ads));
-    errno = ENOMEM;
-    return -1;
-}
-
-static int
-_android_dev_socket_read_response(AndroidDevSocket* ads, char* data, int datasize)
-{
-    int n, res;
-    int success = 0;
-    int failure = 0;
-    int bad_format = 0;
-    char ok[4];
-
-    *data = '\0';
-
-    /* Char by char read from the socket, until ok/ko is read. */
-    for (n = 0; n < 2; n++) {
-        res = _android_dev_socket_recv(ads, ok + n, 1);
-        if (res > 0) {
-            if (ok[n] == '\0') {
-                /* EOS is unexpected here! */
-                D("Bad query reply format on channel '%s'@%d: '%s' is too short.",
-                  _ads_id_str(ads), _ads_port(ads), ok);
-                errno = EINVAL;
-                return -1;
-            }
-        } else {
-            /* I/O error. */
-            return -1;
-        }
-    }
-
-    /* Next character must be either ':', or '\0' */
-    res = _android_dev_socket_recv(ads, ok + n, 1);
-    if (res <= 0) {
-        /* I/O error. */
-        return -1;
-    }
-
-    /*
-     * Verify format.
-     */
-
-    /* Check ok / ko */
-    success = memcmp(ok, "ok", 2) == 0;
-    failure = memcmp(ok, "ko", 2) == 0;
-
-    /* Check the prefix: 'ok'|'ko' & ':'|'\0' */
-    if ((success || failure) && (ok[n] == '\0' || ok[n] == ':')) {
-        /* Format is good. */
-        if (ok[n] == '\0') {
-            /* We're done: no extra data in response. */
-            errno = 0;
-            return success ? 0 : -1;
-        }
-        /* Reset buffer offset, so we will start to read the remaining query
-         * data to the beginning of the supplied buffer. */
-        n = 0;
-    } else {
-        /* Bad format. Lets move what we've read to the main buffer, and
-         * continue filling it in. */
-        bad_format = 1;
-        n++;
-        memcpy(data, ok, n);
-    }
-
-    /* Read the remainder of reply to the supplied data buffer. */
-    res = _android_dev_socket_read_string(ads, data + n, datasize - n);
-    if (res < 0) {
-        return res;
-    }
-
-    /* Lets see if format was bad */
-    if (bad_format) {
-        D("Bad query reply format on channel '%s'@%d: %s.",
-          _ads_id_str(ads), _ads_port(ads), data);
-        errno = EINVAL;
-        return -1;
-    } else {
-        errno = 0;
-        return success ? n : -1;
-    }
-}
-
-/********************************************************************************
- *                        Query socket declarations
- *******************************************************************************/
-
-/* Initializes query socket descriptor.
- * Param:
- *  adsquery - Socket descriptor to initialize.
- *  opaque - An opaque pointer to associate with the socket. Typicaly it's the
- *      same pointer that is associated with AndroidDevice instance.
- *  ad - Android device descriptor that owns the socket.
- *  port - TCP socket port.
- */
-static int _android_query_socket_init(AndroidQuerySocket* adsquery,
-                                      void* opaque,
-                                      AndroidDevice* ad,
-                                      int port);
-
-/* Destroys query socket descriptor. */
-static void _android_query_socket_destroy(AndroidQuerySocket* adsquery);
-
-/* Synchronously connects the query socket, and registers it with the server.
- * Param:
- *  adsquery - Descriptor for the query socket to connect. Must have 'deadline'
- *      field properly setup.
- *  cb - Callback to invoke when socket connection is completed. Can be NULL.
- * Return:
- *  Zero on success, or non-zero on failure.
- */
-static int _android_query_socket_connect(AndroidQuerySocket* adsquery);
-
-/* Disconnects the query socket. */
-static void _android_query_socket_disconnect(AndroidQuerySocket* adsquery);
-
-/********************************************************************************
- *                        Query socket implementation
- *******************************************************************************/
-
-static int
-_android_query_socket_init(AndroidQuerySocket* adsquery,
-                           void* opaque,
-                           AndroidDevice* ad,
-                           int port)
-{
-    return _android_dev_socket_init(&adsquery->dev_socket, opaque, ad, port,
-                                    ADS_TYPE_QUERY);
-}
-
-static void
-_android_query_socket_destroy(AndroidQuerySocket* adsquery)
-{
-    _android_query_socket_disconnect(adsquery);
-    _android_dev_socket_destroy(&adsquery->dev_socket);
-}
-
-static int
-_android_query_socket_connect(AndroidQuerySocket* adsquery)
-{
-    return _android_dev_socket_connect(&adsquery->dev_socket, NULL, NULL);
-}
-
-static void
-_android_query_socket_disconnect(AndroidQuerySocket* adsquery)
-{
-    _android_dev_socket_disconnect(&adsquery->dev_socket);
-}
-
-/********************************************************************************
- *                       Events socket declarations
- *******************************************************************************/
-
-/* Initializes event socket descriptor.
- * Param:
- *  adsevent - Socket descriptor to initialize.
- *  opaque - An opaque pointer to associate with the socket.  Typicaly it's the
- *      same pointer that is associated with AndroidDevice instance.
- *  ad - Android device descriptor that owns the socket.
- *  port - TCP socket port.
- */
-static int _android_event_socket_init(AndroidEventSocket* adsevent,
-                                      void* opaque,
-                                      AndroidDevice* ad,
-                                      int port);
-
-/* Destroys the event socket descriptor. */
-static void _android_event_socket_destroy(AndroidEventSocket* adsevent);
-
-/* Synchronously connects event socket.
- * Param:
- *  adsevent - Descriptor for the event socket to connect. Must have 'deadline'
- *      field properly setup.
- * Return:
- *  Zero on success, or non-zero on failure.
- */
-static int _android_event_socket_connect_sync(AndroidEventSocket* adsevent);
-
-/* Initiates asynchronous event socket connection.
- * Param:
- *  adsevent - Descriptor for the event socket to connect.  Must have 'deadline'
- *      field properly setup.
- *  cb - Callback to invoke when socket connection is completed. Can be NULL.
- * Return:
- *  Zero on success, or non-zero on failure.
- */
-static int _android_event_socket_connect_async(AndroidEventSocket* adsevent,
-                                               ads_socket_connected_cb cb);
-
-/* Disconnects the event socket. */
-static void _android_event_socket_disconnect(AndroidEventSocket* adsevent);
-
-/* Initiates listening on the event socket.
- * Param:
- *  adsevent - Descriptor for the event socket to listen on.
- *  str, strsize - Buffer where to read the string.
- *  cb - A callback to call when the event string is read. Can be NULL.
- * Return:
- *  Zero on success, or non-zero on failure.
- */
-static int _android_event_socket_listen(AndroidEventSocket* adsevent,
-                                        char* str,
-                                        int strsize,
-                                        event_cb cb);
-
-/* Asynchronously sends data via event socket.
- * Param:
- *  adsevent - Descriptor for the event socket to send data to.
- *  data, size - Buffer containing data to send.
- *  free_on_close - A boolean flag indicating whether the data buffer should be
- *      freed upon data transfer completion.
- *  cb - Callback to invoke when data transfer is completed.
- *  opaque - An opaque pointer to pass to the transfer completion callback.
- */
-static int _android_event_socket_send(AndroidEventSocket* adsevent,
-                                      void* data,
-                                      int size,
-                                      int free_on_close,
-                                      async_send_cb cb,
-                                      void* opaque);
-
-/* Cancels all asynchronous data transfers on the event socket.
- * Param:
- *  adsevent - Descriptor for the event socket to cancel data transfer.
- *  reason - Reason for the cancellation.
- */
-static void _android_event_socket_cancel_send(AndroidEventSocket* adsevent,
-                                              ATResult reason);
-
-/* Event socket's asynchronous I/O looper callback.
- * Param:
- *  opaque - AndroidEventSocket instance.
- *  fd - Socket's FD.
- *  events - I/O type bitsmask (read | write).
- */
-static void _on_event_socket_io(void* opaque, int fd, unsigned events);
-
-/* Callback that is invoked when asynchronous event socket connection is
- * completed. */
-static void _on_event_socket_connected(AndroidEventSocket* adsevent, int failure);
-
-/* Callback that is invoked when an event is received from the device. */
-static void _on_event_received(AndroidEventSocket* adsevent);
-
-/* Gets I/O looper for asynchronous I/O on event socket. */
-AINLINED Looper*
-_aes_looper(AndroidEventSocket* adsevent)
-{
-    return adsevent->dev_socket.ad->looper;
-}
-
-/********************************************************************************
- *                       Events socket implementation
- *******************************************************************************/
-
-static int
-_android_event_socket_init(AndroidEventSocket* adsevent,
-                           void* opaque,
-                           AndroidDevice* ad,
-                           int port)
-{
-    return _android_dev_socket_init(&adsevent->dev_socket, opaque, ad, port,
-                                    ADS_TYPE_EVENT);
-}
-
-static void
-_android_event_socket_destroy(AndroidEventSocket* adsevent)
-{
-    _android_event_socket_disconnect(adsevent);
-    _android_dev_socket_destroy(&adsevent->dev_socket);
-}
-
-/* A callback invoked when file descriptor is created for the event socket.
- * We use this callback to initialize the event socket for async I/O right after
- * the FD has been created.
- */
-static void
-_on_event_fd_created(AndroidDevSocket* ads, void* opaque)
-{
-    AndroidEventSocket* adsevent = (AndroidEventSocket*)opaque;
-   /* Prepare for async I/O on the event socket. */
-   loopIo_init(adsevent->io, _aes_looper(adsevent), ads->fd,
-               _on_event_socket_io, adsevent);
-}
-
-static int
-_android_event_socket_connect_sync(AndroidEventSocket* adsevent)
-{
-    return _android_dev_socket_connect(&adsevent->dev_socket,
-                                       _on_event_fd_created, adsevent);
-}
-
-static int
-_android_event_socket_connect_async(AndroidEventSocket* adsevent,
-                                    ads_socket_connected_cb cb)
-{
-    AsyncStatus status;
-    AndroidDevSocket* ads = &adsevent->dev_socket;
-
-    /* Create asynchronous socket. */
-    ads->fd = socket_create_inet(SOCKET_STREAM);
-    if (ads->fd < 0) {
-        D("Unable to create socket for channel '%s'@%d: %s",
-          _ads_id_str(ads), _ads_port(ads), strerror(errno));
-        if (cb != NULL) {
-            cb(ads->opaque, ads, errno);
-        }
-        return -1;
-    }
-    socket_set_nonblock(ads->fd);
-
-    /* Prepare for async I/O on the event socket. */
-    loopIo_init(adsevent->io, _aes_looper(adsevent), ads->fd,
-                _on_event_socket_io, adsevent);
-
-    /* Try to connect. */
-    ads->socket_status = ADS_CONNECTING;
-    adsevent->on_connected = cb;
-    status = asyncConnector_init(adsevent->connector, &ads->address, adsevent->io);
-    switch (status) {
-        case ASYNC_COMPLETE:
-            /* We're connected to the device socket. */
-            ads->socket_status = ADS_CONNECTED;
-            _on_event_socket_connected(adsevent, 0);
-            break;
-        case ASYNC_ERROR:
-            _on_event_socket_connected(adsevent, errno);
-            break;
-        case ASYNC_NEED_MORE:
-            /* Attempt to connect would block, so connection competion is
-             * delegates to the looper's I/O routine. */
-        default:
-            break;
-    }
-
-    return 0;
-}
-
-static void
-_android_event_socket_disconnect(AndroidEventSocket* adsevent)
-{
-    AndroidDevSocket* ads = &adsevent->dev_socket;
-
-    if (ads->socket_status != ADS_DISCONNECTED) {
-        /* Cancel data transfer. */
-        _android_event_socket_cancel_send(adsevent, ATR_DISCONNECT);
-
-        /* Stop all async I/O. */
-        loopIo_done(adsevent->io);
-
-        /* Disconnect common socket. */
-        _android_dev_socket_disconnect(ads);
-    }
-}
-
-static int
-_android_event_socket_listen(AndroidEventSocket* adsevent,
-                             char* str,
-                             int strsize,
-                             event_cb cb)
-{
-    AsyncStatus  status;
-    AndroidDevSocket* ads = &adsevent->dev_socket;
-
-    /* Make sure that device is connected. */
-    if (ads->socket_status < ADS_CONNECTED) {
-        D("Attempt to listen on a disconnected channel '%s'@%d",
-          _ads_id_str(ads),  _ads_port(ads));
-        errno = ECONNRESET;
-        return -1;
-    }
-
-    /* NOTE: only one reader at any given time! */
-    adsevent->on_event = cb;
-    asyncLineReader_init(&adsevent->alr, str, strsize, adsevent->io);
-    /* Default EOL for the line reader was '\n'. */
-    asyncLineReader_setEOL(&adsevent->alr, '\0');
-    status = asyncLineReader_read(&adsevent->alr);
-    if (status == ASYNC_COMPLETE) {
-        /* Data has been transferred immediately. Do the callback here. */
-        _on_event_received(adsevent);
-    } else if (status == ASYNC_ERROR) {
-        D("Error while listening on channel '%s'@%d: %s",
-          _ads_id_str(ads),  _ads_port(ads), strerror(errno));
-        /* There is one special failure here, when buffer was too small to
-         * contain the entire string. This is not an I/O, but rather a
-         * protocol error. So we don't report it to the I/O failure
-         * callback. */
-        if (errno == ENOMEM) {
-            _on_event_received(adsevent);
-        } else {
-            if (ads->ad->on_io_failure != NULL) {
-                ads->ad->on_io_failure(ads->ad->opaque, ads->ad, errno);
-            }
-        }
-        return -1;
-    }
-    return 0;
-}
-
-static int
-_android_event_socket_send(AndroidEventSocket* adsevent,
-                           void* data,
-                           int size,
-                           int free_on_close,
-                           async_send_cb cb,
-                           void* opaque)
-{
-    /* Create data transfer descriptor, and place it at the end of the list. */
-    AsyncSendBuffer* const desc =
-        _async_send_buffer_create(data, size, free_on_close, cb, opaque);
-    AsyncSendBuffer** place = &adsevent->send_pending;
-    while (*place != NULL) {
-        place = &((*place)->next);
-    }
-    *place = desc;
-
-    /* We're ready to transfer data. */
-    loopIo_wantWrite(adsevent->io);
-
-    return 0;
-}
-
-static void
-_android_event_socket_cancel_send(AndroidEventSocket* adsevent, ATResult reason)
-{
-    while (adsevent->send_pending != NULL) {
-        AsyncSendBuffer* const to_cancel = adsevent->send_pending;
-        adsevent->send_pending = to_cancel->next;
-        _async_send_buffer_complete(to_cancel, reason);
-    }
-    loopIo_dontWantWrite(adsevent->io);
-}
-
-static void
-_on_event_socket_io(void* opaque, int fd, unsigned events)
-{
-    AsyncStatus status;
-    AndroidEventSocket* adsevent = (AndroidEventSocket*)opaque;
-    AndroidDevSocket* ads = &adsevent->dev_socket;
-
-    /* Lets see if we're still wating on a connection to occur. */
-    if (ads->socket_status == ADS_CONNECTING) {
-        /* Complete socket connection. */
-        status = asyncConnector_run(adsevent->connector);
-        if (status == ASYNC_COMPLETE) {
-            /* We're connected to the device socket. */
-            ads->socket_status = ADS_CONNECTED;
-            D("Channel '%s'@%d is connected asynchronously",
-              _ads_id_str(ads), _ads_port(ads));
-            _on_event_socket_connected(adsevent, 0);
-        } else if (status == ASYNC_ERROR) {
-            _on_event_socket_connected(adsevent, adsevent->connector->error);
-        }
-        return;
-    }
-
-    /*
-     * Device is connected. Continue with the data transfer.
-     */
-
-    if ((events & LOOP_IO_READ) != 0) {
-        /* Continue reading data. */
-        status = asyncLineReader_read(&adsevent->alr);
-        if (status == ASYNC_COMPLETE) {
-            errno = 0;
-            _on_event_received(adsevent);
-        } else if (status == ASYNC_ERROR) {
-            D("I/O failure while reading from channel '%s'@%d: %s",
-              _ads_id_str(ads),  _ads_port(ads), strerror(errno));
-            /* There is one special failure here, when buffer was too small to
-             * contain the entire string. This is not an I/O, but rather a
-             * protocol error. So we don't report it to the I/O failure
-             * callback. */
-            if (errno == ENOMEM) {
-                _on_event_received(adsevent);
-            } else {
-                if (ads->ad->on_io_failure != NULL) {
-                    ads->ad->on_io_failure(ads->ad->opaque, ads->ad, errno);
-                }
-            }
-        }
-    }
-
-    if ((events & LOOP_IO_WRITE) != 0) {
-        while (adsevent->send_pending != NULL) {
-            AsyncSendBuffer* to_send = adsevent->send_pending;
-            const int offset = to_send->data_size - to_send->data_remaining;
-            const int sent = socket_send(ads->fd, to_send->data + offset,
-                                         to_send->data_remaining);
-            if (sent < 0) {
-                if (errno == EWOULDBLOCK) {
-                    /* Try again later. */
-                    return;
-                } else {
-                    /* An error has occured. */
-                    _android_event_socket_cancel_send(adsevent, ATR_IO_ERROR);
-                    if (ads->ad->on_io_failure != NULL) {
-                        ads->ad->on_io_failure(ads->ad->opaque, ads->ad, errno);
-                    }
-                    return;
-                }
-            } else if (sent == 0) {
-                /* Disconnect condition. */
-                _android_event_socket_cancel_send(adsevent, ATR_DISCONNECT);
-                if (ads->ad->on_io_failure != NULL) {
-                    ads->ad->on_io_failure(ads->ad->opaque, ads->ad, errno);
-                }
-                return;
-            } else if (sent == to_send->data_remaining) {
-                /* All data is sent. */
-                errno = 0;
-                adsevent->send_pending = to_send->next;
-                _async_send_buffer_complete(to_send, ATR_SUCCESS);
-            } else {
-                /* Chunk is sent. */
-                to_send->data_remaining -= sent;
-                return;
-            }
-        }
-        loopIo_dontWantWrite(adsevent->io);
-    }
-}
-
-static void
-_on_event_socket_connected(AndroidEventSocket* adsevent, int failure)
-{
-    int res;
-    AndroidDevSocket* ads = &adsevent->dev_socket;
-
-    if (failure) {
-        _android_event_socket_disconnect(adsevent);
-        if (adsevent->on_connected != NULL) {
-            adsevent->on_connected(ads->opaque, ads, failure);
-        }
-        return;
-    }
-
-    /* Complete event socket connection by identifying it as "event" socket with
-     * the application. */
-    ads->socket_status = ADS_CONNECTED;
-    res = _android_dev_socket_register(ads);
-
-    if (res) {
-        const int save_error = errno;
-        _android_event_socket_disconnect(adsevent);
-        errno = save_error;
-    }
-
-    /* Notify callback about connection completion. */
-    if (adsevent->on_connected != NULL) {
-        if (res) {
-            adsevent->on_connected(ads->opaque, ads, errno);
-        } else {
-            adsevent->on_connected(ads->opaque, ads, 0);
-        }
-    }
-}
-
-static void
-_on_event_received(AndroidEventSocket* adsevent)
-{
-    if (adsevent->on_event != NULL) {
-        AndroidDevice* ad = adsevent->dev_socket.ad;
-        adsevent->on_event(ad->opaque, ad, (char*)adsevent->alr.buffer,
-                           adsevent->alr.pos);
-    }
-}
-
-/********************************************************************************
- *                          Android device connection
- *******************************************************************************/
-
-/* Callback that is invoked when event socket is connected and registered as part
- * of the _android_device_connect_async API.
- * Param:
- *  opaque - Opaque pointer associated with AndroidDevice instance.
- *  ads - Common socket descriptor for the event socket.
- *  failure - If zero connection has succeeded, otherwise contains 'errno'-reason
- *      for connection failure.
- */
-static void
-_on_android_device_connected_async(void* opaque,
-                                   AndroidDevSocket* ads,
-                                   int failure)
-{
-    int res;
-    AndroidDevice* ad = ads->ad;
-
-    if (failure) {
-        /* Depending on the failure code we will either retry, or bail out. */
-        switch (failure) {
-            case EPIPE:
-            case EAGAIN:
-            case EINPROGRESS:
-            case EALREADY:
-            case EHOSTUNREACH:
-            case EHOSTDOWN:
-            case ECONNREFUSED:
-            case ESHUTDOWN:
-            case ENOTCONN:
-            case ECONNRESET:
-            case ECONNABORTED:
-            case ENETRESET:
-            case ENETUNREACH:
-            case ENETDOWN:
-            case EBUSY:
-#if !defined(_DARWIN_C_SOURCE) && !defined(_WIN32)
-            case ERESTART:
-            case ECOMM:
-            case ENONET:
-#endif  /* !_DARWIN_C_SOURCE && !_WIN32 */
-                /* Device is not available / reachable at the moment.
-                 * Retry connection later. */
-                loopTimer_startRelative(ad->timer, ADS_RETRY_CONNECTION_TIMEOUT);
-                return;
-            default:
-                D("Failed to asynchronously connect channel '%s':%d %s",
-                  _ads_id_str(ads), _ads_port(ads), strerror(errno));
-            if (ad->on_connected != NULL) {
-                ad->on_connected(ad->opaque, ad, failure);
-            }
-            break;
-        }
-        return;
-    }
-
-    /* Event socket is connected. Connect the query socket now. Give it 5
-     * seconds to connect. */
-    _ads_set_deadline(&ad->query_socket.dev_socket, 5000);
-    res = _android_query_socket_connect(&ad->query_socket);
-    if (res == 0) {
-        /* Query socket is connected. */
-        if (ad->on_connected != NULL) {
-            ad->on_connected(ad->opaque, ad, 0);
-        }
-    } else {
-        /* If connection completion has failed - disconnect the sockets. */
-        _android_event_socket_disconnect(&ad->event_socket);
-        _android_query_socket_disconnect(&ad->query_socket);
-
-        if (ad->on_connected != NULL) {
-            ad->on_connected(ad->opaque, ad, errno);
-        }
-    }
-}
-
-static void
-_on_timer(void* opaque)
-{
-    /* Retry the connection. */
-    AndroidDevice* ad = (AndroidDevice*)opaque;
-    android_device_connect_async(ad, ad->on_connected);
-}
-
-/* Destroys and frees the descriptor. */
-static void
-_android_device_free(AndroidDevice* ad)
-{
-    if (ad != NULL) {
-        _android_event_socket_destroy(&ad->event_socket);
-        _android_query_socket_destroy(&ad->query_socket);
-
-        /* Delete asynchronous I/O looper. */
-        if (ad->looper != NULL ) {
-            loopTimer_done(ad->timer);
-            looper_free(ad->looper);
-        }
-
-        /* Delete synchronous I/O looper. */
-        if (ad->io_looper != NULL) {
-            iolooper_reset(ad->io_looper);
-            iolooper_free(ad->io_looper);
-        }
-
-        AFREE(ad);
-    }
-}
-
-/********************************************************************************
- *                          Android device API
- *******************************************************************************/
-
-AndroidDevice*
-android_device_init(void* opaque, int port, io_failure_cb on_io_failure)
-{
-    int res;
-    AndroidDevice* ad;
-
-    ANEW0(ad);
-
-    ad->opaque = opaque;
-    ad->on_io_failure = on_io_failure;
-
-    /* Create I/O looper for synchronous I/O on the device. */
-    ad->io_looper = iolooper_new();
-    if (ad->io_looper == NULL) {
-        E("Unable to create synchronous I/O looper for android device.");
-        _android_device_free(ad);
-        return NULL;
-    }
-
-    /* Create a looper for asynchronous I/O on the device. */
-    ad->looper = looper_newCore();
-    if (ad->looper != NULL) {
-        /* Create a timer that will be used for connection retries. */
-        loopTimer_init(ad->timer, ad->looper, _on_timer, ad);
-    } else {
-        E("Unable to create asynchronous I/O looper for android device.");
-        _android_device_free(ad);
-        return NULL;
-    }
-
-    /* Init query socket. */
-    res = _android_query_socket_init(&ad->query_socket, opaque, ad, port);
-    if (res) {
-        _android_device_free(ad);
-        return NULL;
-    }
-
-    /* Init event socket. */
-    res = _android_event_socket_init(&ad->event_socket, opaque, ad, port);
-    if (res) {
-        _android_device_free(ad);
-        return NULL;
-    }
-
-    return ad;
-}
-
-void
-android_device_destroy(AndroidDevice* ad)
-{
-    if (ad != NULL) {
-        _android_device_free(ad);
-    }
-}
-
-int
-android_device_connect_sync(AndroidDevice* ad, int to)
-{
-    int res;
-
-    /* Setup deadline for the connections. */
-    _ads_set_deadline(&ad->query_socket.dev_socket, to);
-    ad->event_socket.dev_socket.deadline = ad->query_socket.dev_socket.deadline;
-
-    /* Connect the query socket first. */
-    res = _android_query_socket_connect(&ad->query_socket);
-    if (!res) {
-        /* Connect to the event socket next. */
-        res = _android_event_socket_connect_sync(&ad->event_socket);
-    }
-
-    return res;
-}
-
-int
-android_device_connect_async(AndroidDevice* ad, device_connected_cb on_connected)
-{
-    /* No deadline for async connections. */
-    ad->query_socket.dev_socket.deadline = DURATION_INFINITE;
-    ad->event_socket.dev_socket.deadline = DURATION_INFINITE;
-
-    /* Connect to the event socket first, and delegate query socket connection
-     * into callback invoked when event socket is connected. NOTE: In case of
-     * failure 'on_connected' callback has already been called from
-     * _on_android_device_connected_async routine. */
-    ad->on_connected = on_connected;
-    return _android_event_socket_connect_async(&ad->event_socket,
-                                               _on_android_device_connected_async);
-}
-
-void
-android_device_disconnect(AndroidDevice* ad)
-{
-    _android_event_socket_disconnect(&ad->event_socket);
-    _android_query_socket_disconnect(&ad->query_socket);
-}
-
-int
-android_device_query(AndroidDevice* ad,
-                     const char* query,
-                     char* buff,
-                     size_t buffsize,
-                     int to)
-{
-    int res;
-
-    /* Setup deadline for the query. */
-    _ads_set_deadline(&ad->query_socket.dev_socket, to);
-
-    /* Send the query. */
-    res = _android_dev_socket_send(&ad->query_socket.dev_socket, query,
-                                   strlen(query) + 1);
-    if (res > 0) {
-        /* Receive the response. */
-        res = _android_dev_socket_read_response(&ad->query_socket.dev_socket,
-                                                buff, buffsize);
-        return (res >= 0) ? 0 : -1;
-    }
-
-    return -1;
-}
-
-int
-android_device_start_query(AndroidDevice* ad, const char* query, int to)
-{
-    int res;
-
-    /* Setup deadline for the query. */
-    _ads_set_deadline(&ad->query_socket.dev_socket, to);
-
-    /* Send the query header. */
-    res = _android_dev_socket_send(&ad->query_socket.dev_socket, query,
-                                   strlen(query) + 1);
-    return (res > 0) ? 0 : -1;
-}
-
-int
-android_device_send_query_data(AndroidDevice* ad, const void* data, int size)
-{
-    return _android_dev_socket_send(&ad->query_socket.dev_socket, data, size);
-}
-
-int
-android_device_complete_query(AndroidDevice* ad, char* buff, size_t buffsize)
-{
-    /* Receive the response to the query. */
-    const int res = _android_dev_socket_read_response(&ad->query_socket.dev_socket,
-                                                      buff, buffsize);
-    return (res >= 0) ? 0 : -1;
-}
-
-int
-android_device_listen(AndroidDevice* ad,
-                      char* buff,
-                      int buffsize,
-                      event_cb on_event)
-{
-    return _android_event_socket_listen(&ad->event_socket, buff, buffsize,
-                                        on_event);
-}
-
-int
-android_device_send_async(AndroidDevice* ad,
-                          void* data,
-                          int size,
-                          int free_on_close,
-                          async_send_cb cb,
-                          void* opaque)
-{
-    return _android_event_socket_send(&ad->event_socket, data, size,
-                                      free_on_close, cb, opaque);
-}
diff --git a/android/android-device.h b/android/android-device.h
deleted file mode 100644
index 6825819..0000000
--- a/android/android-device.h
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_ANDROID_DEVICE_H_
-#define ANDROID_ANDROID_DEVICE_H_
-
-/*
- * Encapsulates an 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 (always use 'adb -d forward ...' variant
- * of this command, so ADB will know to enable port forwarding on the connected
- * device, and not on the emulator's guest system).
- *
- * Exchange protocol contains two channel:
- *
- * - Query channel.
- * - Event channel.
- *
- * Both channels are implemented on top of TCP sockets that are connected to the
- * same port.
- *
- * I QUERY CHANNEL.
- * Query channel is intended to send queries to and receive responses from the
- * connected device. It is implemented on top of iolooper_xxx API (see iolooper.h)
- * because it must work outside of the main event loop. This is required to enable
- * proper initialization of components (such as sensors) that must be set up
- * before emulator enters the main loop.
- *
- * II EVENT CHANNEL.
- * Event channel is intended to listen on events sent from the device, and
- * asynchronously report them back to the client of this API by invoking an event
- * callback that was registered by the client. Event channel is implemented on
- * top of asyncXxx API (see android/async-utils.*). Note that using of asyncXxx
- * API limits the use of event channel to the time after the emulator has entered
- * its main event loop. The only exception is if event channel is connected from
- * android_device_connect_sync API, in which case iolooper_xxx API is used to
- * establish the connection. However, even in this case listening for events will
- * not be available until after the emulator enters its event loop, since event
- * listening always uses asyncXxx API.
- *
- * III. ESTABLISHING CONNECTION.
- * ADB port forwarding requires that the server socket is to be run on the device,
- * while emulator must use a client socket for communication. Thus, it's the
- * emulator that initiates the connection.
- *
- * There are two ways how emulator can initiate the connection:
- *
- * - Synchronous connection.
- * - Asynchronous connection.
- *
- * III.I SYNCHROUNOUS CONNECTION.
- * Synchronous connection is initiated via android_device_connect_sync API, and
- * completes synchronously.
- *
- * This API should be used when connection with the device is required at the time
- * of the call. For instance, when initializing sensor emulation, connection with
- * the device is required to properly set up the emulator before the guest system
- * starts, and before emulator enters its main event loop.
- *
- * III.II ASYNCHRONOUS CONNECTION.
- * Asynchronous connection is initiated via android_device_connect_async API. The
- * main difference with the synchronous connection is that this API will not fail
- * if connection is not immediately available. If connection is not available at
- * the time of the call, the API will schedule a retry (based on a timer), and
- * will continue reprying untill connection becomes available, or until an error
- * occurs that prevent further retries.
- *
- * This API should be used when ... Well, whenever appropriate. For instance,
- * sensor emulation will use this API to restore lost connection with the device.
- *
- * NOTE: Asynchronous connection will complete no sooner than the emulator enters
- * its main loop.
- *
- * IV EXCHANGE PROTOCOL.
- * Obviously, there must be some application running on the device, that implements
- * a socket server listening on the forwarded TCP port, and accepting the clients.
- *
- * IV.I Query vs. event channel.
- * The exchange protocol assumes, that when a channel is connected, it will
- * identify itself by sending a string containing channel type. Only after such
- * identification has been made the channel becomes available for use.
- *
- * IV.II Message format.
- * All data that is transferred in both directions over both channels are zero-
- * terminated strings.
- */
-
-#include "qemu-common.h"
-#include "android/async-utils.h"
-#include "android/utils/debug.h"
-
-/* TCP port reserved for sensor emulation. */
-#define AD_SENSOR_PORT  1968
-
-/* Definis infinite timeout. */
-#define AD_INFINITE_WAIT    -1
-
-/* Enumerates results of asynchronous data transfer.
- */
-typedef enum ATResult {
-    /* Data transfer has been completed. */
-    ATR_SUCCESS,
-    /* Socket got disconnected while data transfer has been in progress. */
-    ATR_DISCONNECT,
-    /* An I/O error has occured. 'errno' contains error value. */
-    ATR_IO_ERROR,
-} ATResult;
-
-/* Android device descriptor. */
-typedef struct AndroidDevice AndroidDevice;
-
-/********************************************************************************
- *                       Callback declarations
- *******************************************************************************/
-
-/* Callback routine that is invoked when android device is connected, or failed
- * to connect. As discussed above, this callback is called when both, query and
- * event channels have been connected. This callback is used only for asynchronous
- * connections.
- * Param:
- *  opaque - Opaque pointer that was passed to android_device_init API.
- *  ad - Androd device descriptor for the connection.
- *  failure - Zero indicates that connection with the device has been successfuly
- *      established. Non-zero vaule passed in this parameter indicates a failure,
- *      and contains 'errno'-reason for failure.
- */
-typedef void (*device_connected_cb)(void* opaque, AndroidDevice* ad, int failure);
-
-/* Callback routine that is invoked on an event received in the event channel.
- * NOTE: It's important to check 'errno' in this callback. If 'errno' is set to
- * ENOMEM, this signals that buffer passed to android_device_listen was too small
- * to contain the entire event message.
- * Param:
- *  opaque - Opaque pointer that was passed to android_device_init API.
- *  ad - Androd device descriptor for the connection.
- *  msg - Event message (a zero-terminated string) received from the device.
- *  msgsize - Event message size (including zero-terminator).
- */
-typedef void (*event_cb)(void* opaque, AndroidDevice* ad, char* msg, int msgsize);
-
-/* Callback routine that is invoked when an I/O failure occurs on a channel.
- * Note that this callback will not be invoked on connection failures.
- * Param:
- *  opaque - Opaque pointer that was passed to android_device_init API.
- *  ad - Android device instance
- *  failure - Contains 'errno' indicating the reason for failure.
- */
-typedef void (*io_failure_cb)(void* opaque, AndroidDevice* ad, int failure);
-
-/* Callback routine that is invoked when an asynchronous data send has been
- * completed.
- * Param:
- *  opaque - An opaque pointer associated with the data.
- *  res - Result of data transfer.
- *  data, size - Transferred data buffer.
- *  sent - Number of sent bytes.
- */
-typedef void (*async_send_cb)(void* opaque,
-                              ATResult res,
-                              void* data,
-                              int size,
-                              int sent);
-
-/********************************************************************************
- *                       Android Device API.
- *******************************************************************************/
-
-/* Initializes android device descriptor.
- * Param:
- *  opaque - An opaque pointer to associate with the descriptor. This pointer
- *      will be passed to all callbacks (see above) that were invoked by the
- *      initializing android device instance.
- *  port - TCP port to use for connection.
- *  on_io_failure - Callback to invoke when an I/O failure occurs on a channel
- *      used by the initializing android device instance. Can be NULL.
- * Return:
- *  Initialized android device descriptor on success, or NULL on failure.
- */
-extern AndroidDevice* android_device_init(void* opaque,
-                                          int port,
-                                          io_failure_cb on_io_failure);
-
-/* Disconnects and destroys android device descriptor.
- * Param:
- *  ad - Android device descriptor, returned from android_device_init API.
- *      Note that memory allocated for this descriptor will be freed in this
- *      routine.
- */
-extern void android_device_destroy(AndroidDevice* ad);
-
-/* Synchronously connects to the device. See notes above for more details.
- * Param:
- *  ad - Android device descriptor, returned from android_device_init API.
- *  to - Milliseconds to wait for connection to be established.
- * Return:
- *  Zero on success, or non-zero value on failure with 'errno' properly set.
- */
-extern int android_device_connect_sync(AndroidDevice* ad, int to);
-
-/* Asynchronously connects to the device. See notes above for more details.
- * Param:
- *  ad - Android device descriptor, returned from android_device_init API.
- *  on_connected - Callback to invoke when connection is completed (i,e, both,
- *      event, and query channels have been connected). This parameter can be
- *      NULL. Note that connection errors will be also reported through this
- *      callback. Also note that this callback will be invoked even if this
- *      routine returns with a failure.
- * Return:
- *  Zero on success, or non-zero value on failure with 'errno' properly set.
- */
-extern int android_device_connect_async(AndroidDevice* ad,
-                                        device_connected_cb on_connected);
-
-/* Disconnects from the android device.
- * Param:
- *  ad - Android device descriptor, returned from android_device_init API.
- */
-extern void android_device_disconnect(AndroidDevice* ad);
-
-/* Queries the device via query channel.
- * Param:
- *  ad - Android device descriptor, returned from android_device_init API.
- *  query - Zero-terminated query string.
- *  buff, buffsize - Buffer where to receive the response to the query.
- *  to - Milliseconds to wait for the entire query to complete.
- * Return:
- *  Zero on success, or non-zero value on failure with 'errno' properly set:
- *      - 0 Indicates that the server has failed the query.
- *      - Anything else indicates an I/O error.
- */
-extern int android_device_query(AndroidDevice* ad,
-                                const char* query,
-                                char* buff,
-                                size_t buffsize,
-                                int to);
-
-/* Starts a query that may require more than one buffer transfer.
- * This routine allows to initiate a query that may require more than one call to
- * send_data, or may have a format that differs from the usual (a zero-terminated
- * string). For instance, sending a BLOB data should use this routine to start a
- * a query, then use android_device_send_query_data to transfer the data, and
- * then call android_device_complete_query to obtain the response.
- * Param:
- *  ad - Android device descriptor, returned from android_device_init API.
- *  query - Zero-terminated query string.
- *  to - Milliseconds to wait for the entire query to complete.
- * Return:
- *  Zero on success, or non-zero value on failure with 'errno' properly set:
- *      - 0 Indicates that the server has failed the query.
- *      - Anything else indicates an I/O error.
- */
-extern int android_device_start_query(AndroidDevice* ad,
-                                      const char* query,
-                                      int to);
-
-/* Sends data block for a query started with android_device_start_query
- * Param:
- *  ad - Android device descriptor, returned from android_device_init API.
- *  data, size - Data to transfer.
- * Return:
- *  Number of bytes transferred on success, or -1 on failure with errno
- *  containing the reason for failure.
- */
-extern int android_device_send_query_data(AndroidDevice* ad,
-                                          const void* data,
-                                          int size);
-
-/* Completes a query started with android_device_start_query, and receives the
- * query response.
- * Param:
- *  ad - Android device descriptor, returned from android_device_init API.
- *  buff, buffsize - Buffer where to receive the response to the query.
- * Return:
- *  Zero on success, or non-zero value on failure with 'errno' properly set.
- */
-extern int android_device_complete_query(AndroidDevice* ad, char* buff, size_t buffsize);
-
-/* Start listening on the event channel.
- * Param:
- *  ad - Android device descriptor, returned from android_device_init API.
- *  buff, buffsize - Buffer where to receive the event message.
- *  on_event - Callback to invoke on event. Note that this callback will be
- *      invoked even if this routine returns with a failure.
- * Return:
- *  Zero on success, or non-zero value on failure with 'errno' properly set.
- */
-extern int android_device_listen(AndroidDevice* ad,
-                                 char* buff,
-                                 int buffsize,
-                                 event_cb on_event);
-
-/* Asynchronously sends data to the android device.
- * Param:
- *  ad - Android device descriptor, returned from android_device_init API.
- *  data, size - Buffer containing data to send.
- *  free_on_close - A boolean flag indicating whether the data buffer should be
- *      freed upon data transfer completion.
- *  cb - Callback to invoke when data transfer is completed.
- *  opaque - An opaque pointer to pass to the transfer completion callback.
- */
-extern int android_device_send_async(AndroidDevice* ad,
-                                     void* data,
-                                     int size,
-                                     int free_on_close,
-                                     async_send_cb cb,
-                                     void* opaque);
-
-#endif  /* ANDROID_ANDROID_DEVICE_H_ */
diff --git a/android/async-socket-connector.c b/android/async-socket-connector.c
index 836016c..0f50399 100644
--- a/android/async-socket-connector.c
+++ b/android/async-socket-connector.c
@@ -20,8 +20,6 @@
  * 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 "utils/panic.h"
diff --git a/android/async-socket-connector.h b/android/async-socket-connector.h
index bedc2df..d49d203 100644
--- a/android/async-socket-connector.h
+++ b/android/async-socket-connector.h
@@ -17,7 +17,9 @@
 #ifndef ANDROID_ASYNC_SOCKET_CONNECTOR_H_
 #define ANDROID_ASYNC_SOCKET_CONNECTOR_H_
 
+#include "qemu-common.h"
 #include "android/async-io-common.h"
+#include "android/async-utils.h"
 
 /*
  * Contains declaration of an API that allows asynchronous connection to a
diff --git a/android/async-socket.c b/android/async-socket.c
index 5e2ae29..ab4e4b6 100644
--- a/android/async-socket.c
+++ b/android/async-socket.c
@@ -20,8 +20,6 @@
  * 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"
@@ -683,10 +681,10 @@
 _async_socket_close_socket(AsyncSocket* as)
 {
     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);
+        loopIo_done(as->io);
+        socket_close(as->fd);
         as->fd = -1;
     }
 }
@@ -1217,15 +1215,22 @@
     AsyncSocketIO* const asr =
         _async_socket_reader_new(as, buffer, len, reader_cb, reader_opaque,
                                  deadline);
-    /* Add new reader to the list. Note that we use initial reference from I/O
-     * 'new' routine as "in the list" reference counter. */
-    if (as->readers_head == NULL) {
-        as->readers_head = as->readers_tail = asr;
+    if (async_socket_is_connected(as)) {
+        /* Add new reader to the list. Note that we use initial reference from I/O
+         * 'new' routine as "in the list" reference counter. */
+        if (as->readers_head == NULL) {
+            as->readers_head = as->readers_tail = asr;
+        } else {
+            as->readers_tail->next = asr;
+            as->readers_tail = asr;
+        }
+        loopIo_wantRead(as->io);
     } else {
-        as->readers_tail->next = asr;
-        as->readers_tail = asr;
+        D("ASocket %s: Read on a disconnected socket.", _async_socket_string(as));
+        errno = ECONNRESET;
+        reader_cb(reader_opaque, asr, ASIO_STATE_FAILED);
+        async_socket_io_release(asr);
     }
-    loopIo_wantRead(as->io);
 }
 
 void
@@ -1253,15 +1258,22 @@
     AsyncSocketIO* const asw =
         _async_socket_writer_new(as, buffer, len, writer_cb, writer_opaque,
                                  deadline);
-    /* Add new writer to the list. Note that we use initial reference from I/O
-     * 'new' routine as "in the list" reference counter. */
-    if (as->writers_head == NULL) {
-        as->writers_head = as->writers_tail = asw;
+    if (async_socket_is_connected(as)) {
+        /* Add new writer to the list. Note that we use initial reference from I/O
+         * 'new' routine as "in the list" reference counter. */
+        if (as->writers_head == NULL) {
+            as->writers_head = as->writers_tail = asw;
+        } else {
+            as->writers_tail->next = asw;
+            as->writers_tail = asw;
+        }
+        loopIo_wantWrite(as->io);
     } else {
-        as->writers_tail->next = asw;
-        as->writers_tail = asw;
+        D("ASocket %s: Write on a disconnected socket.", _async_socket_string(as));
+        errno = ECONNRESET;
+        writer_cb(writer_opaque, asw, ASIO_STATE_FAILED);
+        async_socket_io_release(asw);
     }
-    loopIo_wantWrite(as->io);
 }
 
 void
@@ -1294,3 +1306,9 @@
 {
     return sock_address_get_port(&as->address);
 }
+
+int
+async_socket_is_connected(const AsyncSocket* as)
+{
+    return as->fd >= 0;
+}
diff --git a/android/async-socket.h b/android/async-socket.h
index 503907b..bdfd272 100644
--- a/android/async-socket.h
+++ b/android/async-socket.h
@@ -17,7 +17,9 @@
 #ifndef ANDROID_ASYNC_SOCKET_H_
 #define ANDROID_ASYNC_SOCKET_H_
 
+#include "qemu-common.h"
 #include "android/async-io-common.h"
+#include "android/async-utils.h"
 
 /*
  * Contains declaration of an API that encapsulates communication via an
@@ -264,4 +266,10 @@
 /* Gets TCP port for the socket. */
 extern int async_socket_get_port(const AsyncSocket* as);
 
+/* Checks if socket is connected.
+ * Return:
+ *  Boolean: 1 - socket is connected, 0 - socket is not connected.
+ */
+extern int async_socket_is_connected(const AsyncSocket* as);
+
 #endif  /* ANDROID_ASYNC_SOCKET_H_ */
diff --git a/android/hw-sensors.c b/android/hw-sensors.c
index 17b2491..a7daea5 100644
--- a/android/hw-sensors.c
+++ b/android/hw-sensors.c
@@ -438,7 +438,7 @@
         }
 
         /* If emulating device is connected update sensor state there too. */
-        if (hw->sensors_port != NULL && sensors_port_is_connected(hw->sensors_port)) {
+        if (hw->sensors_port != NULL) {
             if (enabled) {
                 sensors_port_enable_sensor(hw->sensors_port, (const char*)msg);
             } else {
@@ -692,11 +692,6 @@
         h->sensors[ANDROID_SENSOR_TEMPERATURE].enabled = 1;
     }
 
-    if (h->sensors_port != NULL) {
-        /* Init sensors on the attached device. */
-        sensors_port_init_sensors(h->sensors_port);
-    }
-
     /* XXX: TODO: Add other tests when we add the corresponding
         * properties to hardware-properties.ini et al. */
 
diff --git a/android/multitouch-port.c b/android/multitouch-port.c
index 9a9313c..7cb9656 100644
--- a/android/multitouch-port.c
+++ b/android/multitouch-port.c
@@ -19,47 +19,97 @@
 #include "android/hw-events.h"
 #include "android/charmap.h"
 #include "android/multitouch-screen.h"
+#include "android/sdk-controller-socket.h"
 #include "android/multitouch-port.h"
 #include "android/globals.h"  /* for android_hw */
 #include "android/utils/misc.h"
 #include "android/utils/jpeg-compress.h"
+#include "android/utils/debug.h"
 
 #define  E(...)    derror(__VA_ARGS__)
 #define  W(...)    dwarning(__VA_ARGS__)
 #define  D(...)    VERBOSE_PRINT(mtport,__VA_ARGS__)
 #define  D_ACTIVE  VERBOSE_CHECK(mtport)
 
-/* Query timeout in milliseconds. */
-#define MTSP_QUERY_TIMEOUT       3000
-#define MTSP_MAX_MSG             2048
-#define MTSP_MAX_EVENT           2048
+#define TRACE_ON    1
+
+#if TRACE_ON
+#define  T(...)    VERBOSE_PRINT(mtport,__VA_ARGS__)
+#else
+#define  T(...)
+#endif
+
+/* Timeout (millisec) to use when communicating with SDK controller. */
+#define SDKCTL_MT_TIMEOUT      3000
+
+/*
+ * Message types used in multi-touch emulation.
+ */
+
+/* Pointer move message. */
+#define SDKCTL_MT_MOVE          1
+/* First pointer down message. */
+#define SDKCTL_MT_FISRT_DOWN    2
+/* Last pointer up message. */
+#define SDKCTL_MT_LAST_UP       3
+/* Pointer down message. */
+#define SDKCTL_MT_POINTER_DOWN  4
+/* Pointer up message. */
+#define SDKCTL_MT_POINTER_UP    5
+/* Sends framebuffer update. */
+#define SDKCTL_MT_FB_UPDATE     6
 
 /* Multi-touch port descriptor. */
 struct AndroidMTSPort {
     /* Caller identifier. */
-    void*           opaque;
-    /* Connected android device. */
-    AndroidDevice*  device;
+    void*               opaque;
+    /* Communication socket. */
+    SDKCtlSocket*       sdkctl;
     /* Initialized JPEG compressor instance. */
-    AJPEGDesc*      jpeg_compressor;
-    /* Connection status: 1 connected, 0 - disconnected. */
-    int             is_connected;
-    /* Buffer where to receive multitouch messages. */
-    char            mts_msg[MTSP_MAX_MSG];
-    /* Buffer where to receive multitouch events. */
-    char            events[MTSP_MAX_EVENT];
+    AJPEGDesc*          jpeg_compressor;
+    /* Direct packet descriptor for framebuffer updates. */
+    SDKCtlDirectPacket* fb_packet;
 };
 
+/* Data sent with SDKCTL_MT_QUERY_START */
+typedef struct QueryDispData {
+    /* Width of the emulator display. */
+    int     width;
+    /* Height of the emulator display. */
+    int     height;
+} QueryDispData;
+
+/* Multi-touch event structure received from SDK controller port. */
+typedef struct AndroidMTEvent {
+    /* Pointer identifier. */
+    int     pid;
+    /* Pointer 'x' coordinate. */
+    int     x;
+    /* Pointer 'y' coordinate. */
+    int     y;
+    /* Pointer pressure. */
+    int     pressure;
+} AndroidMTEvent;
+
+/* Multi-touch pointer descriptor received from SDK controller port. */
+typedef struct AndroidMTPtr {
+    /* Pointer identifier. */
+    int     pid;
+} AndroidMTPtr;
+
 /* Destroys and frees the descriptor. */
 static void
 _mts_port_free(AndroidMTSPort* mtsp)
 {
     if (mtsp != NULL) {
+        if (mtsp->fb_packet != NULL) {
+            sdkctl_direct_packet_release(mtsp->fb_packet);
+        }
         if (mtsp->jpeg_compressor != NULL) {
             jpeg_compressor_destroy(mtsp->jpeg_compressor);
         }
-        if (mtsp->device != NULL) {
-            android_device_destroy(mtsp->device);
+        if (mtsp->sdkctl != NULL) {
+            sdkctl_socket_release(mtsp->sdkctl);
         }
         AFREE(mtsp);
     }
@@ -114,170 +164,163 @@
  *                          Multi-touch event handlers
  *******************************************************************************/
 
-/* Handles "pointer move" event. */
+/* Handles "pointer move" event.
+ * Param:
+ *  param - Array of moving pointers.
+ *  pointers_count - Number of pointers in the array.
+ */
 static void
-_on_move(const char* param)
+_on_move(const AndroidMTEvent* param, int pointers_count)
 {
-    const char* pid = param;
-    D(">>> MOVE: %s", param);
-    while (pid && *pid) {
-        int pid_val, x, y, pressure = 0;
-        if (!get_token_value_int(pid, "pid", &pid_val) &&
-            !get_token_value_int(pid, "x", &x) &&
-            !get_token_value_int(pid, "y", &y)) {
-            get_token_value_int(pid, "pressure", &pressure);
-            _on_action_move(pid_val, x, y, pressure);
-            pid = strstr(pid + 1, "pid");
-        } else {
-            break;
-        }
+    int n;
+    for (n = 0; n < pointers_count; n++, param++) {
+        T("Multi-touch: MOVE(%d): %d-> %d:%d:%d",
+          n, param->pid, param->x, param->y, param->pressure);
+         _on_action_move(param->pid, param->x, param->y, param->pressure);
     }
 }
 
 /* Handles "first pointer down" event. */
 static void
-_on_down(const char* param)
+_on_down(const AndroidMTEvent* param)
 {
-    int pid_val, x, y, pressure = 0;
-    D(">>> 1-ST DOWN: %s", param);
-    if (!get_token_value_int(param, "pid", &pid_val) &&
-        !get_token_value_int(param, "x", &x) &&
-        !get_token_value_int(param, "y", &y)) {
-        get_token_value_int(param, "pressure", &pressure);
-        _on_action_down(pid_val, x, y, pressure);
-    } else {
-        W("Invalid parameters '%s' for MTS 'down' event", param);
-    }
+    T("Multi-touch: 1-ST DOWN: %d-> %d:%d:%d",
+      param->pid, param->x, param->y, param->pressure);
+    _on_action_down(param->pid, param->x, param->y, param->pressure);
 }
 
 /* Handles "last pointer up" event. */
 static void
-_on_up(const char* param)
+_on_up(const AndroidMTPtr* param)
 {
-    int pid_val;
-    D(">>> LAST UP: %s", param);
-    if (!get_token_value_int(param, "pid", &pid_val)) {
-        _on_action_up(pid_val);
-    } else {
-        W("Invalid parameters '%s' for MTS 'up' event", param);
-    }
+    T("Multi-touch: LAST UP: %d", param->pid);
+    _on_action_up(param->pid);
 }
 
 /* Handles "next pointer down" event. */
 static void
-_on_pdown(const char* param)
+_on_pdown(const AndroidMTEvent* param)
 {
-    int pid_val, x, y, pressure = 0;
-    D(">>> DOWN: %s", param);
-    if (!get_token_value_int(param, "pid", &pid_val) &&
-        !get_token_value_int(param, "x", &x) &&
-        !get_token_value_int(param, "y", &y)) {
-        get_token_value_int(param, "pressure", &pressure);
-        _on_action_pointer_down(pid_val, x, y, pressure);
-    } else {
-        W("Invalid parameters '%s' for MTS 'pointer down' event", param);
-    }
+    T("Multi-touch: DOWN: %d-> %d:%d:%d",
+      param->pid, param->x, param->y, param->pressure);
+    _on_action_pointer_down(param->pid, param->x, param->y, param->pressure);
 }
 
 /* Handles "next pointer up" event. */
 static void
-_on_pup(const char* param)
+_on_pup(const AndroidMTPtr* param)
 {
-    int pid_val;
-    D(">>> UP: %s", param);
-    if (!get_token_value_int(param, "pid", &pid_val)) {
-        _on_action_pointer_up(pid_val);
-    } else {
-        W("Invalid parameters '%s' for MTS 'up' event", param);
-    }
+    T("Multi-touch: UP: %d", param->pid);
+    _on_action_pointer_up(param->pid);
 }
 
 /********************************************************************************
  *                      Device communication callbacks
  *******************************************************************************/
 
-/* Main event handler.
- * This routine is invoked when an event message has been received from the
- * device.
- */
-static void
-_on_event_received(void* opaque, AndroidDevice* ad, char* msg, int msgsize)
+/* A callback that is invoked on SDK controller socket connection events. */
+static AsyncIOAction
+_on_multitouch_socket_connection(void* opaque,
+                                 SDKCtlSocket* sdkctl,
+                                 AsyncIOState status)
 {
-    char* action;
-    int res;
-    AndroidMTSPort* mtsp = (AndroidMTSPort*)opaque;
-
-    if (errno) {
-        D("Multi-touch notification has failed: %s", strerror(errno));
-        return;
-    }
-
-    /* Dispatch the event to an appropriate handler. */
-    res = get_token_value_alloc(msg, "action", &action);
-    if (!res) {
-        const char* param = strchr(msg, ' ');
-        if (param) {
-            param++;
+    if (status == ASIO_STATE_FAILED) {
+        /* Reconnect (after timeout delay) on failures */
+        if (sdkctl_socket_is_handshake_ok(sdkctl)) {
+            sdkctl_socket_reconnect(sdkctl, SDKCTL_DEFAULT_TCP_PORT,
+                                    SDKCTL_MT_TIMEOUT);
         }
-        if (!strcmp(action, "move")) {
-            _on_move(param);
-        } else if (!strcmp(action, "down")) {
-            _on_down(param);
-        } else if (!strcmp(action, "up")) {
-            _on_up(param);
-        } else if (!strcmp(action, "pdown")) {
-            _on_pdown(param);
-        } else if (!strcmp(action, "pup")) {
-            _on_pup(param);
-        } else {
-            D("Unknown multi-touch event action '%s'", action);
-        }
-        free(action);
     }
-
-    /* Listen to the next event. */
-    android_device_listen(ad, mtsp->events, sizeof(mtsp->events),
-                          _on_event_received);
+    return ASIO_ACTION_DONE;
 }
 
-/* A callback that is invoked when android device is connected (i.e. both,
- * command and event channels have been established).
- * Param:
- *  opaque - AndroidMTSPort instance.
- *  ad - Android device used by this port.
- *  failure - Connections status.
- */
+/* A callback that is invoked on SDK controller port connection events. */
 static void
-_on_device_connected(void* opaque, AndroidDevice* ad, int failure)
+_on_multitouch_port_connection(void* opaque,
+                               SDKCtlSocket* sdkctl,
+                               SdkCtlPortStatus status)
 {
-    if (!failure) {
-        AndroidMTSPort* mtsp = (AndroidMTSPort*)opaque;
-        mtsp->is_connected = 1;
-        D("Multi-touch emulation has started");
-        android_device_listen(mtsp->device, mtsp->events, sizeof(mtsp->events),
-                              _on_event_received);
-        mts_port_start(mtsp);
+    switch (status) {
+        case SDKCTL_PORT_CONNECTED:
+            D("Multi-touch: SDK Controller is connected");
+            break;
+
+        case SDKCTL_PORT_DISCONNECTED:
+            D("Multi-touch: SDK Controller is disconnected");
+            break;
+
+        case SDKCTL_PORT_ENABLED:
+            D("Multi-touch: SDK Controller port is enabled.");
+            break;
+
+        case SDKCTL_PORT_DISABLED:
+            D("Multi-touch: SDK Controller port is disabled.");
+            break;
+
+        case SDKCTL_HANDSHAKE_CONNECTED:
+            D("Multi-touch: Handshake succeeded with connected port.");
+            break;
+
+        case SDKCTL_HANDSHAKE_NO_PORT:
+            D("Multi-touch: Handshake succeeded with disconnected port.");
+            break;
+
+        case SDKCTL_HANDSHAKE_DUP:
+            W("Multi-touch: Handshake failed due to port duplication.");
+            sdkctl_socket_disconnect(sdkctl);
+            break;
+
+        case SDKCTL_HANDSHAKE_UNKNOWN_QUERY:
+            W("Multi-touch: Handshake failed due to unknown query.");
+            sdkctl_socket_disconnect(sdkctl);
+            break;
+
+        case SDKCTL_HANDSHAKE_UNKNOWN_RESPONSE:
+        default:
+            W("Multi-touch: Handshake failed due to unknown reason.");
+            sdkctl_socket_disconnect(sdkctl);
+            break;
     }
 }
 
-/* Invoked when an I/O failure occurs on a socket.
- * Note that this callback will not be invoked on connection failures.
- * Param:
- *  opaque - AndroidMTSPort instance.
- *  ad - Android device instance
- *  ads - Connection socket where failure has occured.
- *  failure - Contains 'errno' indicating the reason for failure.
- */
+/* A callback that is invoked when a message is received from the device. */
 static void
-_on_io_failure(void* opaque, AndroidDevice* ad, int failure)
+_on_multitouch_message(void* client_opaque,
+                       SDKCtlSocket* sdkctl,
+                       SDKCtlMessage* message,
+                       int msg_type,
+                       void* msg_data,
+                       int msg_size)
 {
-    AndroidMTSPort* mtsp = (AndroidMTSPort*)opaque;
-    E("Multi-touch port got disconnected: %s", strerror(failure));
-    mtsp->is_connected = 0;
-    android_device_disconnect(ad);
+    switch (msg_type) {
+        case SDKCTL_MT_MOVE: {
+            assert((msg_size / sizeof(AndroidMTEvent)) && !(msg_size % sizeof(AndroidMTEvent)));
+            _on_move((const AndroidMTEvent*)msg_data, msg_size / sizeof(AndroidMTEvent));
+            break;
+        }
 
-    /* Try to reconnect again. */
-    android_device_connect_async(ad, _on_device_connected);
+        case SDKCTL_MT_FISRT_DOWN:
+            assert(msg_size / sizeof(AndroidMTEvent) && !(msg_size % sizeof(AndroidMTEvent)));
+            _on_down((const AndroidMTEvent*)msg_data);
+            break;
+
+        case SDKCTL_MT_LAST_UP:
+            _on_up((const AndroidMTPtr*)msg_data);
+            break;
+
+        case SDKCTL_MT_POINTER_DOWN:
+            assert(msg_size / sizeof(AndroidMTEvent) && !(msg_size % sizeof(AndroidMTEvent)));
+            _on_pdown((const AndroidMTEvent*)msg_data);
+            break;
+
+        case SDKCTL_MT_POINTER_UP:
+            _on_pup((const AndroidMTPtr*)msg_data);
+            break;
+
+        default:
+            W("Multi-touch: Unknown message %d", msg_type);
+            break;
+    }
 }
 
 /********************************************************************************
@@ -288,31 +331,32 @@
 mts_port_create(void* opaque)
 {
     AndroidMTSPort* mtsp;
-    int res;
 
     ANEW0(mtsp);
-    mtsp->opaque = opaque;
-    mtsp->is_connected = 0;
+    mtsp->opaque                = opaque;
 
     /* Initialize default MTS descriptor. */
     multitouch_init(mtsp);
 
-    /* Create JPEG compressor. Put "$BLOB:%09d\0" + MTFrameHeader header in front
-     * of the compressed data. this way we will have entire query ready to be
+    /* Create JPEG compressor. Put message header + MTFrameHeader in front of the
+     * compressed data. this way we will have entire query ready to be
      * transmitted to the device. */
-    mtsp->jpeg_compressor = jpeg_compressor_create(16 + sizeof(MTFrameHeader), 4096);
+    mtsp->jpeg_compressor =
+        jpeg_compressor_create(sdkctl_message_get_header_size() + sizeof(MTFrameHeader), 4096);
 
-    mtsp->device = android_device_init(mtsp, AD_MULTITOUCH_PORT, _on_io_failure);
-    if (mtsp->device == NULL) {
-        _mts_port_free(mtsp);
-        return NULL;
-    }
+    mtsp->sdkctl = sdkctl_socket_new(SDKCTL_MT_TIMEOUT, "multi-touch",
+                                     _on_multitouch_socket_connection,
+                                     _on_multitouch_port_connection,
+                                     _on_multitouch_message, mtsp);
+    sdkctl_init_recycler(mtsp->sdkctl, 64, 8);
 
-    res = android_device_connect_async(mtsp->device, _on_device_connected);
-    if (res != 0) {
-        mts_port_destroy(mtsp);
-        return NULL;
-    }
+    /* Create a direct packet that will wrap up framebuffer updates. Note that
+     * we need to do this after we have initialized the recycler! */
+    mtsp->fb_packet = sdkctl_direct_packet_new(mtsp->sdkctl);
+
+    /* Now we can initiate connection witm MT port on the device. */
+    sdkctl_socket_connect(mtsp->sdkctl, SDKCTL_DEFAULT_TCP_PORT,
+                          SDKCTL_MT_TIMEOUT);
 
     return mtsp;
 }
@@ -323,75 +367,6 @@
     _mts_port_free(mtsp);
 }
 
-int
-mts_port_is_connected(AndroidMTSPort* mtsp)
-{
-    return mtsp->is_connected;
-}
-
-int
-mts_port_start(AndroidMTSPort* mtsp)
-{
-    char qresp[MTSP_MAX_MSG];
-    char query[256];
-    AndroidHwConfig* config = android_hw;
-
-    /* Query the device to start capturing multi-touch events, also providing
-     * the device with width / height of the emulator's screen. This is required
-     * so device can properly adjust multi-touch event coordinates, and display
-     * emulator's framebuffer. */
-    snprintf(query, sizeof(query), "start:%dx%d",
-             config->hw_lcd_width, config->hw_lcd_height);
-    int res = android_device_query(mtsp->device, query, qresp, sizeof(qresp),
-                                   MTSP_QUERY_TIMEOUT);
-    if (!res) {
-        /* By protocol device should reply with its view dimensions. */
-        if (*qresp) {
-            int width, height;
-            if (sscanf(qresp, "%dx%d", &width, &height) == 2) {
-                multitouch_set_device_screen_size(width, height);
-                D("Multi-touch emulation has started. Device dims: %dx%d",
-                  width, height);
-            } else {
-                E("Unexpected reply to MTS 'start' query: %s", qresp);
-                android_device_query(mtsp->device, "stop", qresp, sizeof(qresp),
-                                     MTSP_QUERY_TIMEOUT);
-                res = -1;
-            }
-        } else {
-            E("MTS protocol error: no reply to query 'start'");
-            android_device_query(mtsp->device, "stop", qresp, sizeof(qresp),
-                                 MTSP_QUERY_TIMEOUT);
-            res = -1;
-        }
-    } else {
-        if (errno) {
-            D("Query 'start' failed on I/O: %s", strerror(errno));
-        } else {
-            D("Query 'start' failed on device: %s", qresp);
-        }
-    }
-    return res;
-}
-
-int
-mts_port_stop(AndroidMTSPort* mtsp)
-{
-    char qresp[MTSP_MAX_MSG];
-    const int res =
-        android_device_query(mtsp->device, "stop", qresp, sizeof(qresp),
-                             MTSP_QUERY_TIMEOUT);
-    if (res) {
-        if (errno) {
-            D("Query 'stop' failed on I/O: %s", strerror(errno));
-        } else {
-            D("Query 'stop' failed on device: %s", qresp);
-        }
-    }
-
-    return res;
-}
-
 /********************************************************************************
  *                       Handling framebuffer updates
  *******************************************************************************/
@@ -412,6 +387,8 @@
              int jpeg_quality,
              int ydir)
 {
+    T("Multi-touch: compressing %d bytes frame buffer", fmt->w * fmt->h * fmt->bpp);
+
     jpeg_compressor_compress_fb(mtsp->jpeg_compressor, fmt->x, fmt->y, fmt->w,
                                 fmt->h, fmt->disp_height, fmt->bpp, fmt->bpl,
                                 fb, jpeg_quality, ydir);
@@ -421,15 +398,12 @@
 mts_port_send_frame(AndroidMTSPort* mtsp,
                     MTFrameHeader* fmt,
                     const uint8_t* fb,
-                    async_send_cb cb,
+                    on_sdkctl_direct_cb cb,
                     void* cb_opaque,
                     int ydir)
 {
-    char* query;
-    int blob_size, off;
-
     /* Make sure that port is connected. */
-    if (!mts_port_is_connected(mtsp)) {
+    if (!sdkctl_socket_is_port_ready(mtsp->sdkctl)) {
         return -1;
     }
 
@@ -437,31 +411,32 @@
     fmt->format = MTFB_JPEG;
     _fb_compress(mtsp, fmt, fb, 10, ydir);
 
-    /* Total size of the blob: header + JPEG image. */
-    blob_size = sizeof(MTFrameHeader) +
-                jpeg_compressor_get_jpeg_size(mtsp->jpeg_compressor);
+    /* Total size of the update data: header + JPEG image. */
+    const int update_size =
+        sizeof(MTFrameHeader) + jpeg_compressor_get_jpeg_size(mtsp->jpeg_compressor);
 
-    /* Query starts at the beginning of the buffer allocated by the compressor's
-     * destination manager. */
-    query = (char*)jpeg_compressor_get_buffer(mtsp->jpeg_compressor);
+    /* Update message starts at the beginning of the buffer allocated by the
+     * compressor's destination manager. */
+    uint8_t* const msg = (uint8_t*)jpeg_compressor_get_buffer(mtsp->jpeg_compressor);
 
-    /* Build the $BLOB query to transfer to the device. */
-    snprintf(query, jpeg_compressor_get_header_size(mtsp->jpeg_compressor),
-             "$BLOB:%09d", blob_size);
-    off = strlen(query) + 1;
+    /* Initialize message header. */
+    sdkctl_init_message_header(msg, SDKCTL_MT_FB_UPDATE, update_size);
 
-    /* Copy framebuffer update header to the query. */
-    memcpy(query + off, fmt, sizeof(MTFrameHeader));
+    /* Copy framebuffer update header to the message. */
+    memcpy(msg + sdkctl_message_get_header_size(), fmt, sizeof(MTFrameHeader));
+
+    /* Compression rate... */
+    const float comp_rate = ((float)jpeg_compressor_get_jpeg_size(mtsp->jpeg_compressor) / (fmt->w * fmt->h * fmt->bpp)) * 100;
 
     /* Zeroing the rectangle in the update header we indicate that it contains
      * no updates. */
     fmt->x = fmt->y = fmt->w = fmt->h = 0;
 
-    /* Initiate asynchronous transfer of the updated framebuffer rectangle. */
-    if (android_device_send_async(mtsp->device, query, off + blob_size, 0, cb, cb_opaque)) {
-        D("Unable to send query '%s': %s", query, strerror(errno));
-        return -1;
-    }
+    /* Send update to the device. */
+    sdkctl_direct_packet_send(mtsp->fb_packet, msg, cb, cb_opaque);
+
+    T("Multi-touch: Sent %d bytes in framebuffer update. Compression rate is %.2f%%",
+      update_size, comp_rate);
 
     return 0;
 }
diff --git a/android/multitouch-port.h b/android/multitouch-port.h
index d99b3fd..5652b43 100644
--- a/android/multitouch-port.h
+++ b/android/multitouch-port.h
@@ -17,17 +17,14 @@
 #ifndef ANDROID_ANDROID_MULTITOUCH_PORT_H_
 #define ANDROID_ANDROID_MULTITOUCH_PORT_H_
 
+#include "android/sdk-controller-socket.h"
+
 /*
  * Encapsulates exchange protocol between the multi-touch screen emulator, and an
  * application running on an Android device that provides touch events, and is
  * connected to the host via USB.
  */
 
-#include "android/android-device.h"
-
-/* TCP port reserved for multi-touch emulation. */
-#define AD_MULTITOUCH_PORT      1969
-
 /*
  * Codes that define transmitted framebuffer format:
  *
@@ -89,28 +86,6 @@
 /* Disconnects from the multi-touch port, and destroys the descriptor. */
 extern void mts_port_destroy(AndroidMTSPort* amtp);
 
-/* Checks if port is connected to a MT-emulating application on the device.
- * Note that connection can go out and then be restored at any time after
- * mts_port_create API succeeded.
- */
-extern int mts_port_is_connected(AndroidMTSPort* amtp);
-
-/* Queries the connected application to start delivering multi-touch events.
- * Param:
- *  amtp - Android multi-touch port instance returned from mts_port_create.
- * Return:
- *  Zero on success, failure otherwise.
- */
-extern int mts_port_start(AndroidMTSPort* amtp);
-
-/* Queries the connected application to stop delivering multi-touch events.
- * Param:
- *  amtp - Android multi-touch port instance returned from mts_port_create.
- * Return:
- *  Zero on success, failure otherwise.
- */
-extern int mts_port_stop(AndroidMTSPort* amtp);
-
 /* Sends framebuffer update to the multi-touch emulation application, running on
  * the android device.
  * Param:
@@ -129,7 +104,7 @@
 extern int mts_port_send_frame(AndroidMTSPort* mtsp,
                                MTFrameHeader* fmt,
                                const uint8_t* fb,
-                               async_send_cb cb,
+                               on_sdkctl_direct_cb cb,
                                void* cb_opaque,
                                int ydir);
 
diff --git a/android/multitouch-screen.c b/android/multitouch-screen.c
index bd96f79..59d4d75 100644
--- a/android/multitouch-screen.c
+++ b/android/multitouch-screen.c
@@ -29,6 +29,15 @@
 #define  D(...)    VERBOSE_PRINT(mtscreen,__VA_ARGS__)
 #define  D_ACTIVE  VERBOSE_CHECK(mtscreen)
 
+#define TRACE_ON    1
+
+#if TRACE_ON
+#define  T(...)    VERBOSE_PRINT(mtscreen,__VA_ARGS__)
+#else
+#define  T(...)
+#endif
+
+
 /* Maximum number of pointers, supported by multi-touch emulation. */
 #define MTS_POINTERS_NUM    10
 /* Signals that pointer is not tracked (or is "up"). */
@@ -54,10 +63,6 @@
     AndroidMTSPort* mtsp;
     /* Emulator's display state. */
     DisplayState*   ds;
-    /* Screen width of the device that emulates multi-touch. */
-    int             device_width;
-    /* Screen height of the device that emulates multi-touch. */
-    int             device_height;
     /* Number of tracked pointers. */
     int             tracked_ptr_num;
     /* Index in the 'tracked_pointers' array of the last pointer for which
@@ -239,24 +244,28 @@
 
 /* Callback that is invoked when framebuffer update has been transmitted to the
  * device. */
-static void
-_on_fb_sent(void* opaque, ATResult res, void* data, int size, int sent)
+static AsyncIOAction
+_on_fb_sent(void* opaque, SDKCtlDirectPacket* packet, AsyncIOState status)
 {
     MTSState* const mts_state = (MTSState*)opaque;
 
-    /* Lets see if we have accumulated more changes while transmission has been
-     * in progress. */
-    if (mts_state->fb_header.w && mts_state->fb_header.h) {
-        /* Send accumulated updates. */
-        if (mts_port_send_frame(mts_state->mtsp, &mts_state->fb_header,
-                                mts_state->current_fb, _on_fb_sent, mts_state,
-                                mts_state->ydir)) {
+    if (status == ASIO_STATE_SUCCEEDED) {
+        /* Lets see if we have accumulated more changes while transmission has been
+         * in progress. */
+        if (mts_state->fb_header.w && mts_state->fb_header.h) {
+            /* Send accumulated updates. */
+            if (mts_port_send_frame(mts_state->mtsp, &mts_state->fb_header,
+                                    mts_state->current_fb, _on_fb_sent, mts_state,
+                                    mts_state->ydir)) {
+                mts_state->fb_transfer_in_progress = 0;
+            }
+        } else {
+            /* Framebuffer transfer is completed, and no more updates are pending. */
             mts_state->fb_transfer_in_progress = 0;
         }
-    } else {
-        /* Framebuffer transfer is completed, and no more updates are pending. */
-        mts_state->fb_transfer_in_progress = 0;
     }
+
+    return ASIO_ACTION_DONE;
 }
 
 /* Common handler for framebuffer updates invoked by both, software, and OpenGLES
@@ -324,6 +333,9 @@
     MTSState* const mts_state = (MTSState*)opaque;
     const DisplaySurface* const surface = mts_state->ds->surface;
 
+    T("Multi-touch: Software renderer framebuffer update: %d:%d -> %dx%d",
+      x, y, w, h);
+
     /* TODO: For sofware renderer general framebuffer properties can change on
      * the fly. Find a callback that can catch that. For now, just copy FB
      * properties over in every FB update. */
@@ -336,6 +348,7 @@
 
     _mt_fb_common_update(mts_state, x, y, w, h);
 }
+
 void
 multitouch_opengles_fb_update(void* context,
                               int w, int h, int ydir,
@@ -349,6 +362,8 @@
         return;
     }
 
+    T("Multi-touch: openGLES framebuffer update: 0:0 -> %dx%d", w, h);
+
     /* GLES format is always RGBA8888 */
     mts_state->fb_header.bpp = 4;
     mts_state->fb_header.bpl = 4 * w;
@@ -380,8 +395,6 @@
         for (index = 0; index < MTS_POINTERS_NUM; index++) {
             mts_state->tracked_pointers[index].tracking_id = MTS_POINTER_UP;
         }
-        mts_state->device_width = android_hw->hw_lcd_width;
-        mts_state->device_height = android_hw->hw_lcd_height;
         mts_state->mtsp = mtsp;
         mts_state->fb_header.header_size = sizeof(MTFrameHeader);
         mts_state->fb_transfer_in_progress = 0;
@@ -453,12 +466,3 @@
 {
     return MTS_POINTERS_NUM - 1;
 }
-
-void
-multitouch_set_device_screen_size(int width, int height)
-{
-    MTSState* const mts_state = &_MTSState;
-
-    mts_state->device_width = width;
-    mts_state->device_height = height;
-}
diff --git a/android/multitouch-screen.h b/android/multitouch-screen.h
index 433944d..95ae86a 100644
--- a/android/multitouch-screen.h
+++ b/android/multitouch-screen.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_MULTITOUCH_SCREEN_H_
 #define ANDROID_MULTITOUCH_SCREEN_H_
 
+#include "android/sdk-controller-socket.h"
 #include "android/multitouch-port.h"
 
 /*
@@ -61,9 +62,6 @@
 /* Gets maximum slot index available for the multi-touch emulation. */
 extern int multitouch_get_max_slot();
 
-/* Saves screen size reported by the device that emulates multi-touch. */
-extern void multitouch_set_device_screen_size(int width, int height);
-
 /* A callback set to monitor OpenGLES framebuffer updates.
  * This callback is called by the renderer just before each new frame is
  * displayed, providing a copy of the framebuffer contents.
diff --git a/android/sdk-controller-socket.c b/android/sdk-controller-socket.c
index a300169..6a4ec18 100644
--- a/android/sdk-controller-socket.c
+++ b/android/sdk-controller-socket.c
@@ -20,8 +20,6 @@
  * 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"
@@ -34,7 +32,7 @@
 #define  D(...)    VERBOSE_PRINT(sdkctlsocket,__VA_ARGS__)
 #define  D_ACTIVE  VERBOSE_CHECK(sdkctlsocket)
 
-#define TRACE_ON    1
+#define TRACE_ON    0
 
 #if TRACE_ON
 #define  T(...)    VERBOSE_PRINT(sdkctlsocket,__VA_ARGS__)
@@ -53,12 +51,8 @@
     };
 };
 
-/********************************************************************************
- *                      SDKCtlPacket declarations
- *******************************************************************************/
-
 /*
- * Types of the packets of data sent via SDK controller socket.
+ * Types of the data packets sent via SDK controller socket.
  */
 
 /* The packet is a message. */
@@ -68,12 +62,56 @@
 /* The packet is a response to a query. */
 #define SDKCTL_PACKET_QUERY_RESPONSE    3
 
+/*
+ * Types of intenal port messages sent via SDK controller socket.
+ */
+
+/* Port is connected.
+ * This message is sent by SDK controller when the service connects a socket with
+ * a port that provides requested emulation functionality.
+ */
+#define SDKCTL_MSG_PORT_CONNECTED       -1
+/* Port is disconnected.
+ * This message is sent by SDK controller when a port that provides requested
+ * emulation functionality disconnects from the socket.
+ */
+#define SDKCTL_MSG_PORT_DISCONNECTED    -2
+/* Port is enabled.
+ * This message is sent by SDK controller when a port that provides requested
+ * emulation functionality is ready to do the emulation.
+ */
+#define SDKCTL_MSG_PORT_ENABLED         -3
+/* Port is disabled.
+ * This message is sent by SDK controller when a port that provides requested
+ * emulation functionality is not ready to do the emulation.
+ */
+#define SDKCTL_MSG_PORT_DISABLED        -4
+
+/*
+ * Types of internal 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
+
+/********************************************************************************
+ *                      SDKCtlPacket declarations
+ *******************************************************************************/
+
+/* Packet signature value ('SDKC'). */
+static const int _sdkctl_packet_sig = 0x53444B43;
+
 /* 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 {
+    /* Signature. */
+    int     signature;
     /* Total size of the data to transfer with this packet, including this
      * header. The transferring data should immediatelly follow this header. */
     int     size;
@@ -83,43 +121,61 @@
 } SDKCtlPacketHeader;
 
 /* Packet descriptor, allocated by this API for data packets to be sent to SDK
- * controller service on the device.
+ * controller.
  *
  * 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 {
+typedef struct SDKCtlPacket {
     /* Supports recycling. Don't put anything in front: recycler expects this
      * to be the first field in recyclable descriptor. */
-    SDKCtlRecycled      recycling;
+    SDKCtlRecycled          recycling;
 
-    /* Next packet in the list of packets to send. */
-    SDKCtlPacket*       next;
     /* SDK controller socket that transmits this packet. */
-    SDKCtlSocket*       sdkctl;
+    SDKCtlSocket*           sdkctl;
     /* Number of outstanding references to the packet. */
-    int                 ref_count;
+    int                     ref_count;
 
     /* Common packet header. Packet data immediately follows this header, so it
-     * must be last field in SDKCtlPacket descriptor. */
-    SDKCtlPacketHeader  header;
+     * must be the last field in SDKCtlPacket descriptor. */
+    SDKCtlPacketHeader      header;
+} SDKCtlPacket;
+
+/********************************************************************************
+ *                      SDKCtlDirectPacket declarations
+ *******************************************************************************/
+
+/* Direct packet descriptor, allocated by this API for direct data packets to be
+ * sent to SDK controller service on the device.
+ *
+ * Direct packet (unlike SDKCtlPacket) don't contain data buffer, but rather
+ * reference data allocated by the client. This is useful when client sends large
+ * amount of data (such as framebuffer updates sent my multi-touch port), and
+ * regular packet descriptors for such large transfer cannot be obtained from the
+ * recycler.
+ */
+struct SDKCtlDirectPacket {
+    /* Supports recycling. Don't put anything in front: recycler expects this
+     * to be the first field in recyclable descriptor. */
+    SDKCtlRecycled          recycling;
+
+    /* SDKCtlSocket that owns this packet. */
+    SDKCtlSocket*           sdkctl;
+    /* Packet to send. */
+    SDKCtlPacketHeader*     packet;
+    /* Callback to invoke on packet transmission events. */
+    on_sdkctl_direct_cb     on_sent;
+    /* An opaque pointer to pass to on_sent callback. */
+    void*                   on_sent_opaque;
+    /* Number of outstanding references to the packet. */
+    int                     ref_count;
 };
 
 /********************************************************************************
  *                      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
@@ -131,8 +187,7 @@
     /* 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. */
+    /* Query type. */
     int                 query_type;
 } SDKCtlQueryHeader;
 
@@ -148,12 +203,12 @@
      * to be the first field in recyclable descriptor. */
     SDKCtlRecycled          recycling;
 
-    /* Next query in the list of active, or recycled queries. */
+    /* Next query in the list of active 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. */
+     * timer is set to after query has been transmitted to the service. */
     Duration                deadline;
     /* SDK controller socket that owns the query. */
     SDKCtlSocket*           sdkctl;
@@ -163,20 +218,21 @@
     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). */
+    /* 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. */
+    /* Internal response buffer size used if query creator didn't provide its
+     * own. 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
+    /* Common query header. Query data immediately follows this header, so it
      * must be last field in SDKCtlQuery descriptor. */
     SDKCtlQueryHeader       header;
 };
@@ -195,6 +251,34 @@
 } SDKCtlQueryReplyHeader;
 
 /********************************************************************************
+ *                      SDKCtlMessage declarations
+ *******************************************************************************/
+
+/* Message packet descriptor.
+ *
+ * All messages, sent and received via SDK controller socket begin with this
+ * header, with message data immediately following this header.
+ */
+typedef struct SDKCtlMessageHeader {
+    /* Data packet header for this query. */
+    SDKCtlPacketHeader  packet;
+    /* Message type. */
+    int                 msg_type;
+} SDKCtlMessageHeader;
+
+/* Message packet descriptor.
+ *
+ * All messages, sent and received via SDK controller socket begin with this
+ * header, with message data immediately following this header.
+ */
+typedef struct SDKCtlMessage {
+    /* Data packet descriptor for this message. */
+    SDKCtlPacket  packet;
+    /* Message type. */
+    int           msg_type;
+} SDKCtlMessage;
+
+/********************************************************************************
  *                      SDK Control Socket declarations
  *******************************************************************************/
 
@@ -229,13 +313,15 @@
     /* Unites all types of headers used in SDK controller data exchange. */
     union {
         /* Common packet header. */
-        SDKCtlPacketHeader      header;
+        SDKCtlPacketHeader      packet_header;
         /* Header for a query packet. */
         SDKCtlQueryHeader       query_header;
+        /* Header for a message packet. */
+        SDKCtlMessageHeader     message_header;
         /* Header for a query response packet. */
         SDKCtlQueryReplyHeader  query_reply_header;
     };
-    /* Descriptor of a packet packet received from SDK controller. */
+    /* Descriptor of a packet that is being received from SDK controller. */
     SDKCtlPacket*               packet;
     /* A query for which a reply is currently being received. */
     SDKCtlQuery*                current_query;
@@ -244,42 +330,43 @@
 /* SDK controller socket descriptor. */
 struct SDKCtlSocket {
     /* SDK controller socket state */
-    SDKCtlSocketState           state;
+    SDKCtlSocketState               state;
+    /* SDK controller port status */
+    SdkCtlPortStatus                port_status;
     /* I/O dispatcher for the socket. */
-    SDKCtlIODispatcher          io_dispatcher;
+    SDKCtlIODispatcher              io_dispatcher;
     /* Asynchronous socket connected to SDK Controller on the device. */
-    AsyncSocket*                as;
+    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;
+    on_sdkctl_socket_connection_cb  on_socket_connection;
+    /* Client callback that monitors SDK controller prt connection. */
+    on_sdkctl_port_connection_cb    on_port_connection;
     /* A callback to invoke when a message is received from the SDK controller. */
-    on_sdkctl_message_cb        on_message;
+    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;
+    void*                           opaque;
+    /* Name of an SDK controller port this socket is connected to. */
+    char*                           service_name;
     /* I/O looper for timers. */
-    Looper*                     looper;
+    Looper*                         looper;
     /* Head of the active query list. */
-    SDKCtlQuery*                query_head;
+    SDKCtlQuery*                    query_head;
     /* Tail of the active query list. */
-    SDKCtlQuery*                query_tail;
+    SDKCtlQuery*                    query_tail;
     /* Query ID generator that gets incremented for each new query. */
-    int                         next_query_id;
+    int                             next_query_id;
     /* Timeout before trying to reconnect after disconnection. */
-    int                         reconnect_to;
+    int                             reconnect_to;
     /* Number of outstanding references to this descriptor. */
-    int                         ref_count;
+    int                             ref_count;
     /* Head of the recycled memory */
-    SDKCtlRecycled*             recycler;
+    SDKCtlRecycled*                 recycler;
     /* Recyclable block size. */
-    uint32_t                    recycler_block_size;
+    uint32_t                        recycler_block_size;
     /* Maximum number of blocks to recycle. */
-    int                         recycler_max;
+    int                             recycler_max;
     /* Number of blocs in the recycler. */
-    int                         recycler_count;
+    int                             recycler_count;
 };
 
 /********************************************************************************
@@ -294,16 +381,18 @@
     SDKCtlRecycled* block = NULL;
 
     if (sdkctl->recycler != NULL && size <= sdkctl->recycler_block_size) {
+        assert(sdkctl->recycler_count > 0);
         /* 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. */
+        /* There are no blocks in the recycler, but requested size fits. Lets
+         * allocate block that we can later recycle. */
         block = malloc(sdkctl->recycler_block_size);
         if (block == NULL) {
-            APANIC("SDKCtl %s: Unable to allocate %d bytes block",
+            APANIC("SDKCtl %s: Unable to allocate %d bytes block.",
                    sdkctl->service_name, sdkctl->recycler_block_size);
         }
         block->size = sdkctl->recycler_block_size;
@@ -324,18 +413,19 @@
 static void
 _sdkctl_socket_free_recycler(SDKCtlSocket* sdkctl, void* mem)
 {
-    SDKCtlRecycled* block = (SDKCtlRecycled*)mem;
+    SDKCtlRecycled* const block = (SDKCtlRecycled*)mem;
 
-    if (sdkctl->recycler_count == sdkctl->recycler_max ||
-        block->size != sdkctl->recycler_block_size) {
-        /* Recycler is full, or block cannot be recycled. */
+    if (block->size != sdkctl->recycler_block_size ||
+        sdkctl->recycler_count == sdkctl->recycler_max) {
+        /* Recycler is full, or block cannot be recycled. Just free the memory. */
         free(mem);
-        return;
+    } else {
+        /* Add that block to the recycler. */
+        assert(sdkctl->recycler_count >= 0);
+        block->next = sdkctl->recycler;
+        sdkctl->recycler = block;
+        sdkctl->recycler_count++;
     }
-
-    block->next = sdkctl->recycler;
-    sdkctl->recycler = block;
-    sdkctl->recycler_count++;
 }
 
 /* Empties the recycler for a given SDKCtlSocket. */
@@ -344,7 +434,7 @@
 {
     SDKCtlRecycled* block = sdkctl->recycler;
     while (block != NULL) {
-        void* to_free = block;
+        void* const to_free = block;
         block = block->next;
         free(to_free);
     }
@@ -366,6 +456,7 @@
 {
     SDKCtlSocket* const sdkctl = query->sdkctl;
     if (sdkctl->query_head == NULL) {
+        assert(sdkctl->query_tail == NULL);
         sdkctl->query_head = sdkctl->query_tail = query;
     } else {
         sdkctl->query_tail->next = query;
@@ -378,7 +469,9 @@
 
 /* Removes a query from the list of active queries.
  * Param:
- *  query - Query to remove from the list of active queries.
+ *  query - Query to remove from the list of active queries. If query has been
+ *      removed from the list, it will be dereferenced to offset the reference
+ *      that wad made when the query has been added to the list.
  * Return:
  *  Boolean: 1 if query has been removed, or 0 if query has not been found in the
  *  list of active queries.
@@ -390,11 +483,11 @@
     SDKCtlQuery* prev = NULL;
     SDKCtlQuery* head = sdkctl->query_head;
 
-    /* Quick check: the query could be currently handled by dispatcher. */
+    /* Quick check: the query could be currently handled by the dispatcher. */
     if (sdkctl->io_dispatcher.current_query == query) {
         /* Release the query from dispatcher. */
-        sdkctl_query_release(query);
         sdkctl->io_dispatcher.current_query = NULL;
+        sdkctl_query_release(query);
         return 1;
     }
 
@@ -403,32 +496,34 @@
         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);
+    if (head == NULL) {
+        D("SDKCtl %s: Query %p is not found in the list.",
+          sdkctl->service_name, query);
         return 0;
     }
+
+    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;
 }
 
 /* Removes a query (based on query ID) from the list of active queries.
@@ -437,11 +532,14 @@
  *  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.
+ *  given ID has not been found in the list. Note that query returned from this
+ *  routine still holds the reference made when the query has been added to the
+ *  list.
  */
 static SDKCtlQuery*
 _sdkctl_socket_remove_query_id(SDKCtlSocket* sdkctl, int query_id)
 {
+    SDKCtlQuery* query = NULL;
     SDKCtlQuery* prev = NULL;
     SDKCtlQuery* head = sdkctl->query_head;
 
@@ -449,7 +547,7 @@
     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;
+        query = sdkctl->io_dispatcher.current_query;
         sdkctl->io_dispatcher.current_query = NULL;
         return query;
     }
@@ -459,37 +557,38 @@
         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.",
+    if (head == NULL) {
+        D("SDKCtl %s: Query ID %d is not found in the list.",
           sdkctl->service_name, query_id);
         return NULL;
     }
+
+    /* Query is found in the list. */
+    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;
 }
 
 /* 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
+ *  A query removed from the head of the list of active queries, or NULL if query
  *  list is empty.
  */
 static SDKCtlQuery*
@@ -519,19 +618,24 @@
 
 /* Alocates a packet. */
 static SDKCtlPacket*
-_sdkctl_packet_new(SDKCtlSocket* sdkctl, int size, int type)
+_sdkctl_packet_new(SDKCtlSocket* sdkctl, uint32_t size, int type)
 {
-    const uint32_t total_size = sizeof(SDKCtlPacket) + size;
-    SDKCtlPacket* const packet = _sdkctl_socket_alloc_recycler(sdkctl, total_size);
+    /* Allocate packet descriptor large enough to contain packet data. */
+    SDKCtlPacket* const packet =
+        _sdkctl_socket_alloc_recycler(sdkctl, sizeof(SDKCtlPacket) + size);
 
-    packet->sdkctl      = sdkctl;
-    packet->ref_count   = 1;
-    packet->header.size = size;
-    packet->header.type = type;
+    packet->sdkctl              = sdkctl;
+    packet->ref_count           = 1;
+    packet->header.signature    = _sdkctl_packet_sig;
+    packet->header.size         = size;
+    packet->header.type         = type;
 
     /* Refence SDKCTlSocket that owns this packet. */
     sdkctl_socket_reference(sdkctl);
 
+    T("SDKCtl %s: Packet %p of type %d is allocated for %d bytes transfer.",
+          sdkctl->service_name, packet, type, size);
+
     return packet;
 }
 
@@ -541,23 +645,27 @@
 {
     SDKCtlSocket* const sdkctl = packet->sdkctl;
 
-    /* Free allocated resources. */
+    /* Recycle packet. */
     _sdkctl_socket_free_recycler(packet->sdkctl, packet);
 
+    T("SDKCtl %s: Packet %p is freed.", sdkctl->service_name, packet);
+
     /* Release SDKCTlSocket that owned this packet. */
     sdkctl_socket_release(sdkctl);
 }
 
+/* References a packet. */
 int
-sdkctl_packet_reference(SDKCtlPacket* packet)
+_sdkctl_packet_reference(SDKCtlPacket* packet)
 {
     assert(packet->ref_count > 0);
     packet->ref_count++;
     return packet->ref_count;
 }
 
+/* Releases a packet. */
 int
-sdkctl_packet_release(SDKCtlPacket* packet)
+_sdkctl_packet_release(SDKCtlPacket* packet)
 {
     assert(packet->ref_count > 0);
     packet->ref_count--;
@@ -569,6 +677,272 @@
     return packet->ref_count;
 }
 
+/* An I/O callback invoked on packet transmission.
+ * Param:
+ *  io_opaque SDKCtlPacket instance of the packet that's being sent with this I/O.
+ *  asio - Write I/O descriptor.
+ *  status - I/O status.
+ */
+static AsyncIOAction
+_on_sdkctl_packet_send_io(void* io_opaque,
+                          AsyncSocketIO* asio,
+                          AsyncIOState status)
+{
+    SDKCtlPacket* const packet = (SDKCtlPacket*)io_opaque;
+    AsyncIOAction action = ASIO_ACTION_DONE;
+
+    /* Reference the packet while we're in this callback. */
+    _sdkctl_packet_reference(packet);
+
+    /* Lets see what's going on with query transmission. */
+    switch (status) {
+        case ASIO_STATE_SUCCEEDED:
+            /* Packet has been sent to the service. */
+            T("SDKCtl %s: Packet %p transmission has succeeded.",
+              packet->sdkctl->service_name, packet);
+            break;
+
+        case ASIO_STATE_CANCELLED:
+            T("SDKCtl %s: Packet %p is cancelled.",
+              packet->sdkctl->service_name, packet);
+            break;
+
+        case ASIO_STATE_FAILED:
+            T("SDKCtl %s: Packet %p has failed: %d -> %s",
+              packet->sdkctl->service_name, packet, errno, strerror(errno));
+            break;
+
+        case ASIO_STATE_FINISHED:
+            /* Time to disassociate the packet with the I/O. */
+            _sdkctl_packet_release(packet);
+            break;
+
+        default:
+            /* Transitional state. */
+            break;
+    }
+
+    _sdkctl_packet_release(packet);
+
+    return action;
+}
+
+/* Transmits a packet to SDK Controller.
+ * Param:
+ *  packet - Packet to transmit.
+ */
+static void
+_sdkctl_packet_transmit(SDKCtlPacket* packet)
+{
+    assert(packet->header.signature == _sdkctl_packet_sig);
+
+    /* Reference to associate with the I/O */
+    _sdkctl_packet_reference(packet);
+
+    /* Transmit the packet to SDK controller. */
+    async_socket_write_rel(packet->sdkctl->as, &packet->header, packet->header.size,
+                           _on_sdkctl_packet_send_io, packet, -1);
+
+    T("SDKCtl %s: Packet %p size %d is being sent.",
+      packet->sdkctl->service_name, packet, packet->header.size);
+}
+
+/********************************************************************************
+ *                        SDKCtlDirectPacket implementation
+ ********************************************************************************/
+
+SDKCtlDirectPacket*
+sdkctl_direct_packet_new(SDKCtlSocket* sdkctl)
+{
+    SDKCtlDirectPacket* const packet =
+        _sdkctl_socket_alloc_recycler(sdkctl, sizeof(SDKCtlDirectPacket));
+
+    packet->sdkctl      = sdkctl;
+    packet->ref_count   = 1;
+
+    /* Refence SDKCTlSocket that owns this packet. */
+    sdkctl_socket_reference(packet->sdkctl);
+
+    T("SDKCtl %s: Direct packet %p is allocated.", sdkctl->service_name, packet);
+
+    return packet;
+}
+
+/* Frees a direct packet. */
+static void
+_sdkctl_direct_packet_free(SDKCtlDirectPacket* packet)
+{
+    SDKCtlSocket* const sdkctl = packet->sdkctl;
+
+    /* Free allocated resources. */
+    _sdkctl_socket_free_recycler(packet->sdkctl, packet);
+
+    T("SDKCtl %s: Direct packet %p is freed.", sdkctl->service_name, packet);
+
+    /* Release SDKCTlSocket that owned this packet. */
+    sdkctl_socket_release(sdkctl);
+}
+
+/* References a packet. */
+int
+sdkctl_direct_packet_reference(SDKCtlDirectPacket* packet)
+{
+    assert(packet->ref_count > 0);
+    packet->ref_count++;
+    return packet->ref_count;
+}
+
+/* Releases a packet. */
+int
+sdkctl_direct_packet_release(SDKCtlDirectPacket* packet)
+{
+    assert(packet->ref_count > 0);
+    packet->ref_count--;
+    if (packet->ref_count == 0) {
+        /* Last reference has been dropped. Destroy this object. */
+        _sdkctl_direct_packet_free(packet);
+        return 0;
+    }
+    return packet->ref_count;
+}
+
+/* An I/O callback invoked on direct packet transmission.
+ * Param:
+ *  io_opaque SDKCtlDirectPacket instance of the packet that's being sent with
+ *      this I/O.
+ *  asio - Write I/O descriptor.
+ *  status - I/O status.
+ */
+static AsyncIOAction
+_on_sdkctl_direct_packet_send_io(void* io_opaque,
+                                 AsyncSocketIO* asio,
+                                 AsyncIOState status)
+{
+    SDKCtlDirectPacket* const packet = (SDKCtlDirectPacket*)io_opaque;
+    AsyncIOAction action = ASIO_ACTION_DONE;
+
+    /* Reference the packet while we're in this callback. */
+    sdkctl_direct_packet_reference(packet);
+
+    /* Lets see what's going on with query transmission. */
+    switch (status) {
+        case ASIO_STATE_SUCCEEDED:
+            /* Packet has been sent to the service. */
+            T("SDKCtl %s: Direct packet %p transmission has succeeded.",
+              packet->sdkctl->service_name, packet);
+            packet->on_sent(packet->on_sent_opaque, packet, status);
+            break;
+
+        case ASIO_STATE_CANCELLED:
+            T("SDKCtl %s: Direct packet %p is cancelled.",
+              packet->sdkctl->service_name, packet);
+            packet->on_sent(packet->on_sent_opaque, packet, status);
+            break;
+
+        case ASIO_STATE_FAILED:
+            T("SDKCtl %s: Direct packet %p has failed: %d -> %s",
+              packet->sdkctl->service_name, packet, errno, strerror(errno));
+            packet->on_sent(packet->on_sent_opaque, packet, status);
+            break;
+
+        case ASIO_STATE_FINISHED:
+            /* Time to disassociate with the I/O. */
+            sdkctl_direct_packet_release(packet);
+            break;
+
+        default:
+            /* Transitional state. */
+            break;
+    }
+
+    sdkctl_direct_packet_release(packet);
+
+    return action;
+}
+
+void
+sdkctl_direct_packet_send(SDKCtlDirectPacket* packet,
+                          void* data,
+                          on_sdkctl_direct_cb cb,
+                          void* cb_opaque)
+{
+    packet->packet          = (SDKCtlPacketHeader*)data;
+    packet->on_sent         = cb;
+    packet->on_sent_opaque  = cb_opaque;
+    assert(packet->packet->signature == _sdkctl_packet_sig);
+
+    /* Reference for I/O */
+    sdkctl_direct_packet_reference(packet);
+
+    /* Transmit the packet to SDK controller. */
+    async_socket_write_rel(packet->sdkctl->as, packet->packet, packet->packet->size,
+                           _on_sdkctl_direct_packet_send_io, packet, -1);
+
+    T("SDKCtl %s: Direct packet %p size %d is being sent",
+      packet->sdkctl->service_name, packet, packet->packet->size);
+}
+
+/********************************************************************************
+ *                      SDKCtlMessage implementation
+ *******************************************************************************/
+
+/* Alocates a message descriptor. */
+static SDKCtlMessage*
+_sdkctl_message_new(SDKCtlSocket* sdkctl, uint32_t msg_size, int msg_type)
+{
+    SDKCtlMessage* const msg =
+        (SDKCtlMessage*)_sdkctl_packet_new(sdkctl,
+                                           sizeof(SDKCtlMessageHeader) + msg_size,
+                                           SDKCTL_PACKET_MESSAGE);
+    msg->msg_type = msg_type;
+
+    return msg;
+}
+
+int
+sdkctl_message_reference(SDKCtlMessage* msg)
+{
+    return _sdkctl_packet_reference(&msg->packet);
+}
+
+int
+sdkctl_message_release(SDKCtlMessage* msg)
+{
+    return _sdkctl_packet_release(&msg->packet);
+}
+
+SDKCtlMessage*
+sdkctl_message_send(SDKCtlSocket* sdkctl,
+                    int msg_type,
+                    const void* data,
+                    uint32_t size)
+{
+    SDKCtlMessage* const msg = _sdkctl_message_new(sdkctl, size, msg_type);
+    if (size != 0 && data != NULL) {
+        memcpy(msg + 1, data, size);
+    }
+    _sdkctl_packet_transmit(&msg->packet);
+
+    return msg;
+}
+
+int
+sdkctl_message_get_header_size(void)
+{
+    return sizeof(SDKCtlMessageHeader);
+}
+
+void
+sdkctl_init_message_header(void* msg, int msg_type, int msg_size)
+{
+    SDKCtlMessageHeader* const msg_header = (SDKCtlMessageHeader*)msg;
+
+    msg_header->packet.signature    = _sdkctl_packet_sig;
+    msg_header->packet.size         = sizeof(SDKCtlMessageHeader) + msg_size;
+    msg_header->packet.type         = SDKCTL_PACKET_MESSAGE;
+    msg_header->msg_type            = msg_type;
+}
+
 /********************************************************************************
  *                    SDKCtlQuery implementation
  *******************************************************************************/
@@ -579,19 +953,21 @@
 {
     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)) {
+            /* This query used its internal buffer to receive the response.
+             * Free it. */
             free(query->internal_resp_buffer);
         }
 
         loopTimer_done(query->timer);
+
+        /* Recyle the descriptor. */
         _sdkctl_socket_free_recycler(sdkctl, query);
 
+        T("SDKCtl %s: Query %p is freed.", sdkctl->service_name, query);
+
         /* Release socket that owned this query. */
         sdkctl_socket_release(sdkctl);
     }
@@ -609,8 +985,8 @@
 {
     loopTimer_stop(query->timer);
 
-    T("SDKCtl %s: Query %p ID %d deadline is cancelled.",
-      query->sdkctl->service_name, query, query->header.query_id);
+    T("SDKCtl %s: Query %p ID %d deadline %lld is cancelled.",
+      query->sdkctl->service_name, query, query->header.query_id, query->deadline);
 }
 
 /*
@@ -639,7 +1015,7 @@
      * 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.
+     * the client about query cancellation.
      */
 
     /* Cancel deadline, and inform the client about query cancellation. */
@@ -676,12 +1052,11 @@
     sdkctl_query_release(query);
 }
 
-/* A callback that is invoked when query has been sent to the SDK controller
- * service. */
+/* A callback that is invoked when query has been sent to the SDK controller. */
 static void
 _on_sdkctl_query_sent(SDKCtlQuery* query)
 {
-    T("SDKCtl %s: sent %d bytes of query %p ID %d of type %d",
+    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);
 
@@ -710,31 +1085,25 @@
     /* 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_SUCCEEDED:
+            /* Query has been sent to the service. */
+            _on_sdkctl_query_sent(query);
+            break;
+
         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");
+            T("SDKCtl %s: Query %p ID %d is cancelled in transmission.",
+              query->sdkctl->service_name, query, query->header.query_id);
             /* 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",
+            D("SDKCtl %s: Query %p ID %d with deadline %lld has timed out in transmission 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));
+              query->deadline,  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. */
@@ -744,9 +1113,8 @@
             break;
 
         case ASIO_STATE_FAILED:
-            T("SDKCtl %s: Query %p ID %d failed in %s I/O: %d -> %s",
+            T("SDKCtl %s: Query %p ID %d failed in transmission: %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. */
@@ -779,23 +1147,23 @@
 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;
+    SDKCtlQuery* const query =
+        _sdkctl_socket_alloc_recycler(sdkctl, sizeof(SDKCtlQuery) + in_data_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.signature  = _sdkctl_packet_sig;
+    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);
@@ -822,21 +1190,19 @@
 {
     SDKCtlQuery* const query = sdkctl_query_new(sdkctl, query_type, in_data_size);
 
-    query->response_buffer  = response_buffer;
+    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;
+    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;
+    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);
@@ -859,11 +1225,12 @@
     /* Reference query associated with write I/O. */
     sdkctl_query_reference(query);
 
+    assert(query->header.packet.signature == _sdkctl_packet_sig);
     /* 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",
+    T("SDKCtl %s: Query %p ID %d type %d is being sent with deadline at %lld",
       query->sdkctl->service_name, query, query->header.query_id,
       query->header.query_type, query->deadline);
 }
@@ -908,20 +1275,73 @@
     return query->ref_count;
 }
 
+void*
+sdkctl_query_get_buffer_in(SDKCtlQuery* query)
+{
+    /* Query buffer starts right after the header. */
+    return query + 1;
+}
+
+void*
+sdkctl_query_get_buffer_out(SDKCtlQuery* query)
+{
+    return query->response_buffer != NULL ? *query->response_buffer :
+                                            query->internal_resp_buffer;
+}
+
 /********************************************************************************
  *                      SDKCtlPacket implementation
  *******************************************************************************/
 
-/* A packet has been received from SDK controller. */
+/* A packet has been received from SDK controller.
+ * Note that we expect the packet to be a message, since queries, and query
+ * replies are handled separately. */
 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));
+    assert(packet->header.signature == _sdkctl_packet_sig);
+    if (packet->header.type == SDKCTL_PACKET_MESSAGE) {
+        SDKCtlMessage* const msg = (SDKCtlMessage*)packet;
+        /* Lets see if this is an internal protocol message. */
+        switch (msg->msg_type) {
+            case SDKCTL_MSG_PORT_CONNECTED:
+                sdkctl->port_status = SDKCTL_PORT_CONNECTED;
+                sdkctl->on_port_connection(sdkctl->opaque, sdkctl,
+                                           SDKCTL_PORT_CONNECTED);
+                break;
+
+            case SDKCTL_MSG_PORT_DISCONNECTED:
+                sdkctl->port_status = SDKCTL_PORT_DISCONNECTED;
+                sdkctl->on_port_connection(sdkctl->opaque, sdkctl,
+                                           SDKCTL_PORT_DISCONNECTED);
+                break;
+
+            case SDKCTL_MSG_PORT_ENABLED:
+                sdkctl->port_status = SDKCTL_PORT_ENABLED;
+                sdkctl->on_port_connection(sdkctl->opaque, sdkctl,
+                                           SDKCTL_PORT_ENABLED);
+                break;
+
+            case SDKCTL_MSG_PORT_DISABLED:
+                sdkctl->port_status = SDKCTL_PORT_DISABLED;
+                sdkctl->on_port_connection(sdkctl->opaque, sdkctl,
+                                           SDKCTL_PORT_DISABLED);
+                break;
+
+            default:
+                /* This is a higher-level message. Dispatch the message to the
+                 * client. */
+                sdkctl->on_message(sdkctl->opaque, sdkctl, msg, msg->msg_type, msg + 1,
+                                   packet->header.size - sizeof(SDKCtlMessageHeader));
+                break;
+        }
+    } else {
+        E("SDKCtl %s: Received unknown packet type %d size %d",
+          sdkctl->service_name, packet->header.type, packet->header.size);
+    }
 }
 
 /********************************************************************************
@@ -949,7 +1369,7 @@
     dispatcher->current_query   = NULL;
 
     /* Register a packet header reader with the socket. */
-    async_socket_read_rel(dispatcher->sdkctl->as, &dispatcher->header,
+    async_socket_read_rel(dispatcher->sdkctl->as, &dispatcher->packet_header,
                           sizeof(SDKCtlPacketHeader), _on_sdkctl_io_dispatcher_io,
                           dispatcher, -1);
 }
@@ -969,12 +1389,14 @@
 
     /* Free packet data buffer. */
     if (dispatcher->packet != NULL) {
-        sdkctl_packet_release(dispatcher->packet);
+        _sdkctl_packet_release(dispatcher->packet);
         dispatcher->packet = NULL;
     }
 
     /* Reset dispatcher state. */
     dispatcher->state = SDKCTL_IODISP_EXPECT_HEADER;
+
+    T("SDKCtl %s: I/O Dispatcher is reset", sdkctl->service_name);
 }
 
 /*
@@ -999,7 +1421,7 @@
 
     /* Report disconnection to the client, and let it restore connection in this
      * callback. */
-    sdkctl->on_connection(sdkctl->opaque, sdkctl, ASIO_STATE_FAILED);
+    sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, ASIO_STATE_FAILED);
 }
 
 /* A callback that is invoked when dispatcher's reader has been cancelled. */
@@ -1020,7 +1442,7 @@
 
     /* Discard packet data we've received so far. */
     if (dispatcher->packet != NULL) {
-        sdkctl_packet_release(dispatcher->packet);
+        _sdkctl_packet_release(dispatcher->packet);
         dispatcher->packet = NULL;
     }
 }
@@ -1033,8 +1455,20 @@
     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);
+      dispatcher->sdkctl->service_name, dispatcher->packet_header.type,
+      dispatcher->packet_header.size);
+
+    /* Make sure we have a valid packet header. */
+    if (dispatcher->packet_header.signature != _sdkctl_packet_sig) {
+        E("SDKCtl %s: Invalid packet signature %x for packet type %d, size %d",
+          sdkctl->service_name, dispatcher->packet_header.signature,
+          dispatcher->packet_header.type, dispatcher->packet_header.size);
+        /* This is a protocol failure. Treat it as I/O failure: disconnect, and
+         * let the client to decide what to do next. */
+        errno = EINVAL;
+        _on_io_dispatcher_io_failure(dispatcher, asio);
+        return ASIO_ACTION_DONE;
+    }
 
     /* Here we have three choices for the packet, that define the rest of
      * the data that follow it:
@@ -1044,7 +1478,7 @@
      * Update the state accordingly, and initiate reading of the
      * remaining of the packet.
      */
-     if (dispatcher->header.type == SDKCTL_PACKET_QUERY_RESPONSE) {
+     if (dispatcher->packet_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
@@ -1059,11 +1493,11 @@
          * there. */
         dispatcher->state = SDKCTL_IODISP_EXPECT_DATA;
         dispatcher->packet =
-            _sdkctl_packet_new(sdkctl, dispatcher->header.size,
-                               dispatcher->header.type);
+            _sdkctl_packet_new(sdkctl, dispatcher->packet_header.size,
+                               dispatcher->packet_header.type);
         /* Initiate reading of the packet data. */
         async_socket_read_rel(sdkctl->as, dispatcher->packet + 1,
-                              dispatcher->header.size - sizeof(SDKCtlPacketHeader),
+                              dispatcher->packet_header.size - sizeof(SDKCtlPacketHeader),
                               _on_sdkctl_io_dispatcher_io, dispatcher, -1);
     }
 
@@ -1075,18 +1509,19 @@
 _on_io_dispatcher_packet(SDKCtlIODispatcher* dispatcher, AsyncSocketIO* asio)
 {
     SDKCtlSocket* const sdkctl = dispatcher->sdkctl;
+    SDKCtlPacket* const packet = dispatcher->packet;
+    dispatcher->packet = NULL;
 
     T("SDKCtl %s: Packet type %d, size %d is received.",
-      dispatcher->sdkctl->service_name, dispatcher->header.type,
-      dispatcher->header.size);
+      dispatcher->sdkctl->service_name, dispatcher->packet_header.type,
+      dispatcher->packet_header.size);
 
-    _on_sdkctl_packet_received(sdkctl, dispatcher->packet);
-    sdkctl_packet_release(dispatcher->packet);
-    dispatcher->packet = NULL;
+    _on_sdkctl_packet_received(sdkctl, packet);
+    _sdkctl_packet_release(packet);
 
     /* Get ready for the next I/O cycle. */
     dispatcher->state = SDKCTL_IODISP_EXPECT_HEADER;
-    async_socket_read_rel(sdkctl->as, &dispatcher->header, sizeof(SDKCtlPacketHeader),
+    async_socket_read_rel(sdkctl->as, &dispatcher->packet_header, sizeof(SDKCtlPacketHeader),
                           _on_sdkctl_io_dispatcher_io, dispatcher, -1);
     return ASIO_ACTION_DONE;
 }
@@ -1107,6 +1542,9 @@
     dispatcher->current_query =
         _sdkctl_socket_remove_query_id(sdkctl, dispatcher->query_reply_header.query_id);
     query = dispatcher->current_query;
+    const uint32_t query_data_size =
+        dispatcher->packet_header.size - sizeof(SDKCtlQueryReplyHeader);
+    dispatcher->state = SDKCTL_IODISP_EXPECT_QUERY_REPLY_DATA;
 
     if (query == NULL) {
         D("%s: Query #%d is not found by dispatcher",
@@ -1116,13 +1554,12 @@
          * 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);
+            _sdkctl_packet_new(sdkctl, dispatcher->packet_header.size,
+                               dispatcher->packet_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),
+        async_socket_read_rel(sdkctl->as, dispatcher->packet + 1, query_data_size,
                              _on_sdkctl_io_dispatcher_io, dispatcher, -1);
     } else {
         /* Prepare to receive query reply. For the simplicity sake, cancel query
@@ -1130,9 +1567,6 @@
          * 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) {
@@ -1144,7 +1578,6 @@
         *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);
@@ -1176,14 +1609,14 @@
         /* 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);
+            _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),
+    async_socket_read_rel(sdkctl->as, &dispatcher->packet_header, sizeof(SDKCtlPacketHeader),
                           _on_sdkctl_io_dispatcher_io, dispatcher, -1);
     return ASIO_ACTION_DONE;
 }
@@ -1351,6 +1784,7 @@
     }
 
     sdkctl->state = SDKCTL_SOCKET_DISCONNECTED;
+    sdkctl->port_status = SDKCTL_PORT_DISCONNECTED;
 }
 
 /* Frees SDKCtlSocket instance. */
@@ -1358,6 +1792,8 @@
 _sdkctl_socket_free(SDKCtlSocket* sdkctl)
 {
     if (sdkctl != NULL) {
+        T("SDKCtl %s: descriptor is destroing.", sdkctl->service_name);
+
         /* Disconnect, and release the socket. */
         if (sdkctl->as != NULL) {
             async_socket_disconnect(sdkctl->as);
@@ -1394,7 +1830,7 @@
 
     /* Notify the client that connection is established. */
     const AsyncIOAction action =
-        sdkctl->on_connection(sdkctl->opaque, sdkctl, ASIO_STATE_SUCCEEDED);
+        sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, ASIO_STATE_SUCCEEDED);
 
     if (action == ASIO_ACTION_DONE) {
         /* Initialize, and start main I/O dispatcher. */
@@ -1418,8 +1854,8 @@
 
     _sdkctl_socket_disconnect_socket(sdkctl);
 
-    AsyncIOAction action = sdkctl->on_connection(sdkctl->opaque, sdkctl,
-                                                 ASIO_STATE_FAILED);
+    AsyncIOAction action = sdkctl->on_socket_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;
@@ -1481,35 +1917,38 @@
 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_socket_connection_cb on_socket_connection,
+                  on_sdkctl_port_connection_cb on_port_connection,
                   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->state                   = SDKCTL_SOCKET_DISCONNECTED;
+    sdkctl->port_status             = SDKCTL_PORT_DISCONNECTED;
+    sdkctl->opaque                  = opaque;
+    sdkctl->service_name            = ASTRDUP(service_name);
+    sdkctl->on_socket_connection    = on_socket_connection;
+    sdkctl->on_port_connection      = on_port_connection;
+    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;
+
+    T("SDKCtl %s: descriptor is created.", sdkctl->service_name);
 
     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);
+        on_socket_connection(opaque, sdkctl, ASIO_STATE_FAILED);
         _sdkctl_socket_free(sdkctl);
         return NULL;
     }
@@ -1569,7 +2008,7 @@
     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);
+        sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, ASIO_STATE_FAILED);
     } else {
         async_socket_connect(sdkctl->as, retry_to);
     }
@@ -1599,11 +2038,54 @@
     _sdkctl_socket_disconnect_socket(sdkctl);
 }
 
+int
+sdkctl_socket_is_connected(SDKCtlSocket* sdkctl)
+{
+    return (sdkctl->state == SDKCTL_SOCKET_CONNECTED) ? 1 : 0;
+}
+
+int
+sdkctl_socket_is_port_ready(SDKCtlSocket* sdkctl)
+{
+    return (sdkctl->port_status == SDKCTL_PORT_ENABLED) ? 1 : 0;
+}
+
+SdkCtlPortStatus
+sdkctl_socket_get_port_status(SDKCtlSocket* sdkctl)
+{
+    return sdkctl->port_status;
+}
+
+int
+sdkctl_socket_is_handshake_ok(SDKCtlSocket* sdkctl)
+{
+    switch (sdkctl->port_status) {
+        case SDKCTL_HANDSHAKE_DUP:
+        case SDKCTL_HANDSHAKE_UNKNOWN_QUERY:
+        case SDKCTL_HANDSHAKE_UNKNOWN_RESPONSE:
+            return 0;
+        default:
+        return 1;
+    }
+}
 
 /********************************************************************************
  *                       Handshake query
  *******************************************************************************/
 
+/*
+ * Handshake result values.
+ */
+
+/* Handshake has succeeded completed, and service-side port is connected. */
+#define SDKCTL_HANDSHAKE_RESP_CONNECTED         0
+/* Handshake has succeeded completed, but service-side port is not connected. */
+#define SDKCTL_HANDSHAKE_RESP_NOPORT            1
+/* Handshake has failed due to duplicate connection request. */
+#define SDKCTL_HANDSHAKE_RESP_DUP               -1
+/* Handshake has failed due to unknown query. */
+#define SDKCTL_HANDSHAKE_RESP_QUERY_UNKNOWN     -2
+
 /* A callback that is ivoked on handshake I/O events. */
 static AsyncIOAction
 _on_handshake_io(void* query_opaque,
@@ -1613,12 +2095,41 @@
     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);
+        const int* res = (const int*)(*query->response_buffer);
+        SdkCtlPortStatus handshake_status;
+        switch (*res) {
+            case SDKCTL_HANDSHAKE_RESP_CONNECTED:
+                D("SDKCtl %s: Handshake succeeded. Port is connected",
+                  sdkctl->service_name);
+                handshake_status = SDKCTL_HANDSHAKE_CONNECTED;
+                break;
 
-        /* Handshake is received. Inform the client. */
-        sdkctl->on_handshake(sdkctl->opaque, sdkctl, *query->response_buffer,
-                             *query->response_size, status);
+            case SDKCTL_HANDSHAKE_RESP_NOPORT:
+                D("SDKCtl %s: Handshake succeeded. Port is not connected",
+                  sdkctl->service_name);
+                handshake_status = SDKCTL_HANDSHAKE_NO_PORT;
+                break;
+
+            case SDKCTL_HANDSHAKE_RESP_DUP:
+                D("SDKCtl %s: Handshake failed: duplicate connection.",
+                  sdkctl->service_name);
+                handshake_status = SDKCTL_HANDSHAKE_DUP;
+                break;
+
+            case SDKCTL_HANDSHAKE_RESP_QUERY_UNKNOWN:
+                D("SDKCtl %s: Handshake failed: unknown query.",
+                  sdkctl->service_name);
+                handshake_status = SDKCTL_HANDSHAKE_UNKNOWN_QUERY;
+                break;
+
+            default:
+                E("SDKCtl %s: Unknown handshake response: %d",
+                  sdkctl->service_name, *res);
+                handshake_status = SDKCTL_HANDSHAKE_UNKNOWN_RESPONSE;
+                break;
+        }
+        sdkctl->port_status = handshake_status;
+        sdkctl->on_port_connection(sdkctl->opaque, sdkctl, handshake_status);
     } else {
         /* Something is going on with the handshake... */
         switch (status) {
@@ -1627,9 +2138,8 @@
             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);
+                sdkctl->on_socket_connection(sdkctl->opaque, sdkctl,
+                                             ASIO_STATE_FAILED);
                 break;
 
             default:
@@ -1663,7 +2173,7 @@
                 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);
+                    sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, ASIO_STATE_FAILED);
                     break;
 
                 default:
@@ -1682,7 +2192,7 @@
 static const char _host_end = 1;
 #endif
 
-    D("SDKCtl %s: Sending endianness: %d...", sdkctl->service_name, _host_end);
+    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. */
diff --git a/android/sdk-controller-socket.h b/android/sdk-controller-socket.h
index 03b12f4..e58a4e2 100644
--- a/android/sdk-controller-socket.h
+++ b/android/sdk-controller-socket.h
@@ -18,6 +18,7 @@
 #define ANDROID_SDKCONTROL_SOCKET_H_
 
 #include "android/async-socket.h"
+#include "android/async-utils.h"
 
 /*
  * Contains declaration of an API that encapsulates communication protocol with
@@ -102,20 +103,29 @@
  * 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 message - 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.
  */
 
+/* Default TCP port to use for connection with SDK controller. */
+#define SDKCTL_DEFAULT_TCP_PORT     1970
+
 /* Declares SDK controller socket descriptor. */
 typedef struct SDKCtlSocket SDKCtlSocket;
 
-/* Declares SDK controller data packet descriptor. */
-typedef struct SDKCtlPacket SDKCtlPacket;
+/* Declares SDK controller message descriptor. */
+typedef struct SDKCtlMessage SDKCtlMessage;
 
 /* Declares SDK controller query descriptor. */
 typedef struct SDKCtlQuery SDKCtlQuery;
 
+/* Declares SDK controller direct packet descriptor.
+ * Direct packet (unlike message, or query packets) doesn't contain data buffer,
+ * but rather references message, or query data allocated by the client.
+ */
+typedef struct SDKCtlDirectPacket SDKCtlDirectPacket;
+
 /* Defines client's callback set to monitor SDK controller socket connection.
  *
  * SDKCtlSocket will invoke this callback when connection to TCP port is
@@ -137,30 +147,52 @@
  * Return:
  *  One of the AsyncIOAction values.
  */
-typedef AsyncIOAction (*on_sdkctl_connection_cb)(void* client_opaque,
-                                                 SDKCtlSocket* sdkctl,
-                                                 AsyncIOState status);
+typedef AsyncIOAction (*on_sdkctl_socket_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.
+/* Enumerates port connection statuses passed to port connection callback.
+ */
+typedef enum SdkCtlPortStatus {
+    /* Service-side port has connected to the socket. */
+    SDKCTL_PORT_DISCONNECTED,
+    /* Service-side port has disconnected from the socket. */
+    SDKCTL_PORT_CONNECTED,
+    /* Service-side port has enabled emulation */
+    SDKCTL_PORT_ENABLED,
+    /* Service-side port has disabled emulation */
+    SDKCTL_PORT_DISABLED,
+    /* Handshake request has succeeded, and service-side port is connected. */
+    SDKCTL_HANDSHAKE_CONNECTED,
+    /* Handshake request has succeeded, but service-side port is not connected. */
+    SDKCTL_HANDSHAKE_NO_PORT,
+    /* Handshake request has failed due to port duplication. */
+    SDKCTL_HANDSHAKE_DUP,
+    /* Handshake request has failed on an unknown query. */
+    SDKCTL_HANDSHAKE_UNKNOWN_QUERY,
+    /* Handshake request has failed on an unknown response. */
+    SDKCTL_HANDSHAKE_UNKNOWN_RESPONSE,
+} SdkCtlPortStatus;
+
+/* Defines client's callback set to receive port connection status.
  *
- * Successful handshake means that connection between the client and SDK
- * controller service has been established.
+ * Port connection is different than socket connection, and indicates whether
+ * or not a service-side port that provides requested emulation functionality is
+ * hooked up with the connected socket. For instance, multi-touch port may be
+ * inactive at the time when socket is connected. So, there is a successful
+ * socket connection, but there is no service at the device end that provides
+ * multi-touch functionality. So, for multi-touch example, this callback will be
+ * invoked when multi-touch port at the device end becomes active, and hooks up
+ * with the socket that was connected before.
  *
  * 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.
+ *  status - Port connection status.
  */
-typedef void (*on_sdkctl_handshake_cb)(void* client_opaque,
-                                       SDKCtlSocket* sdkctl,
-                                       void* handshake,
-                                       uint32_t handshake_size,
-                                       AsyncIOState status);
+typedef void (*on_sdkctl_port_connection_cb)(void* client_opaque,
+                                             SDKCtlSocket* sdkctl,
+                                             SdkCtlPortStatus status);
 
 /* Defines a message notification callback.
  * Param:
@@ -176,7 +208,7 @@
  */
 typedef void (*on_sdkctl_message_cb)(void* client_opaque,
                                      SDKCtlSocket* sdkctl,
-                                     SDKCtlPacket* message,
+                                     SDKCtlMessage* message,
                                      int msg_type,
                                      void* msg_data,
                                      int msg_size);
@@ -203,27 +235,114 @@
                                             SDKCtlQuery* query,
                                             AsyncIOState status);
 
+/* Defines direct packet completion callback.
+ * Param:
+ *  opaque - An opaque pointer associated with the direct packet by the client.
+ *  packet - Packet descriptor.  Note that packet descriptor will be released
+ *      upon exit from this callback (thus, could be freed). If the client is
+ *      interested in working with that packet after the callback returns, it
+ *      should reference the packet descriptor in this callback.
+ *  status - Packet status. Can be one of these:
+ *    - ASIO_STATE_SUCCEEDED : Packet has been successfully sent.
+ *    - ASIO_STATE_FAILED    : Packet has failed on an I/O.
+ *    - ASIO_STATE_CANCELLED : Packet has been cancelled due to socket
+ *                             disconnection.
+ * Return:
+ *  One of the AsyncIOAction values.
+ */
+typedef AsyncIOAction (*on_sdkctl_direct_cb)(void* opaque,
+                                             SDKCtlDirectPacket* packet,
+                                             AsyncIOState status);
+
 /********************************************************************************
- *                         SDKCtlPacket API
+ *                        SDKCtlDirectPacket API
  ********************************************************************************/
 
-/* References SDKCtlPacket object.
+/* Creates new SDKCtlDirectPacket descriptor.
  * Param:
- *  packet - Initialized SDKCtlPacket instance.
+ *  sdkctl - Initialized SDKCtlSocket instance to create a direct packet for.
+ * Return:
+ *  Referenced SDKCtlDirectPacket instance.
+ */
+extern SDKCtlDirectPacket* sdkctl_direct_packet_new(SDKCtlSocket* sdkctl);
+
+/* References SDKCtlDirectPacket object.
+ * Param:
+ *  packet - Initialized SDKCtlDirectPacket instance.
  * Return:
  *  Number of outstanding references to the object.
  */
-extern int sdkctl_packet_reference(SDKCtlPacket* packet);
+extern int sdkctl_direct_packet_reference(SDKCtlDirectPacket* packet);
 
-/* Releases SDKCtlPacket object.
+/* Releases SDKCtlDirectPacket 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.
+ *  packet - Initialized SDKCtlDirectPacket instance.
  * Return:
  *  Number of outstanding references to the object.
  */
-extern int sdkctl_packet_release(SDKCtlPacket* packet);
+extern int sdkctl_direct_packet_release(SDKCtlDirectPacket* packet);
+
+/* Sends direct packet.
+ * Param:
+ *  packet - Packet descriptor for the direct packet to send.
+ *  data - Data to send with the packet. Must be fully initialized message, or
+ *      query header.
+ *  cb, cb_opaque - Callback to invoke on packet transmission events.
+ */
+extern void sdkctl_direct_packet_send(SDKCtlDirectPacket* packet,
+                                      void* data,
+                                      on_sdkctl_direct_cb cb,
+                                      void* cb_opaque);
+
+/********************************************************************************
+ *                         SDKCtlMessage API
+ ********************************************************************************/
+
+/* References SDKCtlMessage object.
+ * Param:
+ *  msg - Initialized SDKCtlMessage instance.
+ * Return:
+ *  Number of outstanding references to the object.
+ */
+extern int sdkctl_message_reference(SDKCtlMessage* msg);
+
+/* Releases SDKCtlMessage object.
+ * Note that upon exiting from this routine the object might be destroyed, even
+ * if this routine returns value other than zero.
+ * Param:
+ *  msg - Initialized SDKCtlMessage instance.
+ * Return:
+ *  Number of outstanding references to the object.
+ */
+extern int sdkctl_message_release(SDKCtlMessage* msg);
+
+/* Builds and sends a message to the device.
+ * Param:
+ *  sdkctl - SDKCtlSocket instance for the message.
+ *  msg_type - Defines message type.
+ *  data - Message data. Can be NULL if there is no data associated with the
+ *      message.
+ *  size - Byte size of the data buffer.
+ * Return:
+ *  Referenced SDKCtlQuery descriptor.
+ */
+extern SDKCtlMessage* sdkctl_message_send(SDKCtlSocket* sdkctl,
+                                          int msg_type,
+                                          const void* data,
+                                          uint32_t size);
+
+/* Gets message header size */
+extern int sdkctl_message_get_header_size(void);
+
+/* Initializes message header.
+ * Param:
+ *  msg - Beginning of the message packet.
+ *  msg_type - Message type.
+ *  msg_size - Message data size.
+ */
+extern void sdkctl_init_message_header(void* msg, int msg_type, int msg_size);
 
 /********************************************************************************
  *                          SDKCtlQuery API
@@ -349,6 +468,14 @@
  */
 extern void* sdkctl_query_get_buffer_in(SDKCtlQuery* query);
 
+/* Gets address of query's output data buffer (response data).
+ * Param:
+ *  query - Query to get data buffer for.
+ * Return:
+ *  Address of query's output data buffer.
+ */
+extern void* sdkctl_query_get_buffer_out(SDKCtlQuery* query);
+
 /********************************************************************************
  *                          SDKCtlSocket API
  ********************************************************************************/
@@ -359,8 +486,8 @@
  *      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_socket_connection - A callback to invoke on socket connection events.
+ *  on_port_connection - A callback to invoke on port connection 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.
@@ -369,8 +496,8 @@
  */
 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_socket_connection_cb on_socket_connection,
+                                       on_sdkctl_port_connection_cb on_port_connection,
                                        on_sdkctl_message_cb on_message,
                                        void* opaque);
 
@@ -437,4 +564,36 @@
  */
 extern void sdkctl_socket_disconnect(SDKCtlSocket* sdkctl);
 
+/* Checks if SDK controller socket is connected.
+ * Param:
+ *  sdkctl - Initialized SDKCtlSocket instance.
+ * Return:
+ *  Boolean: 1 if socket is connected, 0 if socket is not connected.
+ */
+extern int sdkctl_socket_is_connected(SDKCtlSocket* sdkctl);
+
+/* Checks if SDK controller port is ready for emulation.
+ * Param:
+ *  sdkctl - Initialized SDKCtlSocket instance.
+ * Return:
+ *  Boolean: 1 if port is ready, 0 if port is not ready.
+ */
+extern int sdkctl_socket_is_port_ready(SDKCtlSocket* sdkctl);
+
+/* Gets status of the SDK controller port for this socket.
+ * Param:
+ *  sdkctl - Initialized SDKCtlSocket instance.
+ * Return:
+ *  Status of the SDK controller port for this socket.
+ */
+extern SdkCtlPortStatus sdkctl_socket_get_port_status(SDKCtlSocket* sdkctl);
+
+/* Checks if handshake was successful.
+ * Param:
+ *  sdkctl - Initialized SDKCtlSocket instance.
+ * Return:
+ *  Boolean: 1 if handshake was successful, 0 if handshake was not successful.
+ */
+extern int sdkctl_socket_is_handshake_ok(SDKCtlSocket* sdkctl);
+
 #endif  /* ANDROID_SDKCONTROL_SOCKET_H_ */
diff --git a/android/sensors-port.c b/android/sensors-port.c
index 8620783..6831fe3 100644
--- a/android/sensors-port.c
+++ b/android/sensors-port.c
@@ -14,370 +14,474 @@
  * limitations under the License.
  */
 
+#include "android/sdk-controller-socket.h"
 #include "android/sensors-port.h"
 #include "android/hw-sensors.h"
+#include "android/utils/debug.h"
 
 #define  E(...)    derror(__VA_ARGS__)
 #define  W(...)    dwarning(__VA_ARGS__)
 #define  D(...)    VERBOSE_PRINT(sensors_port,__VA_ARGS__)
 #define  D_ACTIVE  VERBOSE_CHECK(sensors_port)
 
-/* Maximum number of sensors supported. */
-#define ASP_MAX_SENSOR          12
+#define TRACE_ON    1
 
-/* Maximum length of a sensor message. */
-#define ASP_MAX_SENSOR_MSG      1024
+#if TRACE_ON
+#define  T(...)    VERBOSE_PRINT(sensors_port,__VA_ARGS__)
+#else
+#define  T(...)
+#endif
 
-/* Maximum length of a sensor event. */
-#define ASP_MAX_SENSOR_EVENT    256
+/* Timeout (millisec) to use when communicating with SDK controller. */
+#define SDKCTL_SENSORS_TIMEOUT      3000
 
-/* Query timeout in milliseconds. */
-#define ASP_QUERY_TIMEOUT       3000
+/*
+ * Queries sent to sensors port of the SDK controller.
+ */
+
+/* Queries the port for list of available sensors. */
+#define SDKCTL_SENSORS_QUERY_LIST   1
+
+/*
+ * Messages sent between the emuator, and  sensors port of the SDK controller.
+ */
+
+/* Starts sensor emulation. */
+#define SDKCTL_SENSORS_START            1
+/* Stops sensor emulation. */
+#define SENSOR_SENSORS_STOP             2
+/* Enables emulation for a sensor. */
+#define SDKCTL_SENSORS_ENABLE           3
+/* Disables emulation for a sensor. */
+#define SDKCTL_SENSORS_DISABLE          4
+/* This message delivers sensor values. */
+#define SDKCTL_SENSORS_SENSOR_EVENT     5
+
+
+/* Describes a sensor on the device.
+ * When SDK controller sensors port replies to a "list" query, it replies with
+ * a flat buffer containing entries of this type following each other. End of
+ * each entry is a zero-terminator for its 'sensor_name' field. The end of the
+ * entire list is marked with an entry, containing -1 at its 'sensor_id' field.
+ */
+typedef struct SensorEntry {
+    /* Identifies sensor on the device. Value -1 indicates list terminator,
+     * rather than a valid sensor descriptor. */
+    int     sensor_id;
+    /* Beginning of zero-terminated sensor name. */
+    char    sensor_name[1];
+} SensorEntry;
+
+/* Describes a sensor in the array of emulated sensors. */
+typedef struct SensorDescriptor {
+    /* Identifies sensor on the device. */
+    int         sensor_id;
+    /* Identifies sensor in emulator. */
+    int         emulator_id;
+    /* Sensor name. */
+    char*       sensor_name;
+} SensorDescriptor;
+
+/* Sensor event message descriptor.
+ * Entries of this type are sent along with SDKCTL_SENSORS_SENSOR_EVENT message
+ */
+typedef struct SensorEvent {
+    /* Identifies a device sensor for which values have been delivered. */
+    int     sensor_id;
+    /* Sensor values. */
+    float   fvalues[3];
+} SensorEvent;
 
 /* Sensors port descriptor. */
 struct AndroidSensorsPort {
     /* Caller identifier. */
-    void*           opaque;
-    /* Connected android device. */
-    AndroidDevice*  device;
-    /* String containing list of all available sensors. */
-    char            sensors[ASP_MAX_SENSOR * 64];
-    /* Array of available sensor names. Note that each string in this array
-     * points inside the 'sensors' buffer. */
-    const char*     sensor_list[ASP_MAX_SENSOR];
-    /* Number of available sensors. */
-    int             sensors_num;
-    /* Connection status: 1 connected, 0 - disconnected. */
-    int             is_connected;
-    /* Buffer where to receive sensor messages. */
-    char            sensor_msg[ASP_MAX_SENSOR_MSG];
-    /* Buffer where to receive sensor events. */
-    char            events[ASP_MAX_SENSOR_EVENT];
+    void*               opaque;
+    /* Communication socket. */
+    SDKCtlSocket*       sdkctl;
+    /* Lists sensors available for emulation. */
+    SensorDescriptor**  sensors;
+    /* Number of sensors in 'sensors' list. */
+    int                 sensors_count;
 };
 
+/********************************************************************************
+ *                          Sensors port internals
+ *******************************************************************************/
+
+/* Checks if sensor descriptor is the terminator.
+ * Return:
+ *  Boolean, 1 if it is a terminator, 0 if it is not.
+ */
+static int
+_sensor_entry_is_terminator(const SensorEntry* entry)
+{
+    return entry == NULL || entry->sensor_id == -1;
+}
+
+/* Gets next sensor descriptor.
+ * Return:
+ *  Next sensor desciptor, or NULL if there are no more descriptors in the list.
+ */
+static const SensorEntry*
+_sensor_entry_next(const SensorEntry* entry)
+{
+    if (!_sensor_entry_is_terminator(entry)) {
+        /* Next descriptor begins right after zero-terminator for the sensor_name
+         * field of this descriptor. */
+        entry = (const SensorEntry*)(entry->sensor_name + strlen(entry->sensor_name) + 1);
+        if (!_sensor_entry_is_terminator(entry)) {
+            return entry;
+        }
+    }
+    return NULL;
+}
+
+/* Gets number of entries in the list. */
+static int
+_sensor_entry_list_size(const SensorEntry* entry) {
+    int ret = 0;
+    while (!_sensor_entry_is_terminator(entry)) {
+        ret++;
+        entry = _sensor_entry_next(entry);
+    }
+    return ret;
+}
+
+/* Discards sensors saved in AndroidSensorsPort's array. */
+static void
+_sensors_port_discard_sensors(AndroidSensorsPort* asp)
+{
+    if (asp->sensors != NULL) {
+        int n;
+        for (n = 0; n < asp->sensors_count; n++) {
+            if (asp->sensors[n] != NULL) {
+                free(asp->sensors[n]->sensor_name);
+                AFREE(asp->sensors[n]);
+            }
+        }
+        free(asp->sensors);
+        asp->sensors = NULL;
+    }
+    asp->sensors_count = 0;
+}
+
+
 /* Destroys and frees the descriptor. */
 static void
 _sensors_port_free(AndroidSensorsPort* asp)
 {
     if (asp != NULL) {
-        if (asp->device != NULL) {
-            android_device_destroy(asp->device);
+        _sensors_port_discard_sensors(asp);
+        if (asp->sdkctl != NULL) {
+            sdkctl_socket_release(asp->sdkctl);
         }
         AFREE(asp);
     }
 }
 
+/* Parses flat sensor list, and saves its entries into 'sensors' array filed of
+ * the AndroidSensorsPort descriptor. */
+static void
+_sensors_port_save_sensors(AndroidSensorsPort* asp, const SensorEntry* list)
+{
+    const int count = _sensor_entry_list_size(list);
+    if (count != 0) {
+        int n;
+        /* Allocate array for sensor descriptors. */
+        asp->sensors = malloc(sizeof(SensorDescriptor*) * count);
+
+        /* Iterate through the flat sensor list, filling up array of emulated
+         * sensors. */
+        const SensorEntry* entry = _sensor_entry_is_terminator(list) ? NULL : list;
+        for (n = 0; n < count &&  entry != NULL; n++) {
+            /* Get emulator-side ID for the sensor. < 0 value indicates that
+             * sensor is not supported by the emulator. */
+            const int emulator_id =
+                android_sensors_get_id_from_name((char*)entry->sensor_name);
+            if (emulator_id >= 0) {
+                SensorDescriptor* desc;
+                ANEW0(desc);
+                desc->emulator_id   = emulator_id;
+                desc->sensor_id     = entry->sensor_id;
+                desc->sensor_name   = ASTRDUP(entry->sensor_name);
+
+                asp->sensors[asp->sensors_count++] = desc;
+                D("Sensors: Emulated sensor '%s': Device id = %d, Emulator id = %d",
+                  desc->sensor_name, desc->sensor_id, desc->emulator_id);
+            } else {
+                D("Sensors: Sensor '%s' is not support by emulator",
+                  entry->sensor_name);
+            }
+            entry = _sensor_entry_next(entry);
+        }
+        D("Sensors: Emulating %d sensors", asp->sensors_count);
+    }
+}
+
+/* Finds sensor descriptor for an SDK controller-side ID. */
+static const SensorDescriptor*
+_sensor_from_sdkctl_id(AndroidSensorsPort* asp, int id)
+{
+    int n;
+    for (n = 0; n < asp->sensors_count; n++) {
+        if (asp->sensors[n]->sensor_id == id) {
+            return asp->sensors[n];
+        }
+    }
+    return NULL;
+}
+
+/* Initiates sensor emulation.
+ * Param:
+ *  asp - Android sensors port instance returned from sensors_port_create.
+ * Return:
+ *  Zero on success, failure otherwise.
+ */
+static void
+_sensors_port_start(AndroidSensorsPort* asp)
+{
+    int n;
+
+    if (!sdkctl_socket_is_port_ready(asp->sdkctl)) {
+        /* SDK controller side is not ready for emulation. Retreat... */
+        D("Sensors: SDK controller side is not ready for emulation.");
+        return;
+    }
+
+    /* Disable all sensors, and reenable only those that are emulated by
+     * hardware. */
+    sensors_port_disable_sensor(asp, "all");
+
+    /* Walk throuh the list of enabled sensors enabling them on the device. */
+    for (n = 0; n < asp->sensors_count; n++) {
+        if (android_sensors_get_sensor_status(asp->sensors[n]->emulator_id) == 1) {
+            /* Reenable emulation for this sensor. */
+            sensors_port_enable_sensor(asp, asp->sensors[n]->sensor_name);
+            D("Sensors: Sensor '%s' is enabled on SDK controller.",
+              asp->sensors[n]->sensor_name);
+        }
+    }
+
+    /* Start the emulation. */
+    SDKCtlMessage* const msg =
+        sdkctl_message_send(asp->sdkctl, SDKCTL_SENSORS_START, NULL, 0);
+    sdkctl_message_release(msg);
+
+    D("Sensors: Emulation has been started.");
+}
+
 /********************************************************************************
  *                          Sensors port callbacks
  *******************************************************************************/
 
-/* A callback that invoked on sensor events.
+/* Completion for the "list" query. */
+static AsyncIOAction
+_on_sensor_list_query(void* query_opaque,
+                      SDKCtlQuery* query,
+                      AsyncIOState status)
+{
+    AndroidSensorsPort* const asp = (AndroidSensorsPort*)(query_opaque);
+    if (status != ASIO_STATE_SUCCEEDED) {
+        /* We don't really care about failures at this point. They will
+         * eventually surface up in another place. */
+        return ASIO_ACTION_DONE;
+    }
+
+    /* Parse query response which is a flat list of SensorEntry entries. */
+    const SensorEntry* const list =
+        (const SensorEntry*)sdkctl_query_get_buffer_out(query);
+    D("Sensors: Sensor list received with %d sensors.",
+      _sensor_entry_list_size(list));
+    _sensors_port_save_sensors(asp, list);
+
+    /* At this point we are ready to statr sensor emulation. */
+    _sensors_port_start(asp);
+
+    return ASIO_ACTION_DONE;
+}
+
+/* A callback that is invoked on sensor events.
  * Param:
- *  opaque - AndroidSensorsPort instance.
- *  ad - Android device used by this sensors port.
- *  msg, msgsize - Sensor event message
- *  failure - Message receiving status.
+ *  asp - AndroidSensorsPort instance.
+ *  event - Sensor event.
  */
 static void
-_on_sensor_received(void* opaque, AndroidDevice* ad, char* msg, int msgsize)
+_on_sensor_event(AndroidSensorsPort* asp, const SensorEvent* event)
 {
-    float fvalues[3] = {0, 0, 0};
-    char sensor[ASP_MAX_SENSOR_MSG];
-    char* value;
-    int id;
-    AndroidSensorsPort* asp = (AndroidSensorsPort*)opaque;
-
-    if (errno) {
-        D("Sensors notification has failed on sensors port: %s", strerror(errno));
-        return;
-    }
-
-    /* Parse notification, separating sensor name from parameters. */
-    memcpy(sensor, msg, msgsize);
-    value = strchr(sensor, ':');
-    if (value == NULL) {
-        W("Bad format for sensor notification: %s", msg);
-        return;
-    }
-    sensor[value-sensor] = '\0';
-    value++;
-
-    id = android_sensors_get_id_from_name(sensor);
-    if (id >= 0) {
-        /* Parse the value part to get the sensor values(a, b, c) */
-        int i;
-        char* pnext;
-        char* pend = value + strlen(value);
-        for (i = 0; i < 3; i++, value = pnext + 1) {
-            pnext=strchr( value, ':' );
-            if (pnext) {
-                *pnext = 0;
-            } else {
-                pnext = pend;
-            }
-
-            if (pnext > value) {
-                if (1 != sscanf( value,"%g", &fvalues[i] )) {
-                    W("Bad parameters in sensor notification %s", msg);
-                    return;
-                }
-            }
-        }
-        android_sensors_set(id, fvalues[0], fvalues[1], fvalues[2]);
+    /* Find corresponding server descriptor. */
+    const SensorDescriptor* const desc =
+        _sensor_from_sdkctl_id(asp, event->sensor_id);
+    if (desc != NULL) {
+        T("Sensors: %s -> %f, %f, %f", desc->sensor_name,
+          event->fvalues[0], event->fvalues[1],
+          event->fvalues[2]);
+        /* Fire up sensor change in the guest. */
+        android_sensors_set(desc->emulator_id, event->fvalues[0],
+                            event->fvalues[1], event->fvalues[2]);
     } else {
-        W("Unknown sensor name '%s' in '%s'", sensor, msg);
-    }
-
-    /* Listen to the next event. */
-    android_device_listen(ad, asp->events, sizeof(asp->events), _on_sensor_received);
-}
-
-/* A callback that is invoked when android device is connected (i.e. both, command
- * and event channels have been stablished.
- * Param:
- *  opaque - AndroidSensorsPort instance.
- *  ad - Android device used by this sensors port.
- *  failure - Connections status.
- */
-static void
-_on_device_connected(void* opaque, AndroidDevice* ad, int failure)
-{
-    if (!failure) {
-        AndroidSensorsPort* asp = (AndroidSensorsPort*)opaque;
-        asp->is_connected = 1;
-        D("Sensor emulation has started");
-        /* Initialize sensors on device. */
-        sensors_port_init_sensors(asp);
+        W("Sensors: No descriptor for sensor %d", event->sensor_id);
     }
 }
 
-/* Invoked when an I/O failure occurs on a socket.
- * Note that this callback will not be invoked on connection failures.
- * Param:
- *  opaque - AndroidSensorsPort instance.
- *  ad - Android device instance
- *  ads - Connection socket where failure has occured.
- *  failure - Contains 'errno' indicating the reason for failure.
- */
-static void
-_on_io_failure(void* opaque, AndroidDevice* ad, int failure)
+/* A callback that is invoked on SDK controller socket connection events. */
+static AsyncIOAction
+_on_sensors_socket_connection(void* client_opaque,
+                             SDKCtlSocket* sdkctl,
+                             AsyncIOState status)
 {
-    AndroidSensorsPort* asp = (AndroidSensorsPort*)opaque;
-    E("Sensors port got disconnected: %s", strerror(failure));
-    asp->is_connected = false;
-    android_device_disconnect(ad);
-    android_device_connect_async(ad, _on_device_connected);
+    AndroidSensorsPort* const asp = (AndroidSensorsPort*)client_opaque;
+    if (status == ASIO_STATE_FAILED) {
+        /* Disconnection could mean that user is swapping devices. New device may
+         * have different set of sensors, so we need to re-query sensor list on
+         * reconnection. */
+        _sensors_port_discard_sensors(asp);
+
+        /* Reconnect (after timeout delay) on failures */
+        if (sdkctl_socket_is_handshake_ok(sdkctl)) {
+            sdkctl_socket_reconnect(sdkctl, SDKCTL_DEFAULT_TCP_PORT,
+                                    SDKCTL_SENSORS_TIMEOUT);
+        }
+    }
+    return ASIO_ACTION_DONE;
+}
+
+/* A callback that is invoked on SDK controller port connection events. */
+static void
+_on_sensors_port_connection(void* client_opaque,
+                           SDKCtlSocket* sdkctl,
+                           SdkCtlPortStatus status)
+{
+    AndroidSensorsPort* const asp = (AndroidSensorsPort*)client_opaque;
+    switch (status) {
+        case SDKCTL_PORT_CONNECTED: {
+            D("Sensors: SDK Controller is connected.");
+            /* Query list of available sensors. */
+            SDKCtlQuery* const query =
+                sdkctl_query_build_and_send(asp->sdkctl, SDKCTL_SENSORS_QUERY_LIST,
+                                            0, NULL, NULL, NULL,
+                                            _on_sensor_list_query, asp,
+                                            SDKCTL_SENSORS_TIMEOUT);
+            sdkctl_query_release(query);
+            break;
+        }
+
+        case SDKCTL_PORT_DISCONNECTED:
+            _sensors_port_discard_sensors(asp);
+            D("Sensors: SDK Controller is disconnected.");
+            break;
+
+        case SDKCTL_PORT_ENABLED:
+            _sensors_port_start(asp);
+            D("Sensors: SDK Controller is enabled.");
+            break;
+
+        case SDKCTL_PORT_DISABLED:
+            D("Sensors: SDK Controller is disabled.");
+            break;
+
+        case SDKCTL_HANDSHAKE_CONNECTED:
+            D("Sensors: SDK Controller has succeeded handshake, and port is connected.");
+            break;
+
+        case SDKCTL_HANDSHAKE_NO_PORT:
+            D("Sensors: SDK Controller has succeeded handshake, and port is not connected.");
+            break;
+
+        case SDKCTL_HANDSHAKE_DUP:
+            E("Sensors: SDK Controller has failed the handshake due to port duplication.");
+            sdkctl_socket_disconnect(sdkctl);
+            break;
+
+        case SDKCTL_HANDSHAKE_UNKNOWN_QUERY:
+            E("Sensors: SDK Controller has failed the handshake due to unknown query.");
+            sdkctl_socket_disconnect(sdkctl);
+            break;
+
+        case SDKCTL_HANDSHAKE_UNKNOWN_RESPONSE:
+        default:
+            E("Sensors: Handshake has failed due to unknown reasons.");
+            sdkctl_socket_disconnect(sdkctl);
+            break;
+    }
+}
+
+/* A callback that is invoked when a message is received from SDK controller. */
+static void
+_on_sensors_message(void* client_opaque,
+                   SDKCtlSocket* sdkctl,
+                   SDKCtlMessage* message,
+                   int msg_type,
+                   void* msg_data,
+                   int msg_size)
+{
+    AndroidSensorsPort* const asp = (AndroidSensorsPort*)client_opaque;
+    switch (msg_type) {
+        case SDKCTL_SENSORS_SENSOR_EVENT:
+            _on_sensor_event(asp, (const SensorEvent*)msg_data);
+            break;
+
+        default:
+            E("Sensors: Unknown message type %d", msg_type);
+            break;
+    }
 }
 
 /********************************************************************************
  *                          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)
 {
     AndroidSensorsPort* asp;
-    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;
-
-    asp->device = android_device_init(asp, AD_SENSOR_PORT, _on_io_failure);
-    if (asp->device == NULL) {
-        _sensors_port_free(asp);
-        return NULL;
-    }
-
-    res = android_device_connect_sync(asp->device, ASP_QUERY_TIMEOUT);
-    if (res != 0) {
-        sensors_port_destroy(asp);
-        return NULL;
-    }
-
-    res = android_device_query(asp->device, "list",
-                               asp->sensors, sizeof(asp->sensors),
-                               ASP_QUERY_TIMEOUT);
-    if (res != 0) {
-        sensors_port_destroy(asp);
-        return NULL;
-    }
-
-    /* Parse sensor list. */
-    asp->sensors_num = 0;
-    wrk = asp->sensors;
-
-    while (wrk != NULL && *wrk != '\0' && *wrk != '\n') {
-        asp->sensor_list[asp->sensors_num] = wrk;
-        asp->sensors_num++;
-        wrk = strchr(wrk, '\n');
-        if (wrk != NULL) {
-            *wrk = '\0'; wrk++;
-        }
-    }
-
-    android_device_listen(asp->device, asp->events, sizeof(asp->events),
-                          _on_sensor_received);
+    asp->sensors = NULL;
+    asp->sensors_count = 0;
+    asp->sdkctl = sdkctl_socket_new(SDKCTL_SENSORS_TIMEOUT, "sensors",
+                                    _on_sensors_socket_connection,
+                                    _on_sensors_port_connection,
+                                    _on_sensors_message, asp);
+    sdkctl_init_recycler(asp->sdkctl, 76, 8);
+    sdkctl_socket_connect(asp->sdkctl, SDKCTL_DEFAULT_TCP_PORT,
+                          SDKCTL_SENSORS_TIMEOUT);
     return asp;
 }
 
-int
-sensors_port_init_sensors(AndroidSensorsPort* asp)
-{
-    int res, id;
-
-    /* Disable all sensors for now. Reenable only those that are emulated. */
-    res = sensors_port_disable_sensor(asp, "all");
-    if (res) {
-        return res;
-    }
-
-    /* Start listening on sensor events. */
-    res = android_device_listen(asp->device, asp->events, sizeof(asp->events),
-                                _on_sensor_received);
-    if (res) {
-        return res;
-    }
-
-    /* Walk throuh the list of enabled sensors enabling them on the device. */
-    for (id = 0; id < MAX_SENSORS; id++) {
-        if (android_sensors_get_sensor_status(id) == 1) {
-            res = sensors_port_enable_sensor(asp, android_sensors_get_name_from_id(id));
-            if (res == 0) {
-                D("Sensor '%s' is enabled on the device.",
-                  android_sensors_get_name_from_id(id));
-            }
-        }
-    }
-
-    /* Start sensor events. */
-    return sensors_port_start(asp);
-}
-
 void
 sensors_port_destroy(AndroidSensorsPort* asp)
 {
+    if (asp->sdkctl != NULL) {
+        sdkctl_socket_disconnect(asp->sdkctl);
+    }
     _sensors_port_free(asp);
 }
 
 int
-sensors_port_is_connected(AndroidSensorsPort* asp)
-{
-    return asp->is_connected;
-}
-
-int
 sensors_port_enable_sensor(AndroidSensorsPort* asp, const char* name)
 {
-    char query[1024];
-    char qresp[1024];
-    snprintf(query, sizeof(query), "enable:%s", name);
-    const int res =
-        android_device_query(asp->device, query, qresp, sizeof(qresp),
-                             ASP_QUERY_TIMEOUT);
-    if (res) {
-        if (errno) {
-            D("Query '%s' failed on I/O: %s", query, strerror(errno));
-        } else {
-            D("Query '%s' failed on device: %s", query, qresp);
-        }
+    if (asp->sdkctl != NULL && sdkctl_socket_is_port_ready(asp->sdkctl)) {
+        SDKCtlMessage* const msg = sdkctl_message_send(asp->sdkctl,
+                                                       SDKCTL_SENSORS_ENABLE,
+                                                       name, strlen(name));
+        sdkctl_message_release(msg);
+        return 0;
+    } else {
+        return -1;
     }
-    return res;
 }
 
 int
 sensors_port_disable_sensor(AndroidSensorsPort* asp, const char* name)
 {
-    char query[1024];
-    char qresp[1024];
-    snprintf(query, sizeof(query), "disable:%s", name);
-    const int res =
-        android_device_query(asp->device, query, qresp, sizeof(qresp),
-                             ASP_QUERY_TIMEOUT);
-    if (res) {
-        if (errno) {
-            D("Query '%s' failed on I/O: %s", query, strerror(errno));
-        } else {
-            D("Query '%s' failed on device: %s", query, qresp);
-        }
+    if (asp->sdkctl != NULL && sdkctl_socket_is_port_ready(asp->sdkctl)) {
+        SDKCtlMessage* const msg = sdkctl_message_send(asp->sdkctl,
+                                                       SDKCTL_SENSORS_DISABLE,
+                                                       name, strlen(name));
+        sdkctl_message_release(msg);
+        return 0;
+    } else {
+        return -1;
     }
-    return res;
-}
-
-int
-sensors_port_start(AndroidSensorsPort* asp)
-{
-    char qresp[ASP_MAX_SENSOR_MSG];
-    const int res =
-        android_device_query(asp->device, "start", qresp, sizeof(qresp),
-                             ASP_QUERY_TIMEOUT);
-    if (res) {
-        if (errno) {
-            D("Query 'start' failed on I/O: %s", strerror(errno));
-        } else {
-            D("Query 'start' failed on device: %s", qresp);
-        }
-    }
-    return res;
-}
-
-int
-sensors_port_stop(AndroidSensorsPort* asp)
-{
-    char qresp[ASP_MAX_SENSOR_MSG];
-    const int res =
-        android_device_query(asp->device, "stop", qresp, sizeof(qresp),
-                             ASP_QUERY_TIMEOUT);
-    if (res) {
-        if (errno) {
-            D("Query 'stop' failed on I/O: %s", strerror(errno));
-        } else {
-            D("Query 'stop' failed on device: %s", qresp);
-        }
-    }
-
-    return res;
 }
diff --git a/android/sensors-port.h b/android/sensors-port.h
index dc5c966..a35d700 100644
--- a/android/sensors-port.h
+++ b/android/sensors-port.h
@@ -23,8 +23,6 @@
  * the host via USB.
  */
 
-#include "android/android-device.h"
-
 /* Declares sensors port descriptor. */
 typedef struct AndroidSensorsPort AndroidSensorsPort;
 
@@ -42,15 +40,6 @@
 /* Disconnects from the sensors port, and destroys the descriptor. */
 extern void sensors_port_destroy(AndroidSensorsPort* asp);
 
-/* Initializes sensors on the connected device. */
-extern int sensors_port_init_sensors(AndroidSensorsPort* asp);
-
-/* Checks if port is connected to a sensor reading application on the device.
- * Note that connection can go out and then be restored at any time after
- * sensors_port_create API succeeded.
- */
-extern int sensors_port_is_connected(AndroidSensorsPort* asp);
-
 /* Enables events from a particular sensor.
  * Param:
  *  asp - Android sensors port instance returned from sensors_port_create.
@@ -72,20 +61,4 @@
  */
 extern int sensors_port_disable_sensor(AndroidSensorsPort* asp, const char* name);
 
-/* Queries the connected application to start delivering sensor events.
- * Param:
- *  asp - Android sensors port instance returned from sensors_port_create.
- * Return:
- *  Zero on success, failure otherwise.
- */
-extern int sensors_port_start(AndroidSensorsPort* asp);
-
-/* Queries the connected application to stop delivering sensor events.
- * Param:
- *  asp - Android sensors port instance returned from sensors_port_create.
- * Return:
- *  Zero on success, failure otherwise.
- */
-extern int sensors_port_stop(AndroidSensorsPort* asp);
-
 #endif  /* ANDROID_SENSORS_PORT_H_ */