Implement UI control service and client

Change-Id: Iecc1b5bb9ddcdaf9f22c500a5195718db3f25354
diff --git a/android/console.c b/android/console.c
index 4c3548a..9382e8d 100644
--- a/android/console.c
+++ b/android/console.c
@@ -54,6 +54,7 @@
 #include "android/display-core.h"
 #include "android/framebuffer-core.h"
 #include "android/user-events-core.h"
+#include "android/ui-ctl-core.h"
 
 #if defined(CONFIG_SLIRP)
 #include "libslirp.h"
@@ -126,6 +127,18 @@
 
 /* User events service. */
 CoreUserEvents* core_ue = NULL;
+
+/* UI control service client (UI -> Core). */
+ControlClient ui_core_ctl_client = NULL;
+
+/* UI control service (UI -> Core. */
+// CoreUICtl* ui_core_ctl = NULL;
+
+/* UI control service client (Core-> UI). */
+ControlClient core_ui_ctl_client = NULL;
+
+/* UI control service (Core -> UI. */
+// CoreUICtl* core_ui_ctl = NULL;
 #endif  // CONFIG_STANDALONE_CORE
 
 /* -android-avdname option value. Defined in vl-android.c */
@@ -248,6 +261,16 @@
         coreue_destroy(core_ue);
         user_events_client = NULL;
     }
+
+    if (client == ui_core_ctl_client) {
+        uicorectl_destroy();
+        ui_core_ctl_client = NULL;
+    }
+
+    if (client == core_ui_ctl_client) {
+        coreuictl_destroy();
+        core_ui_ctl_client = NULL;
+    }
 #endif  // CONFIG_STANDALONE_CORE
 
     sock = control_client_detach( client );
@@ -2561,7 +2584,7 @@
 static int
 do_create_user_events_service( ControlClient client, char* args )
 {
-    // Make sure that there are no framebuffer client already existing.
+    // Make sure that there are no user events client already existing.
     if (user_events_client != NULL) {
         control_write( client, "KO: Another user events service is already existing!\r\n" );
         control_client_destroy(client);
@@ -2572,7 +2595,6 @@
     if (core_ue != NULL) {
         char reply_buf[4096];
         user_events_client = client;
-        // Reply "OK" with the framebuffer's bits per pixel
         snprintf(reply_buf, sizeof(reply_buf), "OK\r\n");
         control_write( client, reply_buf);
     } else {
@@ -2591,6 +2613,70 @@
         control_client_destroy(user_events_client);
     }
 }
+
+static int
+do_create_ui_core_ctl_service( ControlClient client, char* args )
+{
+    // Make sure that there are no ui control client already existing.
+    if (ui_core_ctl_client != NULL) {
+        control_write( client, "KO: Another UI control service is already existing!\r\n" );
+        control_client_destroy(client);
+        return -1;
+    }
+
+    if (!uicorectl_create(client->sock)) {
+        char reply_buf[4096];
+        ui_core_ctl_client = client;
+        snprintf(reply_buf, sizeof(reply_buf), "OK\r\n");
+        control_write( client, reply_buf);
+    } else {
+        control_write( client, "KO\r\n" );
+        control_client_destroy(client);
+        return -1;
+    }
+
+    return 0;
+}
+
+void
+destroy_ui_core_ctl_client(void)
+{
+    if (ui_core_ctl_client != NULL) {
+        control_client_destroy(ui_core_ctl_client);
+    }
+}
+
+static int
+do_create_core_ui_ctl_service( ControlClient client, char* args )
+{
+    // Make sure that there are no ui control client already existing.
+    if (core_ui_ctl_client != NULL) {
+        control_write( client, "KO: Another UI control service is already existing!\r\n" );
+        control_client_destroy(client);
+        return -1;
+    }
+
+    if (!coreuictl_create(client->sock)) {
+        char reply_buf[4096];
+        core_ui_ctl_client = client;
+        snprintf(reply_buf, sizeof(reply_buf), "OK\r\n");
+        control_write( client, reply_buf);
+    } else {
+        control_write( client, "KO\r\n" );
+        control_client_destroy(client);
+        return -1;
+    }
+
+    return 0;
+}
+
+void
+destroy_core_ui_ctl_client(void)
+{
+    if (core_ui_ctl_client != NULL) {
+        control_client_destroy(core_ui_ctl_client);
+    }
+}
 #endif  // CONFIG_STANDALONE_CORE
 
 static const CommandDefRec  qemu_commands[] =
@@ -2611,6 +2697,14 @@
     { "user events", "create user events service",
     "Create user events service\r\n",
     NULL, do_create_user_events_service, NULL },
+
+    { "ui-core control", "create UI control service",
+    "Create UI control service\r\n",
+    NULL, do_create_ui_core_ctl_service, NULL },
+
+    { "core-ui control", "create UI control service",
+    "Create UI control service\r\n",
+    NULL, do_create_core_ui_ctl_service, NULL },
 #endif  // CONFIG_STANDALONE_CORE
 
     { NULL, NULL, NULL, NULL, NULL, NULL }
diff --git a/android/core-ui-protocol.c b/android/core-ui-protocol.c
index ff7f3d5..a85ae7c 100644
--- a/android/core-ui-protocol.c
+++ b/android/core-ui-protocol.c
@@ -20,6 +20,9 @@
 #include "android/globals.h"
 #include "android/android.h"
 #include "android/core-ui-protocol.h"
+#if defined(CONFIG_STANDALONE_CORE)
+#include "android/ui-ctl-core.h"
+#endif  // defined(CONFIG_STANDALONE_CORE)
 
 #if !defined(CONFIG_STANDALONE_CORE)
 /* in android/qemulator.c */
@@ -31,6 +34,8 @@
 {
 #if !defined(CONFIG_STANDALONE_CORE)
     android_emulator_set_window_scale(scale, is_dpi);
+#else
+    coreuictl_set_window_scale(scale, is_dpi);
 #endif
 }
 
diff --git a/android/help.c b/android/help.c
index f400bca..efdcd06 100644
--- a/android/help.c
+++ b/android/help.c
@@ -769,8 +769,8 @@
 help_shaper(stralloc_t*  out)
 {
     int  n;
-    NetworkSpeed android_netspeed;
-    NetworkLatency android_netdelay;
+    NetworkSpeed* android_netspeed;
+    NetworkLatency* android_netdelay;
     PRINTF(
     "  the Android emulator supports network throttling, i.e. slower network\n"
     "  bandwidth as well as higher connection latencies. this is done either through\n"
@@ -780,10 +780,11 @@
 
     for (n = 0; !android_core_get_android_netspeed(n, &android_netspeed); n++) {
         PRINTF( "    -netspeed %-12s %-15s  (up: %.1f, down: %.1f)\n",
-                        android_netspeed.name,
-                        android_netspeed.display,
-                        android_netspeed.upload/1000.,
-                        android_netspeed.download/1000. );
+                        android_netspeed->name,
+                        android_netspeed->display,
+                        android_netspeed->upload/1000.,
+                        android_netspeed->download/1000. );
+        free(android_netspeed);
     }
     PRINTF( "\n" );
     PRINTF( "    -netspeed %-12s %s", "<num>", "select both upload and download speed\n");
@@ -792,8 +793,9 @@
     PRINTF( "\n  The format of -netdelay is one of the following (numbers are msec):\n\n" );
     for (n = 0; !android_core_get_android_netdelay(n, &android_netdelay); n++) {
         PRINTF( "    -netdelay %-10s   %-15s  (min %d, max %d)\n",
-                        android_netdelay.name, android_netdelay.display,
-                        android_netdelay.min_ms, android_netdelay.max_ms );
+                        android_netdelay->name, android_netdelay->display,
+                        android_netdelay->min_ms, android_netdelay->max_ms );
+        free(android_netdelay);
     }
     PRINTF( "    -netdelay %-10s   %s", "<num>", "select exact latency\n");
     PRINTF( "    -netdelay %-10s   %s", "<min>:<max>", "select min and max latencies\n\n");
diff --git a/android/main-ui.c b/android/main-ui.c
index 79dbea6..8b25f9c 100644
--- a/android/main-ui.c
+++ b/android/main-ui.c
@@ -61,6 +61,7 @@
 #include "android/snapshot.h"
 #include "android/core-connection.h"
 #include "android/framebuffer-ui.h"
+#include "android/ui-ctl-ui.h"
 
 #include "framebuffer.h"
 #include "iolooper.h"
@@ -959,16 +960,26 @@
     emulator = qemulator_get();
     qemulator_set_title(emulator);
 
+    // Connect to the core's framebuffer service
     fb_client = clientfb_create(&console_socket, "-raw",
                                 qemulator_get_first_framebuffer(emulator));
     if (fb_client == NULL) {
         return -1;
     }
 
+    // Connect to the core's user events service.
     if (clientue_create(&console_socket)) {
         return -1;
     }
 
+    // Connect to the core's UI control services. For the simplicity of
+    // implementation there are two UI control services: "ui-core control" that
+    // handle UI controls initiated in the UI, and "core-ui control" that handle
+    // UI controls initiated in the core.
+    if (clientuictl_create(&console_socket)) {
+        return -1;
+    }
+
     return 0;
 }
 
