| /* |
| * 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/sockets.h" |
| #include "android/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); |
| } |