blob: 0a507941eb0787599c12e24988cc431ce7782b71 [file] [log] [blame]
/* 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;
}
}