diff options
Diffstat (limited to 'android/protocol')
-rw-r--r-- | android/protocol/core-commands-api.h | 95 | ||||
-rw-r--r-- | android/protocol/core-commands-impl.c | 440 | ||||
-rw-r--r-- | android/protocol/core-commands-impl.h | 34 | ||||
-rw-r--r-- | android/protocol/core-commands-proxy.c | 374 | ||||
-rw-r--r-- | android/protocol/core-commands-proxy.h | 32 | ||||
-rw-r--r-- | android/protocol/core-commands-qemu.c | 108 | ||||
-rw-r--r-- | android/protocol/core-commands.h | 104 | ||||
-rw-r--r-- | android/protocol/ui-commands-api.h | 41 | ||||
-rw-r--r-- | android/protocol/ui-commands-impl.c | 257 | ||||
-rw-r--r-- | android/protocol/ui-commands-impl.h | 34 | ||||
-rw-r--r-- | android/protocol/ui-commands-proxy.c | 209 | ||||
-rw-r--r-- | android/protocol/ui-commands-proxy.h | 41 | ||||
-rw-r--r-- | android/protocol/ui-commands-qemu.c | 40 | ||||
-rw-r--r-- | android/protocol/ui-commands.h | 44 | ||||
-rw-r--r-- | android/protocol/ui-common.h | 53 |
15 files changed, 1906 insertions, 0 deletions
diff --git a/android/protocol/core-commands-api.h b/android/protocol/core-commands-api.h new file mode 100644 index 0000000..93a569c --- /dev/null +++ b/android/protocol/core-commands-api.h @@ -0,0 +1,95 @@ +/* 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_PROTOCOL_CORE_COMMANDS_API_H +#define _ANDROID_PROTOCOL_CORE_COMMANDS_API_H + +/* + * Contains the API for calling into the Core with UI control commands. + */ + +#include "android/android.h" +#include "android/hw-sensors.h" + +/* Instructs the Core to change the coarse orientation. + * Return: + * 0 on success, or < 0 on failure. + */ +extern int corecmd_set_coarse_orientation(AndroidCoarseOrientation orient); + +/* Toggles the network in the Core. + * Return: + * 0 on success, or < 0 on failure. + */ +extern int corecmd_toggle_network(); + +/* Starts or stops tracing in the Core. + * Param: + * start - Starts (> 0), or stops (== 0) tracing. + * Return: + * 0 on success, or < 0 on failure. + */ +extern int corecmd_trace_control(int start); + +/* Checks if network is disabled in the Core. + * Return: + * 0 if network is enabled, 1 if it is disabled, or < 0 on failure. + */ +extern int corecmd_is_network_disabled(); + +/* Requests a NetworkSpeed instance from 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. + */ +extern int corecmd_get_netspeed(int index, NetworkSpeed** netspeed); + +/* Requests a NetworkLatency instance from 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. + */ +extern int corecmd_get_netdelay(int index, NetworkLatency** netdelay); + +/* Requests a QEMU file path from the Core. + * Param: + * type, filename - Request parameters that define the file for which path is + * requested. + * Return: + * 0 on success, or < 0 on failure. + */ +extern int corecmd_get_qemu_path(int type, + const char* filename, + char* path, + size_t path_buf_size); + +/* Gets LCD density property from the core properties. + * Return: + * LCD density on success, or < 0 on failure. + */ +extern int corecmd_get_hw_lcd_density(void); + +#endif /* _ANDROID_PROTOCOL_CORE_COMMANDS_API_H */ diff --git a/android/protocol/core-commands-impl.c b/android/protocol/core-commands-impl.c new file mode 100644 index 0000000..7fa2a0b --- /dev/null +++ b/android/protocol/core-commands-impl.c @@ -0,0 +1,440 @@ +/* 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 the Core-side implementation of the "ui-core-control" service that is + * part of the UI control protocol. Here we handle UI control commands sent by + * the UI to the Core. + */ + +#include "android/android.h" +#include "android/globals.h" +#include "telephony/modem_driver.h" +#include "trace.h" +#include "android/looper.h" +#include "android/async-utils.h" +#include "android/sync-utils.h" +#include "android/utils/debug.h" +#include "android/protocol/core-commands.h" +#include "android/protocol/core-commands-impl.h" + +/* Enumerates state values for the command reader in the CoreCmdImpl descriptor. + */ +typedef enum CoreCmdImplState { + /* The reader is waiting on command header. */ + EXPECTS_HEADER, + + /* The reader is waiting on command parameters. */ + EXPECTS_PARAMETERS, +} CoreCmdImplState; + +/* Descriptor for the Core-side implementation of the "ui-core-control" service. + */ +typedef struct CoreCmdImpl { + /* Reader to detect UI disconnection. */ + AsyncReader async_reader; + + /* I/O associated with this descriptor. */ + LoopIo io; + + /* Looper used to communicate with the UI. */ + Looper* looper; + + /* Writer to send responses to the UI commands. */ + SyncSocket* sync_writer; + + /* Socket descriptor for this service. */ + int sock; + + /* Command reader state. */ + CoreCmdImplState cmd_state; + + /* Incoming command header. */ + UICmdHeader cmd_header; + + /* A small preallocated buffer for command parameters. */ + uint8_t cmd_param[256]; + + /* Buffer to use for reading command parameters. Depending on expected size + * of the parameters this buffer can point to cmd_param field of this + * structure (for small commands), or can be allocated for large commands. */ + void* cmd_param_buf; +} CoreCmdImpl; + +/* One and only one CoreCmdImpl instance. */ +static CoreCmdImpl _coreCmdImpl; + +/* Implemented in android/console.c */ +extern void destroy_corecmd_client(void); +/* Implemented in vl-android.c */ +extern char* qemu_find_file(int type, const char* filename); + +/* Properly initializes cmd_param_buf field in CoreCmdImpl instance to receive + * the expected command parameters. + */ +static uint8_t* +_alloc_cmd_param_buf(CoreCmdImpl* corecmd, uint32_t size) +{ + if (size < sizeof(corecmd->cmd_param)) { + // cmd_param can contain all request data. + corecmd->cmd_param_buf = &corecmd->cmd_param[0]; + } else { + // Expected request us too large to fit into preallocated buffer. + corecmd->cmd_param_buf = qemu_malloc(size); + } + return corecmd->cmd_param_buf; +} + +/* Properly frees cmd_param_buf field in CoreCmdImpl instance. + */ +static void +_free_cmd_param_buf(CoreCmdImpl* corecmd) +{ + if (corecmd->cmd_param_buf != &corecmd->cmd_param[0]) { + qemu_free(corecmd->cmd_param_buf); + corecmd->cmd_param_buf = &corecmd->cmd_param[0]; + } +} + +/* Calculates timeout for transferring the given number of bytes via socket. + * Return: + * Number of milliseconds during which the entire number of bytes is expected + * to be transferred via socket for this service. + */ +static int +_coreCmdImpl_get_timeout(size_t data_size) +{ + // Min 2 seconds + 10 millisec for each transferring byte. + // TODO: Come up with a better arithmetics here. + return 2000 + data_size * 10; +} + +/* Sends command response back to the UI. + * Param: + * corecmd - CoreCmdImpl instance to use to send the response. + * resp - Response header. + * resp_data - Response data. Data size is defined by the header. + * Return: + * 0 on success, or < 0 on failure. + */ +static int +_coreCmdImpl_respond(CoreCmdImpl* corecmd, UICmdRespHeader* resp, void* resp_data) +{ + int status = syncsocket_start_write(corecmd->sync_writer); + if (!status) { + // Write the header + status = syncsocket_write(corecmd->sync_writer, resp, + sizeof(UICmdRespHeader), + _coreCmdImpl_get_timeout(sizeof(UICmdRespHeader))); + // Write response data (if any). + if (status > 0 && resp_data != NULL && resp->resp_data_size != 0) { + status = syncsocket_write(corecmd->sync_writer, resp_data, + resp->resp_data_size, + _coreCmdImpl_get_timeout(resp->resp_data_size)); + } + status = syncsocket_result(status); + syncsocket_stop_write(corecmd->sync_writer); + } + if (status < 0) { + derror("Core is unable to respond with %u bytes to the UI control command: %s\n", + resp->resp_data_size, errno_str); + } + return status; +} + +/* Handles UI control command received from the UI. + * Param: + * corecmd - CoreCmdImpl instance that received the command. + * cmd_header - Command header. + * cmd_param - Command data. + */ +static void +_coreCmdImpl_handle_command(CoreCmdImpl* corecmd, + const UICmdHeader* cmd_header, + const uint8_t* cmd_param) +{ + switch (cmd_header->cmd_type) { + case AUICMD_SET_COARSE_ORIENTATION: + { + UICmdSetCoarseOrientation* cmd = + (UICmdSetCoarseOrientation*)cmd_param; + android_sensors_set_coarse_orientation(cmd->orient); + break; + } + + case AUICMD_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 AUICMD_TRACE_CONTROL: + { + UICmdTraceControl* cmd = (UICmdTraceControl*)cmd_param; + if (cmd->start) { + start_tracing(); + } else { + stop_tracing(); + } + break; + } + + case AUICMD_CHK_NETWORK_DISABLED: + { + UICmdRespHeader resp; + resp.resp_data_size = 0; + resp.result = qemu_net_disable; + _coreCmdImpl_respond(corecmd, &resp, NULL); + break; + } + + case AUICMD_GET_NETSPEED: + { + UICmdRespHeader resp; + UICmdGetNetSpeedResp* resp_data = NULL; + UICmdGetNetSpeed* cmd = (UICmdGetNetSpeed*)cmd_param; + + resp.resp_data_size = 0; + resp.result = 0; + + if (cmd->index >= android_netspeeds_count || + android_netspeeds[cmd->index].name == NULL) { + resp.result = -1; + } else { + const NetworkSpeed* netspeed = &android_netspeeds[cmd->index]; + // Calculate size of the response data: + // fixed header + zero-terminated netspeed name. + resp.resp_data_size = sizeof(UICmdGetNetSpeedResp) + + 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 = + (UICmdGetNetSpeedResp*)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, ""); + } + } + _coreCmdImpl_respond(corecmd, &resp, resp_data); + if (resp_data != NULL) { + qemu_free(resp_data); + } + break; + } + + case AUICMD_GET_NETDELAY: + { + UICmdRespHeader resp; + UICmdGetNetDelayResp* resp_data = NULL; + UICmdGetNetDelay* cmd = (UICmdGetNetDelay*)cmd_param; + + resp.resp_data_size = 0; + resp.result = 0; + + if (cmd->index >= android_netdelays_count || + android_netdelays[cmd->index].name == NULL) { + resp.result = -1; + } else { + const NetworkLatency* netdelay = &android_netdelays[cmd->index]; + // Calculate size of the response data: + // fixed header + zero-terminated netdelay name. + resp.resp_data_size = sizeof(UICmdGetNetDelayResp) + + 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 = + (UICmdGetNetDelayResp*)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, ""); + } + } + _coreCmdImpl_respond(corecmd, &resp, resp_data); + if (resp_data != NULL) { + qemu_free(resp_data); + } + break; + } + + case AUICMD_GET_QEMU_PATH: + { + UICmdRespHeader resp; + UICmdGetQemuPath* cmd = (UICmdGetQemuPath*)cmd_param; + char* filepath = NULL; + + resp.resp_data_size = 0; + resp.result = -1; + filepath = qemu_find_file(cmd->type, cmd->filename); + if (filepath != NULL) { + resp.resp_data_size = strlen(filepath) + 1; + } + _coreCmdImpl_respond(corecmd, &resp, filepath); + if (filepath != NULL) { + qemu_free(filepath); + } + break; + } + + case AUICMD_GET_LCD_DENSITY: + { + UICmdRespHeader resp; + resp.resp_data_size = 0; + resp.result = android_hw->hw_lcd_density; + _coreCmdImpl_respond(corecmd, &resp, NULL); + break; + } + + default: + derror("Unknown UI control command %d is received by the Core.\n", + cmd_header->cmd_type); + break; + } +} + +/* Asynchronous I/O callback reading UI control commands. + * Param: + * opaque - CoreCmdImpl instance. + * events - Lists I/O event (read or write) this callback is called for. + */ +static void +_coreCmdImpl_io_func(void* opaque, int fd, unsigned events) +{ + AsyncStatus status; + CoreCmdImpl* corecmd; + + 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 _coreCmdImpl_io_func\n"); + return; + } + + corecmd = (CoreCmdImpl*)opaque; + + // Read whatever is expected from the socket. + status = asyncReader_read(&corecmd->async_reader, &corecmd->io); + switch (status) { + case ASYNC_COMPLETE: + switch (corecmd->cmd_state) { + case EXPECTS_HEADER: + // We just read the command header. Now we expect the param. + if (corecmd->cmd_header.cmd_param_size != 0) { + corecmd->cmd_state = EXPECTS_PARAMETERS; + // Setup the reader to read expected amount of data. + _alloc_cmd_param_buf(corecmd, + corecmd->cmd_header.cmd_param_size); + asyncReader_init(&corecmd->async_reader, + corecmd->cmd_param_buf, + corecmd->cmd_header.cmd_param_size, + &corecmd->io); + } else { + // Command doesn't have param. Go ahead and handle it. + _coreCmdImpl_handle_command(corecmd, &corecmd->cmd_header, + NULL); + // Prepare for the next header. + corecmd->cmd_state = EXPECTS_HEADER; + asyncReader_init(&corecmd->async_reader, + &corecmd->cmd_header, + sizeof(corecmd->cmd_header), + &corecmd->io); + } + break; + + case EXPECTS_PARAMETERS: + // Entore command is received. Handle it. + _coreCmdImpl_handle_command(corecmd, &corecmd->cmd_header, + corecmd->cmd_param_buf); + _free_cmd_param_buf(corecmd); + // Prepare for the next command. + corecmd->cmd_state = EXPECTS_HEADER; + asyncReader_init(&corecmd->async_reader, &corecmd->cmd_header, + sizeof(corecmd->cmd_header), &corecmd->io); + break; + } + break; + + case ASYNC_ERROR: + loopIo_dontWantRead(&corecmd->io); + if (errno == ECONNRESET) { + // UI has exited. We need to destroy the service. + destroy_corecmd_client(); + } + break; + + case ASYNC_NEED_MORE: + // Transfer will eventually come back into this routine. + return; + } +} + +int +coreCmdImpl_create(int fd) +{ + _coreCmdImpl.sock = fd; + _coreCmdImpl.looper = looper_newCore(); + loopIo_init(&_coreCmdImpl.io, _coreCmdImpl.looper, _coreCmdImpl.sock, + _coreCmdImpl_io_func, &_coreCmdImpl); + _coreCmdImpl.cmd_state = EXPECTS_HEADER; + _coreCmdImpl.cmd_param_buf = &_coreCmdImpl.cmd_param[0]; + asyncReader_init(&_coreCmdImpl.async_reader, &_coreCmdImpl.cmd_header, + sizeof(_coreCmdImpl.cmd_header), &_coreCmdImpl.io); + _coreCmdImpl.sync_writer = syncsocket_init(fd); + if (_coreCmdImpl.sync_writer == NULL) { + derror("Unable to create writer for CoreCmdImpl instance: %s\n", + errno_str); + coreCmdImpl_destroy(); + return -1; + } + return 0; +} + +void +coreCmdImpl_destroy() +{ + // Destroy the writer + if (_coreCmdImpl.sync_writer != NULL) { + syncsocket_close(_coreCmdImpl.sync_writer); + syncsocket_free(_coreCmdImpl.sync_writer); + } + if (_coreCmdImpl.looper != NULL) { + // Stop all I/O that may still be going on. + loopIo_done(&_coreCmdImpl.io); + looper_free(_coreCmdImpl.looper); + _coreCmdImpl.looper = NULL; + } + // Free allocated memory. + _free_cmd_param_buf(&_coreCmdImpl); +} diff --git a/android/protocol/core-commands-impl.h b/android/protocol/core-commands-impl.h new file mode 100644 index 0000000..8690613 --- /dev/null +++ b/android/protocol/core-commands-impl.h @@ -0,0 +1,34 @@ +/* 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_PROTOCOL_CORE_COMMANDS_IMPL_H +#define _ANDROID_PROTOCOL_CORE_COMMANDS_IMPL_H + +/* + * Contains the Core-side implementation of the "ui-core-control" service that is + * part of the UI control protocol. Here we handle UI control commands sent by + * the UI to the Core. + */ + +/* Creates and initializes descriptor for the Core-side of the "ui-core-control" + * service. Note that there can be only one instance of this service in the core. + * Param: + * fd - Socket descriptor for the service. + * Return: + * 0 on success, or < 0 on failure. + */ +extern int coreCmdImpl_create(int fd); + +/* Destroys the descriptor for the Core-side of the "ui-core-control" service. */ +extern void coreCmdImpl_destroy(); + +#endif /* _ANDROID_PROTOCOL_CORE_COMMANDS_IMPL_H */ diff --git a/android/protocol/core-commands-proxy.c b/android/protocol/core-commands-proxy.c new file mode 100644 index 0000000..6bd3d4e --- /dev/null +++ b/android/protocol/core-commands-proxy.c @@ -0,0 +1,374 @@ +/* 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 the UI-side implementation of the "ui-core-control" service that is + * part of the UI control protocol. Here we send UI control commands to the Core. + */ + +#include "console.h" +#include "android/looper.h" +#include "android/core-connection.h" +#include "android/async-utils.h" +#include "android/sync-utils.h" +#include "android/utils/debug.h" +#include "android/utils/panic.h" +#include "android/protocol/core-commands.h" +#include "android/protocol/core-commands-proxy.h" +#include "android/protocol/core-commands-api.h" + +/* Descriptor for the UI-side "ui-core-control" service. */ +typedef struct CoreCmdProxy { + /* Core connection established for this service. */ + CoreConnection* core_connection; + + /* Socket descriptor for the UI service. */ + int sock; + + /* Socket wrapper for sync srites. */ + SyncSocket* sync_writer; + + /* Socket wrapper for sync reads. */ + SyncSocket* sync_reader; +} CoreCmdProxy; + +/* One and only one CoreCmdProxy instance. */ +static CoreCmdProxy _coreCmdProxy = { 0 }; + +/* Sends UI command to the core. + * Param: + * cmd_type, cmd_param, cmd_param_size - Define the command. + * Return: + * 0 On success, or < 0 on failure. + */ +static int +_coreCmdProxy_send_command(uint8_t cmd_type, + void* cmd_param, + uint32_t cmd_param_size) +{ + int status; + UICmdHeader header; + + // Prepare the command header. + header.cmd_type = cmd_type; + header.cmd_param_size = cmd_param_size; + status = syncsocket_start_write(_coreCmdProxy.sync_writer); + if (!status) { + // Send the header. + status = syncsocket_write(_coreCmdProxy.sync_writer, &header, + sizeof(header), + core_connection_get_timeout(sizeof(header))); + // If there is request data, send it too. + if (status > 0 && cmd_param != NULL && cmd_param_size > 0) { + status = syncsocket_write(_coreCmdProxy.sync_writer, cmd_param, + cmd_param_size, + core_connection_get_timeout(cmd_param_size)); + } + status = syncsocket_result(status); + syncsocket_stop_write(_coreCmdProxy.sync_writer); + } + if (status < 0) { + derror("Unable to send UI control command %d (size %u): %s\n", + cmd_type, cmd_param_size, errno_str); + } + return status; +} + +/* Reads UI control command response from the core. + * Param: + * resp - Upon success contains command response header. + * resp_data - Upon success contains allocated reponse data (if any). The caller + * is responsible for deallocating the memory returned here. + * Return: + * 0 on success, or < 0 on failure. + */ +static int +_coreCmdProxy_get_response(UICmdRespHeader* resp, void** resp_data) +{ + int status = syncsocket_start_read(_coreCmdProxy.sync_reader); + if (!status) { + // Read the header. + status = syncsocket_read(_coreCmdProxy.sync_reader, resp, + sizeof(UICmdRespHeader), + core_connection_get_timeout(sizeof(UICmdRespHeader))); + // Read response data (if any). + if (status > 0 && resp->resp_data_size) { + *resp_data = malloc(resp->resp_data_size); + if (*resp_data == NULL) { + APANIC("_coreCmdProxy_get_response is unable to allocate response data buffer.\n"); + } + status = syncsocket_read(_coreCmdProxy.sync_reader, *resp_data, + resp->resp_data_size, + core_connection_get_timeout(resp->resp_data_size)); + } + status = syncsocket_result(status); + syncsocket_stop_read(_coreCmdProxy.sync_reader); + } + if (status < 0) { + derror("Unable to get UI command response from the Core: %s\n", + errno_str); + } + return status; +} + +/* Destroys CoreCmdProxy instance. */ +static void +_coreCmdProxy_destroy(void) +{ + if (_coreCmdProxy.sync_writer != NULL) { + syncsocket_close(_coreCmdProxy.sync_writer); + syncsocket_free(_coreCmdProxy.sync_writer); + } + if (_coreCmdProxy.sync_reader != NULL) { + syncsocket_close(_coreCmdProxy.sync_reader); + syncsocket_free(_coreCmdProxy.sync_reader); + } + if (_coreCmdProxy.core_connection != NULL) { + core_connection_close(_coreCmdProxy.core_connection); + core_connection_free(_coreCmdProxy.core_connection); + _coreCmdProxy.core_connection = NULL; + } +} + +int +corecmd_set_coarse_orientation(AndroidCoarseOrientation orient) +{ + UICmdSetCoarseOrientation cmd; + cmd.orient = orient; + return _coreCmdProxy_send_command(AUICMD_SET_COARSE_ORIENTATION, + &cmd, sizeof(cmd)); +} + +int +corecmd_toggle_network() +{ + return _coreCmdProxy_send_command(AUICMD_TOGGLE_NETWORK, NULL, 0); +} + +int +corecmd_trace_control(int start) +{ + UICmdTraceControl cmd; + cmd.start = start; + return _coreCmdProxy_send_command(AUICMD_TRACE_CONTROL, + &cmd, sizeof(cmd)); +} + +int +corecmd_is_network_disabled() +{ + UICmdRespHeader resp; + void* tmp = NULL; + int status; + + status = _coreCmdProxy_send_command(AUICMD_CHK_NETWORK_DISABLED, NULL, 0); + if (status < 0) { + return status; + } + status = _coreCmdProxy_get_response(&resp, &tmp); + if (status < 0) { + return status; + } + return resp.result; +} + +int +corecmd_get_netspeed(int index, NetworkSpeed** netspeed) +{ + UICmdGetNetSpeed req; + UICmdRespHeader resp; + UICmdGetNetSpeedResp* resp_data = NULL; + int status; + + // Initialize and send the query. + req.index = index; + status = _coreCmdProxy_send_command(AUICMD_GET_NETSPEED, &req, sizeof(req)); + if (status < 0) { + return status; + } + + // Obtain the response from the core. + status = _coreCmdProxy_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(UICmdGetNetSpeedResp)); + 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 +corecmd_get_netdelay(int index, NetworkLatency** netdelay) +{ + UICmdGetNetDelay req; + UICmdRespHeader resp; + UICmdGetNetDelayResp* resp_data = NULL; + int status; + + // Initialize and send the query. + req.index = index; + status = _coreCmdProxy_send_command(AUICMD_GET_NETDELAY, &req, sizeof(req)); + if (status < 0) { + return status; + } + + // Obtain the response from the core. + status = _coreCmdProxy_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(UICmdGetNetDelayResp)); + 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 +corecmd_get_qemu_path(int type, + const char* filename, + char* path, + size_t path_buf_size) +{ + UICmdRespHeader resp; + char* resp_data = NULL; + int status; + + // Initialize and send the query. + uint32_t cmd_data_size = sizeof(UICmdGetQemuPath) + strlen(filename) + 1; + UICmdGetQemuPath* req = (UICmdGetQemuPath*)malloc(cmd_data_size); + if (req == NULL) { + APANIC("corecmd_get_qemu_path is unable to allocate %u bytes\n", + cmd_data_size); + } + req->type = type; + strcpy(req->filename, filename); + status = _coreCmdProxy_send_command(AUICMD_GET_QEMU_PATH, req, + cmd_data_size); + if (status < 0) { + return status; + } + + // Obtain the response from the core. + status = _coreCmdProxy_get_response(&resp, (void**)&resp_data); + if (status < 0) { + return status; + } + if (!resp.result && resp_data != NULL) { + strncpy(path, resp_data, path_buf_size); + path[path_buf_size - 1] = '\0'; + } + if (resp_data != NULL) { + free(resp_data); + } + return resp.result; +} + +int +corecmd_get_hw_lcd_density(void) +{ + UICmdRespHeader resp; + void* tmp = NULL; + int status; + + status = _coreCmdProxy_send_command(AUICMD_GET_LCD_DENSITY, NULL, 0); + if (status < 0) { + return status; + } + status = _coreCmdProxy_get_response(&resp, &tmp); + if (status < 0) { + return status; + } + return resp.result; +} + +int +coreCmdProxy_create(SockAddress* console_socket) +{ + char* handshake = NULL; + + // Connect to the ui-core-control service. + _coreCmdProxy.core_connection = + core_connection_create_and_switch(console_socket, "ui-core-control", + &handshake); + if (_coreCmdProxy.core_connection == NULL) { + derror("Unable to connect to the ui-core-control service: %s\n", + errno_str); + return -1; + } + + // Initialze command writer and response reader. + _coreCmdProxy.sock = core_connection_get_socket(_coreCmdProxy.core_connection); + _coreCmdProxy.sync_writer = syncsocket_init(_coreCmdProxy.sock); + if (_coreCmdProxy.sync_writer == NULL) { + derror("Unable to initialize CoreCmdProxy writer: %s\n", errno_str); + _coreCmdProxy_destroy(); + return -1; + } + _coreCmdProxy.sync_reader = syncsocket_init(_coreCmdProxy.sock); + if (_coreCmdProxy.sync_reader == NULL) { + derror("Unable to initialize CoreCmdProxy reader: %s\n", errno_str); + _coreCmdProxy_destroy(); + return -1; + } + + + fprintf(stdout, "ui-core-control is now connected to the core at %s.", + sock_address_to_string(console_socket)); + if (handshake != NULL) { + if (handshake[0] != '\0') { + fprintf(stdout, " Handshake: %s", handshake); + } + free(handshake); + } + fprintf(stdout, "\n"); + + return 0; +} diff --git a/android/protocol/core-commands-proxy.h b/android/protocol/core-commands-proxy.h new file mode 100644 index 0000000..8303ed4 --- /dev/null +++ b/android/protocol/core-commands-proxy.h @@ -0,0 +1,32 @@ +/* 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_PROTOCOL_CORE_COMMANDS_PROXY_H +#define _ANDROID_PROTOCOL_CORE_COMMANDS_PROXY_H + +#include "sockets.h" + +/* + * Contains the UI-side implementation of the "ui-core-control" service that is + * part of the UI control protocol. Here we send UI control commands to the Core. + */ + +/* Creates and initializes descriptor for the UI-side of the "ui-core-control" + * service. Note that there can be only one instance of this service in the UI. + * Param: + * console_socket - Addresses Core's console. + * Return: + * 0 on success, or < 0 on failure. + */ +extern int coreCmdProxy_create(SockAddress* console_socket); + +#endif /* _ANDROID_PROTOCOL_CORE_COMMANDS_PROXY_H */ diff --git a/android/protocol/core-commands-qemu.c b/android/protocol/core-commands-qemu.c new file mode 100644 index 0000000..03fef64 --- /dev/null +++ b/android/protocol/core-commands-qemu.c @@ -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. +*/ + +/* + * Contains implementation of the API for calling into the Core with the UI + * control commands for standalone (monolithic) emulator. + */ + +#include "android/android.h" +#include "android/globals.h" +#include "android/hw-sensors.h" +#include "telephony/modem_driver.h" +#include "trace.h" +#include "audio/audio.h" +#include "android/protocol/core-commands-api.h" + +/* Implemented in vl-android.c */ +extern char* qemu_find_file(int type, const char* filename); + +int +corecmd_set_coarse_orientation(AndroidCoarseOrientation orient) +{ + android_sensors_set_coarse_orientation(orient); + return 0; +} + +int +corecmd_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); + } + return 0; +} + +int corecmd_trace_control(int start) +{ + if (start) { + start_tracing(); + } else { + stop_tracing(); + } + return 0; +} + +int corecmd_is_network_disabled() +{ + return qemu_net_disable; +} + +int +corecmd_get_netspeed(int index, NetworkSpeed** netspeed) +{ + if (index >= android_netspeeds_count || + android_netspeeds[index].name == NULL) { + return -1; + } + *netspeed = (NetworkSpeed*)malloc(sizeof(NetworkSpeed)); + memcpy(*netspeed, &android_netspeeds[index], sizeof(NetworkSpeed)); + return 0; +} + +int +corecmd_get_netdelay(int index, NetworkLatency** netdelay) +{ + if (index >= android_netdelays_count || + android_netdelays[index].name == NULL) { + return -1; + } + *netdelay = (NetworkLatency*)malloc(sizeof(NetworkLatency)); + memcpy(*netdelay, &android_netdelays[index], sizeof(NetworkLatency)); + return 0; +} + +int +corecmd_get_qemu_path(int type, + const char* filename, + char* path, + size_t path_buf_size) +{ + char* filepath = qemu_find_file(type, filename); + if (filepath == NULL) { + return -1; + } + strncpy(path, filepath, path_buf_size); + path[path_buf_size - 1] = '\0'; + qemu_free(filepath); + return 0; +} + +int +corecmd_get_hw_lcd_density(void) +{ + return android_hw->hw_lcd_density; +} diff --git a/android/protocol/core-commands.h b/android/protocol/core-commands.h new file mode 100644 index 0000000..3ac0ca5 --- /dev/null +++ b/android/protocol/core-commands.h @@ -0,0 +1,104 @@ +/* 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_PROTOCOL_CORE_COMMANDS_H +#define _ANDROID_PROTOCOL_CORE_COMMANDS_H + +/* + * Contains declarations related to the UI control commands sent by the UI and + * handled by the Core. + */ + +#include "android/hw-sensors.h" +#include "android/protocol/ui-common.h" + +/* Sets coarse orientation. */ +#define AUICMD_SET_COARSE_ORIENTATION 1 + +/* Toggles the network. */ +#define AUICMD_TOGGLE_NETWORK 2 + +/* Starts / stops the tracing. */ +#define AUICMD_TRACE_CONTROL 3 + +/* Checks if network is disabled. */ +#define AUICMD_CHK_NETWORK_DISABLED 4 + +/* Gets network speed. */ +#define AUICMD_GET_NETSPEED 5 + +/* Gets network delays */ +#define AUICMD_GET_NETDELAY 6 + +/* Gets path to a QEMU file on local host. */ +#define AUICMD_GET_QEMU_PATH 7 + +/* Gets LCD density. */ +#define AUICMD_GET_LCD_DENSITY 8 + +/* Formats AUICMD_SET_COARSE_ORIENTATION UI control command parameters. */ +typedef struct UICmdSetCoarseOrientation { + AndroidCoarseOrientation orient; +} UICmdSetCoarseOrientation; + +/* Formats AUICMD_TRACE_CONTROL UI control command parameters. */ +typedef struct UICmdTraceControl { + int start; +} UICmdTraceControl; + +/* Formats AUICMD_GET_NETSPEED UI control command parameters. */ +typedef struct UICmdGetNetSpeed { + int index; +} UICmdGetNetSpeed; + +/* Formats AUICMD_GET_NETSPEED UI control command response. + * Instances of this structure contains content of the NetworkSpeed structure, + * including actual "name" and "display" strings. */ +typedef struct UICmdGetNetSpeedResp { + int upload; + int download; + /* Zero-terminated NetworkSpeed's "name" strings starts here. The "display" + * string begins inside this structure, right after the "name"'s + * zero-terminator. */ + char name[0]; +} UICmdGetNetSpeedResp; + +/* Formats AUICMD_GET_NETDELAY UI control command parameters. */ +typedef struct UICmdGetNetDelay { + int index; +} UICmdGetNetDelay; + +/* Formats AUICMD_GET_NETDELAY UI control command response. + * Instances of this structure contains content of the NetworkLatency structure, + * including actual "name" and "display" strings. */ +typedef struct UICmdGetNetDelayResp { + int min_ms; + int max_ms; + /* Zero-terminated NetworkLatency's "name" strings starts here. The "display" + * string begins inside this structure, right after the "name"'s + * zero-terminator. */ + char name[0]; +} UICmdGetNetDelayResp; + +/* Formats AUICMD_GET_QEMU_PATH UI control command parameters. */ +typedef struct UICmdGetQemuPath { + int type; + char filename[0]; +} UICmdGetQemuPath; + +/* Formats AUICMD_GET_QEMU_PATH UI control command response. */ +typedef struct UICmdGetQemuPathResp { + /* Queried qemu path begins here. */ + char path[0]; +} UICmdGetQemuPathResp; + +#endif /* _ANDROID_PROTOCOL_CORE_COMMANDS_H */ diff --git a/android/protocol/ui-commands-api.h b/android/protocol/ui-commands-api.h new file mode 100644 index 0000000..d9fe6b0 --- /dev/null +++ b/android/protocol/ui-commands-api.h @@ -0,0 +1,41 @@ +/* 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_PROTOCOL_UI_COMMANDS_API_H +#define _ANDROID_PROTOCOL_UI_COMMANDS_API_H + +/* + * Contains the API for calling into the UI with the Core control commands. + */ + +/* 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 uicmd_set_window_scale(double scale, int is_dpi); + +/* This is temporary redeclaration for AndroidHwLightBrightnessFunc declared + * in android/hw-control.h We redeclare it here in order to keep type + * consistency between android_core_set_brightness_change_callback and + * light_brightness field of AndroidHwControlFuncs structure. + */ +typedef void (*AndroidHwLightBrightnessCallback)(void* opaque, + const char* light, + int brightness); + +/* Registers a UI callback to be called when brightness is changed by the core. */ +extern int uicmd_set_brightness_change_callback(AndroidHwLightBrightnessCallback callback, + void* opaque); + +#endif /* _ANDROID_PROTOCOL_UI_COMMANDS_API_H */ diff --git a/android/protocol/ui-commands-impl.c b/android/protocol/ui-commands-impl.c new file mode 100644 index 0000000..456c61e --- /dev/null +++ b/android/protocol/ui-commands-impl.c @@ -0,0 +1,257 @@ +/* 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 the UI-side implementation of the "core-ui-control" service that is + * part of the UI control protocol. Here we handle UI control commands received + * from the Core. + */ + +#include "console.h" +//#include "android/hw-control.h" +#include "android/looper.h" +#include "android/core-connection.h" +#include "android/async-utils.h" +#include "android/sync-utils.h" +#include "android/utils/system.h" +#include "android/utils/debug.h" +#include "android/utils/panic.h" +#include "android/protocol/ui-commands-impl.h" +#include "android/protocol/ui-commands-api.h" + +/* Enumerates states for the command reader in UICmdImpl instance. */ +typedef enum UICmdImplState { + /* The reader is waiting on command header. */ + EXPECTS_HEADER, + + /* The reader is waiting on command parameters. */ + EXPECTS_PARAMETERS, +} UICmdImplState; + +/* Descriptor for the UI-side of the "core-ui-control" service. */ +typedef struct UICmdImpl { + /* Core connection established for this service. */ + CoreConnection* core_connection; + + /* Socket descriptor for the UI service. */ + int sock; + + /* Command reader state. */ + UICmdImplState reader_state; + + /* Incoming command header. */ + UICmdHeader cmd_header; + + /* Reader's buffer. This field can point to the cmd_header field of this + * structure (when we expect a command header), or to a buffer allocated for + * the (when we expect command parameters). */ + 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; +} UICmdImpl; + +/* Implemented in android/qemulator.c */ +extern void android_emulator_set_window_scale(double scale, int is_dpi); + +/* One and only one UICmdImpl instance. */ +static UICmdImpl _uiCmdImpl; + +/* Display brightness change callback. */ +static AndroidHwLightBrightnessCallback _brightness_change_callback = NULL; +static void* _brightness_change_callback_param = NULL; + +/* Destroys UICmdImpl instance. */ +static void +_uiCmdImpl_destroy() +{ + if (_uiCmdImpl.core_connection != NULL) { + // Disable I/O callbacks. + qemu_set_fd_handler(_uiCmdImpl.sock, NULL, NULL, NULL); + core_connection_close(_uiCmdImpl.core_connection); + core_connection_free(_uiCmdImpl.core_connection); + _uiCmdImpl.core_connection = NULL; + } + // Properly deallocate the reader buffer. + if (_uiCmdImpl.reader_buffer != NULL && + _uiCmdImpl.reader_buffer != (uint8_t*)&_uiCmdImpl.cmd_header) { + free(_uiCmdImpl.reader_buffer); + _uiCmdImpl.reader_buffer = (uint8_t*)&_uiCmdImpl.cmd_header; + } +} + +/* Handles UI control command received from the core. + * Param: + * uicmd - UICmdImpl instance that received the command. + * header - UI control command header. + * data - Command parameters formatted accordingly to the command type. + */ +static void +_uiCmdImpl_handle_command(UICmdImpl* uicmd, + const UICmdHeader* header, + const uint8_t* data) +{ + switch (header->cmd_type) { + case AUICMD_SET_WINDOWS_SCALE: + { + UICmdSetWindowsScale* cmd = (UICmdSetWindowsScale*)data; + android_emulator_set_window_scale(cmd->scale, cmd->is_dpi); + break; + } + + case AUICMD_CHANGE_DISP_BRIGHTNESS: + { + UICmdChangeDispBrightness* cmd = (UICmdChangeDispBrightness*)data; + if (_brightness_change_callback != NULL) { + _brightness_change_callback(_brightness_change_callback_param, + cmd->light, cmd->brightness); + } + break; + } + + default: + derror("Unknown command %d is received from the Core\n", + header->cmd_type); + break; + } +} + +/* Asynchronous I/O callback reading UI control commands. + * Param: + * opaque - UICmdImpl instance. + */ +static void +_uiCmdImpl_io_read(void* opaque) +{ + UICmdImpl* uicmd = opaque; + int status; + + // Read requests while they are immediately available. + for (;;) { + // Read next chunk of data. + status = read(uicmd->sock, uicmd->reader_buffer + uicmd->reader_offset, + uicmd->reader_bytes - uicmd->reader_offset); + if (status == 0) { + /* Disconnection, meaning that the core process got termonated. */ + fprintf(stderr, "core-ui-control service got disconnected\n"); + _uiCmdImpl_destroy(); + return; + } + if (status < 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; + } + } + + uicmd->reader_offset += status; + if (uicmd->reader_offset != uicmd->reader_bytes) { + // There are still some data left in the pipe. + continue; + } + + // All expected data has been read. Time to change the state. + if (uicmd->reader_state == EXPECTS_HEADER) { + // Header has been read. + if (uicmd->cmd_header.cmd_param_size) { + // Prepare for the command parameters. + uicmd->reader_state = EXPECTS_PARAMETERS; + uicmd->reader_offset = 0; + uicmd->reader_bytes = uicmd->cmd_header.cmd_param_size; + uicmd->reader_buffer = malloc(uicmd->reader_bytes); + if (uicmd->reader_buffer == NULL) { + APANIC("Unable to allocate memory for UI command parameters.\n"); + } + } else { + // This command doesn't have any parameters. Handle it now. + _uiCmdImpl_handle_command(uicmd, &uicmd->cmd_header, NULL); + // Prepare for the next command header. + uicmd->reader_state = EXPECTS_HEADER; + uicmd->reader_offset = 0; + uicmd->reader_bytes = sizeof(uicmd->cmd_header); + uicmd->reader_buffer = (uint8_t*)&uicmd->cmd_header; + } + } else { + // All command data is in. Handle it. + _uiCmdImpl_handle_command(uicmd, &uicmd->cmd_header, + uicmd->reader_buffer); + // Prepare for the next command header. + free(uicmd->reader_buffer); + uicmd->reader_state = EXPECTS_HEADER; + uicmd->reader_offset = 0; + uicmd->reader_bytes = sizeof(uicmd->cmd_header); + uicmd->reader_buffer = (uint8_t*)&uicmd->cmd_header; + } + } +} + +int +uiCmdImpl_create(SockAddress* console_socket) +{ + char* handshake = NULL; + + // Setup command reader. + _uiCmdImpl.reader_buffer = (uint8_t*)&_uiCmdImpl.cmd_header; + _uiCmdImpl.reader_state = EXPECTS_HEADER; + _uiCmdImpl.reader_offset = 0; + _uiCmdImpl.reader_bytes = sizeof(UICmdHeader); + + // Connect to the core-ui-control service. + _uiCmdImpl.core_connection = + core_connection_create_and_switch(console_socket, "core-ui-control", + &handshake); + if (_uiCmdImpl.core_connection == NULL) { + derror("Unable to connect to the core-ui-control service: %s\n", + errno_str); + return -1; + } + + // Initialze UI command reader. + _uiCmdImpl.sock = core_connection_get_socket(_uiCmdImpl.core_connection); + if (qemu_set_fd_handler(_uiCmdImpl.sock, _uiCmdImpl_io_read, NULL, + &_uiCmdImpl)) { + derror("Unable to set up UI _uiCmdImpl_io_read callback: %s\n", + errno_str); + _uiCmdImpl_destroy(); + if (handshake != NULL) { + free(handshake); + } + return -1; + } + + fprintf(stdout, "core-ui-control is now connected to the core at %s.", + sock_address_to_string(console_socket)); + if (handshake != NULL) { + if (handshake[0] != '\0') { + fprintf(stdout, " Handshake: %s", handshake); + } + free(handshake); + } + fprintf(stdout, "\n"); + + return 0; +} + +int +uicmd_set_brightness_change_callback(AndroidHwLightBrightnessCallback callback, + void* opaque) +{ + _brightness_change_callback = callback; + _brightness_change_callback_param = opaque; + return 0; +} diff --git a/android/protocol/ui-commands-impl.h b/android/protocol/ui-commands-impl.h new file mode 100644 index 0000000..0e5b52f --- /dev/null +++ b/android/protocol/ui-commands-impl.h @@ -0,0 +1,34 @@ +/* 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_PROTOCOL_UI_COMMANDS_IMPL_H +#define _ANDROID_PROTOCOL_UI_COMMANDS_IMPL_H + +#include "sockets.h" +#include "android/protocol/ui-commands.h" + +/* + * Contains the UI-side implementation of the "core-ui-control" service that is + * part of the UI control protocol. Here we handle UI control commands sent by + * the Core to the UI. + */ + +/* Creates and initializes descriptor for the UI-side of the "core-ui-control" + * service. Note that there can be only one instance of this service in the UI. + * Param: + * console_socket - Addresses Core's console. + * Return: + * 0 on success, or < 0 on failure. + */ +extern int uiCmdImpl_create(SockAddress* console_socket); + +#endif /* _ANDROID_PROTOCOL_UI_COMMANDS_IMPL_H */ diff --git a/android/protocol/ui-commands-proxy.c b/android/protocol/ui-commands-proxy.c new file mode 100644 index 0000000..76bf883 --- /dev/null +++ b/android/protocol/ui-commands-proxy.c @@ -0,0 +1,209 @@ +/* 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 the Core-side implementation of the "core-ui-control" service that is + * part of the UI control protocol. Here we send UI control commands to the UI. + */ + +#include "android/android.h" +#include "android/hw-control.h" +#include "android/looper.h" +#include "android/async-utils.h" +#include "android/sync-utils.h" +#include "android/utils/debug.h" +#include "android/protocol/ui-commands.h" +#include "android/protocol/ui-commands-proxy.h" +#include "android/protocol/ui-commands-api.h" + +/* Descriptor for the UI commands proxy. */ +typedef struct UICmdProxy { + /* 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; +} UICmdProxy; + +/* One and only one UICmdProxy instance. */ +static UICmdProxy _uiCmdProxy; + +/* Implemented in android/console.c */ +extern void destroy_uicmd_client(void); + +/* Calculates timeout for transferring the given number of bytes via socket. + * Return: + * Number of milliseconds during which the entire number of bytes is expected + * to be transferred via socket. + */ +static int +_uiCmdProxy_get_timeout(size_t data_size) +{ + // Min 2 seconds + 10 millisec for each transferring byte. + // TODO: Come up with a better arithmetics here. + return 2000 + data_size * 10; +} + +/* Sends request to the UI client of this service. + * Param: + * cmd_type, cmd_param, cmd_param_size - Define the command to send. + * Return: + * 0 on success, or < 0 on failure. + */ +static int +_uiCmdProxy_send_command(uint8_t cmd_type, + void* cmd_param, + uint32_t cmd_param_size) +{ + UICmdHeader header; + int status = syncsocket_start_write(_uiCmdProxy.sync_writer); + if (!status) { + // Initialize and send the header. + header.cmd_type = cmd_type; + header.cmd_param_size = cmd_param_size; + status = syncsocket_write(_uiCmdProxy.sync_writer, &header, sizeof(header), + _uiCmdProxy_get_timeout(sizeof(header))); + // If there are command parameters, send them too. + if (status > 0 && cmd_param != NULL && cmd_param_size > 0) { + status = syncsocket_write(_uiCmdProxy.sync_writer, cmd_param, + cmd_param_size, + _uiCmdProxy_get_timeout(cmd_param_size)); + } + status = syncsocket_result(status); + syncsocket_stop_write(_uiCmdProxy.sync_writer); + } + if (status < 0) { + derror("Send UI command %d (%u bytes) has failed: %s\n", + cmd_type, cmd_param_size, errno_str); + } + return status; +} + +/* Asynchronous I/O callback for UICmdProxy 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 - UICmdProxy instance. + */ +static void +_uiCmdProxy_io_func(void* opaque, int fd, unsigned events) +{ + UICmdProxy* uicmd = (UICmdProxy*)opaque; + AsyncReader reader; + AsyncStatus status; + uint8_t read_buf[1]; + + if (events & LOOP_IO_WRITE) { + derror("Unexpected LOOP_IO_WRITE in _uiCmdProxy_io_func.\n"); + return; + } + + // Try to read + asyncReader_init(&reader, read_buf, sizeof(read_buf), &uicmd->io); + status = asyncReader_read(&reader, &uicmd->io); + // We expect only error status here. + if (status != ASYNC_ERROR) { + derror("Unexpected read status %d in _uiCmdProxy_io_func\n", status); + return; + } + // We expect only socket disconnection error here. + if (errno != ECONNRESET) { + derror("Unexpected read error %d (%s) in _uiCmdProxy_io_func.\n", + errno, errno_str); + return; + } + + // Client got disconnectted. + destroy_uicmd_client(); +} +/* a callback function called when the system wants to change the brightness + * of a given light. 'light' is a string which can be one of: + * 'lcd_backlight', 'button_backlight' or 'Keyboard_backlight' + * + * brightness is an integer (acceptable range are 0..255), however the + * default is around 105, and we probably don't want to dim the emulator's + * output at that level. + */ +static void +_uiCmdProxy_brightness_change_callback(void* opaque, + const char* light, + int brightness) +{ + // Calculate size of the command parameters. + const size_t cmd_size = sizeof(UICmdChangeDispBrightness) + strlen(light) + 1; + // Allocate and initialize parameters. + UICmdChangeDispBrightness* cmd = + (UICmdChangeDispBrightness*)qemu_malloc(cmd_size); + cmd->brightness = brightness; + strcpy(cmd->light, light); + // Send the command. + _uiCmdProxy_send_command(AUICMD_CHANGE_DISP_BRIGHTNESS, cmd, cmd_size); + qemu_free(cmd); +} + +int +uiCmdProxy_create(int fd) +{ + // Initialize the only UICmdProxy instance. + _uiCmdProxy.sock = fd; + _uiCmdProxy.looper = looper_newCore(); + loopIo_init(&_uiCmdProxy.io, _uiCmdProxy.looper, _uiCmdProxy.sock, + _uiCmdProxy_io_func, &_uiCmdProxy); + loopIo_wantRead(&_uiCmdProxy.io); + _uiCmdProxy.sync_writer = syncsocket_init(fd); + if (_uiCmdProxy.sync_writer == NULL) { + derror("Unable to initialize UICmdProxy writer: %s\n", errno_str); + uiCmdProxy_destroy(); + return -1; + } + { + // Set brighness change callback, so we can notify + // the UI about the event. + AndroidHwControlFuncs funcs; + funcs.light_brightness = _uiCmdProxy_brightness_change_callback; + android_hw_control_init(&_uiCmdProxy, &funcs); + } + return 0; +} + +void +uiCmdProxy_destroy() +{ + // Destroy the sync writer. + if (_uiCmdProxy.sync_writer != NULL) { + syncsocket_close(_uiCmdProxy.sync_writer); + syncsocket_free(_uiCmdProxy.sync_writer); + } + if (_uiCmdProxy.looper != NULL) { + // Stop all I/O that may still be going on. + loopIo_done(&_uiCmdProxy.io); + looper_free(_uiCmdProxy.looper); + _uiCmdProxy.looper = NULL; + } + _uiCmdProxy.sock = -1; +} + +int +uicmd_set_window_scale(double scale, int is_dpi) +{ + UICmdSetWindowsScale cmd; + cmd.scale = scale; + cmd.is_dpi = is_dpi; + return _uiCmdProxy_send_command(AUICMD_SET_WINDOWS_SCALE, &cmd, sizeof(cmd)); +} diff --git a/android/protocol/ui-commands-proxy.h b/android/protocol/ui-commands-proxy.h new file mode 100644 index 0000000..8627537 --- /dev/null +++ b/android/protocol/ui-commands-proxy.h @@ -0,0 +1,41 @@ +/* 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_PROTOCOL_UI_COMMANDS_PROXY_H +#define _ANDROID_PROTOCOL_UI_COMMANDS_PROXY_H + +/* + * Contains the Core-side implementation of the "core-ui-control" service that is + * part of the UI control protocol. Here we send UI control commands to the UI. + */ + +/* Creates and initializes descriptor for the Core-side of the "core-ui-control" + * service. Note that there can be only one instance of this service in the core. + * Param: + * fd - Socket descriptor for the proxy. + * Return: + * 0 on success, or < 0 on failure. + */ +extern int uiCmdProxy_create(int fd); + +/* Destroys the descriptor for the Core-side of the "core-ui-control" service. */ +extern void uiCmdProxy_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 uicmd_set_window_scale(double scale, int is_dpi); + +#endif /* _ANDROID_PROTOCOL_UI_COMMANDS_PROXY_H */ diff --git a/android/protocol/ui-commands-qemu.c b/android/protocol/ui-commands-qemu.c new file mode 100644 index 0000000..3dbed31 --- /dev/null +++ b/android/protocol/ui-commands-qemu.c @@ -0,0 +1,40 @@ +/* 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 implementation of the API for calling into the UI with the Core + * control commands for standalone (monolithic) emulator. + */ + +#include "android/android.h" +#include "android/hw-control.h" +#include "android/protocol/ui-commands-api.h" + +/* Implemented in android/qemulator.c */ +extern void android_emulator_set_window_scale(double scale, int is_dpi); + +int +uicmd_set_window_scale(double scale, int is_dpi) +{ + android_emulator_set_window_scale(scale, is_dpi); + return 0; +} + +int +uicmd_set_brightness_change_callback(AndroidHwLightBrightnessCallback callback, + void* opaque) +{ + AndroidHwControlFuncs funcs; + funcs.light_brightness = callback; + android_hw_control_init(opaque, &funcs); + return 0; +} diff --git a/android/protocol/ui-commands.h b/android/protocol/ui-commands.h new file mode 100644 index 0000000..4e47b83 --- /dev/null +++ b/android/protocol/ui-commands.h @@ -0,0 +1,44 @@ +/* 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_PROTOCOL_UI_COMMANDS_H +#define _ANDROID_PROTOCOL_UI_COMMANDS_H + +/* + * Contains declarations related to the UI control commands sent by the Core and + * handled by the UI. + */ + +#include "android/protocol/ui-common.h" + +/* Sets window scale. */ +#define AUICMD_SET_WINDOWS_SCALE 1 + +/* Changes display brightness. */ +#define AUICMD_CHANGE_DISP_BRIGHTNESS 2 + +/* Formats AUICMD_SET_WINDOWS_SCALE UI control command parameters. + * Contains parameters required by android_emulator_set_window_scale routine. + */ +typedef struct UICmdSetWindowsScale { + double scale; + int is_dpi; +} UICmdSetWindowsScale; + +/* Formats AUICMD_CHANGE_DISP_BRIGHTNESS UI control command parameters. + */ +typedef struct UICmdChangeDispBrightness { + int brightness; + char light[0]; +} UICmdChangeDispBrightness; + +#endif /* _ANDROID_PROTOCOL_UI_COMMANDS_H */ diff --git a/android/protocol/ui-common.h b/android/protocol/ui-common.h new file mode 100644 index 0000000..003ed6d --- /dev/null +++ b/android/protocol/ui-common.h @@ -0,0 +1,53 @@ +/* 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_PROTOCOL_UI_COMMON_H +#define _ANDROID_PROTOCOL_UI_COMMON_H + +/* + * Contains declarations for UI control protocol used by both the Core, + * and the UI. + */ + +/* UI control command header. + * Every UI control command sent by the Core, or by the UI begins with this + * header, immediately followed by the command parameters (if there are any). + * Command type is defined by cmd_type field of this header. If command doesn't + * have any command-specific parameters, cmd_param_size field of this header + * must be 0. + */ +typedef struct UICmdHeader { + /* Command type. */ + uint8_t cmd_type; + + /* Byte size of the buffer containing parameters for the comand defined by + * the cmd_type field. The buffer containing parameters must immediately + * follow this header. If command doesn't have any parameters, this field + * must be 0 */ + uint32_t cmd_param_size; +} UICmdHeader; + +/* UI control command response header. + * If UI control command assumes a response from the remote end, the response + * must start with this header, immediately followed by the response data buffer. + */ +typedef struct UICmdRespHeader { + /* Result of the command handling. */ + int result; + + /* Byte size of the buffer containing response data immediately following + * this header. If there are no response data for the command, this field + * must be 0. */ + uint32_t resp_data_size; +} UICmdRespHeader; + +#endif /* _ANDROID_PROTOCOL_UI_COMMON_H */ |