aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVladimir Chtchetkine <vchtchetkine@google.com>2011-12-09 15:45:46 -0800
committerVladimir Chtchetkine <vchtchetkine@google.com>2011-12-13 11:37:19 -0800
commitd86c724b74e6c04a89219d87559d0b580e100445 (patch)
tree8a12ffaa8445fd55f9683321b623c2ab085096dd
parenta743be75c49f94811e6c8960ee0d6a2d0a65c700 (diff)
downloadexternal_qemu-d86c724b74e6c04a89219d87559d0b580e100445.zip
external_qemu-d86c724b74e6c04a89219d87559d0b580e100445.tar.gz
external_qemu-d86c724b74e6c04a89219d87559d0b580e100445.tar.bz2
Implement ADB communication over QEMU pipe
Change-Id: I62ff5898c7a955aaaa8af8f7ee7ed018af860f80
-rw-r--r--Makefile.common4
-rw-r--r--android/adb-qemud.c285
-rw-r--r--android/adb-qemud.h29
-rw-r--r--android/adb-server.c568
-rw-r--r--android/adb-server.h128
-rw-r--r--android/avd/info.c5
-rw-r--r--android/avd/info.h9
-rw-r--r--android/avd/util.c21
-rw-r--r--android/avd/util.h9
-rw-r--r--android/qemu-setup.c29
-rw-r--r--android/utils/debug.h2
-rw-r--r--docs/ANDROID-ADB-QEMU.TXT53
-rw-r--r--slirp-android/bootp.c4
13 files changed, 1138 insertions, 8 deletions
diff --git a/Makefile.common b/Makefile.common
index 45be256..59c18ea 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -427,7 +427,9 @@ CORE_MISC_SOURCES = \
android/sensors-port.c \
android/utils/timezone.c \
android/camera/camera-format-converters.c \
- android/camera/camera-service.c
+ android/camera/camera-service.c \
+ android/adb-server.c \
+ android/adb-qemud.c
$(call gen-hw-config-defs)
diff --git a/android/adb-qemud.c b/android/adb-qemud.c
new file mode 100644
index 0000000..bc51807
--- /dev/null
+++ b/android/adb-qemud.c
@@ -0,0 +1,285 @@
+/*
+ * 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 "qemu-common.h"
+#include "android/globals.h" /* for android_hw */
+#include "android/hw-qemud.h"
+#include "android/utils/misc.h"
+#include "android/utils/system.h"
+#include "android/utils/debug.h"
+#include "android/adb-server.h"
+#include "android/adb-qemud.h"
+
+#define E(...) derror(__VA_ARGS__)
+#define W(...) dwarning(__VA_ARGS__)
+#define D(...) VERBOSE_PRINT(adbclient,__VA_ARGS__)
+#define D_ACTIVE VERBOSE_CHECK(adbclient)
+#define QB(b, s) quote_bytes((const char*)b, (s < 32) ? s : 32)
+
+#define SERVICE_NAME "adb"
+
+/* Enumerates ADB client state values. */
+typedef enum AdbClientState {
+ /* Waiting on a connection from ADB host. */
+ ADBC_STATE_WAIT_ON_HOST,
+ /* ADB host is connected. Waiting on the transport initialization completion
+ * in the guest. */
+ ADBC_STATE_HOST_CONNECTED,
+ /* Connection between ADB host and ADB guest is fully established. */
+ ADBC_STATE_CONNECTED,
+ /* ADB host has been disconnected. */
+ ADBC_STATE_HOST_DISCONNECTED,
+ /* ADB guest has been disconnected. */
+ ADBC_STATE_GUEST_DISCONNECTED,
+} AdbClientState;
+
+/* ADB client descriptor. */
+typedef struct AdbClient AdbClient;
+struct AdbClient {
+ /* Opaque pointer returned from adb_server_register_guest API. */
+ void* opaque;
+ /* QEMUD client pipe for this client. */
+ QemudClient* qemud_client;
+ /* Connection state. */
+ AdbClientState state;
+};
+
+/********************************************************************************
+ * ADB host communication.
+ *******************************************************************************/
+
+/* A callback that is invoked when the host is connected.
+ * Param:
+ * opaque - AdbClient instance.
+ * connection - An opaque pointer that identifies connection with the ADB host.
+ */
+static void
+_adb_on_host_connected(void* opaque, void* connection)
+{
+ AdbClient* const adb_client = (AdbClient*)opaque;
+
+ if (adb_client->state == ADBC_STATE_WAIT_ON_HOST) {
+ D("ADB client %p(o=%p) is connected to the host %p",
+ adb_client, adb_client->opaque, connection);
+
+ /* Bump the state up. */
+ adb_client->state = ADBC_STATE_HOST_CONNECTED;
+
+ /* Notify the ADB guest that host has been connected.This will unblock
+ * the guest from a 'read', then guest will register the transport, and
+ * will send 'setart' request, indicating that it is ready to receive
+ * data from the host. */
+ qemud_client_send(adb_client->qemud_client, (const uint8_t*)"ok", 2);
+ } else {
+ D("Unexpected ADB host connection while state is %d", adb_client->state);
+ }
+}
+
+/* A callback that is invoked when the host gets disconnected.
+ * Param:
+ * opaque - AdbClient instance.
+ * connection - An opaque pointer that identifies connection with the ADB host.
+ */
+static void
+_adb_on_host_disconnect(void* opaque, void* connection)
+{
+ AdbClient* const adb_client = (AdbClient*)opaque;
+
+ D("ADB client %p(o=%p) is disconnected from the host %p",
+ adb_client, adb_client->opaque, connection);
+ adb_client->state = ADBC_STATE_HOST_DISCONNECTED;
+}
+
+/* A callback that is invoked when the host sends data.
+ * Param:
+ * opaque - AdbClient instance.
+ * connection - An opaque pointer that identifies connection with the ADB host.
+ * buff, size - Buffer containing the host data.
+ */
+static void
+_adb_on_host_data(void* opaque, void* connection, const void* buff, int size)
+{
+ AdbClient* const adb_client = (AdbClient*)opaque;
+ D("ADB client %p(o=%p) received from the host %p %d bytes in %s",
+ adb_client, adb_client->opaque, connection, size, QB(buff, size));
+
+ if (adb_client->state == ADBC_STATE_CONNECTED) {
+ /* Dispatch data down to the guest. */
+ qemud_client_send(adb_client->qemud_client, (const uint8_t*)buff, size);
+ } else {
+ D("Unexpected data from ADB host %p while client %p(o=%p) is in state %d",
+ connection, adb_client, adb_client->opaque, adb_client->state);
+ }
+}
+
+/* ADB guest API required for adb_server_register_guest */
+static AdbGuestRoutines _adb_client_routines = {
+ /* A callback that is invoked when the host is connected. */
+ _adb_on_host_connected,
+ /* A callback that is invoked when the host gets disconnected. */
+ _adb_on_host_disconnect,
+ /* A callback that is invoked when the host sends data. */
+ _adb_on_host_data,
+};
+
+/********************************************************************************
+ * ADB guest communication.
+ *******************************************************************************/
+
+/* Allocates AdbClient instance. */
+static AdbClient*
+_adb_client_new(void)
+{
+ AdbClient* adb_client;
+
+ ANEW0(adb_client);
+
+ return adb_client;
+}
+
+/* Frees AdbClient instance, allocated with _adb_client_new */
+static void
+_adb_client_free(AdbClient* adb_client)
+{
+ if (adb_client != NULL) {
+ free(adb_client);
+ }
+}
+
+/* A callback that is invoked when ADB guest sends data to the service.
+ * Param:
+ * opaque - AdbClient instance.
+ * msg, msglen - Message received from the ADB guest.
+ * client - adb QEMUD client.
+ */
+static void
+_adb_client_recv(void* opaque, uint8_t* msg, int msglen, QemudClient* client)
+{
+ AdbClient* const adb_client = (AdbClient*)opaque;
+
+ D("ADB client %p(o=%p) received from guest %d bytes in %s",
+ adb_client, adb_client->opaque, msglen, QB(msg, msglen));
+
+ /* Properly dispatch the message, depending on the client state. */
+ switch (adb_client->state) {
+ case ADBC_STATE_CONNECTED:
+ /* Connection is fully established. Dispatch the message to the
+ * host. */
+ adb_server_on_guest_message(adb_client->opaque, msg, msglen);
+ break;
+
+ case ADBC_STATE_WAIT_ON_HOST:
+ /* At this state the only message that is allowed is 'accept' */
+ if (msglen == 6 && !memcmp(msg, "accept", 6)) {
+ /* Register ADB guest connection with the ADB server. */
+ adb_client->opaque =
+ adb_server_register_guest(adb_client, &_adb_client_routines);
+ if (adb_client->opaque == NULL) {
+ D("Unable to register ADB guest with the ADB server.");
+ /* KO the guest. */
+ qemud_client_send(adb_client->qemud_client,
+ (const uint8_t*)"ko", 2);
+ }
+ } else {
+ D("Unexpected guest request while waiting on ADB host to connect.");
+ }
+ break;
+
+ case ADBC_STATE_HOST_CONNECTED:
+ /* At this state the only message that is allowed is 'start' */
+ if (msglen == 5 && !memcmp(msg, "start", 5)) {
+ adb_client->state = ADBC_STATE_CONNECTED;
+ adb_server_complete_connection(adb_client->opaque);
+ } else {
+ D("Unexpected request while waiting on connection to start.");
+ }
+ break;
+
+ default:
+ D("Unexpected ADB guest request '%s' while client state is %d.",
+ QB(msg, msglen), adb_client->state);
+ break;
+ }
+}
+
+/* A callback that is invoked when ADB guest disconnects from the service. */
+static void
+_adb_client_close(void* opaque)
+{
+ AdbClient* const adb_client = (AdbClient*)opaque;
+
+ D("ADB client %p(o=%p) is disconnected from the guest.",
+ adb_client, adb_client->opaque);
+ adb_client->state = ADBC_STATE_GUEST_DISCONNECTED;
+ if (adb_client->opaque != NULL) {
+ /* Close connection with the host. */
+ adb_server_on_guest_closed(adb_client->opaque);
+ }
+ _adb_client_free(adb_client);
+}
+
+/* A callback that is invoked when ADB daemon running inside the guest connects
+ * to the service.
+ * Client parameters are ignored here. Typically they contain the ADB port number
+ * which is always 5555 for the device / emulated system.
+ */
+static QemudClient*
+_adb_service_connect(void* opaque,
+ QemudService* serv,
+ int channel,
+ const char* client_param)
+{
+ /* Create new QEMUD client for the connection with ADB daemon. */
+ AdbClient* const adb_client = _adb_client_new();
+
+ D("Connecting ADB guest: '%s'", client_param ? client_param : "<null>");
+ adb_client->qemud_client =
+ qemud_client_new(serv, channel, client_param, adb_client,
+ _adb_client_recv, _adb_client_close, NULL, NULL);
+ if (adb_client->qemud_client == NULL) {
+ D("Unable to create QEMUD client for ADB guest.");
+ _adb_client_free(adb_client);
+ return NULL;
+ }
+
+ return adb_client->qemud_client;
+}
+
+/********************************************************************************
+ * ADB service API.
+ *******************************************************************************/
+
+void
+android_adb_service_init(void)
+{
+static int _inited = 0;
+
+ if (!adb_server_is_initialized()) {
+ return;
+ }
+
+ if (!_inited) {
+ QemudService* serv = qemud_service_register(SERVICE_NAME, 0, NULL,
+ _adb_service_connect,
+ NULL, NULL);
+ if (serv == NULL) {
+ derror("%s: Could not register '%s' service",
+ __FUNCTION__, SERVICE_NAME);
+ return;
+ }
+ D("%s: Registered '%s' qemud service", __FUNCTION__, SERVICE_NAME);
+ }
+}
diff --git a/android/adb-qemud.h b/android/adb-qemud.h
new file mode 100644
index 0000000..a6fccbe
--- /dev/null
+++ b/android/adb-qemud.h
@@ -0,0 +1,29 @@
+/*
+ * 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_ADB_PIPE_H_
+#define ANDROID_ADB_PIPE_H_
+
+/*
+ * Implements 'adb' QEMUD service that is responsible for the data exchange
+ * between the emulator and ADB daemon running on the guest.
+ */
+
+/* Initializes adb QEMUD service. */
+extern void android_adb_service_init(void);
+
+#endif /* ANDROID_ADB_PIPE_H_ */
+
diff --git a/android/adb-server.c b/android/adb-server.c
new file mode 100644
index 0000000..41b2ffd
--- /dev/null
+++ b/android/adb-server.c
@@ -0,0 +1,568 @@
+/*
+ * 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 "qemu-common.h"
+#include "sockets.h"
+#include "iolooper.h"
+#include "android/async-utils.h"
+#include "android/utils/debug.h"
+#include "android/utils/list.h"
+#include "android/utils/misc.h"
+#include "android/adb-server.h"
+
+#define E(...) derror(__VA_ARGS__)
+#define W(...) dwarning(__VA_ARGS__)
+#define D(...) VERBOSE_PRINT(adbserver,__VA_ARGS__)
+#define D_ACTIVE VERBOSE_CHECK(adbserver)
+#define QB(b, s) quote_bytes((const char*)b, (s < 32) ? s : 32)
+
+typedef struct AdbServer AdbServer;
+typedef struct AdbHost AdbHost;
+typedef struct AdbGuest AdbGuest;
+
+/* ADB guest connection descriptor. */
+struct AdbGuest {
+ /* Entry in the list of pending or connected ADB guests.
+ * NOTE: This must be the first entry in the descriptor! */
+ ACList list_entry;
+ /* Opaque pointer associated with the guest. */
+ void* opaque;
+ /* ADB server for this guest. */
+ AdbServer* adb_srv;
+ /* ADB host connected with this ADB guest. */
+ AdbHost* adb_host;
+ /* Callback routines for the ADB guest. */
+ AdbGuestRoutines* callbacks;
+ /* ADB guest connection status. If 0 indicates that ADB guest is not yet
+ * ready to receive data from the host. */
+ int is_connected;
+};
+
+/* ADB host connection descriptor. */
+struct AdbHost {
+ /* Entry in the list of pending or connected ADB hosts.
+ * NOTE: This must be the first entry in the descriptor! */
+ ACList list_entry;
+ /* ADB server for this host. */
+ AdbServer* adb_srv;
+ /* ADB socket connected with the host. */
+ int host_so;
+ /* I/O port for asynchronous I/O on the host socket. */
+ LoopIo io[1];
+ /* ADB guest connected with this ADB host. */
+ AdbGuest* adb_guest;
+ /* Pending data to send to the guest when it is fully connected. */
+ uint8_t* pending_data;
+ /* Size of the pending data buffer. */
+ int pending_data_size;
+ /* Contains data that are pending to be sent to the host. */
+ uint8_t* pending_send_buffer;
+ /* Number of bytes that are pending to be sent to the host. */
+ int pending_send_data_size;
+ /* Size of the pending_send_buffer */
+ int pending_send_buffer_size;
+};
+
+/* ADB server descriptor. */
+struct AdbServer {
+ /* ADB socket address. */
+ SockAddress socket_address;
+ /* Looper for async I/O on ADB server socket. */
+ Looper* looper;
+ /* I/O port for asynchronous I/O on ADB server socket. */
+ LoopIo io[1];
+ /* ADB port. */
+ int port;
+ /* Server socket. */
+ int so;
+ /* List of connected ADB hosts. */
+ ACList adb_hosts;
+ /* List of connected ADB guests. */
+ ACList adb_guests;
+ /* List of ADB hosts pending connection with ADB guest. */
+ ACList pending_hosts;
+ /* List of ADB guests pending connection with ADB host. */
+ ACList pending_guests;
+};
+
+/* One and only one ADB server instance. */
+static AdbServer _adb_server;
+/* ADB server initialization flag. */
+static int _adb_server_initialized = 0;
+
+/********************************************************************************
+ * ADB host API
+ *******************************************************************************/
+
+/* Creates and initializes a new AdbHost instance. */
+static AdbHost*
+_adb_host_new(AdbServer* adb_srv)
+{
+ AdbHost* adb_host;
+
+ ANEW0(adb_host);
+ alist_init(&adb_host->list_entry);
+ adb_host->adb_srv = adb_srv;
+ adb_host->host_so = -1;
+
+ return adb_host;
+}
+
+/* Frees AdbHost instance created with _adb_host_new routine. */
+static void
+_adb_host_free(AdbHost* adb_host)
+{
+ if (adb_host != NULL) {
+ /* At this point it must not be listed anywhere. */
+ assert(alist_is_empty(&adb_host->list_entry));
+
+ /* Close the host socket. */
+ if (adb_host->host_so >= 0) {
+ loopIo_done(adb_host->io);
+ socket_close(adb_host->host_so);
+ }
+
+ /* Free pending data buffers. */
+ if (adb_host->pending_data != NULL) {
+ free(adb_host->pending_data);
+ }
+ if (adb_host->pending_send_buffer != NULL) {
+ free(adb_host->pending_send_buffer);
+ }
+
+ AFREE(adb_host);
+ }
+}
+
+static void
+_adb_host_append_message(AdbHost* adb_host, const void* msg, int msglen)
+{
+ printf("Append %d bytes to ADB host buffer.\n", msglen);
+
+ /* Make sure that buffer can contain the appending data. */
+ if (adb_host->pending_send_buffer == NULL) {
+ adb_host->pending_send_buffer = (uint8_t*)malloc(msglen);
+ adb_host->pending_send_buffer_size = msglen;
+ } else if ((adb_host->pending_send_data_size + msglen) >
+ adb_host->pending_send_buffer_size) {
+ adb_host->pending_send_buffer =
+ (uint8_t*)realloc(adb_host->pending_send_buffer,
+ adb_host->pending_send_data_size + msglen);
+ adb_host->pending_send_buffer_size =
+ adb_host->pending_send_data_size + msglen;
+ }
+
+ if (adb_host->pending_send_buffer == NULL) {
+ D("Unable to allocate %d bytes for pending ADB host data.",
+ adb_host->pending_send_data_size + msglen);
+ adb_host->pending_send_buffer_size = adb_host->pending_send_data_size = 0;
+ loopIo_dontWantWrite(adb_host->io);
+ return;
+ }
+
+ memcpy(adb_host->pending_send_buffer + adb_host->pending_send_data_size,
+ msg, msglen);
+ loopIo_wantWrite(adb_host->io);
+}
+
+/* Connects ADB host with ADB guest. */
+static void
+_adb_connect(AdbHost* adb_host, AdbGuest* adb_guest)
+{
+ D("Connecting ADB host %p(so=%d) with ADB guest %p(o=%p)",
+ adb_host, adb_host->host_so, adb_guest, adb_guest->opaque);
+
+ adb_guest->adb_host = adb_host;
+ adb_host->adb_guest = adb_guest;
+ adb_guest->callbacks->on_connected(adb_guest->opaque, adb_guest);
+}
+
+/* Callback invoked when ADB host socket gets disconnected. */
+static void
+_on_adb_host_disconnected(AdbHost* adb_host)
+{
+ AdbGuest* const adb_guest = adb_host->adb_guest;
+
+ /* Notify the ADB guest that the host got disconnected. */
+ if (adb_guest != NULL) {
+ D("Disconnecting ADB host %p(so=%d) from ADB guest %p(o=%p)",
+ adb_host, adb_host->host_so, adb_guest, adb_guest->opaque);
+ adb_host->adb_guest = NULL;
+ adb_guest->callbacks->on_disconnect(adb_guest->opaque, adb_guest);
+ adb_guest->adb_host = NULL;
+ } else {
+ D("Disconnecting ADB host %p(so=%d)", adb_host, adb_host->host_so);
+ }
+
+ /* Destroy the host. */
+ alist_remove(&adb_host->list_entry);
+ _adb_host_free(adb_host);
+
+ /* Remove the guest from the list. */
+ if (adb_guest != NULL) {
+ alist_remove(&adb_guest->list_entry);
+ }
+}
+
+/* Read I/O callback on ADB host socket. */
+static void
+_on_adb_host_read(AdbHost* adb_host)
+{
+ char buff[4096];
+
+ /* Read data from the socket. */
+ const int size = socket_recv(adb_host->host_so, buff, sizeof(buff));
+ if (size < 0) {
+ D("Error while reading from ADB host %p(so=%d). Error: %s",
+ adb_host, adb_host->host_so, strerror(errno));
+ } else if (size == 0) {
+ /* This is a "disconnect" condition. */
+ _on_adb_host_disconnected(adb_host);
+ } else {
+ D("%s %d bytes received from ADB host %p(so=%d): %s",
+ adb_host->adb_guest ? "Transfer" : "Pend", size, adb_host,
+ adb_host->host_so, QB(buff, size));
+
+ /* Lets see if there is an ADB guest associated with this host, and it
+ * is ready to receive host data. */
+ AdbGuest* const adb_guest = adb_host->adb_guest;
+ if (adb_guest != NULL && adb_guest->is_connected) {
+ /* Channel the data through... */
+ adb_guest->callbacks->on_read(adb_guest->opaque, adb_guest, buff, size);
+ } else {
+ /* Pend the data for the upcoming guest connection. */
+ if (adb_host->pending_data == NULL) {
+ adb_host->pending_data = malloc(size);
+ } else {
+ adb_host->pending_data = realloc(adb_host->pending_data,
+ adb_host->pending_data_size + size);
+ }
+ if (adb_host->pending_data != NULL) {
+ memcpy(adb_host->pending_data + adb_host->pending_data_size,
+ buff, size);
+ adb_host->pending_data_size += size;
+ } else {
+ D("Unable to (re)allocate %d bytes for pending ADB host data",
+ adb_host->pending_data_size + size);
+ }
+ }
+ }
+}
+
+/* Write I/O callback on ADB host socket. */
+static void
+_on_adb_host_write(AdbHost* adb_host)
+{
+ while (adb_host->pending_send_data_size && adb_host->pending_send_buffer != NULL) {
+ const int sent = socket_send(adb_host->host_so,
+ adb_host->pending_send_buffer,
+ adb_host->pending_send_data_size);
+ if (sent < 0) {
+ if (errno == EWOULDBLOCK) {
+ /* Try again later. */
+ return;
+ } else {
+ D("Unable to send pending data to the ADB host: %s",
+ strerror(errno));
+ free(adb_host->pending_send_buffer);
+ adb_host->pending_send_buffer = NULL;
+ adb_host->pending_send_buffer_size = 0;
+ adb_host->pending_send_data_size = 0;
+ break;
+ }
+ } else if (sent == 0) {
+ /* Disconnect condition. */
+ free(adb_host->pending_send_buffer);
+ adb_host->pending_send_buffer = NULL;
+ adb_host->pending_send_buffer_size = 0;
+ adb_host->pending_send_data_size = 0;
+ _on_adb_host_disconnected(adb_host);
+ break;
+ } else if (sent == adb_host->pending_send_data_size) {
+ free(adb_host->pending_send_buffer);
+ adb_host->pending_send_buffer = NULL;
+ adb_host->pending_send_buffer_size = 0;
+ adb_host->pending_send_data_size = 0;
+ } else {
+ adb_host->pending_send_data_size -= sent;
+ memmove(adb_host->pending_send_buffer,
+ adb_host->pending_send_buffer + sent,
+ adb_host->pending_send_data_size);
+ return;
+ }
+ }
+
+ loopIo_dontWantWrite(adb_host->io);
+}
+
+/* I/O callback on ADB host socket. */
+static void
+_on_adb_host_io(void* opaque, int fd, unsigned events)
+{
+ AdbHost* const adb_host = (AdbHost*)opaque;
+ assert(fd == adb_host->host_so);
+
+ /* Dispatch I/O to read / write handlers. */
+ if ((events & LOOP_IO_READ) != 0) {
+ _on_adb_host_read(adb_host);
+ }
+ if ((events & LOOP_IO_WRITE) != 0) {
+ _on_adb_host_write(adb_host);
+ }
+}
+
+/********************************************************************************
+ * ADB guest API
+ *******************************************************************************/
+
+/* Creates and initializes a new AdbGuest instance. */
+static AdbGuest*
+_adb_guest_new(AdbServer* adb_srv)
+{
+ AdbGuest* adb_guest;
+
+ ANEW0(adb_guest);
+ alist_init(&adb_guest->list_entry);
+ adb_guest->adb_srv = adb_srv;
+
+ return adb_guest;
+}
+
+/* Frees AdbGuest instance created with _adb_guest_new routine. */
+static void
+_adb_guest_free(AdbGuest* adb_guest)
+{
+ if (adb_guest != NULL) {
+ /* At this poin the guest must not be in any of the lists. */
+ assert(alist_is_empty(&adb_guest->list_entry));
+ AFREE(adb_guest);
+ }
+}
+
+/********************************************************************************
+ * ADB server internals
+ *******************************************************************************/
+
+/* I/O callback on ADB server socket. */
+static void
+_on_server_socket_io(void* opaque, int fd, unsigned events)
+{
+ AdbHost* adb_host;
+ AdbGuest* adb_guest;
+ AdbServer* adb_srv = (AdbServer*)opaque;
+ assert(adb_srv->so == fd);
+
+ /* Since this is a server socket, we only expect a connection I/O here. */
+ if ((events & LOOP_IO_READ) == 0) {
+ D("Unexpected write I/O on ADB server socket");
+ return;
+ }
+
+ /* Create AdbHost instance for the new host connection. */
+ adb_host = _adb_host_new(adb_srv);
+
+ /* Accept the connection. */
+ adb_host->host_so = socket_accept(fd, &adb_srv->socket_address);
+ if (adb_host->host_so < 0) {
+ D("Unable to accept ADB connection: %s", strerror(errno));
+ _adb_host_free(adb_host);
+ return;
+ }
+
+ /* Prepare for I/O on the host connection socket. */
+ loopIo_init(adb_host->io, adb_srv->looper, adb_host->host_so,
+ _on_adb_host_io, adb_host);
+
+ /* Lets see if there is an ADB guest waiting for a host connection. */
+ adb_guest = (AdbGuest*)alist_remove_head(&adb_srv->pending_guests);
+ if (adb_guest != NULL) {
+ /* Tie up ADB host with the ADB guest. */
+ alist_insert_tail(&adb_srv->adb_guests, &adb_guest->list_entry);
+ alist_insert_tail(&adb_srv->adb_hosts, &adb_host->list_entry);
+ _adb_connect(adb_host, adb_guest);
+ } else {
+ /* Pend this connection. */
+ D("Pend ADB host %p(so=%d)", adb_host, adb_host->host_so);
+ alist_insert_tail(&adb_srv->pending_hosts, &adb_host->list_entry);
+ }
+
+ /* Enable I/O on the host socket. */
+ loopIo_wantRead(adb_host->io);
+}
+
+/********************************************************************************
+ * ADB server API
+ *******************************************************************************/
+int
+adb_server_init(int port)
+{
+ if (!_adb_server_initialized) {
+ /* Initialize the descriptor. */
+ memset(&_adb_server, 0, sizeof(_adb_server));
+ alist_init(&_adb_server.adb_hosts);
+ alist_init(&_adb_server.adb_guests);
+ alist_init(&_adb_server.pending_hosts);
+ alist_init(&_adb_server.pending_guests);
+ _adb_server.port = port;
+
+ /* Create looper for an async I/O on the server. */
+ _adb_server.looper = looper_newCore();
+ if (_adb_server.looper == NULL) {
+ E("Unable to create I/O looper for ADB server");
+ return -1;
+ }
+
+ /* Create loopback server socket for the ADB port. */
+ sock_address_init_inet(&_adb_server.socket_address,
+ SOCK_ADDRESS_INET_LOOPBACK, port);
+ _adb_server.so = socket_loopback_server(port, SOCKET_STREAM);
+ if (_adb_server.so < 0) {
+ E("Unable to create ADB server socket: %s", strerror(errno));
+ return -1;
+ }
+
+ /* Prepare server socket for I/O */
+ socket_set_nonblock(_adb_server.so);
+ loopIo_init(_adb_server.io, _adb_server.looper, _adb_server.so,
+ _on_server_socket_io, &_adb_server);
+ loopIo_wantRead(_adb_server.io);
+
+ D("ADB server has been initialized for port %d. Socket: %d",
+ port, _adb_server.so);
+
+ _adb_server_initialized = 1;
+ }
+
+ return 0;
+}
+
+int
+adb_server_is_initialized(void)
+{
+ return _adb_server_initialized;
+}
+
+void*
+adb_server_register_guest(void* opaque, AdbGuestRoutines* callbacks)
+{
+ if (_adb_server_initialized) {
+ AdbHost* adb_host;
+
+ /* Create and initialize ADB guest descriptor. */
+ AdbGuest* const adb_guest = _adb_guest_new(&_adb_server);
+ adb_guest->opaque = opaque;
+ adb_guest->callbacks = callbacks;
+
+ /* Lets see if there is a pending ADB host for the new guest. */
+ adb_host = (AdbHost*)alist_remove_head(&_adb_server.pending_hosts);
+ if (adb_host != NULL) {
+ /* Tie up ADB host with the ADB guest. */
+ alist_insert_tail(&_adb_server.adb_guests, &adb_guest->list_entry);
+ alist_insert_tail(&_adb_server.adb_hosts, &adb_host->list_entry);
+ _adb_connect(adb_host, adb_guest);
+ } else {
+ /* Host is not available. Pend this guest. */
+ D("Pend ADB guest %p(o=%p)", adb_guest, adb_guest->opaque);
+ alist_insert_tail(&_adb_server.pending_guests, &adb_guest->list_entry);
+ }
+
+ return adb_guest;
+ } else {
+ D("%s is called on an uninitialized ADB server.", __FUNCTION__);
+ return NULL;
+ }
+}
+
+void
+adb_server_complete_connection(void* opaque)
+{
+ AdbGuest* const adb_guest = (AdbGuest*)opaque;
+ AdbHost* const adb_host = adb_guest->adb_host;
+
+ /* Mark the guest as fully connected and ready for the host data. */
+ adb_guest->is_connected = 1;
+
+ /* Lets see if there is a host data pending transmission to the guest. */
+ if (adb_host->pending_data != NULL && adb_host->pending_data_size != 0) {
+ /* Send the pending data to the guest. */
+ D("Pushing %d bytes of the pending ADB host data.",
+ adb_host->pending_data_size);
+ adb_guest->callbacks->on_read(adb_guest->opaque, adb_guest,
+ adb_host->pending_data,
+ adb_host->pending_data_size);
+ free(adb_host->pending_data);
+ adb_host->pending_data = NULL;
+ adb_host->pending_data_size = 0;
+ }
+}
+
+void
+adb_server_on_guest_message(void* opaque, const uint8_t* msg, int msglen)
+{
+ AdbGuest* const adb_guest = (AdbGuest*)opaque;
+ AdbHost* const adb_host = adb_guest->adb_host;
+
+ if (adb_host != NULL) {
+ D("Sending %d bytes to the ADB host: %s", msglen, QB(msg, msglen));
+
+ /* Lets see if we can send the data immediatelly... */
+ if (adb_host->pending_send_buffer == NULL) {
+ /* There are no data that are pending to be sent to the host. Do the
+ * direct send. */
+ const int sent = socket_send(adb_host->host_so, msg, msglen);
+ if (sent < 0) {
+ if (errno == EWOULDBLOCK) {
+ } else {
+ D("Unable to send data to ADB host: %s", strerror(errno));
+ }
+ } else if (sent == 0) {
+ /* Disconnect condition. */
+ _on_adb_host_disconnected(adb_host);
+ } else if (sent < msglen) {
+ /* Couldn't send everything. Schedule write via I/O callback. */
+ _adb_host_append_message(adb_host, msg + sent, msglen - sent);
+ }
+ } else {
+ /* There are data that are pending to be sent to the host. We need
+ * to append new data to the end of the pending data buffer. */
+ _adb_host_append_message(adb_host, msg, msglen);
+ }
+ } else {
+ D("ADB host is disconneted and can't accept %d bytes in %s",
+ msglen, QB(msg, msglen));
+ }
+}
+
+void
+adb_server_on_guest_closed(void* opaque)
+{
+ AdbGuest* const adb_guest = (AdbGuest*)opaque;
+ AdbHost* const adb_host = adb_guest->adb_host;
+
+ /* Remove the guest from the list */
+ if (!alist_is_empty(&adb_guest->list_entry)) {
+ alist_remove(&adb_guest->list_entry);
+ }
+
+ /* Disassociate the host. */
+ if (adb_host != NULL) {
+ if (!alist_is_empty(&adb_host->list_entry)) {
+ alist_remove(&adb_host->list_entry);
+ }
+ _adb_host_free(adb_host);
+ }
+ _adb_guest_free(adb_guest);
+}
diff --git a/android/adb-server.h b/android/adb-server.h
new file mode 100644
index 0000000..33ede53
--- /dev/null
+++ b/android/adb-server.h
@@ -0,0 +1,128 @@
+/*
+ * 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_ADB_SERVER_H_
+#define ANDROID_ADB_SERVER_H_
+
+/*
+ * Encapsulates a socket server that is bound to ADB port, and bridges ADB host
+ * connections and data to ADB daemon running inside the guest.
+ */
+
+/* Callback to be invoked wheh host ADB gets connected with the guest ADB.
+ * Param:
+ * opaque - An opaque pointer associated with the guest. This pointer contains
+ * the 'opaque' parameter that was passed to the adb_server_register_guest
+ * routine.
+ * connection - An opaque pointer defining the connection between the host and
+ * the guest ADBs. This pointer must be used for further operations on the
+ * host <-> guest connection.
+ */
+typedef void (*adbguest_connect)(void* opaque, void* connection);
+
+/* Callback to be invoked wheh the host ADB sends data to the guest ADB.
+ * Param:
+ * opaque - An opaque pointer associated with the guest. This pointer contains
+ * the 'opaque' parameter that was passed to the adb_server_register_guest
+ * routine.
+ * connection - An opaque pointer defining the connection between the host and
+ * the guest ADB. This pointer must be used for further operations on the
+ * host <-> guest connection.
+ * buff, size - Buffer that has ben sent by the host.
+ */
+typedef void (*adbguest_read)(void* opaque,
+ void* connection,
+ const void* buff,
+ int size);
+
+/* Callback to be invoked wheh the host ADB gets disconnected.
+ * Param:
+ * opaque - An opaque pointer associated with the guest. This pointer contains
+ * the 'opaque' parameter that was passed to the adb_server_register_guest
+ * routine.
+ * connection - An opaque pointer defining the connection between the host and
+ * the guest ADB. This pointer must be used for further operations on the
+ * host <-> guest connection.
+ */
+typedef void (*adbguest_disconnect)(void* opaque, void* connection);
+
+/* Defines a set of callbacks for a guest ADB. */
+typedef struct AdbGuestRoutines AdbGuestRoutines;
+struct AdbGuestRoutines {
+ /* Callback to invoke when ADB host is connected. */
+ adbguest_connect on_connected;
+ /* Callback to invoke when ADB host is disconnected. */
+ adbguest_disconnect on_disconnect;
+ /* Callback to invoke when ADB host sends data. */
+ adbguest_read on_read;
+};
+
+/* Initializes ADB server.
+ * Param:
+ * port - socket port that is assigned for communication with the ADB host. This
+ * is 'base port' + 1.
+ * Return:
+ * 0 on success, or != 0 on failure.
+ */
+extern int adb_server_init(int port);
+
+/* Checks if ADB server has been initialized. */
+extern int adb_server_is_initialized(void);
+
+/* Registers ADB guest with the ADB server.
+ * There can be two cases here, as far as connection with the host is concerned:
+ * - There is no host connection to immediately associate the guest with. In
+ * this case the guest will be registered as "pending connection", and routine
+ * will return.
+ * - There is a pending host connection to associate with the new guest. In this
+ * case the association will be made in this routine, and 'adbguest_connect'
+ * callback will be called before this routine returns.
+ * Param:
+ * opaque Opaque pointer associated with the guest. This pointer will be passed
+ * back to thee guest API in callback routines.
+ * callbacks Contains callback routines for the registering guest.
+ * Return:
+ * An opaque pointer associated with the ADB guest on success, or NULL on
+ * failure. The pointer returned from this routine must be passed into ADB
+ * server API called from the guest.
+ */
+extern void* adb_server_register_guest(void* opaque, AdbGuestRoutines* callbacks);
+
+/* Completes connection with the guest.
+ * This routine is called by the guest when it receives a 'start' request from
+ * ADB guest. This request tells the system that ADB daemon running inside the
+ * guest is ready to receive data.
+ * Param:
+ * opaque - An opaque pointer returned from adb_server_register_guest.
+ */
+extern void adb_server_complete_connection(void* opaque);
+
+/* Handles data received from the guest.
+ * Param:
+ * opaque - An opaque pointer returned from adb_server_register_guest.
+ * data, size - Data buffer received from the guest.
+ */
+extern void adb_server_on_guest_message(void* opaque,
+ const uint8_t* data,
+ int size);
+
+/* Notifies the ADB server that the guest has closed its connection.
+ * Param:
+ * opaque - An opaque pointer returned from adb_server_register_guest.
+ */
+extern void adb_server_on_guest_closed(void* opaque);
+
+#endif /* ANDROID_ADB_SERVER_H_ */
diff --git a/android/avd/info.c b/android/avd/info.c
index 7e4f93e..815b0b4 100644
--- a/android/avd/info.c
+++ b/android/avd/info.c
@@ -1202,3 +1202,8 @@ avdInfo_getCharmapFile( AvdInfo* i, const char* charmapName )
return _avdInfo_getContentOrSdkFilePath(i, fileName);
}
+
+int avdInfo_getAdbdCommunicationMode( AvdInfo* i )
+{
+ return path_getAdbdCommunicationMode(i->androidOut);
+}
diff --git a/android/avd/info.h b/android/avd/info.h
index 34997b4..74a9b01 100644
--- a/android/avd/info.h
+++ b/android/avd/info.h
@@ -243,6 +243,15 @@ char* avdInfo_getTracePath( AvdInfo* i, const char* traceName );
*/
const char* avdInfo_getCoreHwIniPath( AvdInfo* i );
+/* Returns mode in which ADB daemon running in the guest communicates with the
+ * emulator
+ * Return:
+ * 0 - ADBD communicates with the emulator via forwarded TCP port 5555 (a
+ * "legacy" mode).
+ * 1 - ADBD communicates with the emulator via 'adb' QEMUD service.
+ */
+int avdInfo_getAdbdCommunicationMode( AvdInfo* i );
+
/* */
#endif /* ANDROID_AVD_INFO_H */
diff --git a/android/avd/util.c b/android/avd/util.c
index 304b98f..82145b5 100644
--- a/android/avd/util.c
+++ b/android/avd/util.c
@@ -313,3 +313,24 @@ path_getBuildTargetApiLevel( const char* androidOut )
return level;
}
+int
+path_getAdbdCommunicationMode( const char* androidOut )
+{
+ char* prop = _getBuildProperty(androidOut, "ro.adb.qemud");
+ if (prop != NULL) {
+ long val = 0;
+ char* end;
+ val = strtol(prop, &end, 10);
+ if (end == NULL || *end != '\0' || val != (int)val) {
+ D("Invalid ro.adb.qemud build property: '%s'", prop);
+ val = 0;
+ } else {
+ D("Found ro.adb.qemud build property: %d", val);
+ }
+ AFREE(prop);
+ return (int)val;
+ } else {
+ /* Missing ro.adb.qemud means "legacy" ADBD. */
+ return 0;
+ }
+}
diff --git a/android/avd/util.h b/android/avd/util.h
index 877d5aa..edef6a0 100644
--- a/android/avd/util.h
+++ b/android/avd/util.h
@@ -64,4 +64,13 @@ char* path_getBuildTargetAbi( const char* androidOut );
*/
int path_getBuildTargetApiLevel( const char* androidOut );
+/* Returns mode in which ADB daemon running in the guest communicates with the
+ * emulator
+ * Return:
+ * 0 - ADBD communicates with the emulator via forwarded TCP port 5555 (a
+ * "legacy" mode).
+ * 1 - ADBD communicates with the emulator via 'adb' QEMUD service.
+ */
+int path_getAdbdCommunicationMode( const char* androidOut );
+
#endif /* _ANDROID_AVD_UTIL_H */
diff --git a/android/qemu-setup.c b/android/qemu-setup.c
index f1a12ee..181c95b 100644
--- a/android/qemu-setup.c
+++ b/android/qemu-setup.c
@@ -23,6 +23,8 @@
#include "android/utils/path.h"
#include "android/utils/system.h"
#include "android/utils/bufprint.h"
+#include "android/adb-server.h"
+#include "android/adb-qemud.h"
#define D(...) do { if (VERBOSE_CHECK(init)) dprint(__VA_ARGS__); } while (0)
@@ -252,6 +254,8 @@ void android_emulation_setup( void )
exit(1);
}
+ int legacy_adb = avdInfo_getAdbdCommunicationMode(android_avdInfo) ? 0 : 1;
+
if (android_op_ports) {
char* comma_location;
char* end;
@@ -276,9 +280,16 @@ void android_emulation_setup( void )
// Set up redirect from host to guest system. adbd on the guest listens
// on 5555.
- slirp_redir( 0, adb_port, guest_ip, 5555 );
+ if (legacy_adb) {
+ slirp_redir( 0, adb_port, guest_ip, 5555 );
+ } else {
+ adb_server_init(adb_port);
+ android_adb_service_init();
+ }
if ( control_console_start( console_port ) < 0 ) {
- slirp_unredir( 0, adb_port );
+ if (legacy_adb) {
+ slirp_unredir( 0, adb_port );
+ }
}
base_port = console_port;
@@ -304,12 +315,20 @@ void android_emulation_setup( void )
for ( ; tries > 0; tries--, base_port += 2 ) {
/* setup first redirection for ADB, the Android Debug Bridge */
- if ( slirp_redir( 0, base_port+1, guest_ip, 5555 ) < 0 )
- continue;
+ if (legacy_adb) {
+ if ( slirp_redir( 0, base_port+1, guest_ip, 5555 ) < 0 )
+ continue;
+ } else {
+ if (adb_server_init(base_port+1))
+ continue;
+ android_adb_service_init();
+ }
/* setup second redirection for the emulator console */
if ( control_console_start( base_port ) < 0 ) {
- slirp_unredir( 0, base_port+1 );
+ if (legacy_adb) {
+ slirp_unredir( 0, base_port+1 );
+ }
continue;
}
diff --git a/android/utils/debug.h b/android/utils/debug.h
index ea5a6b0..272499a 100644
--- a/android/utils/debug.h
+++ b/android/utils/debug.h
@@ -39,6 +39,8 @@
_VERBOSE_TAG(adevice, "android device connected via port forwarding") \
_VERBOSE_TAG(sensors_port, "sensors emulator connected to android device") \
_VERBOSE_TAG(gles, "hardware OpenGLES emulation") \
+ _VERBOSE_TAG(adbserver, "ADB server") \
+ _VERBOSE_TAG(adbclient, "ADB QEMU client") \
#define _VERBOSE_TAG(x,y) VERBOSE_##x,
typedef enum {
diff --git a/docs/ANDROID-ADB-QEMU.TXT b/docs/ANDROID-ADB-QEMU.TXT
new file mode 100644
index 0000000..fba3c82
--- /dev/null
+++ b/docs/ANDROID-ADB-QEMU.TXT
@@ -0,0 +1,53 @@
+ADB COMMUNICATION OVER QEMUD SERVICE
+
+I. Overview:
+------------
+
+In certain cases ADB communication over TCP port forwarding might be unreliable.
+For instance, connection manager in the guest system may decide to reset all
+network connections, in which case connection between the ADB host, and ADB
+daemon running in the guest will be broken, and will not be reestablish until ADB
+host is killed, and then restarted. To address this issue ADB communication has
+been moved to use QEMUD pipe to transfer data between the emulator, and the
+ADB daemon running in the guest.
+
+
+II. Implementation:
+-------------------
+
+There are two major components implemented in the emulator to support ADB
+communication over QEMUD pipe:
+
+1. ADB server.
+2. ADB QEMUD service (or ADB client).
+
+ADB server is implemented as a socket server that is bound to the ADB port (which
+is the 'base port' + 1), and is responsible for accepting connections coming from
+the ADB host, and associatiating accepted connections with an ADB client.
+
+ADB client is implemented as QEMUD service (named 'adb'), and is responsible for
+accepting connections coming from the ADB daemon running in the guest, and
+associating accepted connections with ADB server.
+
+Essentially, ADB server, and ADB client serve as a bridge between ADB running on
+the host, and ADB running on the guest: ADB server receives data from ADB host,
+and channels them through ADB client to the ADB guest. Likewise, ADB client
+receives data from the ADB guest, and channels them through ADB server to the ADB
+host. The only tricky part here is establishing the connection. Once connection
+is established, everything is straight forward.
+
+II.III Establishing the connection:
+-----------------------------------
+
+The connection between ADB host and ADB guest is considered to be established,
+when there is an association between ADB socket connection in the ADB server, and
+QEMUD pipe connection in the ADB client, and ADB guest is ready to receive data
+from the ADB host.
+
+Since there is no particular order, in which ADB server, and ADB client
+connections are made, ADB server runs a list of pending host and guest
+connections. Every time either connection is made, that connection goes into a
+pending list on condition that there is no pending connection on the other side.
+If, however, there is already a pending connection from the other side, that
+pending connection is removed from the pending list, and gets associated with the
+new connection.
diff --git a/slirp-android/bootp.c b/slirp-android/bootp.c
index 814b030..1ff740e 100644
--- a/slirp-android/bootp.c
+++ b/slirp-android/bootp.c
@@ -65,7 +65,7 @@ static BOOTPClient *get_new_addr(SockAddress* paddr,
found:
bc = &bootp_clients[i];
bc->allocated = 1;
- sock_address_init_inet( paddr,
+ sock_address_init_inet( paddr,
special_addr_ip | (i+START_ADDR),
BOOTP_CLIENT );
return bc;
@@ -170,7 +170,7 @@ static void bootp_reply(const struct bootp_t *bp)
dhcp_decode(bp, &dhcp_msg_type, &preq_addr);
dprintf("bootp packet op=%d msgtype=%d", bp->bp_op, dhcp_msg_type);
if (preq_addr) {
- dprintf(" req_addr=%08x\n", ntohl(*preq_addr));
+ dprintf(" req_addr=%08x\n", ntohl(*(uint32_t*)preq_addr));
} else {
dprintf("\n");
}