/* 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); // 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_set(&_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)); }