blob: e933d574cc7eb8d3e123b97509f7a3745daed213 [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{
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100189 UICmdImpl* uicmd = &_uiCmdImpl;
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800190 char* handshake = NULL;
191
192 // Setup command reader.
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100193 uicmd->reader_buffer = (uint8_t*)&uicmd->cmd_header;
194 uicmd->reader_state = EXPECTS_HEADER;
195 uicmd->reader_offset = 0;
196 uicmd->reader_bytes = sizeof(UICmdHeader);
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800197
198 // Connect to the core-ui-control service.
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100199 uicmd->core_connection =
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800200 core_connection_create_and_switch(console_socket, "core-ui-control",
201 &handshake);
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100202 if (uicmd->core_connection == NULL) {
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800203 derror("Unable to connect to the core-ui-control service: %s\n",
204 errno_str);
205 return -1;
206 }
207
David 'Digit' Turner07db3492011-02-02 17:36:34 +0100208 // Initialize UI command reader.
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100209 uicmd->sock = core_connection_get_socket(uicmd->core_connection);
210 loopIo_init(uicmd->io, looper, uicmd->sock,
David 'Digit' Turnerb6c168b2011-02-02 21:39:10 +0100211 _uiCmdImpl_io_callback,
212 &_uiCmdImpl);
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100213 loopIo_wantRead(uicmd->io);
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800214
215 fprintf(stdout, "core-ui-control is now connected to the core at %s.",
216 sock_address_to_string(console_socket));
217 if (handshake != NULL) {
218 if (handshake[0] != '\0') {
219 fprintf(stdout, " Handshake: %s", handshake);
220 }
221 free(handshake);
222 }
223 fprintf(stdout, "\n");
224
225 return 0;
226}
227
Vladimir Chtchetkine85276802011-01-31 15:18:45 -0800228void
229uiCmdImpl_destroy(void)
230{
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100231 UICmdImpl* uicmd = &_uiCmdImpl;
232
233 if (uicmd->core_connection != NULL) {
Vladimir Chtchetkine85276802011-01-31 15:18:45 -0800234 // Disable I/O callbacks.
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100235 loopIo_done(uicmd->io);
236 core_connection_close(uicmd->core_connection);
237 core_connection_free(uicmd->core_connection);
238 uicmd->core_connection = NULL;
Vladimir Chtchetkine85276802011-01-31 15:18:45 -0800239 }
240 // Properly deallocate the reader buffer.
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100241 if (uicmd->reader_buffer != NULL &&
242 uicmd->reader_buffer != (uint8_t*)&uicmd->cmd_header) {
243 free(uicmd->reader_buffer);
244 uicmd->reader_buffer = (uint8_t*)&uicmd->cmd_header;
Vladimir Chtchetkine85276802011-01-31 15:18:45 -0800245 }
246}
247
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800248int
249uicmd_set_brightness_change_callback(AndroidHwLightBrightnessCallback callback,
250 void* opaque)
251{
252 _brightness_change_callback = callback;
253 _brightness_change_callback_param = opaque;
254 return 0;
255}