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.c271
1 files changed, 258 insertions, 13 deletions
diff --git a/android/android-device.c b/android/android-device.c
index 5722a7e..5f88108 100644
--- a/android/android-device.c
+++ b/android/android-device.c
@@ -21,6 +21,7 @@
*/
#include "android/android-device.h"
+#include "utils/panic.h"
#include "iolooper.h"
#define E(...) derror(__VA_ARGS__)
@@ -105,6 +106,24 @@ typedef struct AndroidQuerySocket {
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. */
@@ -120,6 +139,8 @@ typedef struct AndroidEventSocket {
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. */
@@ -142,6 +163,61 @@ struct AndroidDevice {
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
*******************************************************************************/
@@ -164,13 +240,26 @@ static int _android_dev_socket_init(AndroidDevSocket* ads,
/* 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);
+static int _android_dev_socket_connect(AndroidDevSocket* ads,
+ on_socked_fd_created cb,
+ void* opaque);
/* Synchronously registers a connected socket with the server.
* Param:
@@ -314,7 +403,9 @@ _android_dev_socket_destroy(AndroidDevSocket* ads)
}
static int
-_android_dev_socket_connect(AndroidDevSocket* ads)
+_android_dev_socket_connect(AndroidDevSocket* ads,
+ on_socked_fd_created cb,
+ void* opaque)
{
int res;
@@ -327,6 +418,11 @@ _android_dev_socket_connect(AndroidDevSocket* ads)
}
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);
@@ -713,7 +809,7 @@ _android_query_socket_destroy(AndroidQuerySocket* adsquery)
static int
_android_query_socket_connect(AndroidQuerySocket* adsquery)
{
- return _android_dev_socket_connect(&adsquery->dev_socket);
+ return _android_dev_socket_connect(&adsquery->dev_socket, NULL, NULL);
}
static void
@@ -778,6 +874,30 @@ static int _android_event_socket_listen(AndroidEventSocket* adsevent,
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.
@@ -821,18 +941,24 @@ _android_event_socket_destroy(AndroidEventSocket* 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)
{
- AndroidDevSocket* ads = &adsevent->dev_socket;
- const int res = _android_dev_socket_connect(&adsevent->dev_socket);
- if (res == 0) {
- /* Prepare for async I/O on the event socket. */
- loopIo_init(adsevent->io, _aes_looper(adsevent), ads->fd,
- _on_event_socket_io, adsevent);
- }
- return res;
+ return _android_dev_socket_connect(&adsevent->dev_socket,
+ _on_event_fd_created, adsevent);
}
static int
@@ -887,6 +1013,9 @@ _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);
@@ -940,6 +1069,40 @@ _android_event_socket_listen(AndroidEventSocket* adsevent,
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)
{
@@ -964,14 +1127,14 @@ _on_event_socket_io(void* opaque, int fd, unsigned events)
}
/*
- * Device is connected. Continue with the data transfer. We don't expect
- * any writes here, since we always write to the event socket synchronously.
+ * 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",
@@ -989,6 +1152,45 @@ _on_event_socket_io(void* opaque, int fd, unsigned events)
}
}
}
+
+ 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
@@ -1007,7 +1209,9 @@ _on_event_socket_connected(AndroidEventSocket* adsevent, int failure)
/* 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);
@@ -1271,6 +1475,35 @@ android_device_query(AndroidDevice* ad,
}
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,
@@ -1279,3 +1512,15 @@ android_device_listen(AndroidDevice* ad,
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);
+}