1 /* Copyright (C) 2010 The Android Open Source Project 2 ** 3 ** This software is licensed under the terms of the GNU General Public 4 ** License version 2, as published by the Free Software Foundation, and 5 ** may be copied, distributed, and modified under those terms. 6 ** 7 ** This program is distributed in the hope that it will be useful, 8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of 9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 ** GNU General Public License for more details. 11 */ 12 13 /* 14 * Contains the Core-side implementation of the "core-ui-control" service that is 15 * part of the UI control protocol. Here we send UI control commands to the UI. 16 */ 17 18 #include "android/android.h" 19 #include "android/hw-control.h" 20 #include "android/looper.h" 21 #include "android/async-utils.h" 22 #include "android/sync-utils.h" 23 #include "android/utils/debug.h" 24 #include "android/protocol/ui-commands.h" 25 #include "android/protocol/ui-commands-proxy.h" 26 #include "android/protocol/ui-commands-api.h" 27 28 /* Descriptor for the UI commands proxy. */ 29 typedef struct UICmdProxy { 30 /* I/O associated with this descriptor. */ 31 LoopIo io; 32 33 /* Looper associated with this descriptor. */ 34 Looper* looper; 35 36 /* Writer to send UI commands. */ 37 SyncSocket* sync_writer; 38 39 /* Socket descriptor for this service. */ 40 int sock; 41 } UICmdProxy; 42 43 /* One and only one UICmdProxy instance. */ 44 static UICmdProxy _uiCmdProxy; 45 46 /* Implemented in android/console.c */ 47 extern void destroy_uicmd_client(void); 48 49 /* Calculates timeout for transferring the given number of bytes via socket. 50 * Return: 51 * Number of milliseconds during which the entire number of bytes is expected 52 * to be transferred via socket. 53 */ 54 static int 55 _uiCmdProxy_get_timeout(size_t data_size) 56 { 57 // Min 2 seconds + 10 millisec for each transferring byte. 58 // TODO: Come up with a better arithmetics here. 59 return 2000 + data_size * 10; 60 } 61 62 /* Sends request to the UI client of this service. 63 * Param: 64 * cmd_type, cmd_param, cmd_param_size - Define the command to send. 65 * Return: 66 * 0 on success, or < 0 on failure. 67 */ 68 static int 69 _uiCmdProxy_send_command(uint8_t cmd_type, 70 void* cmd_param, 71 uint32_t cmd_param_size) 72 { 73 UICmdHeader header; 74 int status = syncsocket_start_write(_uiCmdProxy.sync_writer); 75 if (!status) { 76 // Initialize and send the header. 77 header.cmd_type = cmd_type; 78 header.cmd_param_size = cmd_param_size; 79 status = syncsocket_write(_uiCmdProxy.sync_writer, &header, sizeof(header), 80 _uiCmdProxy_get_timeout(sizeof(header))); 81 // If there are command parameters, send them too. 82 if (status > 0 && cmd_param != NULL && cmd_param_size > 0) { 83 status = syncsocket_write(_uiCmdProxy.sync_writer, cmd_param, 84 cmd_param_size, 85 _uiCmdProxy_get_timeout(cmd_param_size)); 86 } 87 status = syncsocket_result(status); 88 syncsocket_stop_write(_uiCmdProxy.sync_writer); 89 } 90 if (status < 0) { 91 derror("Send UI command %d (%u bytes) has failed: %s\n", 92 cmd_type, cmd_param_size, errno_str); 93 } 94 return status; 95 } 96 97 /* Asynchronous I/O callback for UICmdProxy instance. 98 * We expect this callback to be called only on UI detachment condition. In this 99 * case the event should be LOOP_IO_READ, and read should fail with errno set 100 * to ECONNRESET. 101 * Param: 102 * opaque - UICmdProxy instance. 103 */ 104 static void 105 _uiCmdProxy_io_func(void* opaque, int fd, unsigned events) 106 { 107 UICmdProxy* uicmd = (UICmdProxy*)opaque; 108 AsyncReader reader; 109 AsyncStatus status; 110 uint8_t read_buf[1]; 111 112 if (events & LOOP_IO_WRITE) { 113 derror("Unexpected LOOP_IO_WRITE in _uiCmdProxy_io_func.\n"); 114 return; 115 } 116 117 // Try to read 118 asyncReader_init(&reader, read_buf, sizeof(read_buf), &uicmd->io); 119 status = asyncReader_read(&reader); 120 // We expect only error status here. 121 if (status != ASYNC_ERROR) { 122 derror("Unexpected read status %d in _uiCmdProxy_io_func\n", status); 123 return; 124 } 125 // We expect only socket disconnection error here. 126 if (errno != ECONNRESET) { 127 derror("Unexpected read error %d (%s) in _uiCmdProxy_io_func.\n", 128 errno, errno_str); 129 return; 130 } 131 132 // Client got disconnectted. 133 destroy_uicmd_client(); 134 } 135 /* a callback function called when the system wants to change the brightness 136 * of a given light. 'light' is a string which can be one of: 137 * 'lcd_backlight', 'button_backlight' or 'Keyboard_backlight' 138 * 139 * brightness is an integer (acceptable range are 0..255), however the 140 * default is around 105, and we probably don't want to dim the emulator's 141 * output at that level. 142 */ 143 static void 144 _uiCmdProxy_brightness_change_callback(void* opaque, 145 const char* light, 146 int brightness) 147 { 148 // Calculate size of the command parameters. 149 const size_t cmd_size = sizeof(UICmdChangeDispBrightness) + strlen(light) + 1; 150 // Allocate and initialize parameters. 151 UICmdChangeDispBrightness* cmd = 152 (UICmdChangeDispBrightness*)qemu_malloc(cmd_size); 153 cmd->brightness = brightness; 154 strcpy(cmd->light, light); 155 // Send the command. 156 _uiCmdProxy_send_command(AUICMD_CHANGE_DISP_BRIGHTNESS, cmd, cmd_size); 157 qemu_free(cmd); 158 } 159 160 int 161 uiCmdProxy_create(int fd) 162 { 163 // Initialize the only UICmdProxy instance. 164 _uiCmdProxy.sock = fd; 165 _uiCmdProxy.looper = looper_newCore(); 166 loopIo_init(&_uiCmdProxy.io, _uiCmdProxy.looper, _uiCmdProxy.sock, 167 _uiCmdProxy_io_func, &_uiCmdProxy); 168 loopIo_wantRead(&_uiCmdProxy.io); 169 _uiCmdProxy.sync_writer = syncsocket_init(fd); 170 if (_uiCmdProxy.sync_writer == NULL) { 171 derror("Unable to initialize UICmdProxy writer: %s\n", errno_str); 172 uiCmdProxy_destroy(); 173 return -1; 174 } 175 { 176 // Set brighness change callback, so we can notify 177 // the UI about the event. 178 AndroidHwControlFuncs funcs; 179 funcs.light_brightness = _uiCmdProxy_brightness_change_callback; 180 android_hw_control_set(&_uiCmdProxy, &funcs); 181 } 182 return 0; 183 } 184 185 void 186 uiCmdProxy_destroy() 187 { 188 // Destroy the sync writer. 189 if (_uiCmdProxy.sync_writer != NULL) { 190 syncsocket_close(_uiCmdProxy.sync_writer); 191 syncsocket_free(_uiCmdProxy.sync_writer); 192 } 193 if (_uiCmdProxy.looper != NULL) { 194 // Stop all I/O that may still be going on. 195 loopIo_done(&_uiCmdProxy.io); 196 looper_free(_uiCmdProxy.looper); 197 _uiCmdProxy.looper = NULL; 198 } 199 _uiCmdProxy.sock = -1; 200 } 201 202 int 203 uicmd_set_window_scale(double scale, int is_dpi) 204 { 205 UICmdSetWindowsScale cmd; 206 cmd.scale = scale; 207 cmd.is_dpi = is_dpi; 208 return _uiCmdProxy_send_command(AUICMD_SET_WINDOWS_SCALE, &cmd, sizeof(cmd)); 209 } 210