/* Copyright (C) 2010 The Android Open Source Project ** ** This software is licensed under the terms of the GNU General Public ** License version 2, as published by the Free Software Foundation, and ** may be copied, distributed, and modified under those terms. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. */ /* * Contains UI-side of UI control protocol. */ #include "console.h" #include "android/android.h" #include "android/globals.h" #include "android/looper.h" #include "android/core-connection.h" #include "android/async-utils.h" #include "android/utils/system.h" #include "android/utils/debug.h" #include "android/sync-utils.h" #include "android/ui-ctl-common.h" #include "android/ui-ctl-ui.h" #define PANIC(...) do { fprintf(stderr, __VA_ARGS__); \ exit(1); \ } while (0) /* * Enumerates states for the request reader in CoreUICtlClient instance. */ typedef enum CoreUICtlClientState { /* The reader is waiting on request header. */ WAIT_HEADER, /* The reader is waiting on request data. */ WAIT_DATA, } CoreUICtlClientState; /* Common descriptor for UI control clients. */ typedef struct UICtlCommon { /* Core connection instance for the UI control client. */ CoreConnection* core_connection; /* Socket wrapper for sync writes. */ SyncSocket* sync_writer; /* Socket descriptor for the UI control client. */ int sock; } UICtlCommon; /* Descriptor for the Core->UI control client. */ typedef struct CoreUICtlClient { /* Common UI control client descriptor. */ UICtlCommon common; /* Current reader state. */ CoreUICtlClientState reader_state; /* Incoming request header. */ UICtlHeader req_header; /* Reader's buffer. */ uint8_t* reader_buffer; /* Offset in the reader's buffer where to read next chunk of data. */ size_t reader_offset; /* Total number of bytes the reader expects to read. */ size_t reader_bytes; } CoreUICtlClient; /* Descriptor for the UI->Core control client. */ typedef struct UICoreCtlClient { /* Common UI control client descriptor. */ UICtlCommon common; /* Socket wrapper for sync reads. */ SyncSocket* sync_reader; } UICoreCtlClient; /* One and only one Core->UI control client instance. */ static CoreUICtlClient _core_ui_client; /* One and only one UI->Core control client instance. */ static UICoreCtlClient _ui_core_client; /* Calculates timeout for transferring the given number of bytes via UI control * socket. * Return: * Number of milliseconds during which the entire number of bytes is expected * to be transferred. */ static int _get_transfer_timeout(size_t data_size) { // Min 200 millisec + one millisec for each transferring byte. // TODO: Come up with a better arithmetics here. return 200 + data_size; } /* Initializes UICtlCommon instance. * Param: * console_socket - Addresses core's console service. * name - Name of the core's service to attach to ("ui-core client", * or "core-ui client"). * uictl_common - UICtlCommon instance to initialize. * Return: * 0 on success, or < 0 on failure. */ static int _clientuictl_create_client(SockAddress* console_socket, char* name, UICtlCommon* uictl_common) { char* connect_message = NULL; char switch_cmd[256]; // Connect to the console service. uictl_common->core_connection = core_connection_create(console_socket); if (uictl_common->core_connection == NULL) { derror("UI control client %s is unable to connect to the console: %s\n", name, errno_str); return -1; } if (core_connection_open(uictl_common->core_connection)) { core_connection_free(uictl_common->core_connection); uictl_common->core_connection = NULL; derror("UI control client %s is unable to open the console: %s\n", name, errno_str); return -1; } snprintf(switch_cmd, sizeof(switch_cmd), "%s", name); if (core_connection_switch_stream(uictl_common->core_connection, switch_cmd, &connect_message)) { derror("Unable to connect to the UI control service %s: %s\n", name, connect_message ? connect_message : ""); if (connect_message != NULL) { free(connect_message); } core_connection_close(uictl_common->core_connection); core_connection_free(uictl_common->core_connection); uictl_common->core_connection = NULL; return -1; } if (connect_message != NULL) { free(connect_message); } // Initialize UICtlCommon instance. uictl_common->sock = core_connection_get_socket(uictl_common->core_connection); uictl_common->sync_writer = syncsocket_init(uictl_common->sock); if (uictl_common->sync_writer == NULL) { derror("Unable to initialize sync writer for %s UI control client: %s\n", name, errno_str); return -1; } return 0; } /* Destroys UICtlCommon instance. */ static void _uictlcommon_destroy(UICtlCommon* desc) { if (desc->core_connection != NULL) { // Disable I/O callbacks. qemu_set_fd_handler(desc->sock, NULL, NULL, NULL); syncsocket_close(desc->sync_writer); syncsocket_free(desc->sync_writer); core_connection_close(desc->core_connection); core_connection_free(desc->core_connection); desc->core_connection = NULL; } } /* * Core->UI control client implementation. */ /* Implemented in android/qemulator.c */ extern void android_emulator_set_window_scale( double scale, int is_dpi ); /* Destroys CoreUICtlClient instance. */ static void _core_ui_client_destroy() { _uictlcommon_destroy(&_core_ui_client.common); if (_core_ui_client.reader_buffer != NULL && _core_ui_client.reader_buffer != (uint8_t*)&_core_ui_client.req_header) { free(_core_ui_client.reader_buffer); } } /* * Handles UI control request received from the core. * Param: * uictl - CoreUICtlClient instance that received the request. * header - UI control request header. * data - Request data formatted accordingly to the request type. */ static void _core_ui_ctl_handle_request(CoreUICtlClient* uictl, UICtlHeader* header, uint8_t* data) { switch (header->msg_type) { case ACORE_UICTL_SET_WINDOWS_SCALE: { UICtlSetWindowsScale* req = (UICtlSetWindowsScale*)data; android_emulator_set_window_scale(req->scale, req->is_dpi); break; } default: derror("Unknown Core UI control %d\n", header->msg_type); break; } } /* * Asynchronous I/O callback launched when UI control requests received from the * core are ready to be read. * Param: * opaque - CoreUICtlClient instance. */ static void _core_ui_client_read_cb(void* opaque) { CoreUICtlClient* uictl = opaque; int ret; // Read requests while they are immediately available. for (;;) { // Read next chunk of data. ret = read(uictl->common.sock, uictl->reader_buffer + uictl->reader_offset, uictl->reader_bytes - uictl->reader_offset); if (ret == 0) { /* disconnection ! */ _core_ui_client_destroy(); return; } if (ret < 0) { if (errno == EINTR) { /* loop on EINTR */ continue; } else if (errno == EWOULDBLOCK || errno == EAGAIN) { // Chunk is not avalable at this point. Come back later. return; } } uictl->reader_offset += ret; if (uictl->reader_offset != uictl->reader_bytes) { // There are still some data left in the pipe. continue; } // All expected data has been read. Time to change the state. if (uictl->reader_state == WAIT_HEADER) { // Header has been read. Prepare for the data. uictl->reader_state = WAIT_DATA; uictl->reader_offset = 0; uictl->reader_bytes = uictl->req_header.msg_data_size; uictl->reader_buffer = malloc(uictl->reader_bytes); if (uictl->reader_buffer == NULL) { PANIC("Unable to allocate memory for UI control request.\n"); } } else { _core_ui_ctl_handle_request(uictl, &uictl->req_header, uictl->reader_buffer); free(uictl->reader_buffer); uictl->reader_state = WAIT_HEADER; uictl->reader_offset = 0; uictl->reader_bytes = sizeof(uictl->req_header); uictl->reader_buffer = (uint8_t*)&uictl->req_header; } } } /* * UI->Core control client implementation. */ /* Sends UI request to the core. * Param: * msg_type, msg_data, msg_data_size - Define the request. * Return: * 0 On success, or < 0 on failure. */ static int _ui_core_ctl_send_request(uint8_t msg_type, void* msg_data, uint32_t msg_data_size) { int status; UICtlHeader header; // Prepare and send the header. header.msg_type = msg_type; header.msg_data_size = msg_data_size; status = syncsocket_start_write(_ui_core_client.common.sync_writer); if (!status) { // Send the header. status = syncsocket_write(_ui_core_client.common.sync_writer, &header, sizeof(header), _get_transfer_timeout(sizeof(header))); // If there is request data, send it too. if (status > 0 && msg_data != NULL && msg_data_size > 0) { status = syncsocket_write(_ui_core_client.common.sync_writer, msg_data, msg_data_size, _get_transfer_timeout(msg_data_size)); } status = syncsocket_result(status); syncsocket_stop_write(_ui_core_client.common.sync_writer); } if (status < 0) { derror("Unable to send UI control request: %s\n", errno_str); } return status; } /* Reads response to a UI control request from the core. * Param: * resp - Upon success contains response header. * resp_data - Upon success contains allocated reponse data (if any). The caller * is responsible for deallocating of the memory returned in this parameter. * Return: * 0 on success, or < 0 on failure. */ static int _ui_core_ctl_get_response(UICtlRespHeader* resp, void** resp_data) { int status = syncsocket_start_read(_ui_core_client.sync_reader); if (!status) { // Read the header. status = syncsocket_read(_ui_core_client.sync_reader, resp, sizeof(UICtlRespHeader), _get_transfer_timeout(sizeof(UICtlRespHeader))); // Read response data (if any). if (status > 0 && resp->resp_data_size) { *resp_data = malloc(resp->resp_data_size); if (*resp_data == NULL) { PANIC("Unable to allocate response data buffer\n"); } status = syncsocket_read(_ui_core_client.sync_reader, *resp_data, resp->resp_data_size, _get_transfer_timeout(resp->resp_data_size)); } status = syncsocket_result(status); syncsocket_stop_read(_ui_core_client.sync_reader); } if (status < 0) { derror("Unable to get UI control response: %s\n", errno_str); } return status; } int clientuictl_set_coarse_orientation(AndroidCoarseOrientation orient) { UICtlSetCoarseOrientation msg; msg.orient = orient; return _ui_core_ctl_send_request(AUI_UICTL_SET_COARSE_ORIENTATION, &msg, sizeof(msg)); } int clientuictl_toggle_network() { return _ui_core_ctl_send_request(AUI_UICTL_TOGGLE_NETWORK, NULL, 0); } int clientuictl_trace_control(int start) { UICtlTraceControl msg; msg.start = start; return _ui_core_ctl_send_request(AUI_UICTL_TRACE_CONTROL, &msg, sizeof(msg)); } int clientuictl_check_network_disabled() { UICtlRespHeader resp; void* tmp = NULL; int status; status = _ui_core_ctl_send_request(AUI_UICTL_CHK_NETWORK_DISABLED, NULL, 0); if (status < 0) { return status; } status = _ui_core_ctl_get_response(&resp, &tmp); if (status < 0) { return status; } return resp.result; } int clientuictl_get_netspeed(int index, NetworkSpeed** netspeed) { UICtlGetNetSpeed req; UICtlRespHeader resp; UICtlGetNetSpeedResp* resp_data = NULL; int status; // Initialize and send the query. req.index = index; status = _ui_core_ctl_send_request(AUI_UICTL_GET_NETSPEED, &req, sizeof(req)); if (status < 0) { return status; } // Obtain the response from the core. status = _ui_core_ctl_get_response(&resp, (void**)&resp_data); if (status < 0) { return status; } if (!resp.result) { NetworkSpeed* ret; // Allocate memory for the returning NetworkSpeed instance. // It includes: NetworkSpeed structure + // size of zero-terminated "name" and "display" strings saved in // resp_data. *netspeed = malloc(sizeof(NetworkSpeed) + 1 + resp.resp_data_size - sizeof(UICtlGetNetSpeedResp)); ret = *netspeed; // Copy data obtained from the core to the returning NetworkSpeed // instance. ret->upload = resp_data->upload; ret->download = resp_data->download; ret->name = (char*)ret + sizeof(NetworkSpeed); strcpy((char*)ret->name, resp_data->name); ret->display = ret->name + strlen(ret->name) + 1; strcpy((char*)ret->display, resp_data->name + strlen(resp_data->name) + 1); } if (resp_data != NULL) { free(resp_data); } return resp.result; } int clientuictl_get_netdelay(int index, NetworkLatency** netdelay) { UICtlGetNetDelay req; UICtlRespHeader resp; UICtlGetNetDelayResp* resp_data = NULL; int status; // Initialize and send the query. req.index = index; status = _ui_core_ctl_send_request(AUI_UICTL_GET_NETDELAY, &req, sizeof(req)); if (status < 0) { return status; } // Obtain the response from the core. status = _ui_core_ctl_get_response(&resp, (void**)&resp_data); if (status < 0) { return status; } if (!resp.result) { NetworkLatency* ret; // Allocate memory for the returning NetworkLatency instance. // It includes: NetworkLatency structure + // size of zero-terminated "name" and "display" strings saved in // resp_data. *netdelay = malloc(sizeof(NetworkLatency) + 1 + resp.resp_data_size - sizeof(UICtlGetNetDelayResp)); ret = *netdelay; // Copy data obtained from the core to the returning NetworkLatency // instance. ret->min_ms = resp_data->min_ms; ret->max_ms = resp_data->max_ms; ret->name = (char*)ret + sizeof(NetworkLatency); strcpy((char*)ret->name, resp_data->name); ret->display = ret->name + strlen(ret->name) + 1; strcpy((char*)ret->display, resp_data->name + strlen(resp_data->name) + 1); } if (resp_data != NULL) { free(resp_data); } return resp.result; } int clientuictl_get_qemu_path(int type, const char* filename, char** path) { UICtlRespHeader resp; char* resp_data = NULL; int status; // Initialize and send the query. uint32_t req_data_size = sizeof(UICtlGetQemuPath) + strlen(filename) + 1; UICtlGetQemuPath* req = (UICtlGetQemuPath*)malloc(req_data_size); if (req == NULL) { PANIC("Unable to allocate query qemu path request\n"); } req->type = type; strcpy(req->filename, filename); status = _ui_core_ctl_send_request(AUI_UICTL_GET_QEMU_PATH, req, req_data_size); if (status < 0) { return status; } // Obtain the response from the core. status = _ui_core_ctl_get_response(&resp, (void**)&resp_data); if (status < 0) { return status; } if (!resp.result && resp_data != NULL) { *path = strdup(resp_data); } if (resp_data != NULL) { free(resp_data); } return resp.result; } int clientuictl_create(SockAddress* console_socket) { // Connect to Core->UI service if (_clientuictl_create_client(console_socket, "core-ui-control", &_core_ui_client.common)) { return -1; } _core_ui_client.reader_state = WAIT_HEADER; if (qemu_set_fd_handler(_core_ui_client.common.sock, _core_ui_client_read_cb, NULL, &_core_ui_client)) { derror("Unable to set up UI control read callback\n"); core_connection_close(_core_ui_client.common.core_connection); core_connection_free(_core_ui_client.common.core_connection); _core_ui_client.common.core_connection = NULL; return -1; } fprintf(stdout, "Core->UI client is now attached to the core %s\n", sock_address_to_string(console_socket)); // Connect to UI->Core service if (_clientuictl_create_client(console_socket, "ui-core-control", &_ui_core_client.common)) { _core_ui_client_destroy(); return -1; } _ui_core_client.sync_reader = syncsocket_init(_ui_core_client.common.sock); if (_ui_core_client.sync_reader == NULL) { derror("Unable to create reader for CoreUICtlClient instance: %s\n", errno_str); _core_ui_client_destroy(); return -1; } fprintf(stdout, "UI->Core client is now attached to the core %s\n", sock_address_to_string(console_socket)); return 0; }