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 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