aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--android/android-device.c190
-rw-r--r--android/android-device.h41
2 files changed, 229 insertions, 2 deletions
diff --git a/android/android-device.c b/android/android-device.c
index f5121e8..37af0c6 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
*******************************************************************************/
@@ -778,6 +854,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.
@@ -887,6 +987,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 +1043,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,8 +1101,7 @@ _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) {
@@ -989,6 +1125,44 @@ _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. */
+ 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
@@ -1308,3 +1482,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);
+}
diff --git a/android/android-device.h b/android/android-device.h
index 769ba66..6825819 100644
--- a/android/android-device.h
+++ b/android/android-device.h
@@ -108,6 +108,17 @@
/* Definis infinite timeout. */
#define AD_INFINITE_WAIT -1
+/* Enumerates results of asynchronous data transfer.
+ */
+typedef enum ATResult {
+ /* Data transfer has been completed. */
+ ATR_SUCCESS,
+ /* Socket got disconnected while data transfer has been in progress. */
+ ATR_DISCONNECT,
+ /* An I/O error has occured. 'errno' contains error value. */
+ ATR_IO_ERROR,
+} ATResult;
+
/* Android device descriptor. */
typedef struct AndroidDevice AndroidDevice;
@@ -149,6 +160,20 @@ typedef void (*event_cb)(void* opaque, AndroidDevice* ad, char* msg, int msgsize
*/
typedef void (*io_failure_cb)(void* opaque, AndroidDevice* ad, int failure);
+/* Callback routine that is invoked when an asynchronous data send has been
+ * completed.
+ * Param:
+ * opaque - An opaque pointer associated with the data.
+ * res - Result of data transfer.
+ * data, size - Transferred data buffer.
+ * sent - Number of sent bytes.
+ */
+typedef void (*async_send_cb)(void* opaque,
+ ATResult res,
+ void* data,
+ int size,
+ int sent);
+
/********************************************************************************
* Android Device API.
*******************************************************************************/
@@ -277,4 +302,20 @@ extern int android_device_listen(AndroidDevice* ad,
int buffsize,
event_cb on_event);
+/* Asynchronously sends data to the android device.
+ * Param:
+ * ad - Android device descriptor, returned from android_device_init API.
+ * 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.
+ */
+extern int android_device_send_async(AndroidDevice* ad,
+ void* data,
+ int size,
+ int free_on_close,
+ async_send_cb cb,
+ void* opaque);
+
#endif /* ANDROID_ANDROID_DEVICE_H_ */