diff options
-rw-r--r-- | Makefile.common | 4 | ||||
-rw-r--r-- | android/adb-qemud.c | 285 | ||||
-rw-r--r-- | android/adb-qemud.h | 29 | ||||
-rw-r--r-- | android/adb-server.c | 568 | ||||
-rw-r--r-- | android/adb-server.h | 128 | ||||
-rw-r--r-- | android/avd/info.c | 5 | ||||
-rw-r--r-- | android/avd/info.h | 9 | ||||
-rw-r--r-- | android/avd/util.c | 21 | ||||
-rw-r--r-- | android/avd/util.h | 9 | ||||
-rw-r--r-- | android/qemu-setup.c | 29 | ||||
-rw-r--r-- | android/utils/debug.h | 2 | ||||
-rw-r--r-- | docs/ANDROID-ADB-QEMU.TXT | 53 | ||||
-rw-r--r-- | slirp-android/bootp.c | 4 |
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"); } |