blob: d16092ad64f1bd53dc3f9acec94a6b4f683ca213 [file] [log] [blame]
/* Copyright (C) 2010 The Android Open Source Project
**
** This software is licensed under the terms of the GNU General Public
** License version 2, as published by the Free Software Foundation, and
** may be copied, distributed, and modified under those terms.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
/*
* Contains the Core-side implementation of the "core-ui-control" service that is
* part of the UI control protocol. Here we send UI control commands to the UI.
*/
#include "android/android.h"
#include "android/hw-control.h"
#include "android/looper.h"
#include "android/async-utils.h"
#include "android/sync-utils.h"
#include "android/utils/debug.h"
#include "android/protocol/ui-commands.h"
#include "android/protocol/ui-commands-proxy.h"
#include "android/protocol/ui-commands-api.h"
/* Descriptor for the UI commands proxy. */
typedef struct UICmdProxy {
/* I/O associated with this descriptor. */
LoopIo io;
/* Looper associated with this descriptor. */
Looper* looper;
/* Writer to send UI commands. */
SyncSocket* sync_writer;
/* Socket descriptor for this service. */
int sock;
} UICmdProxy;
/* One and only one UICmdProxy instance. */
static UICmdProxy _uiCmdProxy;
/* Implemented in android/console.c */
extern void destroy_uicmd_client(void);
/* Calculates timeout for transferring the given number of bytes via socket.
* Return:
* Number of milliseconds during which the entire number of bytes is expected
* to be transferred via socket.
*/
static int
_uiCmdProxy_get_timeout(size_t data_size)
{
// Min 2 seconds + 10 millisec for each transferring byte.
// TODO: Come up with a better arithmetics here.
return 2000 + data_size * 10;
}
/* Sends request to the UI client of this service.
* Param:
* cmd_type, cmd_param, cmd_param_size - Define the command to send.
* Return:
* 0 on success, or < 0 on failure.
*/
static int
_uiCmdProxy_send_command(uint8_t cmd_type,
void* cmd_param,
uint32_t cmd_param_size)
{
UICmdHeader header;
int status = syncsocket_start_write(_uiCmdProxy.sync_writer);
if (!status) {
// Initialize and send the header.
header.cmd_type = cmd_type;
header.cmd_param_size = cmd_param_size;
status = syncsocket_write(_uiCmdProxy.sync_writer, &header, sizeof(header),
_uiCmdProxy_get_timeout(sizeof(header)));
// If there are command parameters, send them too.
if (status > 0 && cmd_param != NULL && cmd_param_size > 0) {
status = syncsocket_write(_uiCmdProxy.sync_writer, cmd_param,
cmd_param_size,
_uiCmdProxy_get_timeout(cmd_param_size));
}
status = syncsocket_result(status);
syncsocket_stop_write(_uiCmdProxy.sync_writer);
}
if (status < 0) {
derror("Send UI command %d (%u bytes) has failed: %s\n",
cmd_type, cmd_param_size, errno_str);
}
return status;
}
/* Asynchronous I/O callback for UICmdProxy instance.
* We expect this callback to be called only on UI detachment condition. In this
* case the event should be LOOP_IO_READ, and read should fail with errno set
* to ECONNRESET.
* Param:
* opaque - UICmdProxy instance.
*/
static void
_uiCmdProxy_io_func(void* opaque, int fd, unsigned events)
{
UICmdProxy* uicmd = (UICmdProxy*)opaque;
AsyncReader reader;
AsyncStatus status;
uint8_t read_buf[1];
if (events & LOOP_IO_WRITE) {
derror("Unexpected LOOP_IO_WRITE in _uiCmdProxy_io_func.\n");
return;
}
// Try to read
asyncReader_init(&reader, read_buf, sizeof(read_buf), &uicmd->io);
status = asyncReader_read(&reader);
// We expect only error status here.
if (status != ASYNC_ERROR) {
derror("Unexpected read status %d in _uiCmdProxy_io_func\n", status);
return;
}
// We expect only socket disconnection error here.
if (errno != ECONNRESET) {
derror("Unexpected read error %d (%s) in _uiCmdProxy_io_func.\n",
errno, errno_str);
return;
}
// Client got disconnectted.
destroy_uicmd_client();
}
/* a callback function called when the system wants to change the brightness
* of a given light. 'light' is a string which can be one of:
* 'lcd_backlight', 'button_backlight' or 'Keyboard_backlight'
*
* brightness is an integer (acceptable range are 0..255), however the
* default is around 105, and we probably don't want to dim the emulator's
* output at that level.
*/
static void
_uiCmdProxy_brightness_change_callback(void* opaque,
const char* light,
int brightness)
{
// Calculate size of the command parameters.
const size_t cmd_size = sizeof(UICmdChangeDispBrightness) + strlen(light) + 1;
// Allocate and initialize parameters.
UICmdChangeDispBrightness* cmd =
(UICmdChangeDispBrightness*)qemu_malloc(cmd_size);
cmd->brightness = brightness;
strcpy(cmd->light, light);
// Send the command.
_uiCmdProxy_send_command(AUICMD_CHANGE_DISP_BRIGHTNESS, cmd, cmd_size);
qemu_free(cmd);
}
int
uiCmdProxy_create(int fd)
{
// Initialize the only UICmdProxy instance.
_uiCmdProxy.sock = fd;
_uiCmdProxy.looper = looper_newCore();
loopIo_init(&_uiCmdProxy.io, _uiCmdProxy.looper, _uiCmdProxy.sock,
_uiCmdProxy_io_func, &_uiCmdProxy);
loopIo_wantRead(&_uiCmdProxy.io);
_uiCmdProxy.sync_writer = syncsocket_init(fd);
if (_uiCmdProxy.sync_writer == NULL) {
derror("Unable to initialize UICmdProxy writer: %s\n", errno_str);
uiCmdProxy_destroy();
return -1;
}
{
// Set brighness change callback, so we can notify
// the UI about the event.
AndroidHwControlFuncs funcs;
funcs.light_brightness = _uiCmdProxy_brightness_change_callback;
android_hw_control_set(&_uiCmdProxy, &funcs);
}
return 0;
}
void
uiCmdProxy_destroy()
{
// Destroy the sync writer.
if (_uiCmdProxy.sync_writer != NULL) {
syncsocket_close(_uiCmdProxy.sync_writer);
syncsocket_free(_uiCmdProxy.sync_writer);
}
if (_uiCmdProxy.looper != NULL) {
// Stop all I/O that may still be going on.
loopIo_done(&_uiCmdProxy.io);
looper_free(_uiCmdProxy.looper);
_uiCmdProxy.looper = NULL;
}
_uiCmdProxy.sock = -1;
}
int
uicmd_set_window_scale(double scale, int is_dpi)
{
UICmdSetWindowsScale cmd;
cmd.scale = scale;
cmd.is_dpi = is_dpi;
return _uiCmdProxy_send_command(AUICMD_SET_WINDOWS_SCALE, &cmd, sizeof(cmd));
}