diff options
Diffstat (limited to 'android/protocol/ui-commands-proxy.c')
-rw-r--r-- | android/protocol/ui-commands-proxy.c | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/android/protocol/ui-commands-proxy.c b/android/protocol/ui-commands-proxy.c new file mode 100644 index 0000000..76bf883 --- /dev/null +++ b/android/protocol/ui-commands-proxy.c @@ -0,0 +1,209 @@ +/* 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 "core-ui-control" service that is + * part of the UI control protocol. Here we send UI control commands to the UI. + */ + +#include "android/android.h" +#include "android/hw-control.h" +#include "android/looper.h" +#include "android/async-utils.h" +#include "android/sync-utils.h" +#include "android/utils/debug.h" +#include "android/protocol/ui-commands.h" +#include "android/protocol/ui-commands-proxy.h" +#include "android/protocol/ui-commands-api.h" + +/* Descriptor for the UI commands proxy. */ +typedef struct UICmdProxy { + /* I/O associated with this descriptor. */ + LoopIo io; + + /* Looper associated with this descriptor. */ + Looper* looper; + + /* Writer to send UI commands. */ + SyncSocket* sync_writer; + + /* Socket descriptor for this service. */ + int sock; +} UICmdProxy; + +/* One and only one UICmdProxy instance. */ +static UICmdProxy _uiCmdProxy; + +/* Implemented in android/console.c */ +extern void destroy_uicmd_client(void); + +/* 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. + */ +static int +_uiCmdProxy_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 request to the UI client of this service. + * Param: + * cmd_type, cmd_param, cmd_param_size - Define the command to send. + * Return: + * 0 on success, or < 0 on failure. + */ +static int +_uiCmdProxy_send_command(uint8_t cmd_type, + void* cmd_param, + uint32_t cmd_param_size) +{ + UICmdHeader header; + int status = syncsocket_start_write(_uiCmdProxy.sync_writer); + if (!status) { + // Initialize and send the header. + header.cmd_type = cmd_type; + header.cmd_param_size = cmd_param_size; + status = syncsocket_write(_uiCmdProxy.sync_writer, &header, sizeof(header), + _uiCmdProxy_get_timeout(sizeof(header))); + // If there are command parameters, send them too. + if (status > 0 && cmd_param != NULL && cmd_param_size > 0) { + status = syncsocket_write(_uiCmdProxy.sync_writer, cmd_param, + cmd_param_size, + _uiCmdProxy_get_timeout(cmd_param_size)); + } + status = syncsocket_result(status); + syncsocket_stop_write(_uiCmdProxy.sync_writer); + } + if (status < 0) { + derror("Send UI command %d (%u bytes) has failed: %s\n", + cmd_type, cmd_param_size, errno_str); + } + return status; +} + +/* Asynchronous I/O callback for UICmdProxy instance. + * We expect this callback to be called only on UI detachment condition. In this + * case the event should be LOOP_IO_READ, and read should fail with errno set + * to ECONNRESET. + * Param: + * opaque - UICmdProxy instance. + */ +static void +_uiCmdProxy_io_func(void* opaque, int fd, unsigned events) +{ + UICmdProxy* uicmd = (UICmdProxy*)opaque; + AsyncReader reader; + AsyncStatus status; + uint8_t read_buf[1]; + + if (events & LOOP_IO_WRITE) { + derror("Unexpected LOOP_IO_WRITE in _uiCmdProxy_io_func.\n"); + return; + } + + // Try to read + asyncReader_init(&reader, read_buf, sizeof(read_buf), &uicmd->io); + status = asyncReader_read(&reader, &uicmd->io); + // We expect only error status here. + if (status != ASYNC_ERROR) { + derror("Unexpected read status %d in _uiCmdProxy_io_func\n", status); + return; + } + // We expect only socket disconnection error here. + if (errno != ECONNRESET) { + derror("Unexpected read error %d (%s) in _uiCmdProxy_io_func.\n", + errno, errno_str); + return; + } + + // Client got disconnectted. + destroy_uicmd_client(); +} +/* a callback function called when the system wants to change the brightness + * of a given light. 'light' is a string which can be one of: + * 'lcd_backlight', 'button_backlight' or 'Keyboard_backlight' + * + * brightness is an integer (acceptable range are 0..255), however the + * default is around 105, and we probably don't want to dim the emulator's + * output at that level. + */ +static void +_uiCmdProxy_brightness_change_callback(void* opaque, + const char* light, + int brightness) +{ + // Calculate size of the command parameters. + const size_t cmd_size = sizeof(UICmdChangeDispBrightness) + strlen(light) + 1; + // Allocate and initialize parameters. + UICmdChangeDispBrightness* cmd = + (UICmdChangeDispBrightness*)qemu_malloc(cmd_size); + cmd->brightness = brightness; + strcpy(cmd->light, light); + // Send the command. + _uiCmdProxy_send_command(AUICMD_CHANGE_DISP_BRIGHTNESS, cmd, cmd_size); + qemu_free(cmd); +} + +int +uiCmdProxy_create(int fd) +{ + // Initialize the only UICmdProxy instance. + _uiCmdProxy.sock = fd; + _uiCmdProxy.looper = looper_newCore(); + loopIo_init(&_uiCmdProxy.io, _uiCmdProxy.looper, _uiCmdProxy.sock, + _uiCmdProxy_io_func, &_uiCmdProxy); + loopIo_wantRead(&_uiCmdProxy.io); + _uiCmdProxy.sync_writer = syncsocket_init(fd); + if (_uiCmdProxy.sync_writer == NULL) { + derror("Unable to initialize UICmdProxy writer: %s\n", errno_str); + uiCmdProxy_destroy(); + return -1; + } + { + // Set brighness change callback, so we can notify + // the UI about the event. + AndroidHwControlFuncs funcs; + funcs.light_brightness = _uiCmdProxy_brightness_change_callback; + android_hw_control_init(&_uiCmdProxy, &funcs); + } + return 0; +} + +void +uiCmdProxy_destroy() +{ + // Destroy the sync writer. + if (_uiCmdProxy.sync_writer != NULL) { + syncsocket_close(_uiCmdProxy.sync_writer); + syncsocket_free(_uiCmdProxy.sync_writer); + } + if (_uiCmdProxy.looper != NULL) { + // Stop all I/O that may still be going on. + loopIo_done(&_uiCmdProxy.io); + looper_free(_uiCmdProxy.looper); + _uiCmdProxy.looper = NULL; + } + _uiCmdProxy.sock = -1; +} + +int +uicmd_set_window_scale(double scale, int is_dpi) +{ + UICmdSetWindowsScale cmd; + cmd.scale = scale; + cmd.is_dpi = is_dpi; + return _uiCmdProxy_send_command(AUICMD_SET_WINDOWS_SCALE, &cmd, sizeof(cmd)); +} |