/* * 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); }