blob: d16092ad64f1bd53dc3f9acec94a6b4f683ca213 [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 Core-side implementation of the "core-ui-control" service that is
15 * part of the UI control protocol. Here we send UI control commands to the UI.
16 */
17
18#include "android/android.h"
19#include "android/hw-control.h"
20#include "android/looper.h"
21#include "android/async-utils.h"
22#include "android/sync-utils.h"
23#include "android/utils/debug.h"
24#include "android/protocol/ui-commands.h"
25#include "android/protocol/ui-commands-proxy.h"
26#include "android/protocol/ui-commands-api.h"
27
28/* Descriptor for the UI commands proxy. */
29typedef struct UICmdProxy {
30 /* I/O associated with this descriptor. */
31 LoopIo io;
32
33 /* Looper associated with this descriptor. */
34 Looper* looper;
35
36 /* Writer to send UI commands. */
37 SyncSocket* sync_writer;
38
39 /* Socket descriptor for this service. */
40 int sock;
41} UICmdProxy;
42
43/* One and only one UICmdProxy instance. */
44static UICmdProxy _uiCmdProxy;
45
46/* Implemented in android/console.c */
47extern void destroy_uicmd_client(void);
48
49/* Calculates timeout for transferring the given number of bytes via socket.
50 * Return:
51 * Number of milliseconds during which the entire number of bytes is expected
52 * to be transferred via socket.
53 */
54static int
55_uiCmdProxy_get_timeout(size_t data_size)
56{
57 // Min 2 seconds + 10 millisec for each transferring byte.
58 // TODO: Come up with a better arithmetics here.
59 return 2000 + data_size * 10;
60}
61
62/* Sends request to the UI client of this service.
63 * Param:
64 * cmd_type, cmd_param, cmd_param_size - Define the command to send.
65 * Return:
66 * 0 on success, or < 0 on failure.
67 */
68static int
69_uiCmdProxy_send_command(uint8_t cmd_type,
70 void* cmd_param,
71 uint32_t cmd_param_size)
72{
73 UICmdHeader header;
74 int status = syncsocket_start_write(_uiCmdProxy.sync_writer);
75 if (!status) {
76 // Initialize and send the header.
77 header.cmd_type = cmd_type;
78 header.cmd_param_size = cmd_param_size;
79 status = syncsocket_write(_uiCmdProxy.sync_writer, &header, sizeof(header),
80 _uiCmdProxy_get_timeout(sizeof(header)));
81 // If there are command parameters, send them too.
82 if (status > 0 && cmd_param != NULL && cmd_param_size > 0) {
83 status = syncsocket_write(_uiCmdProxy.sync_writer, cmd_param,
84 cmd_param_size,
85 _uiCmdProxy_get_timeout(cmd_param_size));
86 }
87 status = syncsocket_result(status);
88 syncsocket_stop_write(_uiCmdProxy.sync_writer);
89 }
90 if (status < 0) {
91 derror("Send UI command %d (%u bytes) has failed: %s\n",
92 cmd_type, cmd_param_size, errno_str);
93 }
94 return status;
95}
96
97/* Asynchronous I/O callback for UICmdProxy instance.
98 * We expect this callback to be called only on UI detachment condition. In this
99 * case the event should be LOOP_IO_READ, and read should fail with errno set
100 * to ECONNRESET.
101 * Param:
102 * opaque - UICmdProxy instance.
103 */
104static void
105_uiCmdProxy_io_func(void* opaque, int fd, unsigned events)
106{
107 UICmdProxy* uicmd = (UICmdProxy*)opaque;
108 AsyncReader reader;
109 AsyncStatus status;
110 uint8_t read_buf[1];
111
112 if (events & LOOP_IO_WRITE) {
113 derror("Unexpected LOOP_IO_WRITE in _uiCmdProxy_io_func.\n");
114 return;
115 }
116
117 // Try to read
118 asyncReader_init(&reader, read_buf, sizeof(read_buf), &uicmd->io);
David 'Digit' Turnerf9e333a2011-03-17 14:57:51 +0100119 status = asyncReader_read(&reader);
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800120 // We expect only error status here.
121 if (status != ASYNC_ERROR) {
122 derror("Unexpected read status %d in _uiCmdProxy_io_func\n", status);
123 return;
124 }
125 // We expect only socket disconnection error here.
126 if (errno != ECONNRESET) {
127 derror("Unexpected read error %d (%s) in _uiCmdProxy_io_func.\n",
128 errno, errno_str);
129 return;
130 }
131
132 // Client got disconnectted.
133 destroy_uicmd_client();
134}
135/* a callback function called when the system wants to change the brightness
136 * of a given light. 'light' is a string which can be one of:
137 * 'lcd_backlight', 'button_backlight' or 'Keyboard_backlight'
138 *
139 * brightness is an integer (acceptable range are 0..255), however the
140 * default is around 105, and we probably don't want to dim the emulator's
141 * output at that level.
142 */
143static void
144_uiCmdProxy_brightness_change_callback(void* opaque,
145 const char* light,
146 int brightness)
147{
148 // Calculate size of the command parameters.
149 const size_t cmd_size = sizeof(UICmdChangeDispBrightness) + strlen(light) + 1;
150 // Allocate and initialize parameters.
151 UICmdChangeDispBrightness* cmd =
152 (UICmdChangeDispBrightness*)qemu_malloc(cmd_size);
153 cmd->brightness = brightness;
154 strcpy(cmd->light, light);
155 // Send the command.
156 _uiCmdProxy_send_command(AUICMD_CHANGE_DISP_BRIGHTNESS, cmd, cmd_size);
157 qemu_free(cmd);
158}
159
160int
161uiCmdProxy_create(int fd)
162{
163 // Initialize the only UICmdProxy instance.
164 _uiCmdProxy.sock = fd;
165 _uiCmdProxy.looper = looper_newCore();
166 loopIo_init(&_uiCmdProxy.io, _uiCmdProxy.looper, _uiCmdProxy.sock,
167 _uiCmdProxy_io_func, &_uiCmdProxy);
168 loopIo_wantRead(&_uiCmdProxy.io);
169 _uiCmdProxy.sync_writer = syncsocket_init(fd);
170 if (_uiCmdProxy.sync_writer == NULL) {
171 derror("Unable to initialize UICmdProxy writer: %s\n", errno_str);
172 uiCmdProxy_destroy();
173 return -1;
174 }
175 {
176 // Set brighness change callback, so we can notify
177 // the UI about the event.
178 AndroidHwControlFuncs funcs;
179 funcs.light_brightness = _uiCmdProxy_brightness_change_callback;
David 'Digit' Turnerca950592011-04-27 12:26:15 +0200180 android_hw_control_set(&_uiCmdProxy, &funcs);
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800181 }
182 return 0;
183}
184
185void
186uiCmdProxy_destroy()
187{
188 // Destroy the sync writer.
189 if (_uiCmdProxy.sync_writer != NULL) {
190 syncsocket_close(_uiCmdProxy.sync_writer);
191 syncsocket_free(_uiCmdProxy.sync_writer);
192 }
193 if (_uiCmdProxy.looper != NULL) {
194 // Stop all I/O that may still be going on.
195 loopIo_done(&_uiCmdProxy.io);
196 looper_free(_uiCmdProxy.looper);
197 _uiCmdProxy.looper = NULL;
198 }
199 _uiCmdProxy.sock = -1;
200}
201
202int
203uicmd_set_window_scale(double scale, int is_dpi)
204{
205 UICmdSetWindowsScale cmd;
206 cmd.scale = scale;
207 cmd.is_dpi = is_dpi;
208 return _uiCmdProxy_send_command(AUICMD_SET_WINDOWS_SCALE, &cmd, sizeof(cmd));
209}