blob: daf6a373f365cb1bd34f069be5a95d7e7c721b4a [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.
*/
/*
* Encapsulates exchange protocol between the emulator, and an Android device
* that is connected to the host via USB. The communication is established over
* a TCP port forwarding, enabled by ADB.
*/
#include "qemu-common.h"
#include "android/async-utils.h"
#include "android/utils/debug.h"
#include "android/async-socket-connector.h"
#include "utils/panic.h"
#include "iolooper.h"
#define E(...) derror(__VA_ARGS__)
#define W(...) dwarning(__VA_ARGS__)
#define D(...) VERBOSE_PRINT(asconnector,__VA_ARGS__)
#define D_ACTIVE VERBOSE_CHECK(asconnector)
/********************************************************************************
* Internals
*******************************************************************************/
struct AsyncSocketConnector {
/* TCP address for the connection. */
SockAddress address;
/* I/O looper for asynchronous I/O. */
Looper* looper;
/* I/O port for asynchronous connection. */
LoopIo connector_io[1];
/* Timer that is used to retry asynchronous connections. */
LoopTimer connector_timer[1];
/* Asynchronous connector to the socket. */
AsyncConnector connector[1];
/* Callback to invoke on connection / connection error. */
asc_event_cb on_connected_cb;
/* An opaque parameter to pass to the connection callback. */
void* on_connected_cb_opaque;
/* Retry timeout in milliseconds. */
int retry_to;
/* Socket descriptor for the connection. */
int fd;
};
/* Asynchronous I/O looper callback invoked by the connector.
* Param:
* opaque - AsyncSocketConnector instance.
* fd, events - Standard I/O callback parameters.
*/
static void _on_async_socket_connector_io(void* opaque, int fd, unsigned events);
/* Gets socket's address string. */
AINLINED const char*
_asc_socket_string(AsyncSocketConnector* connector)
{
return sock_address_to_string(&connector->address);
}
/* Destroys AsyncSocketConnector instance.
* Param:
* connector - Initialized AsyncSocketConnector instance.
*/
static void
_async_socket_connector_free(AsyncSocketConnector* connector)
{
if (connector != NULL) {
if (connector->fd >= 0) {
socket_close(connector->fd);
}
if (connector->looper != NULL) {
loopTimer_done(connector->connector_timer);
looper_free(connector->looper);
}
sock_address_done(&connector->address);
AFREE(connector);
}
}
/* Opens connection socket.
* Param:
* connector - Initialized AsyncSocketConnector instance.
* Return:
* 0 on success, or -1 on failure.
*/
static int
_async_socket_connector_open_socket(AsyncSocketConnector* connector)
{
/* Open socket. */
connector->fd = socket_create_inet(SOCKET_STREAM);
if (connector->fd < 0) {
D("Unable to create connector socket for %s. Error: %s",
_asc_socket_string(connector), strerror(errno));
return -1;
}
/* Prepare for async I/O on the connector. */
socket_set_nonblock(connector->fd);
loopIo_init(connector->connector_io, connector->looper, connector->fd,
_on_async_socket_connector_io, connector);
return 0;
}
/* Closes connection socket.
* Param:
* connector - Initialized AsyncSocketConnector instance.
* Return:
* 0 on success, or -1 on failure.
*/
static void
_async_socket_connector_close_socket(AsyncSocketConnector* connector)
{
if (connector->fd >= 0) {
socket_close(connector->fd);
connector->fd = -1;
}
}
/* Asynchronous connector (AsyncConnector instance) has completed connection
* attempt.
*
* NOTE: Upon exit from this routine AsyncSocketConnector instance might be
* destroyed. So, once this routine is called, there must be no further
* references to AsyncSocketConnector instance passed to this routine.
* Param:
* connector - Initialized AsyncSocketConnector instance.
* status - Status of the connection attempt.
*/
static void
_on_async_socket_connector_connecting(AsyncSocketConnector* connector,
AsyncStatus status)
{
ASCCbRes action;
int do_retry = 0;
switch (status) {
case ASYNC_COMPLETE:
loopIo_done(connector->connector_io);
D("Socket %s is connected", _asc_socket_string(connector));
/* Invoke "on connected" callback */
action = connector->on_connected_cb(connector->on_connected_cb_opaque,
connector, ASC_CONNECTION_SUCCEEDED);
if (action == ASC_CB_RETRY) {
do_retry = 1;
} else if (action == ASC_CB_ABORT) {
_async_socket_connector_close_socket(connector);
}
break;
case ASYNC_ERROR:
loopIo_done(connector->connector_io);
D("Error %d while connecting to socket %s: %s",
errno, _asc_socket_string(connector), strerror(errno));
/* Invoke "on connected" callback */
action = connector->on_connected_cb(connector->on_connected_cb_opaque,
connector, ASC_CONNECTION_FAILED);
if (action == ASC_CB_RETRY) {
do_retry = 1;
} else if (action == ASC_CB_ABORT) {
_async_socket_connector_close_socket(connector);
}
break;
case ASYNC_NEED_MORE:
return;
}
if (do_retry) {
D("Retrying connection to socket %s", _asc_socket_string(connector));
loopTimer_startRelative(connector->connector_timer, connector->retry_to);
} else {
_async_socket_connector_free(connector);
}
}
static void
_on_async_socket_connector_io(void* opaque, int fd, unsigned events)
{
AsyncSocketConnector* const connector = (AsyncSocketConnector*)opaque;
/* Complete socket connection. */
const AsyncStatus status = asyncConnector_run(connector->connector);
_on_async_socket_connector_connecting(connector, status);
}
/* Retry connection timer callback.
* Param:
* opaque - AsyncSocketConnector instance.
*/
static void
_on_async_socket_connector_retry(void* opaque)
{
AsyncSocketConnector* const connector = (AsyncSocketConnector*)opaque;
/* Invoke the callback to notify about a connection retry attempt. */
const ASCCbRes action =
connector->on_connected_cb(connector->on_connected_cb_opaque,
connector, ASC_CONNECTION_RETRY);
if (action == ASC_CB_RETRY) {
AsyncStatus status;
/* Close handle opened for the previous (failed) attempt. */
_async_socket_connector_close_socket(connector);
/* Retry connection attempt. */
if (_async_socket_connector_open_socket(connector) == 0) {
status = asyncConnector_init(connector->connector, &connector->address,
connector->connector_io);
} else {
status = ASYNC_ERROR;
}
_on_async_socket_connector_connecting(connector, status);
} else {
_async_socket_connector_free(connector);
}
}
/********************************************************************************
* Async connector implementation
*******************************************************************************/
AsyncSocketConnector*
async_socket_connector_new(const SockAddress* address,
int retry_to,
asc_event_cb cb,
void* cb_opaque)
{
AsyncSocketConnector* connector;
if (cb == NULL) {
W("No callback for AsyncSocketConnector for %s",
sock_address_to_string(address));
errno = EINVAL;
return NULL;
}
ANEW0(connector);
connector->fd = -1;
connector->retry_to = retry_to;
connector->on_connected_cb = cb;
connector->on_connected_cb_opaque = cb_opaque;
/* Copy socket address. */
if (sock_address_get_family(address) == SOCKET_UNIX) {
sock_address_init_unix(&connector->address, sock_address_get_path(address));
} else {
connector->address = *address;
}
/* Create a looper for asynchronous I/O. */
connector->looper = looper_newCore();
if (connector->looper != NULL) {
/* Create a timer that will be used for connection retries. */
loopTimer_init(connector->connector_timer, connector->looper,
_on_async_socket_connector_retry, connector);
} else {
E("Unable to create I/O looper for asynchronous connector to socket %s",
_asc_socket_string(connector));
_async_socket_connector_free(connector);
return NULL;
}
return connector;
}
ASCConnectRes
async_socket_connector_connect(AsyncSocketConnector* connector)
{
AsyncStatus status;
if (_async_socket_connector_open_socket(connector) == 0) {
status = asyncConnector_init(connector->connector, &connector->address,
connector->connector_io);
} else {
status = ASYNC_ERROR;
}
_on_async_socket_connector_connecting(connector, status);
switch (status) {
case ASYNC_COMPLETE:
return ASC_CONNECT_SUCCEEDED;
case ASYNC_ERROR:
return ASC_CONNECT_FAILED;
case ASYNC_NEED_MORE:
default:
return ASC_CONNECT_IN_PROGRESS;
}
}
int
async_socket_connector_pull_fd(AsyncSocketConnector* connector)
{
const int fd = connector->fd;
if (fd >= 0) {
connector->fd = -1;
}
return fd;
}