aboutsummaryrefslogtreecommitdiffstats
path: root/android
diff options
context:
space:
mode:
authorVladimir Chtchetkine <vchtchetkine@google.com>2011-01-20 18:02:35 -0800
committerVladimir Chtchetkine <vchtchetkine@google.com>2011-01-25 13:37:49 -0800
commit6b985d708d64008eee69fe0c3f03c6e3c61e7f73 (patch)
tree640fa2d7595e61216b042e82b4fe07c59e1742a5 /android
parentb2d3df36e6e0d22c00fcddb51f9b0f4f754bfda4 (diff)
downloadexternal_qemu-6b985d708d64008eee69fe0c3f03c6e3c61e7f73.zip
external_qemu-6b985d708d64008eee69fe0c3f03c6e3c61e7f73.tar.gz
external_qemu-6b985d708d64008eee69fe0c3f03c6e3c61e7f73.tar.bz2
Implement UI control service and client
Change-Id: Iecc1b5bb9ddcdaf9f22c500a5195718db3f25354
Diffstat (limited to 'android')
-rw-r--r--android/console.c98
-rw-r--r--android/core-ui-protocol.c5
-rw-r--r--android/help.c18
-rw-r--r--android/main-ui.c11
-rw-r--r--android/qemulator.c3
-rw-r--r--android/sync-utils.h23
-rw-r--r--android/ui-core-protocol.c88
-rw-r--r--android/ui-core-protocol.h26
-rw-r--r--android/ui-ctl-common.h144
-rw-r--r--android/ui-ctl-core.c568
-rw-r--r--android/ui-ctl-core.h63
-rw-r--r--android/ui-ctl-ui.c568
-rw-r--r--android/ui-ctl-ui.h108
13 files changed, 1631 insertions, 92 deletions
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 @@ ControlClient user_events_client = NULL;
/* 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 @@ control_client_destroy( ControlClient client )
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 @@ destroy_control_fb_client(void)
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 @@ do_create_user_events_service( ControlClient client, char* args )
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 @@ destroy_control_ue_client(void)
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 @@ static const CommandDefRec qemu_commands[] =
{ "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 @@ android_ui_set_window_scale(double scale, int is_dpi)
{
#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 @@ static void
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 @@ help_shaper(stralloc_t* out)
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 @@ help_shaper(stralloc_t* out)
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 @@ attach_to_core(AndroidOptions* opts) {
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 QEmulator qemulator[1];
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 @@ static void qemulator_refresh(QEmulator* emulator)
#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 @@ ssize_t syncsocket_read_line(SyncSocket* ssocket,
*/
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,30 +55,25 @@ android_core_sensors_set_coarse_orientation( AndroidCoarseOrientation orient )
{
#if !defined(CONFIG_STANDALONE_UI)
android_sensors_set_coarse_orientation(orient);
+#else
+ clientuictl_set_coarse_orientation(orient);
#endif // CONFIG_STANDALONE_UI
}
void
-android_core_set_network_enabled(int enabled)
+android_core_toggle_network(void)
{
/* Temporary implementation for the monolitic (core + ui) builds. */
#if !defined(CONFIG_STANDALONE_UI)
+ qemu_net_disable = !qemu_net_disable;
if (android_modem) {
amodem_set_data_registration(
android_modem,
qemu_net_disable ? A_REGISTRATION_UNREGISTERED
: A_REGISTRATION_HOME);
}
-#endif // CONFIG_STANDALONE_UI
-}
-
-void
-android_core_toggle_network(void)
-{
- /* 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);
+#else
+ clientuictl_toggle_network();
#endif // CONFIG_STANDALONE_UI
}
@@ -90,7 +84,7 @@ android_core_is_network_disabled(void)
#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 @@ void android_core_tracing_start(void)
{
#if !defined(CONFIG_STANDALONE_UI)
start_tracing();
+#else
+ clientuictl_trace_control(1);
#endif // CONFIG_STANDALONE_UI
}
@@ -105,11 +101,13 @@ void android_core_tracing_stop(void)
{
#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_core_get_android_netspeed(int index, NetworkSpeed* netspeed) {
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,48 +136,14 @@ android_core_get_android_netdelay(int index, NetworkLatency* delay) {
android_netdelays[index].name == NULL) {
return -1;
}
- *delay = android_netdelays[index];
- return 0;
-#else
- return -1;
-#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';
- }
+ *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
}
-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 @@ android_core_qemu_find_file(int type, const char *filename,
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 @@ void android_core_set_brightness_change_callback(AndroidHwLightBrightnessCallbac
/* 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 @@ void android_core_tracing_stop(void);
* 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 @@ int android_core_get_android_netspeed(int index, NetworkSpeed* netspeed);
* 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 */
+