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
_uiCmdImpl_handle_command(UICmdImpl * uicmd,const UICmdHeader * header,const uint8_t * data)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
_uiCmdImpl_io_callback(void * opaque,int fd,unsigned events)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
uiCmdImpl_create(SockAddress * console_socket,Looper * looper)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
uiCmdImpl_destroy(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
uicmd_set_brightness_change_callback(AndroidHwLightBrightnessCallback callback,void * opaque)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