diff options
Diffstat (limited to 'android/ui-ctl-core.c')
-rw-r--r-- | android/ui-ctl-core.c | 568 |
1 files changed, 568 insertions, 0 deletions
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); +} |