aboutsummaryrefslogtreecommitdiffstats
path: root/android
diff options
context:
space:
mode:
authorVladimir Chtchetkine <vchtchetkine@google.com>2012-03-28 12:11:11 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2012-03-28 12:11:11 -0700
commit300dc988021a26ea93e3f87e15e70fdcb9cb9359 (patch)
treee0421ba301cc609ff8cf764d008c44d1e4c3f86c /android
parent9dcf03fda8001a74016a316b0aa223f6675dff37 (diff)
parent9d36fe7e6f0c5ece4de1b29ec78a15c37c158b1d (diff)
downloadexternal_qemu-300dc988021a26ea93e3f87e15e70fdcb9cb9359.zip
external_qemu-300dc988021a26ea93e3f87e15e70fdcb9cb9359.tar.gz
external_qemu-300dc988021a26ea93e3f87e15e70fdcb9cb9359.tar.bz2
Merge "Implements an asynchronous socket connector with retries"
Diffstat (limited to 'android')
-rw-r--r--android/async-socket-connector.c322
-rw-r--r--android/async-socket-connector.h158
-rw-r--r--android/utils/debug.h1
3 files changed, 481 insertions, 0 deletions
diff --git a/android/async-socket-connector.c b/android/async-socket-connector.c
new file mode 100644
index 0000000..daf6a37
--- /dev/null
+++ b/android/async-socket-connector.c
@@ -0,0 +1,322 @@
+/*
+ * 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;
+}
diff --git a/android/async-socket-connector.h b/android/async-socket-connector.h
new file mode 100644
index 0000000..998d0c8
--- /dev/null
+++ b/android/async-socket-connector.h
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_ASYNC_SOCKET_CONNECTOR_H_
+#define ANDROID_ASYNC_SOCKET_CONNECTOR_H_
+
+/*
+ * Contains declaration of an API that allows asynchronous connection to a
+ * socket with retries.
+ *
+ * The typical usage of this API is as such:
+ *
+ * 1. The client creates an async connector instance by calling async_socket_connector_new
+ * routine, supplying there address of the socket to connect, and a callback
+ * to invoke on connection events.
+ * 2. The client then proceeds with calling async_socket_connector_connect that
+ * would initiate connection attempts.
+ *
+ * The main job on the client side falls on the client's callback routine that
+ * serves the connection events. Once connection has been initiated, the connector
+ * will invoke that callback to report current connection status.
+ *
+ * In general, there are three connection events passed to the callback:
+ * 1. Success.
+ * 2. Failure.
+ * 3. Retry.
+ *
+ * Typically, when client's callback is called for successful connection, the
+ * client will pull connected socket's FD from the connector, and then this FD
+ * will be used by the client for I/O on the connected socket. If socket's FD
+ * is pulled by the client, it must return ASC_CB_KEEP from the callback.
+ *
+ * When client's callback is invoked with an error (ASC_CONNECTION_FAILED event),
+ * the client has an opportunity to review the error (available in 'errno'), and
+ * either abort the connection by returning ASC_CB_ABORT, or schedule a retry
+ * by returning ASC_CB_RETRY from the callback. If client returns ASC_CB_ABORT
+ * from the callback, the connector will stop connection attempts, and will
+ * self-destruct. If ASC_CB_RETRY is returned from the callback, the connector
+ * will retry connection attempt after timeout that was set by the caller in the
+ * call to async_socket_connector_new routine.
+ *
+ * When client's callback is invoked with ASC_CONNECTION_RETRY, the client has an
+ * opportunity to cancel further connection attempts by returning ASC_CB_ABORT,
+ * or it can allow another connection attempt by returning ASC_CB_RETRY.
+ *
+ * The client has no control over the lifespan of initialized connector instance.
+ * It always self-destructs after client's cllback returns with a status other
+ * than ASC_CB_RETRY.
+ */
+
+/* Declares async socket connector descriptor. */
+typedef struct AsyncSocketConnector AsyncSocketConnector;
+
+/* Enumerates connection events.
+ * Values from this enum are passed to the callback that connector's client uses
+ * to monitor connection status / progress.
+ */
+typedef enum ASCEvent {
+ /* Connection with the socket has been successfuly established. */
+ ASC_CONNECTION_SUCCEEDED,
+ /* A failure has occured while establising connection, with errno containing
+ * the actual error. */
+ ASC_CONNECTION_FAILED,
+ /* Async socket connector is about to retry the connection. */
+ ASC_CONNECTION_RETRY,
+} ASCEvent;
+
+/* Enumerates return values from the callback to the connector's client.
+ */
+typedef enum ASCCbRes {
+ /* Keep established connection. */
+ ASC_CB_KEEP,
+ /* Abort connection attempts. */
+ ASC_CB_ABORT,
+ /* Retry connection attempt. */
+ ASC_CB_RETRY,
+} ASCCbRes;
+
+/* Enumerates values returned from the connector routine.
+ */
+typedef enum ASCConnectRes {
+ /* Connection has succeeded in the connector routine. */
+ ASC_CONNECT_SUCCEEDED,
+ /* Connection has failed in the connector routine. */
+ ASC_CONNECT_FAILED,
+ /* Connection is in progress, and will be completed asynchronously. */
+ ASC_CONNECT_IN_PROGRESS,
+} ASCConnectRes;
+
+/* Declares callback that connector's client uses to monitor connection
+ * status / progress.
+ * Param:
+ * opaque - An opaque pointer associated with the client.
+ * connector - Connector instance for thecallback.
+ * event - Event that has occured. If event is set to ASC_CONNECTION_FAILED,
+ * errno contains connection error.
+ * Return:
+ * One of ASCCbRes values.
+ */
+typedef ASCCbRes (*asc_event_cb)(void* opaque,
+ AsyncSocketConnector* connector,
+ ASCEvent event);
+
+/* Creates and initializes AsyncSocketConnector instance.
+ * Param:
+ * address - Initialized socket address to connect to.
+ * retry_to - Retry timeout in milliseconds.
+ * cb, cb_opaque - Callback to invoke on connection events. This callback is
+ * required, and must not be NULL.
+ * Return:
+ * Initialized AsyncSocketConnector instance. Note that AsyncSocketConnector
+ * instance returned from this routine will be destroyed by the connector itself,
+ * when its work on connecting to the socket is completed. Typically, the
+ * connector wil destroy the descriptor after client's callback routine returns
+ * with the status other than ASC_CB_RETRY.
+ */
+extern AsyncSocketConnector* async_socket_connector_new(const SockAddress* address,
+ int retry_to,
+ asc_event_cb cb,
+ void* cb_opaque);
+
+/* Initiates asynchronous connection.
+ * Param:
+ * connector - Initialized AsyncSocketConnector instance.
+ * Return:
+ * Status indicating state of the connection: completed, failed, or in progress.
+ * Note that the connector will always invoke a callback passed to the
+ * async_socket_connector_new routine prior to exiting from this routine with
+ * statuses other ASC_CONNECT_IN_PROGRESS.
+ */
+extern ASCConnectRes async_socket_connector_connect(AsyncSocketConnector* connector);
+
+/* Pulls socket's file descriptor from the connector.
+ * This routine should be called from the connection callback on successful
+ * connection status. This will provide the connector's client with operational
+ * socket FD, and at the same time this will tell the connector not to close
+ * the FD when connector descriptor gets destroyed.
+ * Param:
+ * connector - Initialized AsyncSocketConnector instance.
+ * Return:
+ * File descriptor for the connected socket.
+ */
+extern int async_socket_connector_pull_fd(AsyncSocketConnector* connector);
+
+#endif /* ANDROID_ASYNC_SOCKET_CONNECTOR_H_ */
diff --git a/android/utils/debug.h b/android/utils/debug.h
index e97ec43..d7de9da 100644
--- a/android/utils/debug.h
+++ b/android/utils/debug.h
@@ -44,6 +44,7 @@
_VERBOSE_TAG(adbserver, "ADB server") \
_VERBOSE_TAG(adbclient, "ADB QEMU client") \
_VERBOSE_TAG(adb, "ADB debugger") \
+ _VERBOSE_TAG(asconnector, "Asynchronous socket connector") \
#define _VERBOSE_TAG(x,y) VERBOSE_##x,
typedef enum {