| /* 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 UI-side implementation of the "core-ui-control" service that is |
| * part of the UI control protocol. Here we handle UI control commands received |
| * from the Core. |
| */ |
| |
| #include <unistd.h> |
| #include "android/looper.h" |
| #include "android/async-utils.h" |
| #include "android/sync-utils.h" |
| #include "android/utils/system.h" |
| #include "android/utils/debug.h" |
| #include "android/utils/eintr_wrapper.h" |
| #include "android/utils/panic.h" |
| #include "android/protocol/core-connection.h" |
| #include "android/protocol/ui-commands-impl.h" |
| #include "android/protocol/ui-commands-api.h" |
| |
| /* Enumerates states for the command reader in UICmdImpl instance. */ |
| typedef enum UICmdImplState { |
| /* The reader is waiting on command header. */ |
| EXPECTS_HEADER, |
| |
| /* The reader is waiting on command parameters. */ |
| EXPECTS_PARAMETERS, |
| } UICmdImplState; |
| |
| /* Descriptor for the UI-side of the "core-ui-control" service. */ |
| typedef struct UICmdImpl { |
| /* Core connection established for this service. */ |
| CoreConnection* core_connection; |
| |
| /* Socket descriptor for the UI service. */ |
| int sock; |
| |
| /* Custom i/o handler */ |
| LoopIo io[1]; |
| |
| /* Command reader state. */ |
| UICmdImplState reader_state; |
| |
| /* Incoming command header. */ |
| UICmdHeader cmd_header; |
| |
| /* Reader's buffer. This field can point to the cmd_header field of this |
| * structure (when we expect a command header), or to a buffer allocated for |
| * the (when we expect command parameters). */ |
| uint8_t* reader_buffer; |
| |
| /* Offset in the reader's buffer where to read next chunk of data. */ |
| size_t reader_offset; |
| |
| /* Total number of bytes the reader expects to read. */ |
| size_t reader_bytes; |
| } UICmdImpl; |
| |
| /* Implemented in android/qemulator.c */ |
| extern void android_emulator_set_window_scale(double scale, int is_dpi); |
| |
| /* One and only one UICmdImpl instance. */ |
| static UICmdImpl _uiCmdImpl; |
| |
| /* Display brightness change callback. */ |
| static AndroidHwLightBrightnessCallback _brightness_change_callback = NULL; |
| static void* _brightness_change_callback_param = NULL; |
| |
| /* Handles UI control command received from the core. |
| * Param: |
| * uicmd - UICmdImpl instance that received the command. |
| * header - UI control command header. |
| * data - Command parameters formatted accordingly to the command type. |
| */ |
| static void |
| _uiCmdImpl_handle_command(UICmdImpl* uicmd, |
| const UICmdHeader* header, |
| const uint8_t* data) |
| { |
| switch (header->cmd_type) { |
| case AUICMD_SET_WINDOWS_SCALE: |
| { |
| UICmdSetWindowsScale* cmd = (UICmdSetWindowsScale*)data; |
| android_emulator_set_window_scale(cmd->scale, cmd->is_dpi); |
| break; |
| } |
| |
| case AUICMD_CHANGE_DISP_BRIGHTNESS: |
| { |
| UICmdChangeDispBrightness* cmd = (UICmdChangeDispBrightness*)data; |
| if (_brightness_change_callback != NULL) { |
| _brightness_change_callback(_brightness_change_callback_param, |
| cmd->light, cmd->brightness); |
| } |
| break; |
| } |
| |
| default: |
| derror("Unknown command %d is received from the Core\n", |
| header->cmd_type); |
| break; |
| } |
| } |
| |
| /* Asynchronous I/O callback reading UI control commands. |
| * Param: |
| * opaque - UICmdImpl instance. |
| */ |
| static void |
| _uiCmdImpl_io_callback(void* opaque, int fd, unsigned events) |
| { |
| UICmdImpl* uicmd = opaque; |
| int status; |
| |
| // Read requests while they are immediately available. |
| for (;;) { |
| // Read next chunk of data. |
| status = HANDLE_EINTR( |
| socket_recv(uicmd->sock, |
| uicmd->reader_buffer + uicmd->reader_offset, |
| uicmd->reader_bytes - uicmd->reader_offset)); |
| if (status < 0 && (errno == EWOULDBLOCK || errno == EGAIN) { |
| // Chunk is not avalable at this point. Come back later. |
| return; |
| } |
| if (status <= 0) { |
| /* Disconnection, meaning that the core process got terminated. */ |
| fprintf(stderr, |
| "core-ui-control service got disconnected: %s\n", |
| status < 0 ? |
| strerror(errno) : |
| "unexpected disconnection"); |
| uiCmdImpl_destroy(); |
| return; |
| } |
| |
| uicmd->reader_offset += status; |
| if (uicmd->reader_offset != uicmd->reader_bytes) { |
| // There are still some data left in the pipe. |
| continue; |
| } |
| |
| // All expected data has been read. Time to change the state. |
| if (uicmd->reader_state == EXPECTS_HEADER) { |
| // Header has been read. |
| if (uicmd->cmd_header.cmd_param_size) { |
| // Prepare for the command parameters. |
| uicmd->reader_state = EXPECTS_PARAMETERS; |
| uicmd->reader_offset = 0; |
| uicmd->reader_bytes = uicmd->cmd_header.cmd_param_size; |
| uicmd->reader_buffer = malloc(uicmd->reader_bytes); |
| if (uicmd->reader_buffer == NULL) { |
| APANIC("Unable to allocate memory for UI command parameters.\n"); |
| } |
| } else { |
| // This command doesn't have any parameters. Handle it now. |
| _uiCmdImpl_handle_command(uicmd, &uicmd->cmd_header, NULL); |
| // Prepare for the next command header. |
| uicmd->reader_state = EXPECTS_HEADER; |
| uicmd->reader_offset = 0; |
| uicmd->reader_bytes = sizeof(uicmd->cmd_header); |
| uicmd->reader_buffer = (uint8_t*)&uicmd->cmd_header; |
| } |
| } else { |
| // All command data is in. Handle it. |
| _uiCmdImpl_handle_command(uicmd, &uicmd->cmd_header, |
| uicmd->reader_buffer); |
| // Prepare for the next command header. |
| free(uicmd->reader_buffer); |
| uicmd->reader_state = EXPECTS_HEADER; |
| uicmd->reader_offset = 0; |
| uicmd->reader_bytes = sizeof(uicmd->cmd_header); |
| uicmd->reader_buffer = (uint8_t*)&uicmd->cmd_header; |
| } |
| } |
| } |
| |
| int |
| uiCmdImpl_create(SockAddress* console_socket, Looper* looper) |
| { |
| UICmdImpl* uicmd = &_uiCmdImpl; |
| char* handshake = NULL; |
| |
| // Setup command reader. |
| uicmd->reader_buffer = (uint8_t*)&uicmd->cmd_header; |
| uicmd->reader_state = EXPECTS_HEADER; |
| uicmd->reader_offset = 0; |
| uicmd->reader_bytes = sizeof(UICmdHeader); |
| |
| // Connect to the core-ui-control service. |
| uicmd->core_connection = |
| core_connection_create_and_switch(console_socket, "core-ui-control", |
| &handshake); |
| if (uicmd->core_connection == NULL) { |
| derror("Unable to connect to the core-ui-control service: %s\n", |
| errno_str); |
| return -1; |
| } |
| |
| // Initialize UI command reader. |
| uicmd->sock = core_connection_get_socket(uicmd->core_connection); |
| loopIo_init(uicmd->io, looper, uicmd->sock, |
| _uiCmdImpl_io_callback, |
| &_uiCmdImpl); |
| loopIo_wantRead(uicmd->io); |
| |
| fprintf(stdout, "core-ui-control is now connected to the core at %s.", |
| sock_address_to_string(console_socket)); |
| if (handshake != NULL) { |
| if (handshake[0] != '\0') { |
| fprintf(stdout, " Handshake: %s", handshake); |
| } |
| free(handshake); |
| } |
| fprintf(stdout, "\n"); |
| |
| return 0; |
| } |
| |
| void |
| uiCmdImpl_destroy(void) |
| { |
| UICmdImpl* uicmd = &_uiCmdImpl; |
| |
| if (uicmd->core_connection != NULL) { |
| // Disable I/O callbacks. |
| loopIo_done(uicmd->io); |
| core_connection_close(uicmd->core_connection); |
| core_connection_free(uicmd->core_connection); |
| uicmd->core_connection = NULL; |
| } |
| // Properly deallocate the reader buffer. |
| if (uicmd->reader_buffer != NULL && |
| uicmd->reader_buffer != (uint8_t*)&uicmd->cmd_header) { |
| free(uicmd->reader_buffer); |
| uicmd->reader_buffer = (uint8_t*)&uicmd->cmd_header; |
| } |
| } |
| |
| int |
| uicmd_set_brightness_change_callback(AndroidHwLightBrightnessCallback callback, |
| void* opaque) |
| { |
| _brightness_change_callback = callback; |
| _brightness_change_callback_param = opaque; |
| return 0; |
| } |