| /* 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 "android-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); |
| 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); |
| } |