Home | History | Annotate | Download | only in protocol
      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