Vladimir Chtchetkine | 777eb68 | 2011-01-26 11:19:19 -0800 | [diff] [blame] | 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); |
David 'Digit' Turner | f9e333a | 2011-03-17 14:57:51 +0100 | [diff] [blame] | 119 | status = asyncReader_read(&reader); |
Vladimir Chtchetkine | 777eb68 | 2011-01-26 11:19:19 -0800 | [diff] [blame] | 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; |
David 'Digit' Turner | ca95059 | 2011-04-27 12:26:15 +0200 | [diff] [blame^] | 180 | android_hw_control_set(&_uiCmdProxy, &funcs); |
Vladimir Chtchetkine | 777eb68 | 2011-01-26 11:19:19 -0800 | [diff] [blame] | 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 | } |