diff --git a/android/qemulator.c b/android/qemulator.c
index 054c5a6..a5f0dc0 100644
--- a/android/qemulator.c
+++ b/android/qemulator.c
@@ -25,6 +25,7 @@
 
 static void handle_key_command( void*  opaque, SkinKeyCommand  command, int  param );
 static void qemulator_refresh(QEmulator* emulator);
+extern void qemu_system_shutdown_request(void);
 
 static void
 qemulator_light_brightness( void* opaque, const char*  light, int  value )
@@ -592,7 +593,7 @@
 #endif
             /* only save emulator config through clean exit */
             qemulator_done(qemulator_get());
-            android_core_system_shutdown_request();
+            qemu_system_shutdown_request();
             return;
         }
     }
diff --git a/android/sync-utils.h b/android/sync-utils.h
index 1dcf649..d99caaf 100644
--- a/android/sync-utils.h
+++ b/android/sync-utils.h
@@ -22,6 +22,8 @@
 #ifndef ANDROID_SYNC_UTILS_H
 #define ANDROID_SYNC_UTILS_H
 
+#include "sockets.h"
+
 /* Descriptor for a connected non-blocking socket providing synchronous I/O */
 typedef struct SyncSocket SyncSocket;
 
@@ -224,4 +226,25 @@
  */
 int syncsocket_get_socket(SyncSocket* ssocket);
 
+/* Converts syncsocket_xxx operation status into success / failure result.
+ * Param:
+ *  status - syncsocket_xxx operation status to convert.
+ * Return:
+ *  0 if status passed to this routine indicated a success, or < 0 if status
+ *  indicated a failure.
+ */
+static inline int
+syncsocket_result(int status)
+{
+    if (status == 0) {
+        // Status 0 returned from syncsocket_xxx means "disconnection", which is
+        // a failure.
+        status = -1;
+    } else if (status > 0) {
+        // Status > 0 means success.
+        status = 0;
+    }
+    return status;
+}
+
 #endif  // ANDROID_SYNC_UTILS_H
diff --git a/android/ui-core-protocol.c b/android/ui-core-protocol.c
index 08591c0..8dd14a7 100644
--- a/android/ui-core-protocol.c
+++ b/android/ui-core-protocol.c
@@ -23,6 +23,7 @@
 #include "android/globals.h"
 #include "android/hw-control.h"
 #include "android/ui-core-protocol.h"
+#include "android/ui-ctl-ui.h"
 #if !defined(CONFIG_STANDALONE_UI)
 #include "telephony/modem_driver.h"
 #include "trace.h"
@@ -31,8 +32,6 @@
 extern char* qemu_find_file(int type, const char* filename);
 #endif  // CONFIG_STANDALONE_UI
 
-extern void qemu_system_shutdown_request(void);
-
 int
 android_core_get_hw_lcd_density(void)
 {
@@ -56,20 +55,8 @@
 {
 #if !defined(CONFIG_STANDALONE_UI)
     android_sensors_set_coarse_orientation(orient);
-#endif  // CONFIG_STANDALONE_UI
-}
-
-void
-android_core_set_network_enabled(int enabled)
-{
-    /* Temporary implementation for the monolitic (core + ui) builds. */
-#if !defined(CONFIG_STANDALONE_UI)
-    if (android_modem) {
-        amodem_set_data_registration(
-                android_modem,
-        qemu_net_disable ? A_REGISTRATION_UNREGISTERED
-            : A_REGISTRATION_HOME);
-    }
+#else
+    clientuictl_set_coarse_orientation(orient);
 #endif  // CONFIG_STANDALONE_UI
 }
 
@@ -79,7 +66,14 @@
     /* Temporary implementation for the monolitic (core + ui) builds. */
 #if !defined(CONFIG_STANDALONE_UI)
     qemu_net_disable = !qemu_net_disable;
-    android_core_set_network_enabled(!qemu_net_disable);
+    if (android_modem) {
+        amodem_set_data_registration(
+                android_modem,
+        qemu_net_disable ? A_REGISTRATION_UNREGISTERED
+            : A_REGISTRATION_HOME);
+    }
+#else
+    clientuictl_toggle_network();
 #endif  // CONFIG_STANDALONE_UI
 }
 
@@ -90,7 +84,7 @@
 #if !defined(CONFIG_STANDALONE_UI)
     return qemu_net_disable;
 #else
-    return 0;
+    return clientuictl_check_network_disabled();
 #endif  // CONFIG_STANDALONE_UI
 }
 
@@ -98,6 +92,8 @@
 {
 #if !defined(CONFIG_STANDALONE_UI)
     start_tracing();
+#else
+    clientuictl_trace_control(1);
 #endif  // CONFIG_STANDALONE_UI
 }
 
@@ -105,11 +101,13 @@
 {
 #if !defined(CONFIG_STANDALONE_UI)
     stop_tracing();
+#else
+    clientuictl_trace_control(0);
 #endif  // CONFIG_STANDALONE_UI
 }
 
 int
-android_core_get_android_netspeed(int index, NetworkSpeed* netspeed) {
+android_core_get_android_netspeed(int index, NetworkSpeed** netspeed) {
     /* This is a temporary code used to support current behavior of the
      *monolitic (core + ui in one executable) emulator executed with
      * -help-netspeed option. In the future, when ui and core get separated,
@@ -119,15 +117,16 @@
         android_netspeeds[index].name == NULL) {
         return -1;
     }
-    *netspeed = android_netspeeds[index];
+    *netspeed = (NetworkSpeed*)malloc(sizeof(NetworkSpeed));
+    memcpy(*netspeed, &android_netspeeds[index], sizeof(NetworkSpeed));
     return 0;
 #else
-    return -1;
+    return clientuictl_get_netspeed(index, netspeed);
 #endif  // !CONFIG_STANDALONE_UI
 }
 
 int
-android_core_get_android_netdelay(int index, NetworkLatency* delay) {
+android_core_get_android_netdelay(int index, NetworkLatency** delay) {
     /* This is a temporary code used to support current behavior of the
      * monolitic (core + ui in one executable) emulator executed with
      * -help-netdelays option. In the future, when ui and core get separated,
@@ -137,49 +136,15 @@
         android_netdelays[index].name == NULL) {
         return -1;
     }
-    *delay = android_netdelays[index];
+    *delay = (NetworkLatency*)malloc(sizeof(NetworkLatency));
+    memcpy(*delay, &android_netdelays[index], sizeof(NetworkLatency));
     return 0;
 #else
-    return -1;
+    return clientuictl_get_netdelay(index, delay);
 #endif  // !CONFIG_STANDALONE_UI
 }
 
 int
-android_core_audio_get_backend_name(int is_input, int index,
-                                    char* name, size_t name_buf_size,
-                                    char* descr, size_t descr_buf_size) {
-    /* This is a temporary code used to support current behavior of the
-     * monolitic (core + ui in one executable) emulator executed with
-     * -help-audio-in, and -help-audio-in options. In the future, when ui and
-     * core get separated, behavior of help may change, and this code should
-     * be reviewed. */
-#if !defined(CONFIG_STANDALONE_UI)
-    const char* descr_ptr = NULL;
-    const char* name_ptr = audio_get_backend_name(is_input, index, &descr_ptr);
-    if (name_ptr == NULL) {
-        return -1;
-    }
-    if (name != NULL && name_buf_size) {
-        strncpy(name, name_ptr, name_buf_size);
-        name[name_buf_size - 1] = '\0';
-    }
-    if (descr != NULL && descr_buf_size && descr_ptr != NULL) {
-        strncpy(descr, descr_ptr, descr_buf_size);
-        descr[descr_buf_size - 1] = '\0';
-    }
-    return 0;
-#else
-    return -1;
-#endif  // !CONFIG_STANDALONE_UI
-}
-
-void
-android_core_system_shutdown_request(void)
-{
-    qemu_system_shutdown_request();
-}
-
-int
 android_core_qemu_find_file(int type, const char *filename,
                             char* path, size_t path_buf_size)
 {
@@ -190,10 +155,19 @@
         return -1;
     }
     strncpy(path, filepath, path_buf_size);
-    filepath[path_buf_size - 1] = '\0';
+    path[path_buf_size - 1] = '\0';
     qemu_free(filepath);
     return 0;
 #else
-    return -1;
+    char* ret_path = NULL;
+    int status = clientuictl_get_qemu_path(type, filename, &ret_path);
+    if (!status && ret_path != NULL) {
+        strncpy(path, ret_path, path_buf_size);
+        path[path_buf_size - 1] = '\0';
+    }
+    if (ret_path != NULL) {
+        free(ret_path);
+    }
+    return status;
 #endif  // !CONFIG_STANDALONE_UI
 }
