diff options
Diffstat (limited to 'android/android-device.c')
-rw-r--r-- | android/android-device.c | 271 |
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); +} |