blob: a8c49b291dde309363a815ba248fb0d7d80c8c5e [file] [log] [blame]
/*
* 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);
}