/* * 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); }