diff options
-rw-r--r-- | Makefile.android | 2 | ||||
-rw-r--r-- | android/console.c | 98 | ||||
-rw-r--r-- | android/core-ui-protocol.c | 5 | ||||
-rw-r--r-- | android/help.c | 18 | ||||
-rw-r--r-- | android/main-ui.c | 11 | ||||
-rw-r--r-- | android/qemulator.c | 3 | ||||
-rw-r--r-- | android/sync-utils.h | 23 | ||||
-rw-r--r-- | android/ui-core-protocol.c | 88 | ||||
-rw-r--r-- | android/ui-core-protocol.h | 26 | ||||
-rw-r--r-- | android/ui-ctl-common.h | 144 | ||||
-rw-r--r-- | android/ui-ctl-core.c | 568 | ||||
-rw-r--r-- | android/ui-ctl-core.h | 63 | ||||
-rw-r--r-- | android/ui-ctl-ui.c | 568 | ||||
-rw-r--r-- | android/ui-ctl-ui.h | 108 | ||||
-rw-r--r-- | user-events-ui.c | 22 |
15 files changed, 1645 insertions, 102 deletions
diff --git a/Makefile.android b/Makefile.android index b9a6fa5..58b37f1 100644 --- a/Makefile.android +++ b/Makefile.android @@ -1059,6 +1059,7 @@ VL_SOURCES := framebuffer.c \ android/display-core.c \ android/framebuffer-core.c \ android/user-events-core.c \ + android/ui-ctl-core.c \ # Add common system libraries # @@ -1216,6 +1217,7 @@ VL_SOURCES := framebuffer.c \ console-ui.c \ iolooper-select.c \ android/framebuffer-ui.c \ + android/ui-ctl-ui.c \ # Add common system libraries # 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 */ + diff --git a/user-events-ui.c b/user-events-ui.c index cef3689..03aa11d 100644 --- a/user-events-ui.c +++ b/user-events-ui.c @@ -100,19 +100,21 @@ clientue_send(ClientUserEvents* ue, UserEventHeader header; header.event_type = event; - syncsocket_start_write(ue->sync_socket); - // Send event type first (event header) - res = syncsocket_write(ue->sync_socket, &header, sizeof(header), 500); - if (res < 0) { - return -1; + res = syncsocket_start_write(ue->sync_socket); + if (!res) { + // Send event type first (event header) + res = syncsocket_write(ue->sync_socket, &header, sizeof(header), 500); + if (res > 0) { + // Send event param next. + res = syncsocket_write(ue->sync_socket, event_param, size, 500); + } + res = syncsocket_result(res); + syncsocket_stop_write(ue->sync_socket); } - // Send event param next. - res = syncsocket_write(ue->sync_socket, event_param, size, 500); if (res < 0) { - return -1; + derror("Unable to send user event: %s\n", errno_str); } - syncsocket_stop_write(ue->sync_socket); - return 0; + return res; } void |