aboutsummaryrefslogtreecommitdiffstats
path: root/android/ui-ctl-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'android/ui-ctl-core.c')
-rw-r--r--android/ui-ctl-core.c568
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);
+}