diff --git a/android/ui-core-protocol.h b/android/ui-core-protocol.h
index 3cb239d..2a4cee6 100644
--- a/android/ui-core-protocol.h
+++ b/android/ui-core-protocol.h
@@ -42,9 +42,6 @@
 /* change the coarse orientation value */
 void  android_core_sensors_set_coarse_orientation( AndroidCoarseOrientation  orient );
 
-/* Sets the network state */
-void android_core_set_network_enabled(int enabled);
-
 /* Toggles the network state */
 void android_core_toggle_network(void);
 
@@ -62,7 +59,7 @@
  * Return:
  *  0 on success, or -1 if requested entry index is too large.
  */
-int android_core_get_android_netspeed(int index, NetworkSpeed* netspeed);
+int android_core_get_android_netspeed(int index, NetworkSpeed** netspeed);
 
 /* Gets an entry in android_netdelays array defined in net-android.c
  * Parameters:
@@ -71,26 +68,7 @@
  * Return:
  *  0 on success, or -1 if requested entry index is too large.
  */
-int android_core_get_android_netdelay(int index, NetworkLatency* delay);
-
-/* Get name of a given audio backend.
- * Parameters
- *  is_input - If 1, routine should lookup for input audio backend, if zero,
- *      routine should lookup for output audio backend.
- *  index - Index of the registered audio backend to lookup.
- *  name - Upon successful return contains backend name.
- *  name_buf_size - name buffer size (in characters).
- *  descr - Upon successful return contains backend description.
- *  descr_buf_size - descre buffer size (in characters).
- * Return:
- *  0 on success, or -1 if requested backend has not been found.
- */
-int android_core_audio_get_backend_name(int is_input, int index,
-                                        char* name, size_t name_buf_size,
-                                        char* descr, size_t descr_buf_size);
-
-/* Notifies the core about system shutdown requested by the UI. */
-void android_core_system_shutdown_request(void);
+int android_core_get_android_netdelay(int index, NetworkLatency** delay);
 
 /* Builds a path to a file of the given type in the emulator's data directory.
  * Param:
diff --git a/android/ui-ctl-common.h b/android/ui-ctl-common.h
new file mode 100644
index 0000000..bc5960c
--- /dev/null
+++ b/android/ui-ctl-common.h
@@ -0,0 +1,144 @@
+/* Copyright (C) 2010 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+#ifndef _ANDROID_UI_CONTROL_COMMON_H
+#define _ANDROID_UI_CONTROL_COMMON_H
+
+#include "android/hw-sensors.h"
+
+/*
+ * UI control requests sent by the core to the UI.
+ */
+
+/* Sets window scale. */
+#define ACORE_UICTL_SET_WINDOWS_SCALE       1
+
+/*
+ * UI control requests sent by the UI to the core.
+ */
+
+/* Sets coarse orientation. */
+#define AUI_UICTL_SET_COARSE_ORIENTATION    2
+
+/* Toggles the network (no parameters). */
+#define AUI_UICTL_TOGGLE_NETWORK            3
+
+/* Starts / stops the tracing. */
+#define AUI_UICTL_TRACE_CONTROL             4
+
+/* Checks if network is disabled (no params) */
+#define AUI_UICTL_CHK_NETWORK_DISABLED      5
+
+/* Gets net speed */
+#define AUI_UICTL_GET_NETSPEED              6
+
+/* Gets net delays */
+#define AUI_UICTL_GET_NETDELAY              7
+
+/* Gets path to a QEMU file on local host. */
+#define AUI_UICTL_GET_QEMU_PATH             8
+
+/* UI control message header. */
+typedef struct UICtlHeader {
+    /* Message type. */
+    uint8_t     msg_type;
+
+    /* Size of the message data following this header. */
+    uint32_t    msg_data_size;
+} UICtlHeader;
+
+/* UI control response header. */
+typedef struct UICtlRespHeader {
+    /* Result of the request handling. */
+    int result;
+
+    /* Size of the response data following this header. */
+    uint32_t    resp_data_size;
+} UICtlRespHeader;
+
+/* Formats ACORE_UICTL_SET_WINDOWS_SCALE UI control request.
+ */
+typedef struct UICtlSetWindowsScale {
+    double  scale;
+    int     is_dpi;
+} UICtlSetWindowsScale;
+
+/* Formats AUI_UICTL_SET_COARSE_ORIENTATION UI control request.
+ */
+typedef struct UICtlSetCoarseOrientation {
+    AndroidCoarseOrientation    orient;
+} UICtlSetCoarseOrientation;
+
+/* Formats AUI_UICTL_TRACE_CONTROL UI control request.
+ */
+typedef struct UICtlTraceControl {
+    int start;
+} UICtlTraceControl;
+
+/* Formats AUI_UICTL_GET_NETSPEED UI control request.
+ */
+typedef struct UICtlGetNetSpeed {
+    int index;
+} UICtlGetNetSpeed;
+
+/* Formats AUI_UICTL_GET_NETSPEED UI control request response.
+ */
+typedef struct UICtlGetNetSpeedResp {
+    /* Size of the entire response structure including name and display strings. */
+    int     upload;
+    int     download;
+    /* display field of NetworkSpeed structure is immediately following
+     * this field. */
+    char    name[0];
+} UICtlGetNetSpeedResp;
+
+/* Formats AUI_UICTL_GET_NETDELAY UI control request.
+ */
+typedef struct UICtlGetNetDelay {
+    int index;
+} UICtlGetNetDelay;
+
+/* Formats AUI_UICTL_GET_NETDELAY UI control request response.
+ */
+typedef struct UICtlGetNetDelayResp {
+    /* Size of the entire response structure including name and display strings. */
+    int     min_ms;
+    int     max_ms;
+    /* display field of NetworkLatency structure is immediately following
+     * this field. */
+    char    name[0];
+} UICtlGetNetDelayResp;
+
+/* Formats AUI_UICTL_GET_QEMU_PATH UI control request.
+ */
+typedef struct UICtlGetQemuPath {
+    int     type;
+    char    filename[0];
+} UICtlGetQemuPath;
+
+/* Formats AUI_UICTL_GET_QEMU_PATH UI control request response.
+ */
+typedef struct UICtlGetQemuPathResp {
+    /* Size of the entire response structure. */
+    char    path[0];
+} UICtlGetQemuPathResp;
+
+#if 0
+android_core_get_android_netspeed(int index, NetworkSpeed* netspeed) {
+android_core_get_android_netdelay(int index, NetworkLatency* delay) {
+int
+android_core_qemu_find_file(int type, const char *filename,
+                            char* path, size_t path_buf_size)
+#endif
+
+#endif /* _ANDROID_UI_CONTROL_COMMON_H */
+
diff --git a/android/ui-ctl-core.c b/android/ui-ctl-core.c
new file mode 100644
index 0000000..4552bb8
--- /dev/null
+++ b/android/ui-ctl-core.c
@@ -0,0 +1,568 @@
+/* Copyright (C) 2010 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+#include "qemu-common.h"
+#include "android/globals.h"
+#include "android/android.h"
+#include "android/looper.h"
+#include "android/async-utils.h"
+#include "android/sync-utils.h"
+#include "android/utils/system.h"
+#include "android/utils/debug.h"
+#include "android/ui-ctl-common.h"
+#include "android/ui-ctl-core.h"
+#include "android/hw-sensors.h"
+#include "telephony/modem_driver.h"
+#include "trace.h"
+#include "audio/audio.h"
+
+/* Enumerates state values for UICoreCtl descriptor. */
+typedef enum UICoreCtlState {
+    /* UI message header is expected in the pipe. */
+    UI_STATE_EXPECT_HEADER,
+    /* UI message data are expected in the pipe. */
+    UI_STATE_EXPECT_DATA
+} UICoreCtlState;
+
+/* Core UI control service descriptor used for UI->Core communication. */
+typedef struct UICoreCtl {
+    /* Reader to detect UI disconnection. */
+    AsyncReader     async_reader;
+
+    /* I/O associated with this descriptor. */
+    LoopIo          io;
+
+    /* Looper used to communicate user events. */
+    Looper*         looper;
+
+    /* Writer to send responses to UI requests. */
+    SyncSocket*     sync_writer;
+
+    /* Socket descriptor for this service. */
+    int             sock;
+
+    /* State of incoming requests. */
+    UICoreCtlState  in_req_state;
+
+    /* Incoming request header. */
+    UICtlHeader     req_header;
+
+    /* A buffer for small incoming requests. */
+    uint8_t         req_data[256];
+
+    /* Buffer to use for reading incoming request data. Depending on expected
+     * incoming request size this buffer can point to req_data field of this
+     * structure (for small requests), or can be allocated for large requests. */
+    void*           req_data_buffer;
+} UICoreCtl;
+
+/* Core UI control service descriptor used for Core->UI communication. */
+typedef struct CoreUICtl {
+    /* I/O associated with this descriptor. */
+    LoopIo          io;
+
+    /* Looper associated with this descriptor. */
+    Looper*         looper;
+
+    /* Writer to send UI commands. */
+    SyncSocket*     sync_writer;
+
+    /* Socket descriptor for this service. */
+    int             sock;
+} CoreUICtl;
+
+/* One and only one CoreUICtl instance. */
+static CoreUICtl    _core_ui_ctl;
+
+/* One and only one UICoreCtl instance. */
+static UICoreCtl    _ui_core_ctl;
+
+/* Calculates timeout for transferring the given number of bytes via UI control
+ * socket.
+ * Return:
+ *  Number of milliseconds during which the entire number of bytes is expected
+ *  to be transferred.
+ */
+static int
+_get_transfer_timeout(size_t data_size)
+{
+    // Min 200 millisec + one millisec for each transferring byte.
+    // TODO: Come up with a better arithmetics here.
+    return 200 + data_size;
+}
+
+/*
+ * Core -> UI control implementation
+ */
+
+/* Implemented in android/console.c */
+extern void destroy_core_ui_ctl_client(void);
+
+/* Sends request to the UI client.
+ * Param:
+ *  msg_type, msg_data, msg_data_size - Define core request to send.
+ * Return:
+ *  0 on success, or < 0 on failure.
+ */
+static int
+_coreuictl_send_request(uint8_t msg_type,
+                        void* msg_data,
+                        uint32_t msg_data_size)
+{
+    UICtlHeader header;
+    int status = syncsocket_start_write(_core_ui_ctl.sync_writer);
+    if (!status) {
+
+        // Initialize and send the header.
+        header.msg_type = msg_type;
+        header.msg_data_size = msg_data_size;
+        status = syncsocket_write(_core_ui_ctl.sync_writer, &header, sizeof(header),
+                                  _get_transfer_timeout(sizeof(header)));
+        // If there is request data, send it too.
+        if (status > 0 && msg_data != NULL && msg_data_size > 0) {
+            status = syncsocket_write(_core_ui_ctl.sync_writer, msg_data,
+                                      msg_data_size,
+                                      _get_transfer_timeout(msg_data_size));
+        }
+        status = syncsocket_result(status);
+        syncsocket_stop_write(_core_ui_ctl.sync_writer);
+    }
+    if (status < 0) {
+        derror("Unable to send core UI control request: %s\n", errno_str);
+    }
+    return status;
+}
+
+/*
+ * Asynchronous I/O callback for CoreUICtl instance.
+ * We expect this callback to be called only on UI detachment condition. In this
+ * case the event should be LOOP_IO_READ, and read should fail with errno set
+ * to ECONNRESET.
+ * Param:
+ *  opaque - CoreUICtl instance.
+ */
+static void
+_coreuictl_io_func(void* opaque, int fd, unsigned events)
+{
+    CoreUICtl* uictl = (CoreUICtl*)opaque;
+    AsyncReader reader;
+    AsyncStatus status;
+    uint8_t read_buf[1];
+
+    if (events & LOOP_IO_WRITE) {
+        derror("Unexpected LOOP_IO_WRITE in coreuictl_io_func\n");
+        return;
+    }
+
+    // Try to read
+    asyncReader_init(&reader, read_buf, sizeof(read_buf), &uictl->io);
+    status = asyncReader_read(&reader, &uictl->io);
+    // We expect only error status here.
+    if (status != ASYNC_ERROR) {
+        derror("Unexpected read status %d in coreuictl_io_func\n", status);
+        return;
+    }
+    // We expect only socket disconnection here.
+    if (errno != ECONNRESET) {
+        derror("Unexpected read error %d (%s) in coreuictl_io_func\n",
+               errno, errno_str);
+        return;
+    }
+
+    // Client got disconnectted.
+    destroy_core_ui_ctl_client();
+}
+
+int
+coreuictl_create(int fd)
+{
+    // Initialize _core_ui_ctl instance.
+    _core_ui_ctl.sock = fd;
+    _core_ui_ctl.looper = looper_newCore();
+    loopIo_init(&_core_ui_ctl.io, _core_ui_ctl.looper, _core_ui_ctl.sock,
+                _coreuictl_io_func, &_core_ui_ctl);
+    loopIo_wantRead(&_core_ui_ctl.io);
+    _core_ui_ctl.sync_writer = syncsocket_init(fd);
+    if (_core_ui_ctl.sync_writer == NULL) {
+        derror("Unable to initialize CoreUICtl writer: %s\n", errno_str);
+        return -1;
+    }
+    return 0;
+}
+
+void
+coreuictl_destroy()
+{
+    if (_core_ui_ctl.looper != NULL) {
+        // Stop all I/O that may still be going on.
+        loopIo_done(&_core_ui_ctl.io);
+        looper_free(_core_ui_ctl.looper);
+        _core_ui_ctl.looper = NULL;
+    }
+    if (_core_ui_ctl.sync_writer != NULL) {
+        syncsocket_close(_core_ui_ctl.sync_writer);
+        syncsocket_free(_core_ui_ctl.sync_writer);
+    }
+    _core_ui_ctl.sock = -1;
+}
+
+int
+coreuictl_set_window_scale(double scale, int is_dpi)
+{
+    UICtlSetWindowsScale msg;
+    msg.scale = scale;
+    msg.is_dpi = is_dpi;
+    return _coreuictl_send_request(ACORE_UICTL_SET_WINDOWS_SCALE, &msg,
+                                   sizeof(msg));
+}
+
+/*
+ * UI -> Core control implementation
+ */
+
+/* Implemented in android/console.c */
+extern void destroy_ui_core_ctl_client(void);
+/* Implemented in vl-android.c */
+extern char* qemu_find_file(int type, const char* filename);
+
+/* Properly initializes req_data_buffer field in UICoreCtl instance to receive
+ * the expected incoming request data buffer.
+ */
+static uint8_t*
+_alloc_req_data_buffer(UICoreCtl* uictl, uint32_t size)
+{
+    if (size < sizeof(uictl->req_data)) {
+        // req_data can contain all request data.
+        uictl->req_data_buffer = &uictl->req_data[0];
+    } else {
+        // Expected request us too large to fit into preallocated buffer.
+        uictl->req_data_buffer = qemu_malloc(size);
+    }
+    return uictl->req_data_buffer;
+}
+
+/* Properly frees req_data_buffer field in UICoreCtl instance.
+ */
+static void
+_free_req_data_buffer(UICoreCtl* uictl)
+{
+    if (uictl->req_data_buffer != &uictl->req_data[0]) {
+        qemu_free(uictl->req_data_buffer);
+        uictl->req_data_buffer = &uictl->req_data[0];
+    }
+}
+
+/* Sends response back to the UI
+ * Param:
+ *  uictl - UICoreCtl instance to use for the response sending.
+ *  resp - Response header.
+ *  resp_data - Response data. Data size is defined by the header.
+ * Return:
+ *  0 on success, or < 0 on failure.
+ */
+static int
+_uicorectl_send_response(UICoreCtl* uictl, UICtlRespHeader* resp, void* resp_data)
+{
+    int status = syncsocket_start_write(uictl->sync_writer);
+    if (!status) {
+        // Write the header
+        status = syncsocket_write(uictl->sync_writer, resp,
+                                  sizeof(UICtlRespHeader),
+                                  _get_transfer_timeout(sizeof(UICtlRespHeader)));
+        // Write response data (if any).
+        if (status > 0 && resp_data != NULL && resp->resp_data_size != 0) {
+            status = syncsocket_write(uictl->sync_writer, resp_data,
+                                      resp->resp_data_size,
+                                      _get_transfer_timeout(resp->resp_data_size));
+        }
+        status = syncsocket_result(status);
+        syncsocket_stop_write(uictl->sync_writer);
+    }
+    if (status < 0) {
+        derror("Unable to send UI control response: %s\n", errno_str);
+    }
+    return status;
+}
+
+/* Handles UI control request from the UI.
+ * Param:
+ *  uictl - UICoreCtl instance that received the request.
+ *  req_header - Request header.
+ *  req_data - Request data.
+ */
+static void
+_handle_uictl_request(UICoreCtl* uictl,
+                      const UICtlHeader* req_header,
+                      const uint8_t* req_data)
+{
+    switch (req_header->msg_type) {
+        case AUI_UICTL_SET_COARSE_ORIENTATION:
+        {
+            UICtlSetCoarseOrientation* req = (UICtlSetCoarseOrientation*)req_data;
+            android_sensors_set_coarse_orientation(req->orient);
+            break;
+        }
+
+        case AUI_UICTL_TOGGLE_NETWORK:
+            qemu_net_disable = !qemu_net_disable;
+            if (android_modem) {
+                amodem_set_data_registration(
+                        android_modem,
+                qemu_net_disable ? A_REGISTRATION_UNREGISTERED
+                    : A_REGISTRATION_HOME);
+            }
+            break;
+
+        case AUI_UICTL_TRACE_CONTROL:
+        {
+            UICtlTraceControl* req = (UICtlTraceControl*)req_data;
+            if (req->start) {
+                start_tracing();
+            } else {
+                stop_tracing();
+            }
+            break;
+        }
+
+        case AUI_UICTL_CHK_NETWORK_DISABLED:
+        {
+            UICtlRespHeader resp;
+            resp.resp_data_size = 0;
+            resp.result = qemu_net_disable;
+            _uicorectl_send_response(uictl, &resp, NULL);
+            break;
+        }
+
+        case AUI_UICTL_GET_NETSPEED:
+        {
+            UICtlRespHeader resp;
+            UICtlGetNetSpeedResp* resp_data = NULL;
+            UICtlGetNetSpeed* req = (UICtlGetNetSpeed*)req_data;
+
+            resp.resp_data_size = 0;
+            resp.result = 0;
+
+            if (req->index >= android_netspeeds_count ||
+                android_netspeeds[req->index].name == NULL) {
+                resp.result = -1;
+            } else {
+                const NetworkSpeed* netspeed = &android_netspeeds[req->index];
+                // Calculate size of the response data:
+                // fixed header + zero-terminated netspeed name.
+                resp.resp_data_size = sizeof(UICtlGetNetSpeedResp) +
+                                      strlen(netspeed->name) + 1;
+                // Count in zero-terminated netspeed display.
+                if (netspeed->display != NULL) {
+                    resp.resp_data_size += strlen(netspeed->display) + 1;
+                } else {
+                    resp.resp_data_size++;
+                }
+                // Allocate and initialize response data buffer.
+                resp_data =
+                    (UICtlGetNetSpeedResp*)qemu_malloc(resp.resp_data_size);
+                resp_data->upload = netspeed->upload;
+                resp_data->download = netspeed->download;
+                strcpy(resp_data->name, netspeed->name);
+                if (netspeed->display != NULL) {
+                    strcpy(resp_data->name + strlen(resp_data->name) + 1,
+                           netspeed->display);
+                } else {
+                    strcpy(resp_data->name + strlen(resp_data->name) + 1, "");
+                }
+            }
+            _uicorectl_send_response(uictl, &resp, resp_data);
+            if (resp_data != NULL) {
+                qemu_free(resp_data);
+            }
+            break;
+        }
+
+        case AUI_UICTL_GET_NETDELAY:
+        {
+            UICtlRespHeader resp;
+            UICtlGetNetDelayResp* resp_data = NULL;
+            UICtlGetNetDelay* req = (UICtlGetNetDelay*)req_data;
+
+            resp.resp_data_size = 0;
+            resp.result = 0;
+
+            if (req->index >= android_netdelays_count ||
+                android_netdelays[req->index].name == NULL) {
+                resp.result = -1;
+            } else {
+                const NetworkLatency* netdelay = &android_netdelays[req->index];
+                // Calculate size of the response data:
+                // fixed header + zero-terminated netdelay name.
+                resp.resp_data_size = sizeof(UICtlGetNetDelayResp) +
+                                      strlen(netdelay->name) + 1;
+                // Count in zero-terminated netdelay display.
+                if (netdelay->display != NULL) {
+                    resp.resp_data_size += strlen(netdelay->display) + 1;
+                } else {
+                    resp.resp_data_size++;
+                }
+                // Allocate and initialize response data buffer.
+                resp_data =
+                    (UICtlGetNetDelayResp*)qemu_malloc(resp.resp_data_size);
+                resp_data->min_ms = netdelay->min_ms;
+                resp_data->max_ms = netdelay->max_ms;
+                strcpy(resp_data->name, netdelay->name);
+                if (netdelay->display != NULL) {
+                    strcpy(resp_data->name + strlen(resp_data->name) + 1,
+                           netdelay->display);
+                } else {
+                    strcpy(resp_data->name + strlen(resp_data->name) + 1, "");
+                }
+            }
+            _uicorectl_send_response(uictl, &resp, resp_data);
+            if (resp_data != NULL) {
+                qemu_free(resp_data);
+            }
+            break;
+        }
+
+        case AUI_UICTL_GET_QEMU_PATH:
+        {
+            UICtlRespHeader resp;
+            UICtlGetQemuPath* req = (UICtlGetQemuPath*)req_data;
+            char* filepath = NULL;
+
+            resp.resp_data_size = 0;
+            resp.result = -1;
+            filepath = qemu_find_file(req->type, req->filename);
+            if (filepath != NULL) {
+                resp.resp_data_size = strlen(filepath) + 1;
+            }
+            _uicorectl_send_response(uictl, &resp, filepath);
+            if (filepath != NULL) {
+                qemu_free(filepath);
+            }
+            break;
+        }
+
+        default:
+            derror("Unknown UI control request %d\n", req_header->msg_type);
+            break;
+    }
+}
+
+/* Asynchronous read I/O callback launched when reading UI control requests.
+ */
+static void
+_uicorectl_io_read(UICoreCtl* uictl)
+{
+    // Read whatever is expected from the socket.
+    const AsyncStatus status =
+        asyncReader_read(&uictl->async_reader, &uictl->io);
+
+    switch (status) {
+        case ASYNC_COMPLETE:
+            switch (uictl->in_req_state) {
+                case UI_STATE_EXPECT_HEADER:
+                    // We just read the request header. Now we expect the data.
+                    if (uictl->req_header.msg_data_size != 0) {
+                        uictl->in_req_state = UI_STATE_EXPECT_DATA;
+                        // Setup the reader to read expected amount of the data.
+                        _alloc_req_data_buffer(uictl,
+                                               uictl->req_header.msg_data_size);
+                        asyncReader_init(&uictl->async_reader,
+                                         uictl->req_data_buffer,
+                                         uictl->req_header.msg_data_size,
+                                         &uictl->io);
+                    } else {
+                        // Request doesn't contain data. Go ahead and handle it.
+                        _handle_uictl_request(uictl, &uictl->req_header,
+                                              uictl->req_data_buffer);
+                        // Prepare for the next header.
+                        asyncReader_init(&uictl->async_reader,
+                                         &uictl->req_header,
+                                         sizeof(uictl->req_header), &uictl->io);
+                    }
+                    break;
+
+                case UI_STATE_EXPECT_DATA:
+                    // Request header and data are received. Handle the request.
+                    _handle_uictl_request(uictl, &uictl->req_header,
+                                          uictl->req_data_buffer);
+                    _free_req_data_buffer(uictl);
+                    // Prepare for the next request.
+                    uictl->in_req_state = UI_STATE_EXPECT_HEADER;
+                    asyncReader_init(&uictl->async_reader, &uictl->req_header,
+                                     sizeof(uictl->req_header), &uictl->io);
+                    break;
+            }
+            break;
+        case ASYNC_ERROR:
+            loopIo_dontWantRead(&uictl->io);
+            if (errno == ECONNRESET) {
+                // UI has exited. We need to destroy the service.
+                destroy_ui_core_ctl_client();
+            }
+            break;
+
+        case ASYNC_NEED_MORE:
+            // Transfer will eventually come back into this routine.
+            return;
+    }
+}
+
+/*
+ * Asynchronous I/O callback launched when UI control is received from the UI.
+ * Param:
+ *  opaque - UICoreCtl instance.
+ */
+static void
+_uicorectl_io_func(void* opaque, int fd, unsigned events)
+{
+    if (events & LOOP_IO_READ) {
+        _uicorectl_io_read((UICoreCtl*)opaque);
+    } else if (events & LOOP_IO_WRITE) {
+        // We don't use async writer here, so we don't expect
+        // any write callbacks.
+        derror("Unexpected LOOP_IO_WRITE in _uicorectl_io_func\n");
+    }
+}
+
+int
+uicorectl_create(int fd)
+{
+    _ui_core_ctl.sock = fd;
+    _ui_core_ctl.looper = looper_newCore();
+    loopIo_init(&_ui_core_ctl.io, _ui_core_ctl.looper, _ui_core_ctl.sock,
+                _uicorectl_io_func, &_ui_core_ctl);
+    _ui_core_ctl.in_req_state = UI_STATE_EXPECT_HEADER;
+    _ui_core_ctl.req_data_buffer = &_ui_core_ctl.req_data[0];
+    asyncReader_init(&_ui_core_ctl.async_reader, &_ui_core_ctl.req_header,
+                     sizeof(_ui_core_ctl.req_header), &_ui_core_ctl.io);
+    _ui_core_ctl.sync_writer = syncsocket_init(fd);
+    if (_ui_core_ctl.sync_writer == NULL) {
+        derror("Unable to create writer for UICoreCtl instance: %s\n", errno_str);
+        return -1;
+    }
+    return 0;
+}
+
+void
+uicorectl_destroy()
+{
+    if (_ui_core_ctl.looper != NULL) {
+        // Stop all I/O that may still be going on.
+        loopIo_done(&_ui_core_ctl.io);
+        looper_free(_ui_core_ctl.looper);
+        _ui_core_ctl.looper = NULL;
+    }
+    if (_ui_core_ctl.sync_writer != NULL) {
+        syncsocket_close(_ui_core_ctl.sync_writer);
+        syncsocket_free(_ui_core_ctl.sync_writer);
+    }
+    _free_req_data_buffer(&_ui_core_ctl);
+}
diff --git a/android/ui-ctl-core.h b/android/ui-ctl-core.h
new file mode 100644
index 0000000..4f1f09a
--- /dev/null
+++ b/android/ui-ctl-core.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 2010 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+#ifndef _ANDROID_UI_CONTROL_CORE_H
+#define _ANDROID_UI_CONTROL_CORE_H
+
+/*
+ * Contains core-side of UI control protocols. For the simplicity of the
+ * implementation there are two UI control services: "ui-core control" that
+ * handle UI controls initiated in the UI, and "core-ui control" that handle UI
+ * controls initiated in the core. The reason for hawing two services is that
+ * some of the UI controls expect the core to respond with some data. The
+ * simplest way to differentiate core commands from core responses to the UI
+ * commands, is to have two separate services: one sends commands only, and
+ * another sends only responses.
+ */
+
+/*
+ * Creates and initializes Core->UI UI control service.
+ * Param:
+ *  fd - Socket descriptor for the service.
+ * Return:
+ *  0 on success, or < 0 on failure.
+ */
+extern int coreuictl_create(int fd);
+
+/*
+ * Destroys Core->UI UI control service.
+ */
+extern void coreuictl_destroy();
+
+/* Changes the scale of the emulator window at runtime.
+ * Param:
+ *  scale, is_dpi - New window scale parameters
+ * Return:
+ *  0 on success, or < 0 on failure.
+ */
+extern int coreuictl_set_window_scale(double scale, int is_dpi);
+
+/*
+ * Creates and initializes UI->Core UI control instance.
+ * Param:
+ *  fd - Socket descriptor for the service.
+ * Return:
+ *  0 on success, or < 0 on failure.
+ */
+extern int uicorectl_create(int fd);
+
+/*
+ * Destroys UI->Core UI control service.
+ */
+extern void uicorectl_destroy();
+
+#endif /* _ANDROID_UI_CONTROL_CORE_H */
diff --git a/android/ui-ctl-ui.c b/android/ui-ctl-ui.c
new file mode 100644
index 0000000..9928124
--- /dev/null
+++ b/android/ui-ctl-ui.c
@@ -0,0 +1,568 @@
+/* Copyright (C) 2010 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+/*
+ * Contains UI-side of UI control protocol.
+ */
+
+#include "console.h"
+#include "android/android.h"
+#include "android/globals.h"
+#include "android/looper.h"
+#include "android/core-connection.h"
+#include "android/async-utils.h"
+#include "android/utils/system.h"
+#include "android/utils/debug.h"
+#include "android/sync-utils.h"
+#include "android/ui-ctl-common.h"
+#include "android/ui-ctl-ui.h"
+
+#define  PANIC(...) do { fprintf(stderr, __VA_ARGS__);  \
+                         exit(1);                       \
+                    } while (0)
+
+
+/*
+ * Enumerates states for the request reader in CoreUICtlClient instance.
+ */
+typedef enum CoreUICtlClientState {
+    /* The reader is waiting on request header. */
+    WAIT_HEADER,
+
+    /* The reader is waiting on request data. */
+    WAIT_DATA,
+} CoreUICtlClientState;
+
+/* Common descriptor for UI control clients. */
+typedef struct UICtlCommon {
+    /* Core connection instance for the UI control client. */
+    CoreConnection*     core_connection;
+
+    /* Socket wrapper for sync writes. */
+    SyncSocket*         sync_writer;
+
+    /* Socket descriptor for the UI control client. */
+    int                 sock;
+} UICtlCommon;
+
+/* Descriptor for the Core->UI control client. */
+typedef struct CoreUICtlClient {
+    /* Common UI control client descriptor. */
+    UICtlCommon             common;
+
+    /* Current reader state. */
+    CoreUICtlClientState    reader_state;
+
+    /* Incoming request header. */
+    UICtlHeader             req_header;
+
+    /* Reader's buffer. */
+    uint8_t*                reader_buffer;
+
+    /* Offset in the reader's buffer where to read next chunk of data. */
+    size_t                  reader_offset;
+
+    /* Total number of bytes the reader expects to read. */
+    size_t                  reader_bytes;
+} CoreUICtlClient;
+
+/* Descriptor for the UI->Core control client. */
+typedef struct UICoreCtlClient {
+    /* Common UI control client descriptor. */
+    UICtlCommon         common;
+
+    /* Socket wrapper for sync reads. */
+    SyncSocket*         sync_reader;
+} UICoreCtlClient;
+
+/* One and only one Core->UI control client instance. */
+static CoreUICtlClient  _core_ui_client;
+
+/* One and only one UI->Core control client instance. */
+static UICoreCtlClient  _ui_core_client;
+
+/* Calculates timeout for transferring the given number of bytes via UI control
+ * socket.
+ * Return:
+ *  Number of milliseconds during which the entire number of bytes is expected
+ *  to be transferred.
+ */
+static int
+_get_transfer_timeout(size_t data_size)
+{
+    // Min 200 millisec + one millisec for each transferring byte.
+    // TODO: Come up with a better arithmetics here.
+    return 200 + data_size;
+}
+
+/* Initializes UICtlCommon instance.
+ * Param:
+ *  console_socket - Addresses core's console service.
+ *  name - Name of the core's service to attach to ("ui-core client",
+ *  or "core-ui client").
+ *  uictl_common - UICtlCommon instance to initialize.
+ * Return:
+ *  0 on success, or < 0 on failure.
+ */
+static int
+_clientuictl_create_client(SockAddress* console_socket,
+                           char* name,
+                           UICtlCommon* uictl_common)
+{
+    char* connect_message = NULL;
+    char switch_cmd[256];
+
+    // Connect to the console service.
+    uictl_common->core_connection = core_connection_create(console_socket);
+    if (uictl_common->core_connection == NULL) {
+        derror("UI control client %s is unable to connect to the console: %s\n",
+               name, errno_str);
+        return -1;
+    }
+    if (core_connection_open(uictl_common->core_connection)) {
+        core_connection_free(uictl_common->core_connection);
+        uictl_common->core_connection = NULL;
+        derror("UI control client %s is unable to open the console: %s\n",
+               name, errno_str);
+        return -1;
+    }
+    snprintf(switch_cmd, sizeof(switch_cmd), "%s", name);
+    if (core_connection_switch_stream(uictl_common->core_connection, switch_cmd,
+                                      &connect_message)) {
+        derror("Unable to connect to the UI control service %s: %s\n",
+               name, connect_message ? connect_message : "");
+        if (connect_message != NULL) {
+            free(connect_message);
+        }
+        core_connection_close(uictl_common->core_connection);
+        core_connection_free(uictl_common->core_connection);
+        uictl_common->core_connection = NULL;
+        return -1;
+    }
+    if (connect_message != NULL) {
+        free(connect_message);
+    }
+
+    // Initialize UICtlCommon instance.
+    uictl_common->sock = core_connection_get_socket(uictl_common->core_connection);
+    uictl_common->sync_writer = syncsocket_init(uictl_common->sock);
+    if (uictl_common->sync_writer == NULL) {
+        derror("Unable to initialize sync writer for %s UI control client: %s\n",
+               name, errno_str);
+        return -1;
+    }
+    return 0;
+}
+
+/* Destroys UICtlCommon instance. */
+static void
+_uictlcommon_destroy(UICtlCommon* desc)
+{
+    if (desc->core_connection != NULL) {
+        // Disable I/O callbacks.
+        qemu_set_fd_handler(desc->sock, NULL, NULL, NULL);
+        syncsocket_close(desc->sync_writer);
+        syncsocket_free(desc->sync_writer);
+        core_connection_close(desc->core_connection);
+        core_connection_free(desc->core_connection);
+        desc->core_connection = NULL;
+    }
+}
+
+/*
+ * Core->UI control client implementation.
+ */
+
+/* Implemented in android/qemulator.c */
+extern void android_emulator_set_window_scale( double  scale, int  is_dpi );
+
+/* Destroys CoreUICtlClient instance. */
+static void
+_core_ui_client_destroy()
+{
+    _uictlcommon_destroy(&_core_ui_client.common);
+    if (_core_ui_client.reader_buffer != NULL &&
+        _core_ui_client.reader_buffer != (uint8_t*)&_core_ui_client.req_header) {
+        free(_core_ui_client.reader_buffer);
+    }
+}
+
+/*
+ * Handles UI control request received from the core.
+ * Param:
+ *  uictl - CoreUICtlClient instance that received the request.
+ *  header - UI control request header.
+ *  data - Request data formatted accordingly to the request type.
+ */
+static void
+_core_ui_ctl_handle_request(CoreUICtlClient* uictl,
+                            UICtlHeader* header,
+                            uint8_t* data)
+{
+    switch (header->msg_type) {
+        case ACORE_UICTL_SET_WINDOWS_SCALE:
+        {
+            UICtlSetWindowsScale* req = (UICtlSetWindowsScale*)data;
+            android_emulator_set_window_scale(req->scale, req->is_dpi);
+            break;
+        }
+        default:
+            derror("Unknown Core UI control %d\n", header->msg_type);
+            break;
+    }
+}
+
+/*
+ * Asynchronous I/O callback launched when UI control requests received from the
+ * core are ready to be read.
+ * Param:
+ *  opaque - CoreUICtlClient instance.
+ */
+static void
+_core_ui_client_read_cb(void* opaque)
+{
+    CoreUICtlClient* uictl = opaque;
+    int  ret;
+
+    // Read requests while they are immediately available.
+    for (;;) {
+        // Read next chunk of data.
+        ret = read(uictl->common.sock,
+                   uictl->reader_buffer + uictl->reader_offset,
+                   uictl->reader_bytes - uictl->reader_offset);
+        if (ret == 0) {
+            /* disconnection ! */
+            _core_ui_client_destroy();
+            return;
+        }
+        if (ret < 0) {
+            if (errno == EINTR) {
+                /* loop on EINTR */
+                continue;
+            } else if (errno == EWOULDBLOCK || errno == EAGAIN) {
+                // Chunk is not avalable at this point. Come back later.
+                return;
+            }
+        }
+
+        uictl->reader_offset += ret;
+        if (uictl->reader_offset != uictl->reader_bytes) {
+            // There are still some data left in the pipe.
+            continue;
+        }
+
+        // All expected data has been read. Time to change the state.
+        if (uictl->reader_state == WAIT_HEADER) {
+            // Header has been read. Prepare for the data.
+            uictl->reader_state = WAIT_DATA;
+            uictl->reader_offset = 0;
+            uictl->reader_bytes = uictl->req_header.msg_data_size;
+            uictl->reader_buffer = malloc(uictl->reader_bytes);
+            if (uictl->reader_buffer == NULL) {
+                PANIC("Unable to allocate memory for UI control request.\n");
+            }
+        } else {
+            _core_ui_ctl_handle_request(uictl, &uictl->req_header,
+                                        uictl->reader_buffer);
+            free(uictl->reader_buffer);
+            uictl->reader_state = WAIT_HEADER;
+            uictl->reader_offset = 0;
+            uictl->reader_bytes = sizeof(uictl->req_header);
+            uictl->reader_buffer = (uint8_t*)&uictl->req_header;
+        }
+    }
+}
+
+/*
+ * UI->Core control client implementation.
+ */
+
+/* Sends UI request to the core.
+ * Param:
+ *  msg_type, msg_data, msg_data_size - Define the request.
+ * Return:
+ *  0 On success, or < 0 on failure.
+ */
+static int
+_ui_core_ctl_send_request(uint8_t msg_type,
+                          void* msg_data,
+                          uint32_t msg_data_size)
+{
+    int status;
+    UICtlHeader header;
+
+    // Prepare and send the header.
+    header.msg_type = msg_type;
+    header.msg_data_size = msg_data_size;
+    status = syncsocket_start_write(_ui_core_client.common.sync_writer);
+    if (!status) {
+        // Send the header.
+        status = syncsocket_write(_ui_core_client.common.sync_writer, &header,
+                                  sizeof(header),
+                                  _get_transfer_timeout(sizeof(header)));
+        // If there is request data, send it too.
+        if (status > 0 && msg_data != NULL && msg_data_size > 0) {
+            status = syncsocket_write(_ui_core_client.common.sync_writer, msg_data,
+                                      msg_data_size,
+                                      _get_transfer_timeout(msg_data_size));
+        }
+        status = syncsocket_result(status);
+        syncsocket_stop_write(_ui_core_client.common.sync_writer);
+    }
+    if (status < 0) {
+        derror("Unable to send UI control request: %s\n", errno_str);
+    }
+    return status;
+}
+
+/* Reads response to a UI control request from the core.
+ * Param:
+ *  resp - Upon success contains response header.
+ *  resp_data - Upon success contains allocated reponse data (if any). The caller
+ *      is responsible for deallocating of the memory returned in this parameter.
+ * Return:
+ *  0 on success, or < 0 on failure.
+ */
+static int
+_ui_core_ctl_get_response(UICtlRespHeader* resp, void** resp_data)
+{
+    int status =  syncsocket_start_read(_ui_core_client.sync_reader);
+    if (!status) {
+        // Read the header.
+        status = syncsocket_read(_ui_core_client.sync_reader, resp,
+                                 sizeof(UICtlRespHeader),
+                                 _get_transfer_timeout(sizeof(UICtlRespHeader)));
+        // Read response data (if any).
+        if (status > 0 && resp->resp_data_size) {
+            *resp_data = malloc(resp->resp_data_size);
+            if (*resp_data == NULL) {
+                PANIC("Unable to allocate response data buffer\n");
+            }
+            status = syncsocket_read(_ui_core_client.sync_reader, *resp_data,
+                                     resp->resp_data_size,
+                                     _get_transfer_timeout(resp->resp_data_size));
+        }
+        status = syncsocket_result(status);
+        syncsocket_stop_read(_ui_core_client.sync_reader);
+    }
+    if (status < 0) {
+        derror("Unable to get UI control response: %s\n", errno_str);
+    }
+    return status;
+}
+
+int
+clientuictl_set_coarse_orientation(AndroidCoarseOrientation orient)
+{
+    UICtlSetCoarseOrientation msg;
+    msg.orient = orient;
+    return _ui_core_ctl_send_request(AUI_UICTL_SET_COARSE_ORIENTATION,
+                                     &msg, sizeof(msg));
+}
+
+int
+clientuictl_toggle_network()
+{
+    return _ui_core_ctl_send_request(AUI_UICTL_TOGGLE_NETWORK, NULL, 0);
+}
+
+int
+clientuictl_trace_control(int start)
+{
+    UICtlTraceControl msg;
+    msg.start = start;
+    return _ui_core_ctl_send_request(AUI_UICTL_TRACE_CONTROL,
+                                     &msg, sizeof(msg));
+}
+
+int
+clientuictl_check_network_disabled()
+{
+    UICtlRespHeader resp;
+    void* tmp = NULL;
+    int status;
+
+    status = _ui_core_ctl_send_request(AUI_UICTL_CHK_NETWORK_DISABLED, NULL, 0);
+    if (status < 0) {
+        return status;
+    }
+    status = _ui_core_ctl_get_response(&resp, &tmp);
+    if (status < 0) {
+        return status;
+    }
+    return resp.result;
+}
+
+int
+clientuictl_get_netspeed(int index, NetworkSpeed** netspeed)
+{
+    UICtlGetNetSpeed req;
+    UICtlRespHeader resp;
+    UICtlGetNetSpeedResp* resp_data = NULL;
+    int status;
+
+    // Initialize and send the query.
+    req.index = index;
+    status = _ui_core_ctl_send_request(AUI_UICTL_GET_NETSPEED, &req, sizeof(req));
+    if (status < 0) {
+        return status;
+    }
+
+    // Obtain the response from the core.
+    status = _ui_core_ctl_get_response(&resp, (void**)&resp_data);
+    if (status < 0) {
+        return status;
+    }
+    if (!resp.result) {
+        NetworkSpeed* ret;
+        // Allocate memory for the returning NetworkSpeed instance.
+        // It includes: NetworkSpeed structure +
+        // size of zero-terminated "name" and "display" strings saved in
+        // resp_data.
+        *netspeed = malloc(sizeof(NetworkSpeed) + 1 +
+                           resp.resp_data_size - sizeof(UICtlGetNetSpeedResp));
+        ret = *netspeed;
+
+        // Copy data obtained from the core to the returning NetworkSpeed
+        // instance.
+        ret->upload = resp_data->upload;
+        ret->download = resp_data->download;
+        ret->name = (char*)ret + sizeof(NetworkSpeed);
+        strcpy((char*)ret->name, resp_data->name);
+        ret->display = ret->name + strlen(ret->name) + 1;
+        strcpy((char*)ret->display, resp_data->name + strlen(resp_data->name) + 1);
+    }
+    if (resp_data != NULL) {
+        free(resp_data);
+    }
+    return resp.result;
+}
+
+int
+clientuictl_get_netdelay(int index, NetworkLatency** netdelay)
+{
+    UICtlGetNetDelay req;
+    UICtlRespHeader resp;
+    UICtlGetNetDelayResp* resp_data = NULL;
+    int status;
+
+    // Initialize and send the query.
+    req.index = index;
+    status = _ui_core_ctl_send_request(AUI_UICTL_GET_NETDELAY, &req, sizeof(req));
+    if (status < 0) {
+        return status;
+    }
+
+    // Obtain the response from the core.
+    status = _ui_core_ctl_get_response(&resp, (void**)&resp_data);
+    if (status < 0) {
+        return status;
+    }
+    if (!resp.result) {
+        NetworkLatency* ret;
+        // Allocate memory for the returning NetworkLatency instance.
+        // It includes: NetworkLatency structure +
+        // size of zero-terminated "name" and "display" strings saved in
+        // resp_data.
+        *netdelay = malloc(sizeof(NetworkLatency) + 1 +
+                           resp.resp_data_size - sizeof(UICtlGetNetDelayResp));
+        ret = *netdelay;
+
+        // Copy data obtained from the core to the returning NetworkLatency
+        // instance.
+        ret->min_ms = resp_data->min_ms;
+        ret->max_ms = resp_data->max_ms;
+        ret->name = (char*)ret + sizeof(NetworkLatency);
+        strcpy((char*)ret->name, resp_data->name);
+        ret->display = ret->name + strlen(ret->name) + 1;
+        strcpy((char*)ret->display, resp_data->name + strlen(resp_data->name) + 1);
+    }
+    if (resp_data != NULL) {
+        free(resp_data);
+    }
+    return resp.result;
+}
+
+int
+clientuictl_get_qemu_path(int type, const char* filename, char** path)
+{
+    UICtlRespHeader resp;
+    char* resp_data = NULL;
+    int status;
+
+    // Initialize and send the query.
+    uint32_t req_data_size = sizeof(UICtlGetQemuPath) + strlen(filename) + 1;
+    UICtlGetQemuPath* req = (UICtlGetQemuPath*)malloc(req_data_size);
+    if (req == NULL) {
+        PANIC("Unable to allocate query qemu path request\n");
+    }
+    req->type = type;
+    strcpy(req->filename, filename);
+    status = _ui_core_ctl_send_request(AUI_UICTL_GET_QEMU_PATH, req,
+                                       req_data_size);
+    if (status < 0) {
+        return status;
+    }
+
+    // Obtain the response from the core.
+    status = _ui_core_ctl_get_response(&resp, (void**)&resp_data);
+    if (status < 0) {
+        return status;
+    }
+    if (!resp.result && resp_data != NULL) {
+        *path = strdup(resp_data);
+    }
+    if (resp_data != NULL) {
+        free(resp_data);
+    }
+    return resp.result;
+}
+
+int
+clientuictl_create(SockAddress* console_socket)
+{
+    // Connect to Core->UI service
+    if (_clientuictl_create_client(console_socket, "core-ui control",
+                                   &_core_ui_client.common)) {
+        return -1;
+    }
+    _core_ui_client.reader_state = WAIT_HEADER;
+    if (qemu_set_fd_handler(_core_ui_client.common.sock, _core_ui_client_read_cb,
+                            NULL, &_core_ui_client)) {
+        derror("Unable to set up UI control read callback\n");
+        core_connection_close(_core_ui_client.common.core_connection);
+        core_connection_free(_core_ui_client.common.core_connection);
+        _core_ui_client.common.core_connection = NULL;
+        return -1;
+    }
+    fprintf(stdout, "Core->UI client is now attached to the core %s\n",
+            sock_address_to_string(console_socket));
+
+    // Connect to UI->Core service
+    if (_clientuictl_create_client(console_socket, "ui-core control",
+                                   &_ui_core_client.common)) {
+        _core_ui_client_destroy();
+        return -1;
+    }
+    _ui_core_client.sync_reader = syncsocket_init(_ui_core_client.common.sock);
+    if (_ui_core_client.sync_reader == NULL) {
+        derror("Unable to create reader for CoreUICtlClient instance: %s\n",
+               errno_str);
+        _core_ui_client_destroy();
+        return -1;
+    }
+
+    fprintf(stdout, "UI->Core client is now attached to the core %s\n",
+            sock_address_to_string(console_socket));
+
+    return 0;
+}
diff --git a/android/ui-ctl-ui.h b/android/ui-ctl-ui.h
new file mode 100644
index 0000000..3c0e97f
--- /dev/null
+++ b/android/ui-ctl-ui.h
@@ -0,0 +1,108 @@
+/* Copyright (C) 2010 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+#ifndef _ANDROID_UI_CONTROL_UI_H
+#define _ANDROID_UI_CONTROL_UI_H
+
+/*
+ * Contains UI-side of UI control protocols. For the simplicity of implementation
+ * there are two UI control services: "ui-core control" that handle UI controls
+ * initiated in the UI, and "core-ui control" that handle UI controls initiated
+ * in the core. The reason for hawing two services is that some of the UI
+ * controls expect the core to respond with some data. The simplest way to
+ * differentiate core commands from core responses to the UI commands, is to have
+ * two separate services: one sends commands only, and another sends only
+ * responses.
+ */
+
+#include "sockets.h"
+#include "android/ui-ctl-common.h"
+
+/* Establishes connection with UI control services in the core.
+ * Param:
+ *  console_socket Core's console socket.
+ * Return:
+ *  0 on success, or < 0 on failure.
+ */
+int clientuictl_create(SockAddress* console_socket);
+
+/*
+ * UI->Core API
+ */
+
+/* Sends AUI_UICTL_SET_COARSE_ORIENTATION message to the core.
+ * Return:
+ *  0 on success, or < 0 on failure.
+ */
+int clientuictl_set_coarse_orientation(AndroidCoarseOrientation orient);
+
+/* Sends AUI_UICTL_TOGGLE_NETWORK message to the core.
+ * Return:
+ *  0 on success, or < 0 on failure.
+ */
+int clientuictl_toggle_network();
+
+/* Sends AUI_UICTL_TRACE_CONTROL message to the core.
+ * Param:
+ *  start - Starts (> 0), or stops (== 0) tracing.
+ * Return:
+ *  0 on success, or < 0 on failure.
+ */
+int clientuictl_trace_control(int start);
+
+/* Sends AUI_UICTL_CHK_NETWORK_DISABLED message to the core.
+ * Return:
+ *  0 if network is enabled, 1 if it is disabled, or < 0 on failure.
+ */
+int clientuictl_check_network_disabled();
+
+/* Sends AUI_UICTL_GET_NETSPEED message to the core.
+ * Param:
+ *  index - Index of an entry in the NetworkSpeed array.
+ *  netspeed - Upon success contains allocated and initialized NetworkSpeed
+ *  instance for the given index. Note that strings addressed by "name" and
+ *  "display" fileds in the returned NetworkSpeed instance are containd inside
+ *  the buffer allocated for the returned NetworkSpeed instance. Caller of this
+ *  routine must eventually free the buffer returned in this parameter.
+ * Return:
+ *  0 on success, or < 0 on failure.
+ */
+int clientuictl_get_netspeed(int index, NetworkSpeed** netspeed);
+
+/* Sends AUI_UICTL_GET_NETDELAY message to the core.
+ * Param:
+ *  index - Index of an entry in the NetworkLatency array.
+ *  netdelay - Upon success contains allocated and initialized NetworkLatency
+ *  instance for the given index. Note that strings addressed by "name" and
+ *  "display" fileds in the returned NetworkLatency instance are containd inside
+ *  the buffer allocated for the returned NetworkLatency instance. Caller of this
+ *  routine must eventually free the buffer returned in this parameter.
+ * Return:
+ *  0 on success, or < 0 on failure.
+ */
+int clientuictl_get_netdelay(int index, NetworkLatency** netdelay);
+
+/* Sends AUI_UICTL_GET_QEMU_PATH message to the core.
+ * Param:
+ *  type, filename - Query parameters
+ *  netdelay - Upon success contains allocated and initialized NetworkLatency
+ *  instance for the given index. Note that strings addressed by "name" and
+ *  "display" fileds in the returned NetworkLatency instance are containd inside
+ *  the buffer allocated for the returned NetworkLatency instance. Caller of this
+ *  routine must eventually free the buffer returned in this parameter.
+ * Return:
+ *  0 on success, or < 0 on failure.
+ */
+int clientuictl_get_qemu_path(int type, const char* filename, char** path);
+
+#endif /* _ANDROID_UI_CONTROL_UI_H */
+