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 UI-side implementation of the "core-ui-control" service that is 15 * part of the UI control protocol. Here we handle UI control commands received 16 * from the Core. 17 */ 18 19 #include <unistd.h> 20 #include "android/looper.h" 21 #include "android/async-utils.h" 22 #include "android/sync-utils.h" 23 #include "android/utils/system.h" 24 #include "android/utils/debug.h" 25 #include "android/utils/panic.h" 26 #include "android/protocol/core-connection.h" 27 #include "android/protocol/ui-commands-impl.h" 28 #include "android/protocol/ui-commands-api.h" 29 30 /* Enumerates states for the command reader in UICmdImpl instance. */ 31 typedef enum UICmdImplState { 32 /* The reader is waiting on command header. */ 33 EXPECTS_HEADER, 34 35 /* The reader is waiting on command parameters. */ 36 EXPECTS_PARAMETERS, 37 } UICmdImplState; 38 39 /* Descriptor for the UI-side of the "core-ui-control" service. */ 40 typedef struct UICmdImpl { 41 /* Core connection established for this service. */ 42 CoreConnection* core_connection; 43 44 /* Socket descriptor for the UI service. */ 45 int sock; 46 47 /* Custom i/o handler */ 48 LoopIo io[1]; 49 50 /* Command reader state. */ 51 UICmdImplState reader_state; 52 53 /* Incoming command header. */ 54 UICmdHeader cmd_header; 55 56 /* Reader's buffer. This field can point to the cmd_header field of this 57 * structure (when we expect a command header), or to a buffer allocated for 58 * the (when we expect command parameters). */ 59 uint8_t* reader_buffer; 60 61 /* Offset in the reader's buffer where to read next chunk of data. */ 62 size_t reader_offset; 63 64 /* Total number of bytes the reader expects to read. */ 65 size_t reader_bytes; 66 } UICmdImpl; 67 68 /* Implemented in android/qemulator.c */ 69 extern void android_emulator_set_window_scale(double scale, int is_dpi); 70 71 /* One and only one UICmdImpl instance. */ 72 static UICmdImpl _uiCmdImpl; 73 74 /* Display brightness change callback. */ 75 static AndroidHwLightBrightnessCallback _brightness_change_callback = NULL; 76 static void* _brightness_change_callback_param = NULL; 77 78 /* Handles UI control command received from the core. 79 * Param: 80 * uicmd - UICmdImpl instance that received the command. 81 * header - UI control command header. 82 * data - Command parameters formatted accordingly to the command type. 83 */ 84 static void 85 _uiCmdImpl_handle_command(UICmdImpl* uicmd, 86 const UICmdHeader* header, 87 const uint8_t* data) 88 { 89 switch (header->cmd_type) { 90 case AUICMD_SET_WINDOWS_SCALE: 91 { 92 UICmdSetWindowsScale* cmd = (UICmdSetWindowsScale*)data; 93 android_emulator_set_window_scale(cmd->scale, cmd->is_dpi); 94 break; 95 } 96 97 case AUICMD_CHANGE_DISP_BRIGHTNESS: 98 { 99 UICmdChangeDispBrightness* cmd = (UICmdChangeDispBrightness*)data; 100 if (_brightness_change_callback != NULL) { 101 _brightness_change_callback(_brightness_change_callback_param, 102 cmd->light, cmd->brightness); 103 } 104 break; 105 } 106 107 default: 108 derror("Unknown command %d is received from the Core\n", 109 header->cmd_type); 110 break; 111 } 112 } 113 114 /* Asynchronous I/O callback reading UI control commands. 115 * Param: 116 * opaque - UICmdImpl instance. 117 */ 118 static void 119 _uiCmdImpl_io_callback(void* opaque, int fd, unsigned events) 120 { 121 UICmdImpl* uicmd = opaque; 122 int status; 123 124 // Read requests while they are immediately available. 125 for (;;) { 126 // Read next chunk of data. 127 status = socket_recv(uicmd->sock, 128 uicmd->reader_buffer + uicmd->reader_offset, 129 uicmd->reader_bytes - uicmd->reader_offset); 130 if (status == 0) { 131 /* Disconnection, meaning that the core process got terminated. */ 132 fprintf(stderr, "core-ui-control service got disconnected\n"); 133 uiCmdImpl_destroy(); 134 return; 135 } 136 if (status < 0) { 137 if (errno == EINTR) { 138 /* loop on EINTR */ 139 continue; 140 } else if (errno == EWOULDBLOCK || errno == EAGAIN) { 141 // Chunk is not avalable at this point. Come back later. 142 return; 143 } 144 } 145 146 uicmd->reader_offset += status; 147 if (uicmd->reader_offset != uicmd->reader_bytes) { 148 // There are still some data left in the pipe. 149 continue; 150 } 151 152 // All expected data has been read. Time to change the state. 153 if (uicmd->reader_state == EXPECTS_HEADER) { 154 // Header has been read. 155 if (uicmd->cmd_header.cmd_param_size) { 156 // Prepare for the command parameters. 157 uicmd->reader_state = EXPECTS_PARAMETERS; 158 uicmd->reader_offset = 0; 159 uicmd->reader_bytes = uicmd->cmd_header.cmd_param_size; 160 uicmd->reader_buffer = malloc(uicmd->reader_bytes); 161 if (uicmd->reader_buffer == NULL) { 162 APANIC("Unable to allocate memory for UI command parameters.\n"); 163 } 164 } else { 165 // This command doesn't have any parameters. Handle it now. 166 _uiCmdImpl_handle_command(uicmd, &uicmd->cmd_header, NULL); 167 // Prepare for the next command header. 168 uicmd->reader_state = EXPECTS_HEADER; 169 uicmd->reader_offset = 0; 170 uicmd->reader_bytes = sizeof(uicmd->cmd_header); 171 uicmd->reader_buffer = (uint8_t*)&uicmd->cmd_header; 172 } 173 } else { 174 // All command data is in. Handle it. 175 _uiCmdImpl_handle_command(uicmd, &uicmd->cmd_header, 176 uicmd->reader_buffer); 177 // Prepare for the next command header. 178 free(uicmd->reader_buffer); 179 uicmd->reader_state = EXPECTS_HEADER; 180 uicmd->reader_offset = 0; 181 uicmd->reader_bytes = sizeof(uicmd->cmd_header); 182 uicmd->reader_buffer = (uint8_t*)&uicmd->cmd_header; 183 } 184 } 185 } 186 187 int 188 uiCmdImpl_create(SockAddress* console_socket, Looper* looper) 189 { 190 UICmdImpl* uicmd = &_uiCmdImpl; 191 char* handshake = NULL; 192 193 // Setup command reader. 194 uicmd->reader_buffer = (uint8_t*)&uicmd->cmd_header; 195 uicmd->reader_state = EXPECTS_HEADER; 196 uicmd->reader_offset = 0; 197 uicmd->reader_bytes = sizeof(UICmdHeader); 198 199 // Connect to the core-ui-control service. 200 uicmd->core_connection = 201 core_connection_create_and_switch(console_socket, "core-ui-control", 202 &handshake); 203 if (uicmd->core_connection == NULL) { 204 derror("Unable to connect to the core-ui-control service: %s\n", 205 errno_str); 206 return -1; 207 } 208 209 // Initialize UI command reader. 210 uicmd->sock = core_connection_get_socket(uicmd->core_connection); 211 loopIo_init(uicmd->io, looper, uicmd->sock, 212 _uiCmdImpl_io_callback, 213 &_uiCmdImpl); 214 loopIo_wantRead(uicmd->io); 215 216 fprintf(stdout, "core-ui-control is now connected to the core at %s.", 217 sock_address_to_string(console_socket)); 218 if (handshake != NULL) { 219 if (handshake[0] != '\0') { 220 fprintf(stdout, " Handshake: %s", handshake); 221 } 222 free(handshake); 223 } 224 fprintf(stdout, "\n"); 225 226 return 0; 227 } 228 229 void 230 uiCmdImpl_destroy(void) 231 { 232 UICmdImpl* uicmd = &_uiCmdImpl; 233 234 if (uicmd->core_connection != NULL) { 235 // Disable I/O callbacks. 236 loopIo_done(uicmd->io); 237 core_connection_close(uicmd->core_connection); 238 core_connection_free(uicmd->core_connection); 239 uicmd->core_connection = NULL; 240 } 241 // Properly deallocate the reader buffer. 242 if (uicmd->reader_buffer != NULL && 243 uicmd->reader_buffer != (uint8_t*)&uicmd->cmd_header) { 244 free(uicmd->reader_buffer); 245 uicmd->reader_buffer = (uint8_t*)&uicmd->cmd_header; 246 } 247 } 248 249 int 250 uicmd_set_brightness_change_callback(AndroidHwLightBrightnessCallback callback, 251 void* opaque) 252 { 253 _brightness_change_callback = callback; 254 _brightness_change_callback_param = opaque; 255 return 0; 256 } 257