blob: 307e7c729d4b742fc4c9cfbeeeb51f2fe16efdf8 [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"
David 'Digit' Turneraf81d742014-02-03 17:11:18 +010025#include "android/utils/eintr_wrapper.h"
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -080026#include "android/utils/panic.h"
David 'Digit' Turnere9931262011-02-02 14:05:23 +010027#include "android/protocol/core-connection.h"
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -080028#include "android/protocol/ui-commands-impl.h"
29#include "android/protocol/ui-commands-api.h"
30
31/* Enumerates states for the command reader in UICmdImpl instance. */
32typedef enum UICmdImplState {
33 /* The reader is waiting on command header. */
34 EXPECTS_HEADER,
35
36 /* The reader is waiting on command parameters. */
37 EXPECTS_PARAMETERS,
38} UICmdImplState;
39
40/* Descriptor for the UI-side of the "core-ui-control" service. */
41typedef struct UICmdImpl {
42 /* Core connection established for this service. */
43 CoreConnection* core_connection;
44
45 /* Socket descriptor for the UI service. */
46 int sock;
47
David 'Digit' Turnerb6c168b2011-02-02 21:39:10 +010048 /* Custom i/o handler */
49 LoopIo io[1];
50
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -080051 /* Command reader state. */
52 UICmdImplState reader_state;
53
54 /* Incoming command header. */
55 UICmdHeader cmd_header;
56
57 /* Reader's buffer. This field can point to the cmd_header field of this
58 * structure (when we expect a command header), or to a buffer allocated for
59 * the (when we expect command parameters). */
60 uint8_t* reader_buffer;
61
62 /* Offset in the reader's buffer where to read next chunk of data. */
63 size_t reader_offset;
64
65 /* Total number of bytes the reader expects to read. */
66 size_t reader_bytes;
67} UICmdImpl;
68
69/* Implemented in android/qemulator.c */
70extern void android_emulator_set_window_scale(double scale, int is_dpi);
71
72/* One and only one UICmdImpl instance. */
73static UICmdImpl _uiCmdImpl;
74
75/* Display brightness change callback. */
76static AndroidHwLightBrightnessCallback _brightness_change_callback = NULL;
77static void* _brightness_change_callback_param = NULL;
78
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -080079/* Handles UI control command received from the core.
80 * Param:
81 * uicmd - UICmdImpl instance that received the command.
82 * header - UI control command header.
83 * data - Command parameters formatted accordingly to the command type.
84 */
85static void
86_uiCmdImpl_handle_command(UICmdImpl* uicmd,
87 const UICmdHeader* header,
88 const uint8_t* data)
89{
90 switch (header->cmd_type) {
91 case AUICMD_SET_WINDOWS_SCALE:
92 {
93 UICmdSetWindowsScale* cmd = (UICmdSetWindowsScale*)data;
94 android_emulator_set_window_scale(cmd->scale, cmd->is_dpi);
95 break;
96 }
97
98 case AUICMD_CHANGE_DISP_BRIGHTNESS:
99 {
100 UICmdChangeDispBrightness* cmd = (UICmdChangeDispBrightness*)data;
101 if (_brightness_change_callback != NULL) {
102 _brightness_change_callback(_brightness_change_callback_param,
103 cmd->light, cmd->brightness);
104 }
105 break;
106 }
107
108 default:
109 derror("Unknown command %d is received from the Core\n",
110 header->cmd_type);
111 break;
112 }
113}
114
115/* Asynchronous I/O callback reading UI control commands.
116 * Param:
117 * opaque - UICmdImpl instance.
118 */
119static void
David 'Digit' Turnerb6c168b2011-02-02 21:39:10 +0100120_uiCmdImpl_io_callback(void* opaque, int fd, unsigned events)
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800121{
122 UICmdImpl* uicmd = opaque;
123 int status;
124
125 // Read requests while they are immediately available.
126 for (;;) {
127 // Read next chunk of data.
David 'Digit' Turneraf81d742014-02-03 17:11:18 +0100128 status = HANDLE_EINTR(
129 socket_recv(uicmd->sock,
130 uicmd->reader_buffer + uicmd->reader_offset,
131 uicmd->reader_bytes - uicmd->reader_offset));
132 if (status < 0 && (errno == EWOULDBLOCK || errno == EGAIN) {
133 // Chunk is not avalable at this point. Come back later.
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800134 return;
135 }
David 'Digit' Turneraf81d742014-02-03 17:11:18 +0100136 if (status <= 0) {
137 /* Disconnection, meaning that the core process got terminated. */
138 fprintf(stderr,
139 "core-ui-control service got disconnected: %s\n",
140 status < 0 ?
141 strerror(errno) :
142 "unexpected disconnection");
143 uiCmdImpl_destroy();
144 return;
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800145 }
146
147 uicmd->reader_offset += status;
148 if (uicmd->reader_offset != uicmd->reader_bytes) {
149 // There are still some data left in the pipe.
150 continue;
151 }
152
153 // All expected data has been read. Time to change the state.
154 if (uicmd->reader_state == EXPECTS_HEADER) {
155 // Header has been read.
156 if (uicmd->cmd_header.cmd_param_size) {
157 // Prepare for the command parameters.
158 uicmd->reader_state = EXPECTS_PARAMETERS;
159 uicmd->reader_offset = 0;
160 uicmd->reader_bytes = uicmd->cmd_header.cmd_param_size;
161 uicmd->reader_buffer = malloc(uicmd->reader_bytes);
162 if (uicmd->reader_buffer == NULL) {
163 APANIC("Unable to allocate memory for UI command parameters.\n");
164 }
165 } else {
166 // This command doesn't have any parameters. Handle it now.
167 _uiCmdImpl_handle_command(uicmd, &uicmd->cmd_header, NULL);
168 // Prepare for the next command header.
169 uicmd->reader_state = EXPECTS_HEADER;
170 uicmd->reader_offset = 0;
171 uicmd->reader_bytes = sizeof(uicmd->cmd_header);
172 uicmd->reader_buffer = (uint8_t*)&uicmd->cmd_header;
173 }
174 } else {
175 // All command data is in. Handle it.
176 _uiCmdImpl_handle_command(uicmd, &uicmd->cmd_header,
177 uicmd->reader_buffer);
178 // Prepare for the next command header.
179 free(uicmd->reader_buffer);
180 uicmd->reader_state = EXPECTS_HEADER;
181 uicmd->reader_offset = 0;
182 uicmd->reader_bytes = sizeof(uicmd->cmd_header);
183 uicmd->reader_buffer = (uint8_t*)&uicmd->cmd_header;
184 }
185 }
186}
187
188int
David 'Digit' Turnerb6c168b2011-02-02 21:39:10 +0100189uiCmdImpl_create(SockAddress* console_socket, Looper* looper)
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800190{
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100191 UICmdImpl* uicmd = &_uiCmdImpl;
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800192 char* handshake = NULL;
193
194 // Setup command reader.
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100195 uicmd->reader_buffer = (uint8_t*)&uicmd->cmd_header;
196 uicmd->reader_state = EXPECTS_HEADER;
197 uicmd->reader_offset = 0;
198 uicmd->reader_bytes = sizeof(UICmdHeader);
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800199
200 // Connect to the core-ui-control service.
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100201 uicmd->core_connection =
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800202 core_connection_create_and_switch(console_socket, "core-ui-control",
203 &handshake);
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100204 if (uicmd->core_connection == NULL) {
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800205 derror("Unable to connect to the core-ui-control service: %s\n",
206 errno_str);
207 return -1;
208 }
209
David 'Digit' Turner07db3492011-02-02 17:36:34 +0100210 // Initialize UI command reader.
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100211 uicmd->sock = core_connection_get_socket(uicmd->core_connection);
212 loopIo_init(uicmd->io, looper, uicmd->sock,
David 'Digit' Turnerb6c168b2011-02-02 21:39:10 +0100213 _uiCmdImpl_io_callback,
214 &_uiCmdImpl);
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100215 loopIo_wantRead(uicmd->io);
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800216
217 fprintf(stdout, "core-ui-control is now connected to the core at %s.",
218 sock_address_to_string(console_socket));
219 if (handshake != NULL) {
220 if (handshake[0] != '\0') {
221 fprintf(stdout, " Handshake: %s", handshake);
222 }
223 free(handshake);
224 }
225 fprintf(stdout, "\n");
226
227 return 0;
228}
229
Vladimir Chtchetkine85276802011-01-31 15:18:45 -0800230void
231uiCmdImpl_destroy(void)
232{
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100233 UICmdImpl* uicmd = &_uiCmdImpl;
234
235 if (uicmd->core_connection != NULL) {
Vladimir Chtchetkine85276802011-01-31 15:18:45 -0800236 // Disable I/O callbacks.
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100237 loopIo_done(uicmd->io);
238 core_connection_close(uicmd->core_connection);
239 core_connection_free(uicmd->core_connection);
240 uicmd->core_connection = NULL;
Vladimir Chtchetkine85276802011-01-31 15:18:45 -0800241 }
242 // Properly deallocate the reader buffer.
David 'Digit' Turner21cbdd22011-02-02 22:57:35 +0100243 if (uicmd->reader_buffer != NULL &&
244 uicmd->reader_buffer != (uint8_t*)&uicmd->cmd_header) {
245 free(uicmd->reader_buffer);
246 uicmd->reader_buffer = (uint8_t*)&uicmd->cmd_header;
Vladimir Chtchetkine85276802011-01-31 15:18:45 -0800247 }
248}
249
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800250int
251uicmd_set_brightness_change_callback(AndroidHwLightBrightnessCallback callback,
252 void* opaque)
253{
254 _brightness_change_callback = callback;
255 _brightness_change_callback_param = opaque;
256 return 0;
257}