/* 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/async-utils.h" #include "android/sync-utils.h" #include "android/utils/debug.h" #include "android/utils/panic.h" #include "android/protocol/core-connection.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; } 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; } /* Destroys CoreCmdProxy instance. */ void coreCmdProxy_destroy(void) { if (_coreCmdProxy.sync_writer != NULL) { syncsocket_close(_coreCmdProxy.sync_writer); syncsocket_free(_coreCmdProxy.sync_writer); _coreCmdProxy.sync_writer = NULL; } if (_coreCmdProxy.sync_reader != NULL) { syncsocket_close(_coreCmdProxy.sync_reader); syncsocket_free(_coreCmdProxy.sync_reader); _coreCmdProxy.sync_reader = NULL; } if (_coreCmdProxy.core_connection != NULL) { core_connection_close(_coreCmdProxy.core_connection); core_connection_free(_coreCmdProxy.core_connection); _coreCmdProxy.core_connection = NULL; } }