blob: f07a1adf9f880fad1950655b7bf6969b545c1628 [file] [log] [blame]
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -08001/* 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
David 'Digit' Turnerb6c168b2011-02-02 21:39:10 +010019#include <unistd.h>
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -080020#include "android/looper.h"
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -080021#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"
David 'Digit' Turnere9931262011-02-02 14:05:23 +010026#include "android/protocol/core-connection.h"
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -080027#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. */
31typedef 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. */
40typedef 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
David 'Digit' Turnerb6c168b2011-02-02 21:39:10 +010047 /* Custom i/o handler */
48 LoopIo io[1];
49
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -080050 /* 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 */
69extern void android_emulator_set_window_scale(double scale, int is_dpi);
70
71/* One and only one UICmdImpl instance. */
72static UICmdImpl _uiCmdImpl;
73
74/* Display brightness change callback. */
75static AndroidHwLightBrightnessCallback _brightness_change_callback = NULL;
76static void* _brightness_change_callback_param = NULL;
77
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -080078/* 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 */
84static 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 */
118static void
David 'Digit' Turnerb6c168b2011-02-02 21:39:10 +0100119_uiCmdImpl_io_callback(void* opaque, int fd, unsigned events)
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800120{
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 = read(uicmd->sock, uicmd->reader_buffer + uicmd->reader_offset,
128 uicmd->reader_bytes - uicmd->reader_offset);
129 if (status == 0) {
David 'Digit' Turner07db3492011-02-02 17:36:34 +0100130 /* Disconnection, meaning that the core process got terminated. */
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800131 fprintf(stderr, "core-ui-control service got disconnected\n");
Vladimir Chtchetkine85276802011-01-31 15:18:45 -0800132 uiCmdImpl_destroy();
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800133 return;
134 }
135 if (status < 0) {
136 if (errno == EINTR) {
137 /* loop on EINTR */
138 continue;
139 } else if (errno == EWOULDBLOCK || errno == EAGAIN) {
140 // Chunk is not avalable at this point. Come back later.
141 return;
142 }
143 }
144
145 uicmd->reader_offset += status;
146 if (uicmd->reader_offset != uicmd->reader_bytes) {
147 // There are still some data left in the pipe.
148 continue;
149 }
150
151 // All expected data has been read. Time to change the state.
152 if (uicmd->reader_state == EXPECTS_HEADER) {
153 // Header has been read.
154 if (uicmd->cmd_header.cmd_param_size) {
155 // Prepare for the command parameters.
156 uicmd->reader_state = EXPECTS_PARAMETERS;
157 uicmd->reader_offset = 0;
158 uicmd->reader_bytes = uicmd->cmd_header.cmd_param_size;
159 uicmd->reader_buffer = malloc(uicmd->reader_bytes);
160 if (uicmd->reader_buffer == NULL) {
161 APANIC("Unable to allocate memory for UI command parameters.\n");
162 }
163 } else {
164 // This command doesn't have any parameters. Handle it now.
165 _uiCmdImpl_handle_command(uicmd, &uicmd->cmd_header, NULL);
166 // Prepare for the next command header.
167 uicmd->reader_state = EXPECTS_HEADER;
168 uicmd->reader_offset = 0;
169 uicmd->reader_bytes = sizeof(uicmd->cmd_header);
170 uicmd->reader_buffer = (uint8_t*)&uicmd->cmd_header;
171 }
172 } else {
173 // All command data is in. Handle it.
174 _uiCmdImpl_handle_command(uicmd, &uicmd->cmd_header,
175 uicmd->reader_buffer);
176 // Prepare for the next command header.
177 free(uicmd->reader_buffer);
178 uicmd->reader_state = EXPECTS_HEADER;
179 uicmd->reader_offset = 0;
180 uicmd->reader_bytes = sizeof(uicmd->cmd_header);
181 uicmd->reader_buffer = (uint8_t*)&uicmd->cmd_header;
182 }
183 }
184}
185
186int
David 'Digit' Turnerb6c168b2011-02-02 21:39:10 +0100187uiCmdImpl_create(SockAddress* console_socket, Looper* looper)
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800188{
189 char* handshake = NULL;
190
191 // Setup command reader.
192 _uiCmdImpl.reader_buffer = (uint8_t*)&_uiCmdImpl.cmd_header;
193 _uiCmdImpl.reader_state = EXPECTS_HEADER;
194 _uiCmdImpl.reader_offset = 0;
195 _uiCmdImpl.reader_bytes = sizeof(UICmdHeader);
196
197 // Connect to the core-ui-control service.
198 _uiCmdImpl.core_connection =
199 core_connection_create_and_switch(console_socket, "core-ui-control",
200 &handshake);
201 if (_uiCmdImpl.core_connection == NULL) {
202 derror("Unable to connect to the core-ui-control service: %s\n",
203 errno_str);
204 return -1;
205 }
206
David 'Digit' Turner07db3492011-02-02 17:36:34 +0100207 // Initialize UI command reader.
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800208 _uiCmdImpl.sock = core_connection_get_socket(_uiCmdImpl.core_connection);
David 'Digit' Turnerb6c168b2011-02-02 21:39:10 +0100209 loopIo_init(_uiCmdImpl.io, looper, _uiCmdImpl.sock,
210 _uiCmdImpl_io_callback,
211 &_uiCmdImpl);
212 loopIo_wantRead(_uiCmdImpl.io);
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800213
214 fprintf(stdout, "core-ui-control is now connected to the core at %s.",
215 sock_address_to_string(console_socket));
216 if (handshake != NULL) {
217 if (handshake[0] != '\0') {
218 fprintf(stdout, " Handshake: %s", handshake);
219 }
220 free(handshake);
221 }
222 fprintf(stdout, "\n");
223
224 return 0;
225}
226
Vladimir Chtchetkine85276802011-01-31 15:18:45 -0800227void
228uiCmdImpl_destroy(void)
229{
230 if (_uiCmdImpl.core_connection != NULL) {
231 // Disable I/O callbacks.
David 'Digit' Turnerb6c168b2011-02-02 21:39:10 +0100232 loopIo_done(_uiCmdImpl.io);
Vladimir Chtchetkine85276802011-01-31 15:18:45 -0800233 core_connection_close(_uiCmdImpl.core_connection);
234 core_connection_free(_uiCmdImpl.core_connection);
235 _uiCmdImpl.core_connection = NULL;
236 }
237 // Properly deallocate the reader buffer.
238 if (_uiCmdImpl.reader_buffer != NULL &&
239 _uiCmdImpl.reader_buffer != (uint8_t*)&_uiCmdImpl.cmd_header) {
240 free(_uiCmdImpl.reader_buffer);
241 _uiCmdImpl.reader_buffer = (uint8_t*)&_uiCmdImpl.cmd_header;
242 }
243}
244
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800245int
246uicmd_set_brightness_change_callback(AndroidHwLightBrightnessCallback callback,
247 void* opaque)
248{
249 _brightness_change_callback = callback;
250 _brightness_change_callback_param = opaque;
251 return 0;
252}