• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
_uiCmdProxy_get_timeout(size_t data_size)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
_uiCmdProxy_send_command(uint8_t cmd_type,void * cmd_param,uint32_t cmd_param_size)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
_uiCmdProxy_io_func(void * opaque,int fd,unsigned events)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
_uiCmdProxy_brightness_change_callback(void * opaque,const char * light,int brightness)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
uiCmdProxy_create(int fd)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
uiCmdProxy_destroy()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
uicmd_set_window_scale(double scale,int is_dpi)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