aboutsummaryrefslogtreecommitdiffstats
path: root/android/android-device.c
diff options
context:
space:
mode:
Diffstat (limited to 'android/android-device.c')
-rw-r--r--android/android-device.c1526
1 files changed, 0 insertions, 1526 deletions
diff --git a/android/android-device.c b/android/android-device.c
deleted file mode 100644
index 5f88108..0000000
--- a/android/android-device.c
+++ /dev/null
@@ -1,1526 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * Encapsulates exchange protocol between the emulator, and an Android device
- * that is connected to the host via USB. The communication is established over
- * a TCP port forwarding, enabled by ADB.
- */
-
-#include "android/android-device.h"
-#include "utils/panic.h"
-#include "iolooper.h"
-
-#define E(...) derror(__VA_ARGS__)
-#define W(...) dwarning(__VA_ARGS__)
-#define D(...) VERBOSE_PRINT(adevice,__VA_ARGS__)
-#define D_ACTIVE VERBOSE_CHECK(adevice)
-
-/********************************************************************************
- * Common android device socket
- *******************************************************************************/
-
-/* Milliseconds between retrying asynchronous connections to the device. */
-#define ADS_RETRY_CONNECTION_TIMEOUT 3000
-
-/* Socket type. */
-typedef enum ADSType {
- /* Query socket. */
- ADS_TYPE_QUERY = 0,
- /* Events socket. */
- ADS_TYPE_EVENT = 1
-} ADSType;
-
-/* Status of the socket. */
-typedef enum ADSStatus {
- /* Socket is disconnected. */
- ADS_DISCONNECTED,
- /* Connection process has been started. */
- ADS_CONNECTING,
- /* Connection has been established. */
- ADS_CONNECTED,
- /* Socket has been registered with the server. */
- ADS_REGISTERED,
-} ADSStatus;
-
-/* Identifies socket as a "query" socket with the server. */
-static const char* _ads_query_socket_id = "query";
-/* Identifies socket as an "event" socket with the server. */
-static const char* _ads_event_socket_id = "event";
-
-/* Android device socket descriptor. */
-typedef struct AndroidDevSocket AndroidDevSocket;
-
-/*
- * Callback routines.
- */
-
-/* Callback routine that is called when a socket is connected.
- * Param:
- * opaque - Opaque pointer associated with the socket. Typicaly it's the same
- * pointer that is associated with AndroidDevice instance.
- * ads - Connection socket.
- * failure - If zero, indicates that socket has been successuly connected. If a
- * connection error has occured, this parameter contains the error code (as
- * in 'errno).
- */
-typedef void (*ads_socket_connected_cb)(void* opaque,
- struct AndroidDevSocket* ads,
- int failure);
-
-/* Android device socket descriptor. */
-struct AndroidDevSocket {
- /* Socket type. */
- ADSType type;
- /* Socket status. */
- ADSStatus socket_status;
- /* TCP address for the socket. */
- SockAddress address;
- /* Android device descriptor that owns the socket. */
- AndroidDevice* ad;
- /* Opaque pointer associated with the socket. Typicaly it's the same
- * pointer that is associated with AndroidDevice instance.*/
- void* opaque;
- /* Deadline for current I/O performed on the socket. */
- Duration deadline;
- /* Socket's file descriptor. */
- int fd;
-};
-
-/* Query socket descriptor. */
-typedef struct AndroidQuerySocket {
- /* Common device socket. */
- AndroidDevSocket dev_socket;
-} AndroidQuerySocket;
-
-/* Describes data to send via an asynchronous socket. */
-typedef struct AsyncSendBuffer {
- /* Next buffer to send. */
- struct AsyncSendBuffer* next;
- /* Callback to invoke when data transfer is completed. */
- async_send_cb complete_cb;
- /* An opaque pointer to pass to the transfer completion callback. */
- void* complete_opaque;
- /* Data to send. */
- uint8_t* data;
- /* Size of the entire data buffer. */
- int data_size;
- /* Remaining bytes to send. */
- int data_remaining;
- /* Boolean flag indicating whether to free data buffer upon completion. */
- int free_on_completion;
-} AsyncSendBuffer;
-
-/* Event socket descriptor. */
-typedef struct AndroidEventSocket {
- /* Common socket descriptor. */
- AndroidDevSocket dev_socket;
- /* Asynchronous connector to the device. */
- AsyncConnector connector[1];
- /* I/O port for asynchronous I/O on this socket. */
- LoopIo io[1];
- /* Asynchronous string reader. */
- AsyncLineReader alr;
- /* Callback to call at the end of the asynchronous connection to this socket.
- * Can be NULL. */
- ads_socket_connected_cb on_connected;
- /* Callback to call when an event is received on this socket. Can be NULL. */
- event_cb on_event;
- /* Lists buffers that are pending to be sent. */
- AsyncSendBuffer* send_pending;
-} AndroidEventSocket;
-
-/* Android device descriptor. */
-struct AndroidDevice {
- /* Query socket for the device. */
- AndroidQuerySocket query_socket;
- /* Event socket for the device. */
- AndroidEventSocket event_socket;
- /* An opaque pointer associated with this descriptor. */
- void* opaque;
- /* I/O looper for synchronous I/O on the sockets for this device. */
- IoLooper* io_looper;
- /* Timer that is used to retry asynchronous connections. */
- LoopTimer timer[1];
- /* I/O looper for asynchronous I/O. */
- Looper* looper;
- /* Callback to call when device is fully connected. */
- device_connected_cb on_connected;
- /* I/O failure callback .*/
- io_failure_cb on_io_failure;
-};
-
-/* Creates descriptor for a buffer to send asynchronously.
- * Param:
- * data, size - Buffer to send.
- * free_on_close - Boolean flag indicating whether to free data buffer upon
- * completion.
- * cb - Callback to invoke when data transfer is completed.
- * opaque - An opaque pointer to pass to the transfer completion callback.
- */
-static AsyncSendBuffer*
-_async_send_buffer_create(void* data,
- int size,
- int free_on_close,
- async_send_cb cb,
- void* opaque)
-{
- AsyncSendBuffer* desc = malloc(sizeof(AsyncSendBuffer));
- if (desc == NULL) {
- APANIC("Unable to allocate %d bytes for AsyncSendBuffer",
- sizeof(AsyncSendBuffer));
- }
- desc->next = NULL;
- desc->data = (uint8_t*)data;
- desc->data_size = desc->data_remaining = size;
- desc->free_on_completion = free_on_close;
- desc->complete_cb = cb;
- desc->complete_opaque = opaque;
-
- return desc;
-}
-
-/* Completes data transfer for the given descriptor.
- * Note that this routine will free the descriptor.
- * Param:
- * desc - Asynchronous data transfer descriptor. Will be freed upon the exit
- * from this routine.
- * res - Data transfer result.
- */
-static void
-_async_send_buffer_complete(AsyncSendBuffer* desc, ATResult res)
-{
- /* Invoke completion callback (if present) */
- if (desc->complete_cb) {
- desc->complete_cb(desc->complete_opaque, res, desc->data, desc->data_size,
- desc->data_size - desc->data_remaining);
- }
-
- /* Free data buffer (if required) */
- if (desc->free_on_completion) {
- free(desc->data);
- }
-
- /* Free the descriptor itself. */
- free(desc);
-}
-
-/********************************************************************************
- * Common socket declarations
- *******************************************************************************/
-
-/* Initializes common device socket.
- * Param:
- * ads - Socket descriptor to initialize.
- * opaque - An opaque pointer to associate with the socket. Typicaly it's the
- * same pointer that is associated with AndroidDevice instance.
- * ad - Android device descriptor that owns the socket.
- * port - Socket's TCP port.
- * type - Socket type (query, or event).
- */
-static int _android_dev_socket_init(AndroidDevSocket* ads,
- void* opaque,
- AndroidDevice* ad,
- int port,
- ADSType type);
-
-/* Destroys socket descriptor. */
-static void _android_dev_socket_destroy(AndroidDevSocket* ads);
-
-/* Callback that is ivoked from _android_dev_socket_connect when a file
- * descriptor has been created for a socket.
- * Param:
- * ads - Socket descritor.
- * opaque - An opaque pointer associated with the callback.
- */
-typedef void (*on_socked_fd_created)(AndroidDevSocket* ads, void* opaque);
-
-/* Synchronously connects to the socket, and registers it with the server.
- * Param:
- * ads - Socket to connect. Must have 'deadline' field properly setup.
- * cb, opaque - A callback to invoke (and opaque parameters to pass to the
- * callback) when a file descriptor has been created for a socket. These
- * parameters are optional and can be NULL.
- * Return:
- * 0 on success, -1 on failure with errno containing the reason for failure.
- */
-static int _android_dev_socket_connect(AndroidDevSocket* ads,
- on_socked_fd_created cb,
- void* opaque);
-
-/* Synchronously registers a connected socket with the server.
- * Param:
- * ads - Socket to register. Must be connected, and must have 'deadline' field
- * properly setup.
- * Return:
- * 0 on success, -1 on failure with errno containing the reason for failure.
- */
-static int _android_dev_socket_register(AndroidDevSocket* ads);
-
-/* Disconnects the socket (if it was connected) */
-static void _android_dev_socket_disconnect(AndroidDevSocket* ads);
-
-/* Synchronously sends data to the socket.
- * Param:
- * ads - Socket to send the data to. Must be connected, and must have 'deadline'
- * field properly setup.
- * buff, buffsize - Buffer to send.
- * Return:
- * Number of bytes sent on success, or -1 on failure with errno containing the
- * reason for failure.
- */
-static int _android_dev_socket_send(AndroidDevSocket* ads,
- const char* buff,
- int buffsize);
-
-/* Synchronously receives data from the socket.
- * Param:
- * ads - Socket to receive the data from. Must be connected, and must have
- * 'deadline' field properly setup.
- * buff, buffsize - Buffer where to receive data.
- * Return:
- * Number of bytes received on success, or -1 on failure with errno containing
- * the reason for failure.
- */
-static int _android_dev_socket_recv(AndroidDevSocket* ads,
- char* buf,
- int bufsize);
-
-/* Synchronously reads zero-terminated string from the socket.
- * Param:
- * ads - Socket to read the string from. Must be connected, and must have
- * 'deadline' field properly setup.
- * str, strsize - Buffer where to read the string.
- * Return:
- * Number of charactes read into the string buffer (including zero-terminator)
- * on success, or -1 on failure with 'errno' containing the reason for failure.
- * If this routine returns -1, and errno contains ENOMEM, this is an indicator
- * that supplied string buffer was too small for the receiving string.
- */
-static int _android_dev_socket_read_string(AndroidDevSocket* ads,
- char* str,
- int strsize);
-
-/* Synchronously reads zero-terminated query response from the socket.
- * All queries respond with an 'ok', or 'ko' prefix, indicating a success, or
- * failure. Prefix can be followed by more query response data, separated from
- * the prefix with a ':' character. This routine helps separating prefix from the
- * data, by placing only the query response data into provided buffer. 'ko' or
- * 'ok' will be encoded in the return value.
- * Param:
- * ads - Socket to read the response from. Must be connected, and must have
- * 'deadline' field properly setup.
- * data, datasize - Buffer where to read the query response data.
- * Return:
- * Number of charactes read into the data buffer (including zero-terminator) on
- * success, or -1 on failure with errno containing the reason for failure.
- * If the query has been completed with 'ko', this routine will return -1, with
- * errno set to 0. If this routine returned -1, and errno is set to EINVAL, this
- * indicates that reply string didn't match expected query reply format.
- */
-static int _android_dev_socket_read_response(AndroidDevSocket* ads,
- char* str,
- int strsize);
-
-/* Gets ID string for the channel. */
-AINLINED const char*
-_ads_id_str(AndroidDevSocket* ads)
-{
- return (ads->type == ADS_TYPE_QUERY) ? _ads_query_socket_id :
- _ads_event_socket_id;
-}
-
-/* Gets socket's TCP port. */
-AINLINED int
-_ads_port(AndroidDevSocket* ads)
-{
- return sock_address_get_port(&ads->address);
-}
-
-/* Gets synchronous I/O looper for the socket. */
-AINLINED IoLooper*
-_ads_io_looper(AndroidDevSocket* ads)
-{
- return ads->ad->io_looper;
-}
-
-/* Sets deadline on a socket operation, given relative timeout.
- * Param:
- * ads - Socket descriptor to set deadline for.
- * to - Relative timeout (in millisec) for the operation.
- * AD_INFINITE_WAIT passed in this parameter means "no deadline".
- */
-AINLINED void
-_ads_set_deadline(AndroidDevSocket* ads, int to)
-{
- ads->deadline = (to == AD_INFINITE_WAIT) ? DURATION_INFINITE :
- iolooper_now() + to;
-}
-
-/********************************************************************************
- * Common socket implementation
- *******************************************************************************/
-
-static int
-_android_dev_socket_init(AndroidDevSocket* ads,
- void* opaque,
- AndroidDevice* ad,
- int port,
- ADSType type)
-{
- ads->type = type;
- ads->socket_status = ADS_DISCONNECTED;
- ads->opaque = opaque;
- ads->ad = ad;
- ads->fd = -1;
- sock_address_init_inet(&ads->address, SOCK_ADDRESS_INET_LOOPBACK, port);
-
- return 0;
-}
-
-static void
-_android_dev_socket_destroy(AndroidDevSocket* ads)
-{
- /* Make sure it's disconnected. */
- _android_dev_socket_disconnect(ads);
-
- /* Finalize socket address. */
- sock_address_done(&ads->address);
- memset(&ads->address, 0, sizeof(ads->address));
-}
-
-static int
-_android_dev_socket_connect(AndroidDevSocket* ads,
- on_socked_fd_created cb,
- void* opaque)
-{
- int res;
-
- /* Create communication socket. */
- ads->fd = socket_create_inet(SOCKET_STREAM);
- if (ads->fd < 0) {
- D("Unable to create socket for channel '%s'@%d: %s",
- _ads_id_str(ads), _ads_port(ads), strerror(errno));
- return -1;
- }
- socket_set_nonblock(ads->fd);
-
- /* Invoke FD creation callback (if required) */
- if (cb != NULL) {
- cb(ads, opaque);
- }
-
- /* Synchronously connect to it. */
- ads->socket_status = ADS_CONNECTING;
- iolooper_add_write(_ads_io_looper(ads), ads->fd);
- res = socket_connect(ads->fd, &ads->address);
- while (res < 0 && errno == EINTR) {
- res = socket_connect(ads->fd, &ads->address);
- }
-
- if (res && (errno == EINPROGRESS || errno == EWOULDBLOCK || errno == EAGAIN)) {
- /* Connection is delayed. Wait for it until timeout expires. */
- res = iolooper_wait_absolute(_ads_io_looper(ads), ads->deadline);
- if (res > 0) {
- /* Pick up on possible connection error. */
- errno = socket_get_error(ads->fd);
- res = (errno == 0) ? 0 : -1;
- } else {
- res = -1;
- }
- }
- iolooper_del_write(_ads_io_looper(ads), ads->fd);
-
- if (res == 0) {
- D("Channel '%s'@%d is connected", _ads_id_str(ads), _ads_port(ads));
- /* Socket is connected. Now register it with the server. */
- ads->socket_status = ADS_CONNECTED;
- res = _android_dev_socket_register(ads);
- } else {
- D("Unable to connect channel '%s' to port %d: %s",
- _ads_id_str(ads), _ads_port(ads), strerror(errno));
- }
-
- if (res) {
- _android_dev_socket_disconnect(ads);
- }
-
- return res;
-}
-
-static int
-_android_dev_socket_register(AndroidDevSocket* ads)
-{
- /* Make sure that socket is connected. */
- if (ads->socket_status < ADS_CONNECTED) {
- D("Attempt to register a disconnected channel '%s'@%d",
- _ads_id_str(ads), _ads_port(ads));
- errno = ECONNRESET;
- return -1;
- }
-
- /* Register this socket accordingly to its type. */
- const char* reg_str = _ads_id_str(ads);
- int res = _android_dev_socket_send(ads, reg_str, strlen(reg_str) + 1);
- if (res > 0) {
- /* Receive reply. Note that according to the protocol, the server should
- * reply to channel registration with 'ok', or 'ko' (just like with queries),
- * so we can use query reply reader here. */
- char reply[256];
- res = _android_dev_socket_read_response(ads, reply, sizeof(reply));
- if (res >= 0) {
- /* Socket is now registered. */
- ads->socket_status = ADS_REGISTERED;
- D("Channel '%s'@%d is registered", _ads_id_str(ads), _ads_port(ads));
- res = 0;
- } else {
- if (errno == 0) {
- /* 'ko' condition */
- D("Device failed registration of channel '%s'@%d: %s",
- _ads_id_str(ads), _ads_port(ads), reply);
- errno = EINVAL;
- } else {
- D("I/O failure while registering channel '%s'@%d: %s",
- _ads_id_str(ads), _ads_port(ads), strerror(errno));
- }
- res = -1;
- }
- } else {
- D("Unable to send registration query for channel '%s'@%d: %s",
- _ads_id_str(ads), _ads_port(ads), strerror(errno));
- res = -1;
- }
-
- return res;
-}
-
-static void
-_android_dev_socket_disconnect(AndroidDevSocket* ads)
-{
- /* Preserve errno */
- const int save_error = errno;
- if (ads->socket_status != ADS_DISCONNECTED) {
- /* Reset I/O looper for this socket. */
- iolooper_modify(_ads_io_looper(ads), ads->fd,
- IOLOOPER_READ | IOLOOPER_WRITE, 0);
-
- /* Mark as disconnected. */
- ads->socket_status = ADS_DISCONNECTED;
-
- /* Close socket. */
- if (ads->fd >= 0) {
- socket_close(ads->fd);
- ads->fd = -1;
- }
- }
- errno = save_error;
-}
-
-static int
-_android_dev_socket_send(AndroidDevSocket* ads, const char* buff, int to_send)
-{
- int sent = 0;
-
- /* Make sure that socket is connected. */
- if (ads->socket_status < ADS_CONNECTED) {
- D("Attempt to send via disconnected channel '%s'@%d",
- _ads_id_str(ads), _ads_port(ads));
- errno = ECONNRESET;
- return -1;
- }
-
- iolooper_add_write(_ads_io_looper(ads), ads->fd);
- do {
- int res = socket_send(ads->fd, buff + sent, to_send - sent);
- if (res == 0) {
- /* Disconnection. */
- errno = ECONNRESET;
- sent = -1;
- break;
- }
-
- if (res < 0) {
- if (errno == EINTR) {
- /* loop on EINTR */
- continue;
- }
-
- if (errno == EWOULDBLOCK || errno == EAGAIN) {
- res = iolooper_wait_absolute(_ads_io_looper(ads), ads->deadline);
- if (res > 0) {
- /* Ready to write. */
- continue;
- }
- }
- sent = -1;
- break;
- }
- sent += res;
- } while (sent < to_send);
- iolooper_del_write(_ads_io_looper(ads), ads->fd);
-
- /* In case of an I/O failure we have to invoke failure callback. Note that we
- * report I/O failures only on registered sockets. */
- if (sent < 0) {
- D("I/O error while sending data via channel '%s'@%d: %s",
- _ads_id_str(ads), _ads_port(ads), strerror(errno));
-
- if (ads->ad->on_io_failure != NULL && ads->socket_status > ADS_CONNECTED) {
- const char save_error = errno;
- ads->ad->on_io_failure(ads->opaque, ads->ad, save_error);
- errno = save_error;
- }
- }
-
- return sent;
-}
-
-static int
-_android_dev_socket_recv(AndroidDevSocket* ads, char* buf, int bufsize)
-{
- int recvd = 0;
-
- /* Make sure that socket is connected. */
- if (ads->socket_status < ADS_CONNECTED) {
- D("Attempt to receive from disconnected channel '%s'@%d",
- _ads_id_str(ads), _ads_port(ads));
- errno = ECONNRESET;
- return -1;
- }
-
- iolooper_add_read(_ads_io_looper(ads), ads->fd);
- do {
- int res = socket_recv(ads->fd, buf + recvd, bufsize - recvd);
- if (res == 0) {
- /* Disconnection. */
- errno = ECONNRESET;
- recvd = -1;
- break;
- }
-
- if (res < 0) {
- if (errno == EINTR) {
- /* loop on EINTR */
- continue;
- }
-
- if (errno == EWOULDBLOCK || errno == EAGAIN) {
- res = iolooper_wait_absolute(_ads_io_looper(ads), ads->deadline);
- if (res > 0) {
- /* Ready to read. */
- continue;
- }
- }
- recvd = -1;
- break;
- }
- recvd += res;
- } while (recvd < bufsize);
- iolooper_del_read(_ads_io_looper(ads), ads->fd);
-
- /* In case of an I/O failure we have to invoke failure callback. Note that we
- * report I/O failures only on registered sockets. */
- if (recvd < 0) {
- D("I/O error while receiving from channel '%s'@%d: %s",
- _ads_id_str(ads), _ads_port(ads), strerror(errno));
-
- if (ads->ad->on_io_failure != NULL && ads->socket_status > ADS_CONNECTED) {
- const char save_error = errno;
- ads->ad->on_io_failure(ads->opaque, ads->ad, save_error);
- errno = save_error;
- }
- }
-
- return recvd;
-}
-
-static int
-_android_dev_socket_read_string(AndroidDevSocket* ads, char* str, int strsize)
-{
- int n;
-
- /* Char by char read from the socket, until zero-terminator is read. */
- for (n = 0; n < strsize; n++) {
- if (_android_dev_socket_recv(ads, str + n, 1) > 0) {
- if (str[n] == '\0') {
- /* Done. */
- return n + 1; /* Including zero-terminator. */
- }
- } else {
- /* I/O error. */
- return -1;
- }
- }
-
- /* Buffer was too small. Report that by setting errno to ENOMEM. */
- D("Buffer %d is too small to receive a string from channel '%s'@%d",
- strsize, _ads_id_str(ads), _ads_port(ads));
- errno = ENOMEM;
- return -1;
-}
-
-static int
-_android_dev_socket_read_response(AndroidDevSocket* ads, char* data, int datasize)
-{
- int n, res;
- int success = 0;
- int failure = 0;
- int bad_format = 0;
- char ok[4];
-
- *data = '\0';
-
- /* Char by char read from the socket, until ok/ko is read. */
- for (n = 0; n < 2; n++) {
- res = _android_dev_socket_recv(ads, ok + n, 1);
- if (res > 0) {
- if (ok[n] == '\0') {
- /* EOS is unexpected here! */
- D("Bad query reply format on channel '%s'@%d: '%s' is too short.",
- _ads_id_str(ads), _ads_port(ads), ok);
- errno = EINVAL;
- return -1;
- }
- } else {
- /* I/O error. */
- return -1;
- }
- }
-
- /* Next character must be either ':', or '\0' */
- res = _android_dev_socket_recv(ads, ok + n, 1);
- if (res <= 0) {
- /* I/O error. */
- return -1;
- }
-
- /*
- * Verify format.
- */
-
- /* Check ok / ko */
- success = memcmp(ok, "ok", 2) == 0;
- failure = memcmp(ok, "ko", 2) == 0;
-
- /* Check the prefix: 'ok'|'ko' & ':'|'\0' */
- if ((success || failure) && (ok[n] == '\0' || ok[n] == ':')) {
- /* Format is good. */
- if (ok[n] == '\0') {
- /* We're done: no extra data in response. */
- errno = 0;
- return success ? 0 : -1;
- }
- /* Reset buffer offset, so we will start to read the remaining query
- * data to the beginning of the supplied buffer. */
- n = 0;
- } else {
- /* Bad format. Lets move what we've read to the main buffer, and
- * continue filling it in. */
- bad_format = 1;
- n++;
- memcpy(data, ok, n);
- }
-
- /* Read the remainder of reply to the supplied data buffer. */
- res = _android_dev_socket_read_string(ads, data + n, datasize - n);
- if (res < 0) {
- return res;
- }
-
- /* Lets see if format was bad */
- if (bad_format) {
- D("Bad query reply format on channel '%s'@%d: %s.",
- _ads_id_str(ads), _ads_port(ads), data);
- errno = EINVAL;
- return -1;
- } else {
- errno = 0;
- return success ? n : -1;
- }
-}
-
-/********************************************************************************
- * Query socket declarations
- *******************************************************************************/
-
-/* Initializes query socket descriptor.
- * Param:
- * adsquery - Socket descriptor to initialize.
- * opaque - An opaque pointer to associate with the socket. Typicaly it's the
- * same pointer that is associated with AndroidDevice instance.
- * ad - Android device descriptor that owns the socket.
- * port - TCP socket port.
- */
-static int _android_query_socket_init(AndroidQuerySocket* adsquery,
- void* opaque,
- AndroidDevice* ad,
- int port);
-
-/* Destroys query socket descriptor. */
-static void _android_query_socket_destroy(AndroidQuerySocket* adsquery);
-
-/* Synchronously connects the query socket, and registers it with the server.
- * Param:
- * adsquery - Descriptor for the query socket to connect. Must have 'deadline'
- * field properly setup.
- * cb - Callback to invoke when socket connection is completed. Can be NULL.
- * Return:
- * Zero on success, or non-zero on failure.
- */
-static int _android_query_socket_connect(AndroidQuerySocket* adsquery);
-
-/* Disconnects the query socket. */
-static void _android_query_socket_disconnect(AndroidQuerySocket* adsquery);
-
-/********************************************************************************
- * Query socket implementation
- *******************************************************************************/
-
-static int
-_android_query_socket_init(AndroidQuerySocket* adsquery,
- void* opaque,
- AndroidDevice* ad,
- int port)
-{
- return _android_dev_socket_init(&adsquery->dev_socket, opaque, ad, port,
- ADS_TYPE_QUERY);
-}
-
-static void
-_android_query_socket_destroy(AndroidQuerySocket* adsquery)
-{
- _android_query_socket_disconnect(adsquery);
- _android_dev_socket_destroy(&adsquery->dev_socket);
-}
-
-static int
-_android_query_socket_connect(AndroidQuerySocket* adsquery)
-{
- return _android_dev_socket_connect(&adsquery->dev_socket, NULL, NULL);
-}
-
-static void
-_android_query_socket_disconnect(AndroidQuerySocket* adsquery)
-{
- _android_dev_socket_disconnect(&adsquery->dev_socket);
-}
-
-/********************************************************************************
- * Events socket declarations
- *******************************************************************************/
-
-/* Initializes event socket descriptor.
- * Param:
- * adsevent - Socket descriptor to initialize.
- * opaque - An opaque pointer to associate with the socket. Typicaly it's the
- * same pointer that is associated with AndroidDevice instance.
- * ad - Android device descriptor that owns the socket.
- * port - TCP socket port.
- */
-static int _android_event_socket_init(AndroidEventSocket* adsevent,
- void* opaque,
- AndroidDevice* ad,
- int port);
-
-/* Destroys the event socket descriptor. */
-static void _android_event_socket_destroy(AndroidEventSocket* adsevent);
-
-/* Synchronously connects event socket.
- * Param:
- * adsevent - Descriptor for the event socket to connect. Must have 'deadline'
- * field properly setup.
- * Return:
- * Zero on success, or non-zero on failure.
- */
-static int _android_event_socket_connect_sync(AndroidEventSocket* adsevent);
-
-/* Initiates asynchronous event socket connection.
- * Param:
- * adsevent - Descriptor for the event socket to connect. Must have 'deadline'
- * field properly setup.
- * cb - Callback to invoke when socket connection is completed. Can be NULL.
- * Return:
- * Zero on success, or non-zero on failure.
- */
-static int _android_event_socket_connect_async(AndroidEventSocket* adsevent,
- ads_socket_connected_cb cb);
-
-/* Disconnects the event socket. */
-static void _android_event_socket_disconnect(AndroidEventSocket* adsevent);
-
-/* Initiates listening on the event socket.
- * Param:
- * adsevent - Descriptor for the event socket to listen on.
- * str, strsize - Buffer where to read the string.
- * cb - A callback to call when the event string is read. Can be NULL.
- * Return:
- * Zero on success, or non-zero on failure.
- */
-static int _android_event_socket_listen(AndroidEventSocket* adsevent,
- char* str,
- int strsize,
- event_cb cb);
-
-/* Asynchronously sends data via event socket.
- * Param:
- * adsevent - Descriptor for the event socket to send data to.
- * data, size - Buffer containing data to send.
- * free_on_close - A boolean flag indicating whether the data buffer should be
- * freed upon data transfer completion.
- * cb - Callback to invoke when data transfer is completed.
- * opaque - An opaque pointer to pass to the transfer completion callback.
- */
-static int _android_event_socket_send(AndroidEventSocket* adsevent,
- void* data,
- int size,
- int free_on_close,
- async_send_cb cb,
- void* opaque);
-
-/* Cancels all asynchronous data transfers on the event socket.
- * Param:
- * adsevent - Descriptor for the event socket to cancel data transfer.
- * reason - Reason for the cancellation.
- */
-static void _android_event_socket_cancel_send(AndroidEventSocket* adsevent,
- ATResult reason);
-
-/* Event socket's asynchronous I/O looper callback.
- * Param:
- * opaque - AndroidEventSocket instance.
- * fd - Socket's FD.
- * events - I/O type bitsmask (read | write).
- */
-static void _on_event_socket_io(void* opaque, int fd, unsigned events);
-
-/* Callback that is invoked when asynchronous event socket connection is
- * completed. */
-static void _on_event_socket_connected(AndroidEventSocket* adsevent, int failure);
-
-/* Callback that is invoked when an event is received from the device. */
-static void _on_event_received(AndroidEventSocket* adsevent);
-
-/* Gets I/O looper for asynchronous I/O on event socket. */
-AINLINED Looper*
-_aes_looper(AndroidEventSocket* adsevent)
-{
- return adsevent->dev_socket.ad->looper;
-}
-
-/********************************************************************************
- * Events socket implementation
- *******************************************************************************/
-
-static int
-_android_event_socket_init(AndroidEventSocket* adsevent,
- void* opaque,
- AndroidDevice* ad,
- int port)
-{
- return _android_dev_socket_init(&adsevent->dev_socket, opaque, ad, port,
- ADS_TYPE_EVENT);
-}
-
-static void
-_android_event_socket_destroy(AndroidEventSocket* adsevent)
-{
- _android_event_socket_disconnect(adsevent);
- _android_dev_socket_destroy(&adsevent->dev_socket);
-}
-
-/* A callback invoked when file descriptor is created for the event socket.
- * We use this callback to initialize the event socket for async I/O right after
- * the FD has been created.
- */
-static void
-_on_event_fd_created(AndroidDevSocket* ads, void* opaque)
-{
- AndroidEventSocket* adsevent = (AndroidEventSocket*)opaque;
- /* Prepare for async I/O on the event socket. */
- loopIo_init(adsevent->io, _aes_looper(adsevent), ads->fd,
- _on_event_socket_io, adsevent);
-}
-
-static int
-_android_event_socket_connect_sync(AndroidEventSocket* adsevent)
-{
- return _android_dev_socket_connect(&adsevent->dev_socket,
- _on_event_fd_created, adsevent);
-}
-
-static int
-_android_event_socket_connect_async(AndroidEventSocket* adsevent,
- ads_socket_connected_cb cb)
-{
- AsyncStatus status;
- AndroidDevSocket* ads = &adsevent->dev_socket;
-
- /* Create asynchronous socket. */
- ads->fd = socket_create_inet(SOCKET_STREAM);
- if (ads->fd < 0) {
- D("Unable to create socket for channel '%s'@%d: %s",
- _ads_id_str(ads), _ads_port(ads), strerror(errno));
- if (cb != NULL) {
- cb(ads->opaque, ads, errno);
- }
- return -1;
- }
- socket_set_nonblock(ads->fd);
-
- /* Prepare for async I/O on the event socket. */
- loopIo_init(adsevent->io, _aes_looper(adsevent), ads->fd,
- _on_event_socket_io, adsevent);
-
- /* Try to connect. */
- ads->socket_status = ADS_CONNECTING;
- adsevent->on_connected = cb;
- status = asyncConnector_init(adsevent->connector, &ads->address, adsevent->io);
- switch (status) {
- case ASYNC_COMPLETE:
- /* We're connected to the device socket. */
- ads->socket_status = ADS_CONNECTED;
- _on_event_socket_connected(adsevent, 0);
- break;
- case ASYNC_ERROR:
- _on_event_socket_connected(adsevent, errno);
- break;
- case ASYNC_NEED_MORE:
- /* Attempt to connect would block, so connection competion is
- * delegates to the looper's I/O routine. */
- default:
- break;
- }
-
- return 0;
-}
-
-static void
-_android_event_socket_disconnect(AndroidEventSocket* adsevent)
-{
- AndroidDevSocket* ads = &adsevent->dev_socket;
-
- if (ads->socket_status != ADS_DISCONNECTED) {
- /* Cancel data transfer. */
- _android_event_socket_cancel_send(adsevent, ATR_DISCONNECT);
-
- /* Stop all async I/O. */
- loopIo_done(adsevent->io);
-
- /* Disconnect common socket. */
- _android_dev_socket_disconnect(ads);
- }
-}
-
-static int
-_android_event_socket_listen(AndroidEventSocket* adsevent,
- char* str,
- int strsize,
- event_cb cb)
-{
- AsyncStatus status;
- AndroidDevSocket* ads = &adsevent->dev_socket;
-
- /* Make sure that device is connected. */
- if (ads->socket_status < ADS_CONNECTED) {
- D("Attempt to listen on a disconnected channel '%s'@%d",
- _ads_id_str(ads), _ads_port(ads));
- errno = ECONNRESET;
- return -1;
- }
-
- /* NOTE: only one reader at any given time! */
- adsevent->on_event = cb;
- asyncLineReader_init(&adsevent->alr, str, strsize, adsevent->io);
- /* Default EOL for the line reader was '\n'. */
- asyncLineReader_setEOL(&adsevent->alr, '\0');
- status = asyncLineReader_read(&adsevent->alr);
- if (status == ASYNC_COMPLETE) {
- /* Data has been transferred immediately. Do the callback here. */
- _on_event_received(adsevent);
- } else if (status == ASYNC_ERROR) {
- D("Error while listening on channel '%s'@%d: %s",
- _ads_id_str(ads), _ads_port(ads), strerror(errno));
- /* There is one special failure here, when buffer was too small to
- * contain the entire string. This is not an I/O, but rather a
- * protocol error. So we don't report it to the I/O failure
- * callback. */
- if (errno == ENOMEM) {
- _on_event_received(adsevent);
- } else {
- if (ads->ad->on_io_failure != NULL) {
- ads->ad->on_io_failure(ads->ad->opaque, ads->ad, errno);
- }
- }
- return -1;
- }
- return 0;
-}
-
-static int
-_android_event_socket_send(AndroidEventSocket* adsevent,
- void* data,
- int size,
- int free_on_close,
- async_send_cb cb,
- void* opaque)
-{
- /* Create data transfer descriptor, and place it at the end of the list. */
- AsyncSendBuffer* const desc =
- _async_send_buffer_create(data, size, free_on_close, cb, opaque);
- AsyncSendBuffer** place = &adsevent->send_pending;
- while (*place != NULL) {
- place = &((*place)->next);
- }
- *place = desc;
-
- /* We're ready to transfer data. */
- loopIo_wantWrite(adsevent->io);
-
- return 0;
-}
-
-static void
-_android_event_socket_cancel_send(AndroidEventSocket* adsevent, ATResult reason)
-{
- while (adsevent->send_pending != NULL) {
- AsyncSendBuffer* const to_cancel = adsevent->send_pending;
- adsevent->send_pending = to_cancel->next;
- _async_send_buffer_complete(to_cancel, reason);
- }
- loopIo_dontWantWrite(adsevent->io);
-}
-
-static void
-_on_event_socket_io(void* opaque, int fd, unsigned events)
-{
- AsyncStatus status;
- AndroidEventSocket* adsevent = (AndroidEventSocket*)opaque;
- AndroidDevSocket* ads = &adsevent->dev_socket;
-
- /* Lets see if we're still wating on a connection to occur. */
- if (ads->socket_status == ADS_CONNECTING) {
- /* Complete socket connection. */
- status = asyncConnector_run(adsevent->connector);
- if (status == ASYNC_COMPLETE) {
- /* We're connected to the device socket. */
- ads->socket_status = ADS_CONNECTED;
- D("Channel '%s'@%d is connected asynchronously",
- _ads_id_str(ads), _ads_port(ads));
- _on_event_socket_connected(adsevent, 0);
- } else if (status == ASYNC_ERROR) {
- _on_event_socket_connected(adsevent, adsevent->connector->error);
- }
- return;
- }
-
- /*
- * Device is connected. Continue with the data transfer.
- */
-
- if ((events & LOOP_IO_READ) != 0) {
- /* Continue reading data. */
- status = asyncLineReader_read(&adsevent->alr);
- if (status == ASYNC_COMPLETE) {
- errno = 0;
- _on_event_received(adsevent);
- } else if (status == ASYNC_ERROR) {
- D("I/O failure while reading from channel '%s'@%d: %s",
- _ads_id_str(ads), _ads_port(ads), strerror(errno));
- /* There is one special failure here, when buffer was too small to
- * contain the entire string. This is not an I/O, but rather a
- * protocol error. So we don't report it to the I/O failure
- * callback. */
- if (errno == ENOMEM) {
- _on_event_received(adsevent);
- } else {
- if (ads->ad->on_io_failure != NULL) {
- ads->ad->on_io_failure(ads->ad->opaque, ads->ad, errno);
- }
- }
- }
- }
-
- if ((events & LOOP_IO_WRITE) != 0) {
- while (adsevent->send_pending != NULL) {
- AsyncSendBuffer* to_send = adsevent->send_pending;
- const int offset = to_send->data_size - to_send->data_remaining;
- const int sent = socket_send(ads->fd, to_send->data + offset,
- to_send->data_remaining);
- if (sent < 0) {
- if (errno == EWOULDBLOCK) {
- /* Try again later. */
- return;
- } else {
- /* An error has occured. */
- _android_event_socket_cancel_send(adsevent, ATR_IO_ERROR);
- if (ads->ad->on_io_failure != NULL) {
- ads->ad->on_io_failure(ads->ad->opaque, ads->ad, errno);
- }
- return;
- }
- } else if (sent == 0) {
- /* Disconnect condition. */
- _android_event_socket_cancel_send(adsevent, ATR_DISCONNECT);
- if (ads->ad->on_io_failure != NULL) {
- ads->ad->on_io_failure(ads->ad->opaque, ads->ad, errno);
- }
- return;
- } else if (sent == to_send->data_remaining) {
- /* All data is sent. */
- errno = 0;
- adsevent->send_pending = to_send->next;
- _async_send_buffer_complete(to_send, ATR_SUCCESS);
- } else {
- /* Chunk is sent. */
- to_send->data_remaining -= sent;
- return;
- }
- }
- loopIo_dontWantWrite(adsevent->io);
- }
-}
-
-static void
-_on_event_socket_connected(AndroidEventSocket* adsevent, int failure)
-{
- int res;
- AndroidDevSocket* ads = &adsevent->dev_socket;
-
- if (failure) {
- _android_event_socket_disconnect(adsevent);
- if (adsevent->on_connected != NULL) {
- adsevent->on_connected(ads->opaque, ads, failure);
- }
- return;
- }
-
- /* Complete event socket connection by identifying it as "event" socket with
- * the application. */
- ads->socket_status = ADS_CONNECTED;
- res = _android_dev_socket_register(ads);
-
- if (res) {
- const int save_error = errno;
- _android_event_socket_disconnect(adsevent);
- errno = save_error;
- }
-
- /* Notify callback about connection completion. */
- if (adsevent->on_connected != NULL) {
- if (res) {
- adsevent->on_connected(ads->opaque, ads, errno);
- } else {
- adsevent->on_connected(ads->opaque, ads, 0);
- }
- }
-}
-
-static void
-_on_event_received(AndroidEventSocket* adsevent)
-{
- if (adsevent->on_event != NULL) {
- AndroidDevice* ad = adsevent->dev_socket.ad;
- adsevent->on_event(ad->opaque, ad, (char*)adsevent->alr.buffer,
- adsevent->alr.pos);
- }
-}
-
-/********************************************************************************
- * Android device connection
- *******************************************************************************/
-
-/* Callback that is invoked when event socket is connected and registered as part
- * of the _android_device_connect_async API.
- * Param:
- * opaque - Opaque pointer associated with AndroidDevice instance.
- * ads - Common socket descriptor for the event socket.
- * failure - If zero connection has succeeded, otherwise contains 'errno'-reason
- * for connection failure.
- */
-static void
-_on_android_device_connected_async(void* opaque,
- AndroidDevSocket* ads,
- int failure)
-{
- int res;
- AndroidDevice* ad = ads->ad;
-
- if (failure) {
- /* Depending on the failure code we will either retry, or bail out. */
- switch (failure) {
- case EPIPE:
- case EAGAIN:
- case EINPROGRESS:
- case EALREADY:
- case EHOSTUNREACH:
- case EHOSTDOWN:
- case ECONNREFUSED:
- case ESHUTDOWN:
- case ENOTCONN:
- case ECONNRESET:
- case ECONNABORTED:
- case ENETRESET:
- case ENETUNREACH:
- case ENETDOWN:
- case EBUSY:
-#if !defined(_DARWIN_C_SOURCE) && !defined(_WIN32)
- case ERESTART:
- case ECOMM:
- case ENONET:
-#endif /* !_DARWIN_C_SOURCE && !_WIN32 */
- /* Device is not available / reachable at the moment.
- * Retry connection later. */
- loopTimer_startRelative(ad->timer, ADS_RETRY_CONNECTION_TIMEOUT);
- return;
- default:
- D("Failed to asynchronously connect channel '%s':%d %s",
- _ads_id_str(ads), _ads_port(ads), strerror(errno));
- if (ad->on_connected != NULL) {
- ad->on_connected(ad->opaque, ad, failure);
- }
- break;
- }
- return;
- }
-
- /* Event socket is connected. Connect the query socket now. Give it 5
- * seconds to connect. */
- _ads_set_deadline(&ad->query_socket.dev_socket, 5000);
- res = _android_query_socket_connect(&ad->query_socket);
- if (res == 0) {
- /* Query socket is connected. */
- if (ad->on_connected != NULL) {
- ad->on_connected(ad->opaque, ad, 0);
- }
- } else {
- /* If connection completion has failed - disconnect the sockets. */
- _android_event_socket_disconnect(&ad->event_socket);
- _android_query_socket_disconnect(&ad->query_socket);
-
- if (ad->on_connected != NULL) {
- ad->on_connected(ad->opaque, ad, errno);
- }
- }
-}
-
-static void
-_on_timer(void* opaque)
-{
- /* Retry the connection. */
- AndroidDevice* ad = (AndroidDevice*)opaque;
- android_device_connect_async(ad, ad->on_connected);
-}
-
-/* Destroys and frees the descriptor. */
-static void
-_android_device_free(AndroidDevice* ad)
-{
- if (ad != NULL) {
- _android_event_socket_destroy(&ad->event_socket);
- _android_query_socket_destroy(&ad->query_socket);
-
- /* Delete asynchronous I/O looper. */
- if (ad->looper != NULL ) {
- loopTimer_done(ad->timer);
- looper_free(ad->looper);
- }
-
- /* Delete synchronous I/O looper. */
- if (ad->io_looper != NULL) {
- iolooper_reset(ad->io_looper);
- iolooper_free(ad->io_looper);
- }
-
- AFREE(ad);
- }
-}
-
-/********************************************************************************
- * Android device API
- *******************************************************************************/
-
-AndroidDevice*
-android_device_init(void* opaque, int port, io_failure_cb on_io_failure)
-{
- int res;
- AndroidDevice* ad;
-
- ANEW0(ad);
-
- ad->opaque = opaque;
- ad->on_io_failure = on_io_failure;
-
- /* Create I/O looper for synchronous I/O on the device. */
- ad->io_looper = iolooper_new();
- if (ad->io_looper == NULL) {
- E("Unable to create synchronous I/O looper for android device.");
- _android_device_free(ad);
- return NULL;
- }
-
- /* Create a looper for asynchronous I/O on the device. */
- ad->looper = looper_newCore();
- if (ad->looper != NULL) {
- /* Create a timer that will be used for connection retries. */
- loopTimer_init(ad->timer, ad->looper, _on_timer, ad);
- } else {
- E("Unable to create asynchronous I/O looper for android device.");
- _android_device_free(ad);
- return NULL;
- }
-
- /* Init query socket. */
- res = _android_query_socket_init(&ad->query_socket, opaque, ad, port);
- if (res) {
- _android_device_free(ad);
- return NULL;
- }
-
- /* Init event socket. */
- res = _android_event_socket_init(&ad->event_socket, opaque, ad, port);
- if (res) {
- _android_device_free(ad);
- return NULL;
- }
-
- return ad;
-}
-
-void
-android_device_destroy(AndroidDevice* ad)
-{
- if (ad != NULL) {
- _android_device_free(ad);
- }
-}
-
-int
-android_device_connect_sync(AndroidDevice* ad, int to)
-{
- int res;
-
- /* Setup deadline for the connections. */
- _ads_set_deadline(&ad->query_socket.dev_socket, to);
- ad->event_socket.dev_socket.deadline = ad->query_socket.dev_socket.deadline;
-
- /* Connect the query socket first. */
- res = _android_query_socket_connect(&ad->query_socket);
- if (!res) {
- /* Connect to the event socket next. */
- res = _android_event_socket_connect_sync(&ad->event_socket);
- }
-
- return res;
-}
-
-int
-android_device_connect_async(AndroidDevice* ad, device_connected_cb on_connected)
-{
- /* No deadline for async connections. */
- ad->query_socket.dev_socket.deadline = DURATION_INFINITE;
- ad->event_socket.dev_socket.deadline = DURATION_INFINITE;
-
- /* Connect to the event socket first, and delegate query socket connection
- * into callback invoked when event socket is connected. NOTE: In case of
- * failure 'on_connected' callback has already been called from
- * _on_android_device_connected_async routine. */
- ad->on_connected = on_connected;
- return _android_event_socket_connect_async(&ad->event_socket,
- _on_android_device_connected_async);
-}
-
-void
-android_device_disconnect(AndroidDevice* ad)
-{
- _android_event_socket_disconnect(&ad->event_socket);
- _android_query_socket_disconnect(&ad->query_socket);
-}
-
-int
-android_device_query(AndroidDevice* ad,
- const char* query,
- char* buff,
- size_t buffsize,
- int to)
-{
- int res;
-
- /* Setup deadline for the query. */
- _ads_set_deadline(&ad->query_socket.dev_socket, to);
-
- /* Send the query. */
- res = _android_dev_socket_send(&ad->query_socket.dev_socket, query,
- strlen(query) + 1);
- if (res > 0) {
- /* Receive the response. */
- res = _android_dev_socket_read_response(&ad->query_socket.dev_socket,
- buff, buffsize);
- return (res >= 0) ? 0 : -1;
- }
-
- return -1;
-}
-
-int
-android_device_start_query(AndroidDevice* ad, const char* query, int to)
-{
- int res;
-
- /* Setup deadline for the query. */
- _ads_set_deadline(&ad->query_socket.dev_socket, to);
-
- /* Send the query header. */
- res = _android_dev_socket_send(&ad->query_socket.dev_socket, query,
- strlen(query) + 1);
- return (res > 0) ? 0 : -1;
-}
-
-int
-android_device_send_query_data(AndroidDevice* ad, const void* data, int size)
-{
- return _android_dev_socket_send(&ad->query_socket.dev_socket, data, size);
-}
-
-int
-android_device_complete_query(AndroidDevice* ad, char* buff, size_t buffsize)
-{
- /* Receive the response to the query. */
- const int res = _android_dev_socket_read_response(&ad->query_socket.dev_socket,
- buff, buffsize);
- return (res >= 0) ? 0 : -1;
-}
-
-int
-android_device_listen(AndroidDevice* ad,
- char* buff,
- int buffsize,
- event_cb on_event)
-{
- return _android_event_socket_listen(&ad->event_socket, buff, buffsize,
- on_event);
-}
-
-int
-android_device_send_async(AndroidDevice* ad,
- void* data,
- int size,
- int free_on_close,
- async_send_cb cb,
- void* opaque)
-{
- return _android_event_socket_send(&ad->event_socket, data, size,
- free_on_close, cb, opaque);
-}