| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| #include "emulator-console.h" |
| #include "sockets.h" |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #define DEBUG 0 |
| #if DEBUG >= 1 |
| # define D(...) printf(__VA_ARGS__), printf("\n") |
| #else |
| # define D(...) ((void)0) |
| #endif |
| #if DEBUG >= 2 |
| # define DD(...) printf(__VA_ARGS__), printf("\n") |
| #else |
| # define DD(...) ((void)0) |
| #endif |
| |
| #define ANEW0(p) (p) = calloc(sizeof(*(p)), 1) |
| |
| enum { |
| STATE_CONNECTING = 0, |
| STATE_CONNECTED, |
| STATE_WAITING, |
| STATE_ERROR = 2 |
| }; |
| |
| typedef struct Msg { |
| const char* data; // pointer to data |
| int size; // size of data |
| int sent; // already sent (so sent..size remain in buffer). |
| struct Msg* next; // next message in queue. |
| } Msg; |
| |
| static Msg* |
| msg_alloc( const char* data, int datalen ) |
| { |
| Msg* msg; |
| |
| msg = malloc(sizeof(*msg) + datalen); |
| msg->data = (const char*)(msg + 1); |
| msg->size = datalen; |
| msg->sent = 0; |
| memcpy((char*)msg->data, data, datalen); |
| msg->next = NULL; |
| |
| return msg; |
| } |
| |
| static void |
| msg_free( Msg* msg ) |
| { |
| free(msg); |
| } |
| |
| struct EmulatorConsole { |
| int fd; |
| IoLooper* looper; |
| int state; |
| Msg* out_msg; |
| SockAddress address; |
| int64_t waitUntil; |
| }; |
| |
| /* Read as much from the input as possible, ignoring it. |
| */ |
| static int |
| emulatorConsole_eatInput( EmulatorConsole* con ) |
| { |
| for (;;) { |
| char temp[64]; |
| int ret = socket_recv(con->fd, temp, sizeof temp); |
| if (ret < 0) { |
| if (errno == EAGAIN || errno == EWOULDBLOCK) { |
| return 0; |
| } |
| return -1; |
| } |
| if (ret == 0) { |
| return -1; |
| } |
| DD("Console received: '%.*s'", ret, temp); |
| } |
| } |
| |
| static int |
| emulatorConsole_sendOutput( EmulatorConsole* con ) |
| { |
| if (con->state != STATE_CONNECTED) { |
| errno = EINVAL; |
| return -1; |
| } |
| |
| while (con->out_msg != NULL) { |
| Msg* msg = con->out_msg; |
| int ret; |
| |
| ret = socket_send(con->fd, |
| msg->data + msg->sent, |
| msg->size - msg->sent); |
| if (ret > 0) { |
| DD("Console sent: '%.*s'", ret, msg->data + msg->sent); |
| |
| msg->sent += ret; |
| if (msg->sent == msg->size) { |
| con->out_msg = msg->next; |
| msg_free(msg); |
| } |
| continue; |
| } |
| if (ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) { |
| return 0; |
| } |
| con->state = STATE_ERROR; |
| D("Console error when sending: %s", strerror(errno)); |
| return -1; |
| } |
| iolooper_del_write(con->looper, con->fd); |
| return 0; |
| } |
| |
| static void |
| emulatorConsole_completeConnect(EmulatorConsole* con) |
| { |
| D("Console connected!"); |
| iolooper_add_read(con->looper, con->fd); |
| iolooper_del_write(con->looper, con->fd); |
| con->state = STATE_CONNECTED; |
| if (con->out_msg != NULL) { |
| iolooper_add_write(con->looper, con->fd); |
| emulatorConsole_sendOutput(con); |
| } |
| } |
| |
| static void |
| emulatorConsole_retry(EmulatorConsole* con) |
| { |
| /* Not possible yet, wait one second */ |
| D("Could not connect to emulator, waiting 1 second: %s", errno_str); |
| con->state = STATE_WAITING; |
| con->waitUntil = iolooper_now() + 5000; |
| } |
| |
| static void |
| emulatorConsole_connect(EmulatorConsole* con) |
| { |
| D("Trying to connect!"); |
| if (con->fd < 0) { |
| con->fd = socket_create_inet( SOCKET_STREAM ); |
| if (con->fd < 0) { |
| D("ERROR: Could not create socket: %s", errno_str); |
| con->state = STATE_ERROR; |
| return; |
| } |
| socket_set_nonblock(con->fd); |
| } |
| con->state = STATE_CONNECTING; |
| if (socket_connect(con->fd, &con->address) < 0) { |
| if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS) { |
| iolooper_add_write(con->looper, con->fd); |
| } else { |
| emulatorConsole_retry(con); |
| } |
| return; |
| } |
| |
| emulatorConsole_completeConnect(con); |
| } |
| |
| static void |
| emulatorConsole_reset( EmulatorConsole* con ) |
| { |
| D("Resetting console connection"); |
| while (con->out_msg) { |
| Msg* msg = con->out_msg; |
| con->out_msg = msg->next; |
| msg_free(msg); |
| } |
| iolooper_del_read(con->looper, con->fd); |
| iolooper_del_write(con->looper, con->fd); |
| socket_close(con->fd); |
| con->fd = -1; |
| emulatorConsole_connect(con); |
| } |
| |
| /* Create a new EmulatorConsole object to connect asynchronously to |
| * a given emulator port. Note that this should always succeeds since |
| * the connection is asynchronous. |
| */ |
| EmulatorConsole* |
| emulatorConsole_new(int port, IoLooper* looper) |
| { |
| EmulatorConsole* con; |
| SockAddress addr; |
| |
| ANEW0(con); |
| con->looper = looper; |
| con->fd = -1; |
| sock_address_init_inet(&con->address, SOCK_ADDRESS_INET_LOOPBACK, port); |
| |
| emulatorConsole_connect(con); |
| return con; |
| } |
| |
| int |
| emulatorConsole_poll( EmulatorConsole* con ) |
| { |
| int ret; |
| |
| if (con->state == STATE_WAITING) { |
| if (iolooper_now() >= con->waitUntil) |
| emulatorConsole_connect(con); |
| return 0; |
| } |
| |
| if (!iolooper_is_read(con->looper, con->fd) && |
| !iolooper_is_write(con->looper, con->fd)) |
| { |
| return 0; |
| } |
| |
| LOOP: |
| switch (con->state) { |
| case STATE_ERROR: |
| return -1; |
| |
| case STATE_CONNECTING: |
| // read socket error to determine success / error. |
| if (socket_get_error(con->fd) != 0) { |
| emulatorConsole_retry(con); |
| } else { |
| emulatorConsole_completeConnect(con); |
| } |
| return 0; |
| |
| case STATE_CONNECTED: |
| /* ignore input, if any */ |
| if (iolooper_is_read(con->looper, con->fd)) { |
| if (emulatorConsole_eatInput(con) < 0) { |
| goto SET_ERROR; |
| } |
| } |
| /* send outgoing data, if any */ |
| if (iolooper_is_write(con->looper, con->fd)) { |
| if (emulatorConsole_sendOutput(con) < 0) { |
| goto SET_ERROR; |
| } |
| } |
| return 0; |
| |
| default: |
| D("UNSUPPORTED STATE!"); |
| break; |
| } |
| |
| SET_ERROR: |
| D("Console ERROR!: %s\n", errno_str); |
| con->state = STATE_ERROR; |
| emulatorConsole_reset(con); |
| return -1; |
| } |
| |
| /* Send a message to the console asynchronously. Any answer will be |
| * ignored. */ |
| void |
| emulatorConsole_send( EmulatorConsole* con, const char* command ) |
| { |
| int cmdlen = strlen(command); |
| Msg* msg; |
| Msg** plast; |
| |
| if (cmdlen == 0) |
| return; |
| |
| /* Append new message at end of outgoing list */ |
| msg = msg_alloc(command, cmdlen); |
| plast = &con->out_msg; |
| while (*plast) { |
| plast = &(*plast)->next; |
| } |
| *plast = msg; |
| if (con->out_msg == msg) { |
| iolooper_add_write(con->looper, con->fd); |
| } |
| emulatorConsole_sendOutput(con); |
| } |
| |
| |
| void |
| emulatorConsole_sendMouseDown( EmulatorConsole* con, int x, int y ) |
| { |
| char temp[128]; |
| |
| D("sendMouseDown(%d,%d)", x, y); |
| snprintf(temp, sizeof temp, |
| "event send 3:0:%d 3:1:%d 1:330:1 0:0:0\r\n", |
| x, y); |
| emulatorConsole_send(con, temp); |
| } |
| |
| void |
| emulatorConsole_sendMouseMotion( EmulatorConsole* con, int x, int y ) |
| { |
| /* Same as mouse down */ |
| emulatorConsole_sendMouseDown(con, x, y); |
| } |
| |
| void |
| emulatorConsole_sendMouseUp( EmulatorConsole* con, int x, int y ) |
| { |
| char temp[128]; |
| |
| D("sendMouseUp(%d,%d)", x, y); |
| snprintf(temp, sizeof temp, |
| "event send 3:0:%d 3:1:%d 1:330:0 0:0:0\r\n", |
| x, y); |
| emulatorConsole_send(con, temp); |
| } |
| |
| #define EE(x,y) if (keycode == x) return y; |
| |
| void |
| emulatorConsole_sendKey( EmulatorConsole* con, int keycode, int down ) |
| { |
| char temp[128]; |
| |
| snprintf(temp, sizeof temp, |
| "event send EV_KEY:%d:%d 0:0:0\r\n", keycode, down); |
| emulatorConsole_send(con, temp); |
| } |