diff options
author | Vladimir Chtchetkine <vchtchetkine@google.com> | 2011-12-09 15:45:46 -0800 |
---|---|---|
committer | Vladimir Chtchetkine <vchtchetkine@google.com> | 2011-12-13 11:37:19 -0800 |
commit | d86c724b74e6c04a89219d87559d0b580e100445 (patch) | |
tree | 8a12ffaa8445fd55f9683321b623c2ab085096dd /android/adb-server.c | |
parent | a743be75c49f94811e6c8960ee0d6a2d0a65c700 (diff) | |
download | external_qemu-d86c724b74e6c04a89219d87559d0b580e100445.zip external_qemu-d86c724b74e6c04a89219d87559d0b580e100445.tar.gz external_qemu-d86c724b74e6c04a89219d87559d0b580e100445.tar.bz2 |
Implement ADB communication over QEMU pipe
Change-Id: I62ff5898c7a955aaaa8af8f7ee7ed018af860f80
Diffstat (limited to 'android/adb-server.c')
-rw-r--r-- | android/adb-server.c | 568 |
1 files changed, 568 insertions, 0 deletions
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); +} |