blob: 99281245b4bd02dd7c54706205fd397a780fecf6 [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 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;
}