blob: 2ceeca10a0a83e73ddbc3507ab281f6c22a9039c [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.
Vladimir Chtchetkine17ecca62011-02-07 14:14:14 -0800127 status = socket_recv(uicmd->sock,
128 uicmd->reader_buffer + uicmd->reader_offset,
129 uicmd->reader_bytes - uicmd->reader_offset);
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800130 if (status == 0) {
David 'Digit' Turner07db3492011-02-02 17:36:34 +0100131 /* Disconnection, meaning that the core process got terminated. */
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800132 fprintf(stderr, "core-ui-control service got disconnected\n");
Vladimir Chtchetkine85276802011-01-31 15:18:45 -0800133 uiCmdImpl_destroy();
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800134 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
187int
David 'Digit' Turnerb6c168b2011-02-02 21:39:10 +0100188uiCmdImpl_create(SockAddress* console_socket, Looper* looper)
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800189{
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100190 UICmdImpl* uicmd = &_uiCmdImpl;
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800191 char* handshake = NULL;
192
193 // Setup command reader.
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100194 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);
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800198
199 // Connect to the core-ui-control service.
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100200 uicmd->core_connection =
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800201 core_connection_create_and_switch(console_socket, "core-ui-control",
202 &handshake);
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100203 if (uicmd->core_connection == NULL) {
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800204 derror("Unable to connect to the core-ui-control service: %s\n",
205 errno_str);
206 return -1;
207 }
208
David 'Digit' Turner07db3492011-02-02 17:36:34 +0100209 // Initialize UI command reader.
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100210 uicmd->sock = core_connection_get_socket(uicmd->core_connection);
211 loopIo_init(uicmd->io, looper, uicmd->sock,
David 'Digit' Turnerb6c168b2011-02-02 21:39:10 +0100212 _uiCmdImpl_io_callback,
213 &_uiCmdImpl);
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100214 loopIo_wantRead(uicmd->io);
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800215
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
Vladimir Chtchetkine85276802011-01-31 15:18:45 -0800229void
230uiCmdImpl_destroy(void)
231{
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100232 UICmdImpl* uicmd = &_uiCmdImpl;
233
234 if (uicmd->core_connection != NULL) {
Vladimir Chtchetkine85276802011-01-31 15:18:45 -0800235 // Disable I/O callbacks.
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100236 loopIo_done(uicmd->io);
237 core_connection_close(uicmd->core_connection);
238 core_connection_free(uicmd->core_connection);
239 uicmd->core_connection = NULL;
Vladimir Chtchetkine85276802011-01-31 15:18:45 -0800240 }
241 // Properly deallocate the reader buffer.
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100242 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;
Vladimir Chtchetkine85276802011-01-31 15:18:45 -0800246 }
247}
248
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800249int
250uicmd_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}