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. */
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
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 */
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
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 */
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
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
187int
188uiCmdImpl_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
229void
230uiCmdImpl_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
249int
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}
257