diff options
author | Vladimir Chtchetkine <vchtchetkine@google.com> | 2012-04-10 13:39:24 -0700 |
---|---|---|
committer | Vladimir Chtchetkine <vchtchetkine@google.com> | 2012-04-30 07:14:35 -0700 |
commit | 7136b053b7fc7840ec64e01d1d19ab822e1f949a (patch) | |
tree | 549e5a10878ab27ab9169f83863460b71878c34c /android | |
parent | 6f50aa39e741a1d1f3081665d0b7f8d56b7b793c (diff) | |
download | external_qemu-7136b053b7fc7840ec64e01d1d19ab822e1f949a.zip external_qemu-7136b053b7fc7840ec64e01d1d19ab822e1f949a.tar.gz external_qemu-7136b053b7fc7840ec64e01d1d19ab822e1f949a.tar.bz2 |
Use new SdkController communication protocol for emulation ports
android/sdk-control-socket.* has replaced android/android-device.* as the back-bone
of communicating with SDK controller on the device. The major differences are:
- New communication protocol uses just one (async) socket connection to communicate
with the device (the old one used two sockets: one sync, and another - async).
- New communication protocol connects to one TCP port (1970 in this CL) for all emulation
ports. Channel multiplexing is done by using port names, and assigning a separate socket
for communication inside each separate port. The old protocol had separate TCP ports for
each emulation ports (1968 for sensors, and 1969 for multi-touch)
Change-Id: I779fcbdfba2f9b4c433a9d76a567975708b00469
Diffstat (limited to 'android')
-rw-r--r-- | android/android-device.c | 1526 | ||||
-rw-r--r-- | android/android-device.h | 321 | ||||
-rw-r--r-- | android/async-socket-connector.c | 2 | ||||
-rw-r--r-- | android/async-socket-connector.h | 2 | ||||
-rw-r--r-- | android/async-socket.c | 54 | ||||
-rw-r--r-- | android/async-socket.h | 8 | ||||
-rw-r--r-- | android/hw-sensors.c | 7 | ||||
-rw-r--r-- | android/multitouch-port.c | 465 | ||||
-rw-r--r-- | android/multitouch-port.h | 31 | ||||
-rw-r--r-- | android/multitouch-screen.c | 58 | ||||
-rw-r--r-- | android/multitouch-screen.h | 4 | ||||
-rw-r--r-- | android/sdk-controller-socket.c | 996 | ||||
-rw-r--r-- | android/sdk-controller-socket.h | 225 | ||||
-rw-r--r-- | android/sensors-port.c | 654 | ||||
-rw-r--r-- | android/sensors-port.h | 27 |
15 files changed, 1626 insertions, 2754 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); -} diff --git a/android/android-device.h b/android/android-device.h deleted file mode 100644 index 6825819..0000000 --- a/android/android-device.h +++ /dev/null @@ -1,321 +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. - */ - -#ifndef ANDROID_ANDROID_DEVICE_H_ -#define ANDROID_ANDROID_DEVICE_H_ - -/* - * Encapsulates an 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 (always use 'adb -d forward ...' variant - * of this command, so ADB will know to enable port forwarding on the connected - * device, and not on the emulator's guest system). - * - * Exchange protocol contains two channel: - * - * - Query channel. - * - Event channel. - * - * Both channels are implemented on top of TCP sockets that are connected to the - * same port. - * - * I QUERY CHANNEL. - * Query channel is intended to send queries to and receive responses from the - * connected device. It is implemented on top of iolooper_xxx API (see iolooper.h) - * because it must work outside of the main event loop. This is required to enable - * proper initialization of components (such as sensors) that must be set up - * before emulator enters the main loop. - * - * II EVENT CHANNEL. - * Event channel is intended to listen on events sent from the device, and - * asynchronously report them back to the client of this API by invoking an event - * callback that was registered by the client. Event channel is implemented on - * top of asyncXxx API (see android/async-utils.*). Note that using of asyncXxx - * API limits the use of event channel to the time after the emulator has entered - * its main event loop. The only exception is if event channel is connected from - * android_device_connect_sync API, in which case iolooper_xxx API is used to - * establish the connection. However, even in this case listening for events will - * not be available until after the emulator enters its event loop, since event - * listening always uses asyncXxx API. - * - * III. ESTABLISHING CONNECTION. - * ADB port forwarding requires that the server socket is to be run on the device, - * while emulator must use a client socket for communication. Thus, it's the - * emulator that initiates the connection. - * - * There are two ways how emulator can initiate the connection: - * - * - Synchronous connection. - * - Asynchronous connection. - * - * III.I SYNCHROUNOUS CONNECTION. - * Synchronous connection is initiated via android_device_connect_sync API, and - * completes synchronously. - * - * This API should be used when connection with the device is required at the time - * of the call. For instance, when initializing sensor emulation, connection with - * the device is required to properly set up the emulator before the guest system - * starts, and before emulator enters its main event loop. - * - * III.II ASYNCHRONOUS CONNECTION. - * Asynchronous connection is initiated via android_device_connect_async API. The - * main difference with the synchronous connection is that this API will not fail - * if connection is not immediately available. If connection is not available at - * the time of the call, the API will schedule a retry (based on a timer), and - * will continue reprying untill connection becomes available, or until an error - * occurs that prevent further retries. - * - * This API should be used when ... Well, whenever appropriate. For instance, - * sensor emulation will use this API to restore lost connection with the device. - * - * NOTE: Asynchronous connection will complete no sooner than the emulator enters - * its main loop. - * - * IV EXCHANGE PROTOCOL. - * Obviously, there must be some application running on the device, that implements - * a socket server listening on the forwarded TCP port, and accepting the clients. - * - * IV.I Query vs. event channel. - * The exchange protocol assumes, that when a channel is connected, it will - * identify itself by sending a string containing channel type. Only after such - * identification has been made the channel becomes available for use. - * - * IV.II Message format. - * All data that is transferred in both directions over both channels are zero- - * terminated strings. - */ - -#include "qemu-common.h" -#include "android/async-utils.h" -#include "android/utils/debug.h" - -/* TCP port reserved for sensor emulation. */ -#define AD_SENSOR_PORT 1968 - -/* 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; - -/******************************************************************************** - * Callback declarations - *******************************************************************************/ - -/* Callback routine that is invoked when android device is connected, or failed - * to connect. As discussed above, this callback is called when both, query and - * event channels have been connected. This callback is used only for asynchronous - * connections. - * Param: - * opaque - Opaque pointer that was passed to android_device_init API. - * ad - Androd device descriptor for the connection. - * failure - Zero indicates that connection with the device has been successfuly - * established. Non-zero vaule passed in this parameter indicates a failure, - * and contains 'errno'-reason for failure. - */ -typedef void (*device_connected_cb)(void* opaque, AndroidDevice* ad, int failure); - -/* Callback routine that is invoked on an event received in the event channel. - * NOTE: It's important to check 'errno' in this callback. If 'errno' is set to - * ENOMEM, this signals that buffer passed to android_device_listen was too small - * to contain the entire event message. - * Param: - * opaque - Opaque pointer that was passed to android_device_init API. - * ad - Androd device descriptor for the connection. - * msg - Event message (a zero-terminated string) received from the device. - * msgsize - Event message size (including zero-terminator). - */ -typedef void (*event_cb)(void* opaque, AndroidDevice* ad, char* msg, int msgsize); - -/* Callback routine that is invoked when an I/O failure occurs on a channel. - * Note that this callback will not be invoked on connection failures. - * Param: - * opaque - Opaque pointer that was passed to android_device_init API. - * ad - Android device instance - * failure - Contains 'errno' indicating the reason for failure. - */ -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. - *******************************************************************************/ - -/* Initializes android device descriptor. - * Param: - * opaque - An opaque pointer to associate with the descriptor. This pointer - * will be passed to all callbacks (see above) that were invoked by the - * initializing android device instance. - * port - TCP port to use for connection. - * on_io_failure - Callback to invoke when an I/O failure occurs on a channel - * used by the initializing android device instance. Can be NULL. - * Return: - * Initialized android device descriptor on success, or NULL on failure. - */ -extern AndroidDevice* android_device_init(void* opaque, - int port, - io_failure_cb on_io_failure); - -/* Disconnects and destroys android device descriptor. - * Param: - * ad - Android device descriptor, returned from android_device_init API. - * Note that memory allocated for this descriptor will be freed in this - * routine. - */ -extern void android_device_destroy(AndroidDevice* ad); - -/* Synchronously connects to the device. See notes above for more details. - * Param: - * ad - Android device descriptor, returned from android_device_init API. - * to - Milliseconds to wait for connection to be established. - * Return: - * Zero on success, or non-zero value on failure with 'errno' properly set. - */ -extern int android_device_connect_sync(AndroidDevice* ad, int to); - -/* Asynchronously connects to the device. See notes above for more details. - * Param: - * ad - Android device descriptor, returned from android_device_init API. - * on_connected - Callback to invoke when connection is completed (i,e, both, - * event, and query channels have been connected). This parameter can be - * NULL. Note that connection errors will be also reported through this - * callback. Also note that this callback will be invoked even if this - * routine returns with a failure. - * Return: - * Zero on success, or non-zero value on failure with 'errno' properly set. - */ -extern int android_device_connect_async(AndroidDevice* ad, - device_connected_cb on_connected); - -/* Disconnects from the android device. - * Param: - * ad - Android device descriptor, returned from android_device_init API. - */ -extern void android_device_disconnect(AndroidDevice* ad); - -/* Queries the device via query channel. - * Param: - * ad - Android device descriptor, returned from android_device_init API. - * query - Zero-terminated query string. - * buff, buffsize - Buffer where to receive the response to the query. - * to - Milliseconds to wait for the entire query to complete. - * Return: - * Zero on success, or non-zero value on failure with 'errno' properly set: - * - 0 Indicates that the server has failed the query. - * - Anything else indicates an I/O error. - */ -extern int android_device_query(AndroidDevice* ad, - const char* query, - char* buff, - size_t buffsize, - int to); - -/* Starts a query that may require more than one buffer transfer. - * This routine allows to initiate a query that may require more than one call to - * send_data, or may have a format that differs from the usual (a zero-terminated - * string). For instance, sending a BLOB data should use this routine to start a - * a query, then use android_device_send_query_data to transfer the data, and - * then call android_device_complete_query to obtain the response. - * Param: - * ad - Android device descriptor, returned from android_device_init API. - * query - Zero-terminated query string. - * to - Milliseconds to wait for the entire query to complete. - * Return: - * Zero on success, or non-zero value on failure with 'errno' properly set: - * - 0 Indicates that the server has failed the query. - * - Anything else indicates an I/O error. - */ -extern int android_device_start_query(AndroidDevice* ad, - const char* query, - int to); - -/* Sends data block for a query started with android_device_start_query - * Param: - * ad - Android device descriptor, returned from android_device_init API. - * data, size - Data to transfer. - * Return: - * Number of bytes transferred on success, or -1 on failure with errno - * containing the reason for failure. - */ -extern int android_device_send_query_data(AndroidDevice* ad, - const void* data, - int size); - -/* Completes a query started with android_device_start_query, and receives the - * query response. - * Param: - * ad - Android device descriptor, returned from android_device_init API. - * buff, buffsize - Buffer where to receive the response to the query. - * Return: - * Zero on success, or non-zero value on failure with 'errno' properly set. - */ -extern int android_device_complete_query(AndroidDevice* ad, char* buff, size_t buffsize); - -/* Start listening on the event channel. - * Param: - * ad - Android device descriptor, returned from android_device_init API. - * buff, buffsize - Buffer where to receive the event message. - * on_event - Callback to invoke on event. Note that this callback will be - * invoked even if this routine returns with a failure. - * Return: - * Zero on success, or non-zero value on failure with 'errno' properly set. - */ -extern int android_device_listen(AndroidDevice* ad, - char* buff, - 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_ */ diff --git a/android/async-socket-connector.c b/android/async-socket-connector.c index 836016c..0f50399 100644 --- a/android/async-socket-connector.c +++ b/android/async-socket-connector.c @@ -20,8 +20,6 @@ * a TCP port forwarding, enabled by ADB. */ -#include "qemu-common.h" -#include "android/async-utils.h" #include "android/utils/debug.h" #include "android/async-socket-connector.h" #include "utils/panic.h" diff --git a/android/async-socket-connector.h b/android/async-socket-connector.h index bedc2df..d49d203 100644 --- a/android/async-socket-connector.h +++ b/android/async-socket-connector.h @@ -17,7 +17,9 @@ #ifndef ANDROID_ASYNC_SOCKET_CONNECTOR_H_ #define ANDROID_ASYNC_SOCKET_CONNECTOR_H_ +#include "qemu-common.h" #include "android/async-io-common.h" +#include "android/async-utils.h" /* * Contains declaration of an API that allows asynchronous connection to a diff --git a/android/async-socket.c b/android/async-socket.c index 5e2ae29..ab4e4b6 100644 --- a/android/async-socket.c +++ b/android/async-socket.c @@ -20,8 +20,6 @@ * a TCP port forwarding, enabled by ADB. */ -#include "qemu-common.h" -#include "android/async-utils.h" #include "android/utils/debug.h" #include "android/async-socket-connector.h" #include "android/async-socket.h" @@ -683,10 +681,10 @@ static void _async_socket_close_socket(AsyncSocket* as) { if (as->fd >= 0) { - loopIo_done(as->io); - socket_close(as->fd); T("ASocket %s: Socket handle %d is closed.", _async_socket_string(as), as->fd); + loopIo_done(as->io); + socket_close(as->fd); as->fd = -1; } } @@ -1217,15 +1215,22 @@ async_socket_read_abs(AsyncSocket* as, AsyncSocketIO* const asr = _async_socket_reader_new(as, buffer, len, reader_cb, reader_opaque, deadline); - /* Add new reader to the list. Note that we use initial reference from I/O - * 'new' routine as "in the list" reference counter. */ - if (as->readers_head == NULL) { - as->readers_head = as->readers_tail = asr; + if (async_socket_is_connected(as)) { + /* Add new reader to the list. Note that we use initial reference from I/O + * 'new' routine as "in the list" reference counter. */ + if (as->readers_head == NULL) { + as->readers_head = as->readers_tail = asr; + } else { + as->readers_tail->next = asr; + as->readers_tail = asr; + } + loopIo_wantRead(as->io); } else { - as->readers_tail->next = asr; - as->readers_tail = asr; + D("ASocket %s: Read on a disconnected socket.", _async_socket_string(as)); + errno = ECONNRESET; + reader_cb(reader_opaque, asr, ASIO_STATE_FAILED); + async_socket_io_release(asr); } - loopIo_wantRead(as->io); } void @@ -1253,15 +1258,22 @@ async_socket_write_abs(AsyncSocket* as, AsyncSocketIO* const asw = _async_socket_writer_new(as, buffer, len, writer_cb, writer_opaque, deadline); - /* Add new writer to the list. Note that we use initial reference from I/O - * 'new' routine as "in the list" reference counter. */ - if (as->writers_head == NULL) { - as->writers_head = as->writers_tail = asw; + if (async_socket_is_connected(as)) { + /* Add new writer to the list. Note that we use initial reference from I/O + * 'new' routine as "in the list" reference counter. */ + if (as->writers_head == NULL) { + as->writers_head = as->writers_tail = asw; + } else { + as->writers_tail->next = asw; + as->writers_tail = asw; + } + loopIo_wantWrite(as->io); } else { - as->writers_tail->next = asw; - as->writers_tail = asw; + D("ASocket %s: Write on a disconnected socket.", _async_socket_string(as)); + errno = ECONNRESET; + writer_cb(writer_opaque, asw, ASIO_STATE_FAILED); + async_socket_io_release(asw); } - loopIo_wantWrite(as->io); } void @@ -1294,3 +1306,9 @@ async_socket_get_port(const AsyncSocket* as) { return sock_address_get_port(&as->address); } + +int +async_socket_is_connected(const AsyncSocket* as) +{ + return as->fd >= 0; +} diff --git a/android/async-socket.h b/android/async-socket.h index 503907b..bdfd272 100644 --- a/android/async-socket.h +++ b/android/async-socket.h @@ -17,7 +17,9 @@ #ifndef ANDROID_ASYNC_SOCKET_H_ #define ANDROID_ASYNC_SOCKET_H_ +#include "qemu-common.h" #include "android/async-io-common.h" +#include "android/async-utils.h" /* * Contains declaration of an API that encapsulates communication via an @@ -264,4 +266,10 @@ extern void* async_socket_get_client_opaque(const AsyncSocket* as); /* Gets TCP port for the socket. */ extern int async_socket_get_port(const AsyncSocket* as); +/* Checks if socket is connected. + * Return: + * Boolean: 1 - socket is connected, 0 - socket is not connected. + */ +extern int async_socket_is_connected(const AsyncSocket* as); + #endif /* ANDROID_ASYNC_SOCKET_H_ */ diff --git a/android/hw-sensors.c b/android/hw-sensors.c index 17b2491..a7daea5 100644 --- a/android/hw-sensors.c +++ b/android/hw-sensors.c @@ -438,7 +438,7 @@ _hwSensorClient_receive( HwSensorClient* cl, uint8_t* msg, int msglen ) } /* If emulating device is connected update sensor state there too. */ - if (hw->sensors_port != NULL && sensors_port_is_connected(hw->sensors_port)) { + if (hw->sensors_port != NULL) { if (enabled) { sensors_port_enable_sensor(hw->sensors_port, (const char*)msg); } else { @@ -692,11 +692,6 @@ _hwSensors_init( HwSensors* h ) h->sensors[ANDROID_SENSOR_TEMPERATURE].enabled = 1; } - if (h->sensors_port != NULL) { - /* Init sensors on the attached device. */ - sensors_port_init_sensors(h->sensors_port); - } - /* XXX: TODO: Add other tests when we add the corresponding * properties to hardware-properties.ini et al. */ diff --git a/android/multitouch-port.c b/android/multitouch-port.c index 9a9313c..7cb9656 100644 --- a/android/multitouch-port.c +++ b/android/multitouch-port.c @@ -19,47 +19,97 @@ #include "android/hw-events.h" #include "android/charmap.h" #include "android/multitouch-screen.h" +#include "android/sdk-controller-socket.h" #include "android/multitouch-port.h" #include "android/globals.h" /* for android_hw */ #include "android/utils/misc.h" #include "android/utils/jpeg-compress.h" +#include "android/utils/debug.h" #define E(...) derror(__VA_ARGS__) #define W(...) dwarning(__VA_ARGS__) #define D(...) VERBOSE_PRINT(mtport,__VA_ARGS__) #define D_ACTIVE VERBOSE_CHECK(mtport) -/* Query timeout in milliseconds. */ -#define MTSP_QUERY_TIMEOUT 3000 -#define MTSP_MAX_MSG 2048 -#define MTSP_MAX_EVENT 2048 +#define TRACE_ON 1 + +#if TRACE_ON +#define T(...) VERBOSE_PRINT(mtport,__VA_ARGS__) +#else +#define T(...) +#endif + +/* Timeout (millisec) to use when communicating with SDK controller. */ +#define SDKCTL_MT_TIMEOUT 3000 + +/* + * Message types used in multi-touch emulation. + */ + +/* Pointer move message. */ +#define SDKCTL_MT_MOVE 1 +/* First pointer down message. */ +#define SDKCTL_MT_FISRT_DOWN 2 +/* Last pointer up message. */ +#define SDKCTL_MT_LAST_UP 3 +/* Pointer down message. */ +#define SDKCTL_MT_POINTER_DOWN 4 +/* Pointer up message. */ +#define SDKCTL_MT_POINTER_UP 5 +/* Sends framebuffer update. */ +#define SDKCTL_MT_FB_UPDATE 6 /* Multi-touch port descriptor. */ struct AndroidMTSPort { /* Caller identifier. */ - void* opaque; - /* Connected android device. */ - AndroidDevice* device; + void* opaque; + /* Communication socket. */ + SDKCtlSocket* sdkctl; /* Initialized JPEG compressor instance. */ - AJPEGDesc* jpeg_compressor; - /* Connection status: 1 connected, 0 - disconnected. */ - int is_connected; - /* Buffer where to receive multitouch messages. */ - char mts_msg[MTSP_MAX_MSG]; - /* Buffer where to receive multitouch events. */ - char events[MTSP_MAX_EVENT]; + AJPEGDesc* jpeg_compressor; + /* Direct packet descriptor for framebuffer updates. */ + SDKCtlDirectPacket* fb_packet; }; +/* Data sent with SDKCTL_MT_QUERY_START */ +typedef struct QueryDispData { + /* Width of the emulator display. */ + int width; + /* Height of the emulator display. */ + int height; +} QueryDispData; + +/* Multi-touch event structure received from SDK controller port. */ +typedef struct AndroidMTEvent { + /* Pointer identifier. */ + int pid; + /* Pointer 'x' coordinate. */ + int x; + /* Pointer 'y' coordinate. */ + int y; + /* Pointer pressure. */ + int pressure; +} AndroidMTEvent; + +/* Multi-touch pointer descriptor received from SDK controller port. */ +typedef struct AndroidMTPtr { + /* Pointer identifier. */ + int pid; +} AndroidMTPtr; + /* Destroys and frees the descriptor. */ static void _mts_port_free(AndroidMTSPort* mtsp) { if (mtsp != NULL) { + if (mtsp->fb_packet != NULL) { + sdkctl_direct_packet_release(mtsp->fb_packet); + } if (mtsp->jpeg_compressor != NULL) { jpeg_compressor_destroy(mtsp->jpeg_compressor); } - if (mtsp->device != NULL) { - android_device_destroy(mtsp->device); + if (mtsp->sdkctl != NULL) { + sdkctl_socket_release(mtsp->sdkctl); } AFREE(mtsp); } @@ -114,170 +164,163 @@ _on_action_move(int tracking_id, int x, int y, int pressure) * Multi-touch event handlers *******************************************************************************/ -/* Handles "pointer move" event. */ +/* Handles "pointer move" event. + * Param: + * param - Array of moving pointers. + * pointers_count - Number of pointers in the array. + */ static void -_on_move(const char* param) +_on_move(const AndroidMTEvent* param, int pointers_count) { - const char* pid = param; - D(">>> MOVE: %s", param); - while (pid && *pid) { - int pid_val, x, y, pressure = 0; - if (!get_token_value_int(pid, "pid", &pid_val) && - !get_token_value_int(pid, "x", &x) && - !get_token_value_int(pid, "y", &y)) { - get_token_value_int(pid, "pressure", &pressure); - _on_action_move(pid_val, x, y, pressure); - pid = strstr(pid + 1, "pid"); - } else { - break; - } + int n; + for (n = 0; n < pointers_count; n++, param++) { + T("Multi-touch: MOVE(%d): %d-> %d:%d:%d", + n, param->pid, param->x, param->y, param->pressure); + _on_action_move(param->pid, param->x, param->y, param->pressure); } } /* Handles "first pointer down" event. */ static void -_on_down(const char* param) +_on_down(const AndroidMTEvent* param) { - int pid_val, x, y, pressure = 0; - D(">>> 1-ST DOWN: %s", param); - if (!get_token_value_int(param, "pid", &pid_val) && - !get_token_value_int(param, "x", &x) && - !get_token_value_int(param, "y", &y)) { - get_token_value_int(param, "pressure", &pressure); - _on_action_down(pid_val, x, y, pressure); - } else { - W("Invalid parameters '%s' for MTS 'down' event", param); - } + T("Multi-touch: 1-ST DOWN: %d-> %d:%d:%d", + param->pid, param->x, param->y, param->pressure); + _on_action_down(param->pid, param->x, param->y, param->pressure); } /* Handles "last pointer up" event. */ static void -_on_up(const char* param) +_on_up(const AndroidMTPtr* param) { - int pid_val; - D(">>> LAST UP: %s", param); - if (!get_token_value_int(param, "pid", &pid_val)) { - _on_action_up(pid_val); - } else { - W("Invalid parameters '%s' for MTS 'up' event", param); - } + T("Multi-touch: LAST UP: %d", param->pid); + _on_action_up(param->pid); } /* Handles "next pointer down" event. */ static void -_on_pdown(const char* param) +_on_pdown(const AndroidMTEvent* param) { - int pid_val, x, y, pressure = 0; - D(">>> DOWN: %s", param); - if (!get_token_value_int(param, "pid", &pid_val) && - !get_token_value_int(param, "x", &x) && - !get_token_value_int(param, "y", &y)) { - get_token_value_int(param, "pressure", &pressure); - _on_action_pointer_down(pid_val, x, y, pressure); - } else { - W("Invalid parameters '%s' for MTS 'pointer down' event", param); - } + T("Multi-touch: DOWN: %d-> %d:%d:%d", + param->pid, param->x, param->y, param->pressure); + _on_action_pointer_down(param->pid, param->x, param->y, param->pressure); } /* Handles "next pointer up" event. */ static void -_on_pup(const char* param) +_on_pup(const AndroidMTPtr* param) { - int pid_val; - D(">>> UP: %s", param); - if (!get_token_value_int(param, "pid", &pid_val)) { - _on_action_pointer_up(pid_val); - } else { - W("Invalid parameters '%s' for MTS 'up' event", param); - } + T("Multi-touch: UP: %d", param->pid); + _on_action_pointer_up(param->pid); } /******************************************************************************** * Device communication callbacks *******************************************************************************/ -/* Main event handler. - * This routine is invoked when an event message has been received from the - * device. - */ -static void -_on_event_received(void* opaque, AndroidDevice* ad, char* msg, int msgsize) +/* A callback that is invoked on SDK controller socket connection events. */ +static AsyncIOAction +_on_multitouch_socket_connection(void* opaque, + SDKCtlSocket* sdkctl, + AsyncIOState status) { - char* action; - int res; - AndroidMTSPort* mtsp = (AndroidMTSPort*)opaque; - - if (errno) { - D("Multi-touch notification has failed: %s", strerror(errno)); - return; - } - - /* Dispatch the event to an appropriate handler. */ - res = get_token_value_alloc(msg, "action", &action); - if (!res) { - const char* param = strchr(msg, ' '); - if (param) { - param++; + if (status == ASIO_STATE_FAILED) { + /* Reconnect (after timeout delay) on failures */ + if (sdkctl_socket_is_handshake_ok(sdkctl)) { + sdkctl_socket_reconnect(sdkctl, SDKCTL_DEFAULT_TCP_PORT, + SDKCTL_MT_TIMEOUT); } - if (!strcmp(action, "move")) { - _on_move(param); - } else if (!strcmp(action, "down")) { - _on_down(param); - } else if (!strcmp(action, "up")) { - _on_up(param); - } else if (!strcmp(action, "pdown")) { - _on_pdown(param); - } else if (!strcmp(action, "pup")) { - _on_pup(param); - } else { - D("Unknown multi-touch event action '%s'", action); - } - free(action); } - - /* Listen to the next event. */ - android_device_listen(ad, mtsp->events, sizeof(mtsp->events), - _on_event_received); + return ASIO_ACTION_DONE; } -/* A callback that is invoked when android device is connected (i.e. both, - * command and event channels have been established). - * Param: - * opaque - AndroidMTSPort instance. - * ad - Android device used by this port. - * failure - Connections status. - */ +/* A callback that is invoked on SDK controller port connection events. */ static void -_on_device_connected(void* opaque, AndroidDevice* ad, int failure) +_on_multitouch_port_connection(void* opaque, + SDKCtlSocket* sdkctl, + SdkCtlPortStatus status) { - if (!failure) { - AndroidMTSPort* mtsp = (AndroidMTSPort*)opaque; - mtsp->is_connected = 1; - D("Multi-touch emulation has started"); - android_device_listen(mtsp->device, mtsp->events, sizeof(mtsp->events), - _on_event_received); - mts_port_start(mtsp); + switch (status) { + case SDKCTL_PORT_CONNECTED: + D("Multi-touch: SDK Controller is connected"); + break; + + case SDKCTL_PORT_DISCONNECTED: + D("Multi-touch: SDK Controller is disconnected"); + break; + + case SDKCTL_PORT_ENABLED: + D("Multi-touch: SDK Controller port is enabled."); + break; + + case SDKCTL_PORT_DISABLED: + D("Multi-touch: SDK Controller port is disabled."); + break; + + case SDKCTL_HANDSHAKE_CONNECTED: + D("Multi-touch: Handshake succeeded with connected port."); + break; + + case SDKCTL_HANDSHAKE_NO_PORT: + D("Multi-touch: Handshake succeeded with disconnected port."); + break; + + case SDKCTL_HANDSHAKE_DUP: + W("Multi-touch: Handshake failed due to port duplication."); + sdkctl_socket_disconnect(sdkctl); + break; + + case SDKCTL_HANDSHAKE_UNKNOWN_QUERY: + W("Multi-touch: Handshake failed due to unknown query."); + sdkctl_socket_disconnect(sdkctl); + break; + + case SDKCTL_HANDSHAKE_UNKNOWN_RESPONSE: + default: + W("Multi-touch: Handshake failed due to unknown reason."); + sdkctl_socket_disconnect(sdkctl); + break; } } -/* Invoked when an I/O failure occurs on a socket. - * Note that this callback will not be invoked on connection failures. - * Param: - * opaque - AndroidMTSPort instance. - * ad - Android device instance - * ads - Connection socket where failure has occured. - * failure - Contains 'errno' indicating the reason for failure. - */ +/* A callback that is invoked when a message is received from the device. */ static void -_on_io_failure(void* opaque, AndroidDevice* ad, int failure) +_on_multitouch_message(void* client_opaque, + SDKCtlSocket* sdkctl, + SDKCtlMessage* message, + int msg_type, + void* msg_data, + int msg_size) { - AndroidMTSPort* mtsp = (AndroidMTSPort*)opaque; - E("Multi-touch port got disconnected: %s", strerror(failure)); - mtsp->is_connected = 0; - android_device_disconnect(ad); + switch (msg_type) { + case SDKCTL_MT_MOVE: { + assert((msg_size / sizeof(AndroidMTEvent)) && !(msg_size % sizeof(AndroidMTEvent))); + _on_move((const AndroidMTEvent*)msg_data, msg_size / sizeof(AndroidMTEvent)); + break; + } + + case SDKCTL_MT_FISRT_DOWN: + assert(msg_size / sizeof(AndroidMTEvent) && !(msg_size % sizeof(AndroidMTEvent))); + _on_down((const AndroidMTEvent*)msg_data); + break; - /* Try to reconnect again. */ - android_device_connect_async(ad, _on_device_connected); + case SDKCTL_MT_LAST_UP: + _on_up((const AndroidMTPtr*)msg_data); + break; + + case SDKCTL_MT_POINTER_DOWN: + assert(msg_size / sizeof(AndroidMTEvent) && !(msg_size % sizeof(AndroidMTEvent))); + _on_pdown((const AndroidMTEvent*)msg_data); + break; + + case SDKCTL_MT_POINTER_UP: + _on_pup((const AndroidMTPtr*)msg_data); + break; + + default: + W("Multi-touch: Unknown message %d", msg_type); + break; + } } /******************************************************************************** @@ -288,31 +331,32 @@ AndroidMTSPort* mts_port_create(void* opaque) { AndroidMTSPort* mtsp; - int res; ANEW0(mtsp); - mtsp->opaque = opaque; - mtsp->is_connected = 0; + mtsp->opaque = opaque; /* Initialize default MTS descriptor. */ multitouch_init(mtsp); - /* Create JPEG compressor. Put "$BLOB:%09d\0" + MTFrameHeader header in front - * of the compressed data. this way we will have entire query ready to be + /* Create JPEG compressor. Put message header + MTFrameHeader in front of the + * compressed data. this way we will have entire query ready to be * transmitted to the device. */ - mtsp->jpeg_compressor = jpeg_compressor_create(16 + sizeof(MTFrameHeader), 4096); + mtsp->jpeg_compressor = + jpeg_compressor_create(sdkctl_message_get_header_size() + sizeof(MTFrameHeader), 4096); - mtsp->device = android_device_init(mtsp, AD_MULTITOUCH_PORT, _on_io_failure); - if (mtsp->device == NULL) { - _mts_port_free(mtsp); - return NULL; - } + mtsp->sdkctl = sdkctl_socket_new(SDKCTL_MT_TIMEOUT, "multi-touch", + _on_multitouch_socket_connection, + _on_multitouch_port_connection, + _on_multitouch_message, mtsp); + sdkctl_init_recycler(mtsp->sdkctl, 64, 8); - res = android_device_connect_async(mtsp->device, _on_device_connected); - if (res != 0) { - mts_port_destroy(mtsp); - return NULL; - } + /* Create a direct packet that will wrap up framebuffer updates. Note that + * we need to do this after we have initialized the recycler! */ + mtsp->fb_packet = sdkctl_direct_packet_new(mtsp->sdkctl); + + /* Now we can initiate connection witm MT port on the device. */ + sdkctl_socket_connect(mtsp->sdkctl, SDKCTL_DEFAULT_TCP_PORT, + SDKCTL_MT_TIMEOUT); return mtsp; } @@ -323,75 +367,6 @@ mts_port_destroy(AndroidMTSPort* mtsp) _mts_port_free(mtsp); } -int -mts_port_is_connected(AndroidMTSPort* mtsp) -{ - return mtsp->is_connected; -} - -int -mts_port_start(AndroidMTSPort* mtsp) -{ - char qresp[MTSP_MAX_MSG]; - char query[256]; - AndroidHwConfig* config = android_hw; - - /* Query the device to start capturing multi-touch events, also providing - * the device with width / height of the emulator's screen. This is required - * so device can properly adjust multi-touch event coordinates, and display - * emulator's framebuffer. */ - snprintf(query, sizeof(query), "start:%dx%d", - config->hw_lcd_width, config->hw_lcd_height); - int res = android_device_query(mtsp->device, query, qresp, sizeof(qresp), - MTSP_QUERY_TIMEOUT); - if (!res) { - /* By protocol device should reply with its view dimensions. */ - if (*qresp) { - int width, height; - if (sscanf(qresp, "%dx%d", &width, &height) == 2) { - multitouch_set_device_screen_size(width, height); - D("Multi-touch emulation has started. Device dims: %dx%d", - width, height); - } else { - E("Unexpected reply to MTS 'start' query: %s", qresp); - android_device_query(mtsp->device, "stop", qresp, sizeof(qresp), - MTSP_QUERY_TIMEOUT); - res = -1; - } - } else { - E("MTS protocol error: no reply to query 'start'"); - android_device_query(mtsp->device, "stop", qresp, sizeof(qresp), - MTSP_QUERY_TIMEOUT); - res = -1; - } - } else { - if (errno) { - D("Query 'start' failed on I/O: %s", strerror(errno)); - } else { - D("Query 'start' failed on device: %s", qresp); - } - } - return res; -} - -int -mts_port_stop(AndroidMTSPort* mtsp) -{ - char qresp[MTSP_MAX_MSG]; - const int res = - android_device_query(mtsp->device, "stop", qresp, sizeof(qresp), - MTSP_QUERY_TIMEOUT); - if (res) { - if (errno) { - D("Query 'stop' failed on I/O: %s", strerror(errno)); - } else { - D("Query 'stop' failed on device: %s", qresp); - } - } - - return res; -} - /******************************************************************************** * Handling framebuffer updates *******************************************************************************/ @@ -412,6 +387,8 @@ _fb_compress(const AndroidMTSPort* mtsp, int jpeg_quality, int ydir) { + T("Multi-touch: compressing %d bytes frame buffer", fmt->w * fmt->h * fmt->bpp); + jpeg_compressor_compress_fb(mtsp->jpeg_compressor, fmt->x, fmt->y, fmt->w, fmt->h, fmt->disp_height, fmt->bpp, fmt->bpl, fb, jpeg_quality, ydir); @@ -421,15 +398,12 @@ int mts_port_send_frame(AndroidMTSPort* mtsp, MTFrameHeader* fmt, const uint8_t* fb, - async_send_cb cb, + on_sdkctl_direct_cb cb, void* cb_opaque, int ydir) { - char* query; - int blob_size, off; - /* Make sure that port is connected. */ - if (!mts_port_is_connected(mtsp)) { + if (!sdkctl_socket_is_port_ready(mtsp->sdkctl)) { return -1; } @@ -437,31 +411,32 @@ mts_port_send_frame(AndroidMTSPort* mtsp, fmt->format = MTFB_JPEG; _fb_compress(mtsp, fmt, fb, 10, ydir); - /* Total size of the blob: header + JPEG image. */ - blob_size = sizeof(MTFrameHeader) + - jpeg_compressor_get_jpeg_size(mtsp->jpeg_compressor); + /* Total size of the update data: header + JPEG image. */ + const int update_size = + sizeof(MTFrameHeader) + jpeg_compressor_get_jpeg_size(mtsp->jpeg_compressor); + + /* Update message starts at the beginning of the buffer allocated by the + * compressor's destination manager. */ + uint8_t* const msg = (uint8_t*)jpeg_compressor_get_buffer(mtsp->jpeg_compressor); - /* Query starts at the beginning of the buffer allocated by the compressor's - * destination manager. */ - query = (char*)jpeg_compressor_get_buffer(mtsp->jpeg_compressor); + /* Initialize message header. */ + sdkctl_init_message_header(msg, SDKCTL_MT_FB_UPDATE, update_size); - /* Build the $BLOB query to transfer to the device. */ - snprintf(query, jpeg_compressor_get_header_size(mtsp->jpeg_compressor), - "$BLOB:%09d", blob_size); - off = strlen(query) + 1; + /* Copy framebuffer update header to the message. */ + memcpy(msg + sdkctl_message_get_header_size(), fmt, sizeof(MTFrameHeader)); - /* Copy framebuffer update header to the query. */ - memcpy(query + off, fmt, sizeof(MTFrameHeader)); + /* Compression rate... */ + const float comp_rate = ((float)jpeg_compressor_get_jpeg_size(mtsp->jpeg_compressor) / (fmt->w * fmt->h * fmt->bpp)) * 100; /* Zeroing the rectangle in the update header we indicate that it contains * no updates. */ fmt->x = fmt->y = fmt->w = fmt->h = 0; - /* Initiate asynchronous transfer of the updated framebuffer rectangle. */ - if (android_device_send_async(mtsp->device, query, off + blob_size, 0, cb, cb_opaque)) { - D("Unable to send query '%s': %s", query, strerror(errno)); - return -1; - } + /* Send update to the device. */ + sdkctl_direct_packet_send(mtsp->fb_packet, msg, cb, cb_opaque); + + T("Multi-touch: Sent %d bytes in framebuffer update. Compression rate is %.2f%%", + update_size, comp_rate); return 0; } diff --git a/android/multitouch-port.h b/android/multitouch-port.h index d99b3fd..5652b43 100644 --- a/android/multitouch-port.h +++ b/android/multitouch-port.h @@ -17,17 +17,14 @@ #ifndef ANDROID_ANDROID_MULTITOUCH_PORT_H_ #define ANDROID_ANDROID_MULTITOUCH_PORT_H_ +#include "android/sdk-controller-socket.h" + /* * Encapsulates exchange protocol between the multi-touch screen emulator, and an * application running on an Android device that provides touch events, and is * connected to the host via USB. */ -#include "android/android-device.h" - -/* TCP port reserved for multi-touch emulation. */ -#define AD_MULTITOUCH_PORT 1969 - /* * Codes that define transmitted framebuffer format: * @@ -89,28 +86,6 @@ extern AndroidMTSPort* mts_port_create(void* opaque); /* Disconnects from the multi-touch port, and destroys the descriptor. */ extern void mts_port_destroy(AndroidMTSPort* amtp); -/* Checks if port is connected to a MT-emulating application on the device. - * Note that connection can go out and then be restored at any time after - * mts_port_create API succeeded. - */ -extern int mts_port_is_connected(AndroidMTSPort* amtp); - -/* Queries the connected application to start delivering multi-touch events. - * Param: - * amtp - Android multi-touch port instance returned from mts_port_create. - * Return: - * Zero on success, failure otherwise. - */ -extern int mts_port_start(AndroidMTSPort* amtp); - -/* Queries the connected application to stop delivering multi-touch events. - * Param: - * amtp - Android multi-touch port instance returned from mts_port_create. - * Return: - * Zero on success, failure otherwise. - */ -extern int mts_port_stop(AndroidMTSPort* amtp); - /* Sends framebuffer update to the multi-touch emulation application, running on * the android device. * Param: @@ -129,7 +104,7 @@ extern int mts_port_stop(AndroidMTSPort* amtp); extern int mts_port_send_frame(AndroidMTSPort* mtsp, MTFrameHeader* fmt, const uint8_t* fb, - async_send_cb cb, + on_sdkctl_direct_cb cb, void* cb_opaque, int ydir); diff --git a/android/multitouch-screen.c b/android/multitouch-screen.c index bd96f79..59d4d75 100644 --- a/android/multitouch-screen.c +++ b/android/multitouch-screen.c @@ -29,6 +29,15 @@ #define D(...) VERBOSE_PRINT(mtscreen,__VA_ARGS__) #define D_ACTIVE VERBOSE_CHECK(mtscreen) +#define TRACE_ON 1 + +#if TRACE_ON +#define T(...) VERBOSE_PRINT(mtscreen,__VA_ARGS__) +#else +#define T(...) +#endif + + /* Maximum number of pointers, supported by multi-touch emulation. */ #define MTS_POINTERS_NUM 10 /* Signals that pointer is not tracked (or is "up"). */ @@ -54,10 +63,6 @@ typedef struct MTSState { AndroidMTSPort* mtsp; /* Emulator's display state. */ DisplayState* ds; - /* Screen width of the device that emulates multi-touch. */ - int device_width; - /* Screen height of the device that emulates multi-touch. */ - int device_height; /* Number of tracked pointers. */ int tracked_ptr_num; /* Index in the 'tracked_pointers' array of the last pointer for which @@ -239,24 +244,28 @@ static int _is_mt_initialized = 0; /* Callback that is invoked when framebuffer update has been transmitted to the * device. */ -static void -_on_fb_sent(void* opaque, ATResult res, void* data, int size, int sent) +static AsyncIOAction +_on_fb_sent(void* opaque, SDKCtlDirectPacket* packet, AsyncIOState status) { MTSState* const mts_state = (MTSState*)opaque; - /* Lets see if we have accumulated more changes while transmission has been - * in progress. */ - if (mts_state->fb_header.w && mts_state->fb_header.h) { - /* Send accumulated updates. */ - if (mts_port_send_frame(mts_state->mtsp, &mts_state->fb_header, - mts_state->current_fb, _on_fb_sent, mts_state, - mts_state->ydir)) { + if (status == ASIO_STATE_SUCCEEDED) { + /* Lets see if we have accumulated more changes while transmission has been + * in progress. */ + if (mts_state->fb_header.w && mts_state->fb_header.h) { + /* Send accumulated updates. */ + if (mts_port_send_frame(mts_state->mtsp, &mts_state->fb_header, + mts_state->current_fb, _on_fb_sent, mts_state, + mts_state->ydir)) { + mts_state->fb_transfer_in_progress = 0; + } + } else { + /* Framebuffer transfer is completed, and no more updates are pending. */ mts_state->fb_transfer_in_progress = 0; } - } else { - /* Framebuffer transfer is completed, and no more updates are pending. */ - mts_state->fb_transfer_in_progress = 0; } + + return ASIO_ACTION_DONE; } /* Common handler for framebuffer updates invoked by both, software, and OpenGLES @@ -324,6 +333,9 @@ _mt_fb_update(void* opaque, int x, int y, int w, int h) MTSState* const mts_state = (MTSState*)opaque; const DisplaySurface* const surface = mts_state->ds->surface; + T("Multi-touch: Software renderer framebuffer update: %d:%d -> %dx%d", + x, y, w, h); + /* TODO: For sofware renderer general framebuffer properties can change on * the fly. Find a callback that can catch that. For now, just copy FB * properties over in every FB update. */ @@ -336,6 +348,7 @@ _mt_fb_update(void* opaque, int x, int y, int w, int h) _mt_fb_common_update(mts_state, x, y, w, h); } + void multitouch_opengles_fb_update(void* context, int w, int h, int ydir, @@ -349,6 +362,8 @@ multitouch_opengles_fb_update(void* context, return; } + T("Multi-touch: openGLES framebuffer update: 0:0 -> %dx%d", w, h); + /* GLES format is always RGBA8888 */ mts_state->fb_header.bpp = 4; mts_state->fb_header.bpl = 4 * w; @@ -380,8 +395,6 @@ multitouch_init(AndroidMTSPort* mtsp) for (index = 0; index < MTS_POINTERS_NUM; index++) { mts_state->tracked_pointers[index].tracking_id = MTS_POINTER_UP; } - mts_state->device_width = android_hw->hw_lcd_width; - mts_state->device_height = android_hw->hw_lcd_height; mts_state->mtsp = mtsp; mts_state->fb_header.header_size = sizeof(MTFrameHeader); mts_state->fb_transfer_in_progress = 0; @@ -453,12 +466,3 @@ multitouch_get_max_slot() { return MTS_POINTERS_NUM - 1; } - -void -multitouch_set_device_screen_size(int width, int height) -{ - MTSState* const mts_state = &_MTSState; - - mts_state->device_width = width; - mts_state->device_height = height; -} diff --git a/android/multitouch-screen.h b/android/multitouch-screen.h index 433944d..95ae86a 100644 --- a/android/multitouch-screen.h +++ b/android/multitouch-screen.h @@ -17,6 +17,7 @@ #ifndef ANDROID_MULTITOUCH_SCREEN_H_ #define ANDROID_MULTITOUCH_SCREEN_H_ +#include "android/sdk-controller-socket.h" #include "android/multitouch-port.h" /* @@ -61,9 +62,6 @@ extern void multitouch_update_pointer(MTESource source, /* Gets maximum slot index available for the multi-touch emulation. */ extern int multitouch_get_max_slot(); -/* Saves screen size reported by the device that emulates multi-touch. */ -extern void multitouch_set_device_screen_size(int width, int height); - /* A callback set to monitor OpenGLES framebuffer updates. * This callback is called by the renderer just before each new frame is * displayed, providing a copy of the framebuffer contents. diff --git a/android/sdk-controller-socket.c b/android/sdk-controller-socket.c index a300169..6a4ec18 100644 --- a/android/sdk-controller-socket.c +++ b/android/sdk-controller-socket.c @@ -20,8 +20,6 @@ * a TCP port forwarding, enabled by ADB. */ -#include "qemu-common.h" -#include "android/async-utils.h" #include "android/utils/debug.h" #include "android/async-socket-connector.h" #include "android/async-socket.h" @@ -34,7 +32,7 @@ #define D(...) VERBOSE_PRINT(sdkctlsocket,__VA_ARGS__) #define D_ACTIVE VERBOSE_CHECK(sdkctlsocket) -#define TRACE_ON 1 +#define TRACE_ON 0 #if TRACE_ON #define T(...) VERBOSE_PRINT(sdkctlsocket,__VA_ARGS__) @@ -53,12 +51,8 @@ struct SDKCtlRecycled { }; }; -/******************************************************************************** - * SDKCtlPacket declarations - *******************************************************************************/ - /* - * Types of the packets of data sent via SDK controller socket. + * Types of the data packets sent via SDK controller socket. */ /* The packet is a message. */ @@ -68,12 +62,56 @@ struct SDKCtlRecycled { /* The packet is a response to a query. */ #define SDKCTL_PACKET_QUERY_RESPONSE 3 +/* + * Types of intenal port messages sent via SDK controller socket. + */ + +/* Port is connected. + * This message is sent by SDK controller when the service connects a socket with + * a port that provides requested emulation functionality. + */ +#define SDKCTL_MSG_PORT_CONNECTED -1 +/* Port is disconnected. + * This message is sent by SDK controller when a port that provides requested + * emulation functionality disconnects from the socket. + */ +#define SDKCTL_MSG_PORT_DISCONNECTED -2 +/* Port is enabled. + * This message is sent by SDK controller when a port that provides requested + * emulation functionality is ready to do the emulation. + */ +#define SDKCTL_MSG_PORT_ENABLED -3 +/* Port is disabled. + * This message is sent by SDK controller when a port that provides requested + * emulation functionality is not ready to do the emulation. + */ +#define SDKCTL_MSG_PORT_DISABLED -4 + +/* + * Types of internal queries sent via SDK controller socket. + */ + +/* Handshake query. + * This query is sent to SDK controller service as part of the connection + * protocol implementation. + */ +#define SDKCTL_QUERY_HANDSHAKE -1 + +/******************************************************************************** + * SDKCtlPacket declarations + *******************************************************************************/ + +/* Packet signature value ('SDKC'). */ +static const int _sdkctl_packet_sig = 0x53444B43; + /* Data packet descriptor. * * All packets, sent and received via SDK controller socket begin with this * header, with packet data immediately following this header. */ typedef struct SDKCtlPacketHeader { + /* Signature. */ + int signature; /* Total size of the data to transfer with this packet, including this * header. The transferring data should immediatelly follow this header. */ int size; @@ -83,42 +121,60 @@ typedef struct SDKCtlPacketHeader { } SDKCtlPacketHeader; /* Packet descriptor, allocated by this API for data packets to be sent to SDK - * controller service on the device. + * controller. * * When packet descriptors are allocated by this API, they are allocated large * enough to contain this header, and packet data to send to the service, * immediately following this descriptor. */ -struct SDKCtlPacket { +typedef struct SDKCtlPacket { /* Supports recycling. Don't put anything in front: recycler expects this * to be the first field in recyclable descriptor. */ - SDKCtlRecycled recycling; + SDKCtlRecycled recycling; - /* Next packet in the list of packets to send. */ - SDKCtlPacket* next; /* SDK controller socket that transmits this packet. */ - SDKCtlSocket* sdkctl; + SDKCtlSocket* sdkctl; /* Number of outstanding references to the packet. */ - int ref_count; + int ref_count; /* Common packet header. Packet data immediately follows this header, so it - * must be last field in SDKCtlPacket descriptor. */ - SDKCtlPacketHeader header; -}; + * must be the last field in SDKCtlPacket descriptor. */ + SDKCtlPacketHeader header; +} SDKCtlPacket; /******************************************************************************** - * SDKCtlQuery declarations + * SDKCtlDirectPacket declarations *******************************************************************************/ -/* - * Types of queries sent via SDK controller socket. +/* Direct packet descriptor, allocated by this API for direct data packets to be + * sent to SDK controller service on the device. + * + * Direct packet (unlike SDKCtlPacket) don't contain data buffer, but rather + * reference data allocated by the client. This is useful when client sends large + * amount of data (such as framebuffer updates sent my multi-touch port), and + * regular packet descriptors for such large transfer cannot be obtained from the + * recycler. */ +struct SDKCtlDirectPacket { + /* Supports recycling. Don't put anything in front: recycler expects this + * to be the first field in recyclable descriptor. */ + SDKCtlRecycled recycling; -/* Handshake query. - * This query is sent to SDK controller service as part of the connection - * protocol implementation. - */ -#define SDKCTL_QUERY_HANDSHAKE -1 + /* SDKCtlSocket that owns this packet. */ + SDKCtlSocket* sdkctl; + /* Packet to send. */ + SDKCtlPacketHeader* packet; + /* Callback to invoke on packet transmission events. */ + on_sdkctl_direct_cb on_sent; + /* An opaque pointer to pass to on_sent callback. */ + void* on_sent_opaque; + /* Number of outstanding references to the packet. */ + int ref_count; +}; + +/******************************************************************************** + * SDKCtlQuery declarations + *******************************************************************************/ /* Query packet descriptor. * @@ -131,8 +187,7 @@ typedef struct SDKCtlQueryHeader { /* A unique query identifier. This ID is used to track the query in the * asynchronous environment in whcih SDK controller socket operates. */ int query_id; - /* Query type. See SDKCTL_QUERY_XXX for the list of query types used by SDK - * controller. */ + /* Query type. */ int query_type; } SDKCtlQueryHeader; @@ -148,12 +203,12 @@ struct SDKCtlQuery { * to be the first field in recyclable descriptor. */ SDKCtlRecycled recycling; - /* Next query in the list of active, or recycled queries. */ + /* Next query in the list of active queries. */ SDKCtlQuery* next; /* A timer to run time out on this query after it has been sent. */ LoopTimer timer[1]; /* Absolute time for this query's deadline. This is the value that query's - * timer is set for after query has been transmitted to the service. */ + * timer is set to after query has been transmitted to the service. */ Duration deadline; /* SDK controller socket that owns the query. */ SDKCtlSocket* sdkctl; @@ -163,20 +218,21 @@ struct SDKCtlQuery { void* query_opaque; /* Points to an address of a buffer where to save query response. */ void** response_buffer; - /* Points to a variable containing size of the response buffer (on the way in), - * or actual query response size (when query is completed). */ + /* Points to a variable containing size of the response buffer (on the way + * in), or actual query response size (when query is completed). */ uint32_t* response_size; /* Internal response buffer, allocated if query creator didn't provide its * own. This field is valid only if response_buffer field is NULL, or is * pointing to this field. */ void* internal_resp_buffer; - /* Internal response buffer size This field is valid only if response_size - * field is NULL, or is pointing to this field. */ + /* Internal response buffer size used if query creator didn't provide its + * own. This field is valid only if response_size field is NULL, or is + * pointing to this field. */ uint32_t internal_resp_size; /* Number of outstanding references to the query. */ int ref_count; - /* Common packet header. Query data immediately follows this header, so it + /* Common query header. Query data immediately follows this header, so it * must be last field in SDKCtlQuery descriptor. */ SDKCtlQueryHeader header; }; @@ -195,6 +251,34 @@ typedef struct SDKCtlQueryReplyHeader { } SDKCtlQueryReplyHeader; /******************************************************************************** + * SDKCtlMessage declarations + *******************************************************************************/ + +/* Message packet descriptor. + * + * All messages, sent and received via SDK controller socket begin with this + * header, with message data immediately following this header. + */ +typedef struct SDKCtlMessageHeader { + /* Data packet header for this query. */ + SDKCtlPacketHeader packet; + /* Message type. */ + int msg_type; +} SDKCtlMessageHeader; + +/* Message packet descriptor. + * + * All messages, sent and received via SDK controller socket begin with this + * header, with message data immediately following this header. + */ +typedef struct SDKCtlMessage { + /* Data packet descriptor for this message. */ + SDKCtlPacket packet; + /* Message type. */ + int msg_type; +} SDKCtlMessage; + +/******************************************************************************** * SDK Control Socket declarations *******************************************************************************/ @@ -229,13 +313,15 @@ typedef struct SDKCtlIODispatcher { /* Unites all types of headers used in SDK controller data exchange. */ union { /* Common packet header. */ - SDKCtlPacketHeader header; + SDKCtlPacketHeader packet_header; /* Header for a query packet. */ SDKCtlQueryHeader query_header; + /* Header for a message packet. */ + SDKCtlMessageHeader message_header; /* Header for a query response packet. */ SDKCtlQueryReplyHeader query_reply_header; }; - /* Descriptor of a packet packet received from SDK controller. */ + /* Descriptor of a packet that is being received from SDK controller. */ SDKCtlPacket* packet; /* A query for which a reply is currently being received. */ SDKCtlQuery* current_query; @@ -244,42 +330,43 @@ typedef struct SDKCtlIODispatcher { /* SDK controller socket descriptor. */ struct SDKCtlSocket { /* SDK controller socket state */ - SDKCtlSocketState state; + SDKCtlSocketState state; + /* SDK controller port status */ + SdkCtlPortStatus port_status; /* I/O dispatcher for the socket. */ - SDKCtlIODispatcher io_dispatcher; + SDKCtlIODispatcher io_dispatcher; /* Asynchronous socket connected to SDK Controller on the device. */ - AsyncSocket* as; + AsyncSocket* as; /* Client callback that monitors this socket connection. */ - on_sdkctl_connection_cb on_connection; - /* A callback to invoke when handshake message is received from the - * SDK controller. */ - on_sdkctl_handshake_cb on_handshake; + on_sdkctl_socket_connection_cb on_socket_connection; + /* Client callback that monitors SDK controller prt connection. */ + on_sdkctl_port_connection_cb on_port_connection; /* A callback to invoke when a message is received from the SDK controller. */ - on_sdkctl_message_cb on_message; + on_sdkctl_message_cb on_message; /* An opaque pointer associated with this socket. */ - void* opaque; - /* Name of an SDK controller service this socket is connected to. */ - char* service_name; + void* opaque; + /* Name of an SDK controller port this socket is connected to. */ + char* service_name; /* I/O looper for timers. */ - Looper* looper; + Looper* looper; /* Head of the active query list. */ - SDKCtlQuery* query_head; + SDKCtlQuery* query_head; /* Tail of the active query list. */ - SDKCtlQuery* query_tail; + SDKCtlQuery* query_tail; /* Query ID generator that gets incremented for each new query. */ - int next_query_id; + int next_query_id; /* Timeout before trying to reconnect after disconnection. */ - int reconnect_to; + int reconnect_to; /* Number of outstanding references to this descriptor. */ - int ref_count; + int ref_count; /* Head of the recycled memory */ - SDKCtlRecycled* recycler; + SDKCtlRecycled* recycler; /* Recyclable block size. */ - uint32_t recycler_block_size; + uint32_t recycler_block_size; /* Maximum number of blocks to recycle. */ - int recycler_max; + int recycler_max; /* Number of blocs in the recycler. */ - int recycler_count; + int recycler_count; }; /******************************************************************************** @@ -294,16 +381,18 @@ _sdkctl_socket_alloc_recycler(SDKCtlSocket* sdkctl, uint32_t size) SDKCtlRecycled* block = NULL; if (sdkctl->recycler != NULL && size <= sdkctl->recycler_block_size) { + assert(sdkctl->recycler_count > 0); /* There are blocks in the recycler, and requested size fits. */ block = sdkctl->recycler; sdkctl->recycler = block->next; block->size = sdkctl->recycler_block_size; sdkctl->recycler_count--; } else if (size <= sdkctl->recycler_block_size) { - /* There are no blocks in the recycler, but requested size fits. */ + /* There are no blocks in the recycler, but requested size fits. Lets + * allocate block that we can later recycle. */ block = malloc(sdkctl->recycler_block_size); if (block == NULL) { - APANIC("SDKCtl %s: Unable to allocate %d bytes block", + APANIC("SDKCtl %s: Unable to allocate %d bytes block.", sdkctl->service_name, sdkctl->recycler_block_size); } block->size = sdkctl->recycler_block_size; @@ -324,18 +413,19 @@ _sdkctl_socket_alloc_recycler(SDKCtlSocket* sdkctl, uint32_t size) static void _sdkctl_socket_free_recycler(SDKCtlSocket* sdkctl, void* mem) { - SDKCtlRecycled* block = (SDKCtlRecycled*)mem; + SDKCtlRecycled* const block = (SDKCtlRecycled*)mem; - if (sdkctl->recycler_count == sdkctl->recycler_max || - block->size != sdkctl->recycler_block_size) { - /* Recycler is full, or block cannot be recycled. */ + if (block->size != sdkctl->recycler_block_size || + sdkctl->recycler_count == sdkctl->recycler_max) { + /* Recycler is full, or block cannot be recycled. Just free the memory. */ free(mem); - return; + } else { + /* Add that block to the recycler. */ + assert(sdkctl->recycler_count >= 0); + block->next = sdkctl->recycler; + sdkctl->recycler = block; + sdkctl->recycler_count++; } - - block->next = sdkctl->recycler; - sdkctl->recycler = block; - sdkctl->recycler_count++; } /* Empties the recycler for a given SDKCtlSocket. */ @@ -344,7 +434,7 @@ _sdkctl_socket_empty_recycler(SDKCtlSocket* sdkctl) { SDKCtlRecycled* block = sdkctl->recycler; while (block != NULL) { - void* to_free = block; + void* const to_free = block; block = block->next; free(to_free); } @@ -366,6 +456,7 @@ _sdkctl_socket_add_query(SDKCtlQuery* query) { SDKCtlSocket* const sdkctl = query->sdkctl; if (sdkctl->query_head == NULL) { + assert(sdkctl->query_tail == NULL); sdkctl->query_head = sdkctl->query_tail = query; } else { sdkctl->query_tail->next = query; @@ -378,7 +469,9 @@ _sdkctl_socket_add_query(SDKCtlQuery* query) /* Removes a query from the list of active queries. * Param: - * query - Query to remove from the list of active queries. + * query - Query to remove from the list of active queries. If query has been + * removed from the list, it will be dereferenced to offset the reference + * that wad made when the query has been added to the list. * Return: * Boolean: 1 if query has been removed, or 0 if query has not been found in the * list of active queries. @@ -390,11 +483,11 @@ _sdkctl_socket_remove_query(SDKCtlQuery* query) SDKCtlQuery* prev = NULL; SDKCtlQuery* head = sdkctl->query_head; - /* Quick check: the query could be currently handled by dispatcher. */ + /* Quick check: the query could be currently handled by the dispatcher. */ if (sdkctl->io_dispatcher.current_query == query) { /* Release the query from dispatcher. */ - sdkctl_query_release(query); sdkctl->io_dispatcher.current_query = NULL; + sdkctl_query_release(query); return 1; } @@ -403,32 +496,34 @@ _sdkctl_socket_remove_query(SDKCtlQuery* query) prev = head; head = head->next; } - if (head != NULL) { - if (prev == NULL) { - /* Query is at the head of the list. */ - assert(query == sdkctl->query_head); - sdkctl->query_head = query->next; - } else { - /* Query is in the middle / at the end of the list. */ - assert(query != sdkctl->query_head); - prev->next = query->next; - } - if (sdkctl->query_tail == query) { - /* Query is at the tail of the list. */ - assert(query->next == NULL); - sdkctl->query_tail = prev; - } - query->next = NULL; + if (head == NULL) { + D("SDKCtl %s: Query %p is not found in the list.", + sdkctl->service_name, query); + return 0; + } - /* Release query that is now removed from the list. Note that query - * passed to this routine should hold an extra reference, owned by the - * caller. */ - sdkctl_query_release(query); - return 1; + if (prev == NULL) { + /* Query is at the head of the list. */ + assert(query == sdkctl->query_head); + sdkctl->query_head = query->next; } else { - D("%s: Query %p is not found in the list.", sdkctl->service_name, query); - return 0; + /* Query is in the middle / at the end of the list. */ + assert(query != sdkctl->query_head); + prev->next = query->next; } + if (sdkctl->query_tail == query) { + /* Query is at the tail of the list. */ + assert(query->next == NULL); + sdkctl->query_tail = prev; + } + query->next = NULL; + + /* Release query that is now removed from the list. Note that query + * passed to this routine should hold an extra reference, owned by the + * caller. */ + sdkctl_query_release(query); + + return 1; } /* Removes a query (based on query ID) from the list of active queries. @@ -437,11 +532,14 @@ _sdkctl_socket_remove_query(SDKCtlQuery* query) * query_id - Identifies the query to remove. * Return: * A query removed from the list of active queries, or NULL if query with the - * given ID has not been found in the list. + * given ID has not been found in the list. Note that query returned from this + * routine still holds the reference made when the query has been added to the + * list. */ static SDKCtlQuery* _sdkctl_socket_remove_query_id(SDKCtlSocket* sdkctl, int query_id) { + SDKCtlQuery* query = NULL; SDKCtlQuery* prev = NULL; SDKCtlQuery* head = sdkctl->query_head; @@ -449,7 +547,7 @@ _sdkctl_socket_remove_query_id(SDKCtlSocket* sdkctl, int query_id) if (sdkctl->io_dispatcher.current_query != NULL && sdkctl->io_dispatcher.current_query->header.query_id == query_id) { /* Release the query from dispatcher. */ - SDKCtlQuery* const query = sdkctl->io_dispatcher.current_query; + query = sdkctl->io_dispatcher.current_query; sdkctl->io_dispatcher.current_query = NULL; return query; } @@ -459,37 +557,38 @@ _sdkctl_socket_remove_query_id(SDKCtlSocket* sdkctl, int query_id) prev = head; head = head->next; } - if (head != NULL) { - /* Query is found in the list. */ - SDKCtlQuery* const query = head; - if (prev == NULL) { - /* Query is at the head of the list. */ - assert(query == sdkctl->query_head); - sdkctl->query_head = query->next; - } else { - /* Query is in the middle, or at the end of the list. */ - assert(query != sdkctl->query_head); - prev->next = query->next; - } - if (sdkctl->query_tail == query) { - /* Query is at the tail of the list. */ - assert(query->next == NULL); - sdkctl->query_tail = prev; - } - query->next = NULL; - return query; - } else { - D("%s: Query ID %d is not found in the list.", + if (head == NULL) { + D("SDKCtl %s: Query ID %d is not found in the list.", sdkctl->service_name, query_id); return NULL; } + + /* Query is found in the list. */ + query = head; + if (prev == NULL) { + /* Query is at the head of the list. */ + assert(query == sdkctl->query_head); + sdkctl->query_head = query->next; + } else { + /* Query is in the middle, or at the end of the list. */ + assert(query != sdkctl->query_head); + prev->next = query->next; + } + if (sdkctl->query_tail == query) { + /* Query is at the tail of the list. */ + assert(query->next == NULL); + sdkctl->query_tail = prev; + } + query->next = NULL; + + return query; } /* Pulls the first query from the list of active queries. * Param: * sdkctl - SDKCtlSocket instance that owns the query. * Return: - * A query removed pulled from the list of active queries, or NULL if query + * A query removed from the head of the list of active queries, or NULL if query * list is empty. */ static SDKCtlQuery* @@ -519,19 +618,24 @@ _sdkctl_socket_next_query_id(SDKCtlSocket* sdkctl) /* Alocates a packet. */ static SDKCtlPacket* -_sdkctl_packet_new(SDKCtlSocket* sdkctl, int size, int type) +_sdkctl_packet_new(SDKCtlSocket* sdkctl, uint32_t size, int type) { - const uint32_t total_size = sizeof(SDKCtlPacket) + size; - SDKCtlPacket* const packet = _sdkctl_socket_alloc_recycler(sdkctl, total_size); + /* Allocate packet descriptor large enough to contain packet data. */ + SDKCtlPacket* const packet = + _sdkctl_socket_alloc_recycler(sdkctl, sizeof(SDKCtlPacket) + size); - packet->sdkctl = sdkctl; - packet->ref_count = 1; - packet->header.size = size; - packet->header.type = type; + packet->sdkctl = sdkctl; + packet->ref_count = 1; + packet->header.signature = _sdkctl_packet_sig; + packet->header.size = size; + packet->header.type = type; /* Refence SDKCTlSocket that owns this packet. */ sdkctl_socket_reference(sdkctl); + T("SDKCtl %s: Packet %p of type %d is allocated for %d bytes transfer.", + sdkctl->service_name, packet, type, size); + return packet; } @@ -541,23 +645,27 @@ _sdkctl_packet_free(SDKCtlPacket* packet) { SDKCtlSocket* const sdkctl = packet->sdkctl; - /* Free allocated resources. */ + /* Recycle packet. */ _sdkctl_socket_free_recycler(packet->sdkctl, packet); + T("SDKCtl %s: Packet %p is freed.", sdkctl->service_name, packet); + /* Release SDKCTlSocket that owned this packet. */ sdkctl_socket_release(sdkctl); } +/* References a packet. */ int -sdkctl_packet_reference(SDKCtlPacket* packet) +_sdkctl_packet_reference(SDKCtlPacket* packet) { assert(packet->ref_count > 0); packet->ref_count++; return packet->ref_count; } +/* Releases a packet. */ int -sdkctl_packet_release(SDKCtlPacket* packet) +_sdkctl_packet_release(SDKCtlPacket* packet) { assert(packet->ref_count > 0); packet->ref_count--; @@ -569,6 +677,272 @@ sdkctl_packet_release(SDKCtlPacket* packet) return packet->ref_count; } +/* An I/O callback invoked on packet transmission. + * Param: + * io_opaque SDKCtlPacket instance of the packet that's being sent with this I/O. + * asio - Write I/O descriptor. + * status - I/O status. + */ +static AsyncIOAction +_on_sdkctl_packet_send_io(void* io_opaque, + AsyncSocketIO* asio, + AsyncIOState status) +{ + SDKCtlPacket* const packet = (SDKCtlPacket*)io_opaque; + AsyncIOAction action = ASIO_ACTION_DONE; + + /* Reference the packet while we're in this callback. */ + _sdkctl_packet_reference(packet); + + /* Lets see what's going on with query transmission. */ + switch (status) { + case ASIO_STATE_SUCCEEDED: + /* Packet has been sent to the service. */ + T("SDKCtl %s: Packet %p transmission has succeeded.", + packet->sdkctl->service_name, packet); + break; + + case ASIO_STATE_CANCELLED: + T("SDKCtl %s: Packet %p is cancelled.", + packet->sdkctl->service_name, packet); + break; + + case ASIO_STATE_FAILED: + T("SDKCtl %s: Packet %p has failed: %d -> %s", + packet->sdkctl->service_name, packet, errno, strerror(errno)); + break; + + case ASIO_STATE_FINISHED: + /* Time to disassociate the packet with the I/O. */ + _sdkctl_packet_release(packet); + break; + + default: + /* Transitional state. */ + break; + } + + _sdkctl_packet_release(packet); + + return action; +} + +/* Transmits a packet to SDK Controller. + * Param: + * packet - Packet to transmit. + */ +static void +_sdkctl_packet_transmit(SDKCtlPacket* packet) +{ + assert(packet->header.signature == _sdkctl_packet_sig); + + /* Reference to associate with the I/O */ + _sdkctl_packet_reference(packet); + + /* Transmit the packet to SDK controller. */ + async_socket_write_rel(packet->sdkctl->as, &packet->header, packet->header.size, + _on_sdkctl_packet_send_io, packet, -1); + + T("SDKCtl %s: Packet %p size %d is being sent.", + packet->sdkctl->service_name, packet, packet->header.size); +} + +/******************************************************************************** + * SDKCtlDirectPacket implementation + ********************************************************************************/ + +SDKCtlDirectPacket* +sdkctl_direct_packet_new(SDKCtlSocket* sdkctl) +{ + SDKCtlDirectPacket* const packet = + _sdkctl_socket_alloc_recycler(sdkctl, sizeof(SDKCtlDirectPacket)); + + packet->sdkctl = sdkctl; + packet->ref_count = 1; + + /* Refence SDKCTlSocket that owns this packet. */ + sdkctl_socket_reference(packet->sdkctl); + + T("SDKCtl %s: Direct packet %p is allocated.", sdkctl->service_name, packet); + + return packet; +} + +/* Frees a direct packet. */ +static void +_sdkctl_direct_packet_free(SDKCtlDirectPacket* packet) +{ + SDKCtlSocket* const sdkctl = packet->sdkctl; + + /* Free allocated resources. */ + _sdkctl_socket_free_recycler(packet->sdkctl, packet); + + T("SDKCtl %s: Direct packet %p is freed.", sdkctl->service_name, packet); + + /* Release SDKCTlSocket that owned this packet. */ + sdkctl_socket_release(sdkctl); +} + +/* References a packet. */ +int +sdkctl_direct_packet_reference(SDKCtlDirectPacket* packet) +{ + assert(packet->ref_count > 0); + packet->ref_count++; + return packet->ref_count; +} + +/* Releases a packet. */ +int +sdkctl_direct_packet_release(SDKCtlDirectPacket* packet) +{ + assert(packet->ref_count > 0); + packet->ref_count--; + if (packet->ref_count == 0) { + /* Last reference has been dropped. Destroy this object. */ + _sdkctl_direct_packet_free(packet); + return 0; + } + return packet->ref_count; +} + +/* An I/O callback invoked on direct packet transmission. + * Param: + * io_opaque SDKCtlDirectPacket instance of the packet that's being sent with + * this I/O. + * asio - Write I/O descriptor. + * status - I/O status. + */ +static AsyncIOAction +_on_sdkctl_direct_packet_send_io(void* io_opaque, + AsyncSocketIO* asio, + AsyncIOState status) +{ + SDKCtlDirectPacket* const packet = (SDKCtlDirectPacket*)io_opaque; + AsyncIOAction action = ASIO_ACTION_DONE; + + /* Reference the packet while we're in this callback. */ + sdkctl_direct_packet_reference(packet); + + /* Lets see what's going on with query transmission. */ + switch (status) { + case ASIO_STATE_SUCCEEDED: + /* Packet has been sent to the service. */ + T("SDKCtl %s: Direct packet %p transmission has succeeded.", + packet->sdkctl->service_name, packet); + packet->on_sent(packet->on_sent_opaque, packet, status); + break; + + case ASIO_STATE_CANCELLED: + T("SDKCtl %s: Direct packet %p is cancelled.", + packet->sdkctl->service_name, packet); + packet->on_sent(packet->on_sent_opaque, packet, status); + break; + + case ASIO_STATE_FAILED: + T("SDKCtl %s: Direct packet %p has failed: %d -> %s", + packet->sdkctl->service_name, packet, errno, strerror(errno)); + packet->on_sent(packet->on_sent_opaque, packet, status); + break; + + case ASIO_STATE_FINISHED: + /* Time to disassociate with the I/O. */ + sdkctl_direct_packet_release(packet); + break; + + default: + /* Transitional state. */ + break; + } + + sdkctl_direct_packet_release(packet); + + return action; +} + +void +sdkctl_direct_packet_send(SDKCtlDirectPacket* packet, + void* data, + on_sdkctl_direct_cb cb, + void* cb_opaque) +{ + packet->packet = (SDKCtlPacketHeader*)data; + packet->on_sent = cb; + packet->on_sent_opaque = cb_opaque; + assert(packet->packet->signature == _sdkctl_packet_sig); + + /* Reference for I/O */ + sdkctl_direct_packet_reference(packet); + + /* Transmit the packet to SDK controller. */ + async_socket_write_rel(packet->sdkctl->as, packet->packet, packet->packet->size, + _on_sdkctl_direct_packet_send_io, packet, -1); + + T("SDKCtl %s: Direct packet %p size %d is being sent", + packet->sdkctl->service_name, packet, packet->packet->size); +} + +/******************************************************************************** + * SDKCtlMessage implementation + *******************************************************************************/ + +/* Alocates a message descriptor. */ +static SDKCtlMessage* +_sdkctl_message_new(SDKCtlSocket* sdkctl, uint32_t msg_size, int msg_type) +{ + SDKCtlMessage* const msg = + (SDKCtlMessage*)_sdkctl_packet_new(sdkctl, + sizeof(SDKCtlMessageHeader) + msg_size, + SDKCTL_PACKET_MESSAGE); + msg->msg_type = msg_type; + + return msg; +} + +int +sdkctl_message_reference(SDKCtlMessage* msg) +{ + return _sdkctl_packet_reference(&msg->packet); +} + +int +sdkctl_message_release(SDKCtlMessage* msg) +{ + return _sdkctl_packet_release(&msg->packet); +} + +SDKCtlMessage* +sdkctl_message_send(SDKCtlSocket* sdkctl, + int msg_type, + const void* data, + uint32_t size) +{ + SDKCtlMessage* const msg = _sdkctl_message_new(sdkctl, size, msg_type); + if (size != 0 && data != NULL) { + memcpy(msg + 1, data, size); + } + _sdkctl_packet_transmit(&msg->packet); + + return msg; +} + +int +sdkctl_message_get_header_size(void) +{ + return sizeof(SDKCtlMessageHeader); +} + +void +sdkctl_init_message_header(void* msg, int msg_type, int msg_size) +{ + SDKCtlMessageHeader* const msg_header = (SDKCtlMessageHeader*)msg; + + msg_header->packet.signature = _sdkctl_packet_sig; + msg_header->packet.size = sizeof(SDKCtlMessageHeader) + msg_size; + msg_header->packet.type = SDKCTL_PACKET_MESSAGE; + msg_header->msg_type = msg_type; +} + /******************************************************************************** * SDKCtlQuery implementation *******************************************************************************/ @@ -579,19 +953,21 @@ _sdkctl_query_free(SDKCtlQuery* query) { if (query != NULL) { SDKCtlSocket* const sdkctl = query->sdkctl; - T("SDKCtl %s: Query %p ID %d is freed.", - sdkctl->service_name, query, query->header.query_id); - - /* Free allocated resources. */ if (query->internal_resp_buffer != NULL && (query->response_buffer == NULL || query->response_buffer == &query->internal_resp_buffer)) { + /* This query used its internal buffer to receive the response. + * Free it. */ free(query->internal_resp_buffer); } loopTimer_done(query->timer); + + /* Recyle the descriptor. */ _sdkctl_socket_free_recycler(sdkctl, query); + T("SDKCtl %s: Query %p is freed.", sdkctl->service_name, query); + /* Release socket that owned this query. */ sdkctl_socket_release(sdkctl); } @@ -609,8 +985,8 @@ _sdkctl_query_cancel_timeout(SDKCtlQuery* query) { loopTimer_stop(query->timer); - T("SDKCtl %s: Query %p ID %d deadline is cancelled.", - query->sdkctl->service_name, query, query->header.query_id); + T("SDKCtl %s: Query %p ID %d deadline %lld is cancelled.", + query->sdkctl->service_name, query, query->header.query_id, query->deadline); } /* @@ -639,7 +1015,7 @@ _on_sdkctl_query_cancelled(SDKCtlQuery* query) * Query cancellation means that SDK controller is disconnected. In turn, * this means that SDK controller socket will handle disconnection in its * connection callback. So, at this point all we need to do here is to inform - * the client, and then unlist the query. + * the client about query cancellation. */ /* Cancel deadline, and inform the client about query cancellation. */ @@ -676,12 +1052,11 @@ _on_skdctl_query_timeout(void* opaque) sdkctl_query_release(query); } -/* A callback that is invoked when query has been sent to the SDK controller - * service. */ +/* A callback that is invoked when query has been sent to the SDK controller. */ static void _on_sdkctl_query_sent(SDKCtlQuery* query) { - T("SDKCtl %s: sent %d bytes of query %p ID %d of type %d", + T("SDKCtl %s: Sent %d bytes of query %p ID %d of type %d", query->sdkctl->service_name, query->header.packet.size, query, query->header.query_id, query->header.query_type); @@ -710,31 +1085,25 @@ _on_sdkctl_query_send_io(void* io_opaque, /* Reference the query while we're in this callback. */ sdkctl_query_reference(query); - if (status == ASIO_STATE_SUCCEEDED) { - /* Query has been sent to the service. */ - _on_sdkctl_query_sent(query); - - sdkctl_query_release(query); - - return ASIO_ACTION_DONE; - } - /* Lets see what's going on with query transmission. */ switch (status) { + case ASIO_STATE_SUCCEEDED: + /* Query has been sent to the service. */ + _on_sdkctl_query_sent(query); + break; + case ASIO_STATE_CANCELLED: - T("SDKCtl %s: Query %p ID %d is cancelled in %s I/O.", - query->sdkctl->service_name, query, query->header.query_id, - async_socket_io_is_read(asio) ? "READ" : "WRITE"); + T("SDKCtl %s: Query %p ID %d is cancelled in transmission.", + query->sdkctl->service_name, query, query->header.query_id); /* Remove the query from the list of active queries. */ _sdkctl_socket_remove_query(query); _on_sdkctl_query_cancelled(query); break; case ASIO_STATE_TIMED_OUT: - D("SDKCtl %s: Query %p ID %d with deadline %lld has timed out in %s I/O at %lld", + D("SDKCtl %s: Query %p ID %d with deadline %lld has timed out in transmission at %lld", query->sdkctl->service_name, query, query->header.query_id, - query->deadline, async_socket_io_is_read(asio) ? "READ" : "WRITE", - async_socket_deadline(query->sdkctl->as, 0)); + query->deadline, async_socket_deadline(query->sdkctl->as, 0)); /* Invoke query's callback. */ action = query->query_cb(query->query_opaque, query, status); /* For actions other than retry we need to stop the query. */ @@ -744,9 +1113,8 @@ _on_sdkctl_query_send_io(void* io_opaque, break; case ASIO_STATE_FAILED: - T("SDKCtl %s: Query %p ID %d failed in %s I/O: %d -> %s", + T("SDKCtl %s: Query %p ID %d failed in transmission: %d -> %s", query->sdkctl->service_name, query, query->header.query_id, - async_socket_io_is_read(asio) ? "READ" : "WRITE", errno, strerror(errno)); /* Invoke query's callback. Note that we will let the client to * decide what to do on I/O failure. */ @@ -779,23 +1147,23 @@ _on_sdkctl_query_send_io(void* io_opaque, SDKCtlQuery* sdkctl_query_new(SDKCtlSocket* sdkctl, int query_type, uint32_t in_data_size) { - const uint32_t total_size = sizeof(SDKCtlQuery) + in_data_size; - - SDKCtlQuery* const query = _sdkctl_socket_alloc_recycler(sdkctl, total_size); - query->next = NULL; - query->sdkctl = sdkctl; - query->response_buffer = NULL; - query->response_size = NULL; - query->internal_resp_buffer = NULL; - query->internal_resp_size = 0; - query->query_cb = NULL; - query->query_opaque = NULL; - query->deadline = DURATION_INFINITE; - query->ref_count = 1; - query->header.packet.size = sizeof(SDKCtlQueryHeader) + in_data_size; - query->header.packet.type = SDKCTL_PACKET_QUERY; - query->header.query_id = _sdkctl_socket_next_query_id(sdkctl); - query->header.query_type = query_type; + SDKCtlQuery* const query = + _sdkctl_socket_alloc_recycler(sdkctl, sizeof(SDKCtlQuery) + in_data_size); + query->next = NULL; + query->sdkctl = sdkctl; + query->response_buffer = NULL; + query->response_size = NULL; + query->internal_resp_buffer = NULL; + query->internal_resp_size = 0; + query->query_cb = NULL; + query->query_opaque = NULL; + query->deadline = DURATION_INFINITE; + query->ref_count = 1; + query->header.packet.signature = _sdkctl_packet_sig; + query->header.packet.size = sizeof(SDKCtlQueryHeader) + in_data_size; + query->header.packet.type = SDKCTL_PACKET_QUERY; + query->header.query_id = _sdkctl_socket_next_query_id(sdkctl); + query->header.query_type = query_type; /* Initialize timer to fire up on query deadline expiration. */ loopTimer_init(query->timer, sdkctl->looper, _on_skdctl_query_timeout, query); @@ -822,21 +1190,19 @@ sdkctl_query_new_ex(SDKCtlSocket* sdkctl, { SDKCtlQuery* const query = sdkctl_query_new(sdkctl, query_type, in_data_size); - query->response_buffer = response_buffer; + query->response_buffer = response_buffer; if (query->response_buffer == NULL) { /* Creator didn't supply a buffer. Use internal one instead. */ query->response_buffer = &query->internal_resp_buffer; - query->internal_resp_buffer = NULL; } - query->response_size = response_size; + query->response_size = response_size; if (query->response_size == NULL) { /* Creator didn't supply a buffer for response size. Use internal one * instead. */ query->response_size = &query->internal_resp_size; - query->internal_resp_size = 0; } - query->query_cb = query_cb; - query->query_opaque = query_opaque; + query->query_cb = query_cb; + query->query_opaque = query_opaque; /* Init query's input buffer. */ if (in_data_size != 0 && in_data != NULL) { memcpy(query + 1, in_data, in_data_size); @@ -859,11 +1225,12 @@ sdkctl_query_send(SDKCtlQuery* query, int to) /* Reference query associated with write I/O. */ sdkctl_query_reference(query); + assert(query->header.packet.signature == _sdkctl_packet_sig); /* Transmit the query to SDK controller. */ async_socket_write_abs(sdkctl->as, &query->header, query->header.packet.size, _on_sdkctl_query_send_io, query, query->deadline); - T("SDKCtl %s: Query %p ID %d type %d is sent with deadline at %lld", + T("SDKCtl %s: Query %p ID %d type %d is being sent with deadline at %lld", query->sdkctl->service_name, query, query->header.query_id, query->header.query_type, query->deadline); } @@ -908,20 +1275,73 @@ sdkctl_query_release(SDKCtlQuery* query) return query->ref_count; } +void* +sdkctl_query_get_buffer_in(SDKCtlQuery* query) +{ + /* Query buffer starts right after the header. */ + return query + 1; +} + +void* +sdkctl_query_get_buffer_out(SDKCtlQuery* query) +{ + return query->response_buffer != NULL ? *query->response_buffer : + query->internal_resp_buffer; +} + /******************************************************************************** * SDKCtlPacket implementation *******************************************************************************/ -/* A packet has been received from SDK controller. */ +/* A packet has been received from SDK controller. + * Note that we expect the packet to be a message, since queries, and query + * replies are handled separately. */ static void _on_sdkctl_packet_received(SDKCtlSocket* sdkctl, SDKCtlPacket* packet) { T("SDKCtl %s: Received packet size: %d, type: %d", sdkctl->service_name, packet->header.size, packet->header.type); - /* Dispatch received packet to the client. */ - sdkctl->on_message(sdkctl->opaque, sdkctl, packet, packet->header.type, - packet + 1, packet->header.size - sizeof(SDKCtlPacketHeader)); + assert(packet->header.signature == _sdkctl_packet_sig); + if (packet->header.type == SDKCTL_PACKET_MESSAGE) { + SDKCtlMessage* const msg = (SDKCtlMessage*)packet; + /* Lets see if this is an internal protocol message. */ + switch (msg->msg_type) { + case SDKCTL_MSG_PORT_CONNECTED: + sdkctl->port_status = SDKCTL_PORT_CONNECTED; + sdkctl->on_port_connection(sdkctl->opaque, sdkctl, + SDKCTL_PORT_CONNECTED); + break; + + case SDKCTL_MSG_PORT_DISCONNECTED: + sdkctl->port_status = SDKCTL_PORT_DISCONNECTED; + sdkctl->on_port_connection(sdkctl->opaque, sdkctl, + SDKCTL_PORT_DISCONNECTED); + break; + + case SDKCTL_MSG_PORT_ENABLED: + sdkctl->port_status = SDKCTL_PORT_ENABLED; + sdkctl->on_port_connection(sdkctl->opaque, sdkctl, + SDKCTL_PORT_ENABLED); + break; + + case SDKCTL_MSG_PORT_DISABLED: + sdkctl->port_status = SDKCTL_PORT_DISABLED; + sdkctl->on_port_connection(sdkctl->opaque, sdkctl, + SDKCTL_PORT_DISABLED); + break; + + default: + /* This is a higher-level message. Dispatch the message to the + * client. */ + sdkctl->on_message(sdkctl->opaque, sdkctl, msg, msg->msg_type, msg + 1, + packet->header.size - sizeof(SDKCtlMessageHeader)); + break; + } + } else { + E("SDKCtl %s: Received unknown packet type %d size %d", + sdkctl->service_name, packet->header.type, packet->header.size); + } } /******************************************************************************** @@ -949,7 +1369,7 @@ _sdkctl_io_dispatcher_start(SDKCtlSocket* sdkctl) { dispatcher->current_query = NULL; /* Register a packet header reader with the socket. */ - async_socket_read_rel(dispatcher->sdkctl->as, &dispatcher->header, + async_socket_read_rel(dispatcher->sdkctl->as, &dispatcher->packet_header, sizeof(SDKCtlPacketHeader), _on_sdkctl_io_dispatcher_io, dispatcher, -1); } @@ -969,12 +1389,14 @@ _sdkctl_io_dispatcher_reset(SDKCtlSocket* sdkctl) { /* Free packet data buffer. */ if (dispatcher->packet != NULL) { - sdkctl_packet_release(dispatcher->packet); + _sdkctl_packet_release(dispatcher->packet); dispatcher->packet = NULL; } /* Reset dispatcher state. */ dispatcher->state = SDKCTL_IODISP_EXPECT_HEADER; + + T("SDKCtl %s: I/O Dispatcher is reset", sdkctl->service_name); } /* @@ -999,7 +1421,7 @@ _on_io_dispatcher_io_failure(SDKCtlIODispatcher* dispatcher, /* Report disconnection to the client, and let it restore connection in this * callback. */ - sdkctl->on_connection(sdkctl->opaque, sdkctl, ASIO_STATE_FAILED); + sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, ASIO_STATE_FAILED); } /* A callback that is invoked when dispatcher's reader has been cancelled. */ @@ -1020,7 +1442,7 @@ _on_io_dispatcher_io_cancelled(SDKCtlIODispatcher* dispatcher, /* Discard packet data we've received so far. */ if (dispatcher->packet != NULL) { - sdkctl_packet_release(dispatcher->packet); + _sdkctl_packet_release(dispatcher->packet); dispatcher->packet = NULL; } } @@ -1033,8 +1455,20 @@ _on_io_dispatcher_packet_header(SDKCtlIODispatcher* dispatcher, SDKCtlSocket* const sdkctl = dispatcher->sdkctl; T("SDKCtl %s: Packet header type %d, size %d is received.", - dispatcher->sdkctl->service_name, dispatcher->header.type, - dispatcher->header.size); + dispatcher->sdkctl->service_name, dispatcher->packet_header.type, + dispatcher->packet_header.size); + + /* Make sure we have a valid packet header. */ + if (dispatcher->packet_header.signature != _sdkctl_packet_sig) { + E("SDKCtl %s: Invalid packet signature %x for packet type %d, size %d", + sdkctl->service_name, dispatcher->packet_header.signature, + dispatcher->packet_header.type, dispatcher->packet_header.size); + /* This is a protocol failure. Treat it as I/O failure: disconnect, and + * let the client to decide what to do next. */ + errno = EINVAL; + _on_io_dispatcher_io_failure(dispatcher, asio); + return ASIO_ACTION_DONE; + } /* Here we have three choices for the packet, that define the rest of * the data that follow it: @@ -1044,7 +1478,7 @@ _on_io_dispatcher_packet_header(SDKCtlIODispatcher* dispatcher, * Update the state accordingly, and initiate reading of the * remaining of the packet. */ - if (dispatcher->header.type == SDKCTL_PACKET_QUERY_RESPONSE) { + if (dispatcher->packet_header.type == SDKCTL_PACKET_QUERY_RESPONSE) { /* This is a response to the query. Before receiving response data we * need to locate the relevant query, and use its response buffer to read * the data. For that we need to obtain query ID firts. So, initiate @@ -1059,11 +1493,11 @@ _on_io_dispatcher_packet_header(SDKCtlIODispatcher* dispatcher, * there. */ dispatcher->state = SDKCTL_IODISP_EXPECT_DATA; dispatcher->packet = - _sdkctl_packet_new(sdkctl, dispatcher->header.size, - dispatcher->header.type); + _sdkctl_packet_new(sdkctl, dispatcher->packet_header.size, + dispatcher->packet_header.type); /* Initiate reading of the packet data. */ async_socket_read_rel(sdkctl->as, dispatcher->packet + 1, - dispatcher->header.size - sizeof(SDKCtlPacketHeader), + dispatcher->packet_header.size - sizeof(SDKCtlPacketHeader), _on_sdkctl_io_dispatcher_io, dispatcher, -1); } @@ -1075,18 +1509,19 @@ static AsyncIOAction _on_io_dispatcher_packet(SDKCtlIODispatcher* dispatcher, AsyncSocketIO* asio) { SDKCtlSocket* const sdkctl = dispatcher->sdkctl; + SDKCtlPacket* const packet = dispatcher->packet; + dispatcher->packet = NULL; T("SDKCtl %s: Packet type %d, size %d is received.", - dispatcher->sdkctl->service_name, dispatcher->header.type, - dispatcher->header.size); + dispatcher->sdkctl->service_name, dispatcher->packet_header.type, + dispatcher->packet_header.size); - _on_sdkctl_packet_received(sdkctl, dispatcher->packet); - sdkctl_packet_release(dispatcher->packet); - dispatcher->packet = NULL; + _on_sdkctl_packet_received(sdkctl, packet); + _sdkctl_packet_release(packet); /* Get ready for the next I/O cycle. */ dispatcher->state = SDKCTL_IODISP_EXPECT_HEADER; - async_socket_read_rel(sdkctl->as, &dispatcher->header, sizeof(SDKCtlPacketHeader), + async_socket_read_rel(sdkctl->as, &dispatcher->packet_header, sizeof(SDKCtlPacketHeader), _on_sdkctl_io_dispatcher_io, dispatcher, -1); return ASIO_ACTION_DONE; } @@ -1107,6 +1542,9 @@ _on_io_dispatcher_query_reply_header(SDKCtlIODispatcher* dispatcher, dispatcher->current_query = _sdkctl_socket_remove_query_id(sdkctl, dispatcher->query_reply_header.query_id); query = dispatcher->current_query; + const uint32_t query_data_size = + dispatcher->packet_header.size - sizeof(SDKCtlQueryReplyHeader); + dispatcher->state = SDKCTL_IODISP_EXPECT_QUERY_REPLY_DATA; if (query == NULL) { D("%s: Query #%d is not found by dispatcher", @@ -1116,13 +1554,12 @@ _on_io_dispatcher_query_reply_header(SDKCtlIODispatcher* dispatcher, * and then discard when it's over. */ dispatcher->state = SDKCTL_IODISP_EXPECT_QUERY_REPLY_DATA; dispatcher->packet = - _sdkctl_packet_new(sdkctl, dispatcher->header.size, - dispatcher->header.type); + _sdkctl_packet_new(sdkctl, dispatcher->packet_header.size, + dispatcher->packet_header.type); /* Copy query reply info to the packet. */ memcpy(&dispatcher->packet->header, &dispatcher->query_reply_header, sizeof(SDKCtlQueryReplyHeader)); - async_socket_read_rel(sdkctl->as, &dispatcher->query_header + 1, - dispatcher->header.size - sizeof(SDKCtlQueryReplyHeader), + async_socket_read_rel(sdkctl->as, dispatcher->packet + 1, query_data_size, _on_sdkctl_io_dispatcher_io, dispatcher, -1); } else { /* Prepare to receive query reply. For the simplicity sake, cancel query @@ -1130,9 +1567,6 @@ _on_io_dispatcher_query_reply_header(SDKCtlIODispatcher* dispatcher, * receiving query's reply. */ _sdkctl_query_cancel_timeout(query); - /* Adjust the reply buffer set for the query (if needed). */ - const uint32_t query_data_size = - dispatcher->header.size - sizeof(SDKCtlQueryReplyHeader); if (*query->response_size < query_data_size) { *query->response_buffer = malloc(query_data_size); if (*query->response_buffer == NULL) { @@ -1144,7 +1578,6 @@ _on_io_dispatcher_query_reply_header(SDKCtlIODispatcher* dispatcher, *query->response_size = query_data_size; /* Start reading query response. */ - dispatcher->state = SDKCTL_IODISP_EXPECT_QUERY_REPLY_DATA; async_socket_read_rel(sdkctl->as, *query->response_buffer, *query->response_size, _on_sdkctl_io_dispatcher_io, dispatcher, -1); @@ -1176,14 +1609,14 @@ _on_io_dispatcher_query_reply(SDKCtlIODispatcher* dispatcher, AsyncSocketIO* asi /* This was "read up in the air" for a cancelled query. Just discard the * read data. */ if (dispatcher->packet != NULL) { - sdkctl_packet_release(dispatcher->packet); + _sdkctl_packet_release(dispatcher->packet); dispatcher->packet = NULL; } } /* Get ready for the next I/O cycle. */ dispatcher->state = SDKCTL_IODISP_EXPECT_HEADER; - async_socket_read_rel(sdkctl->as, &dispatcher->header, sizeof(SDKCtlPacketHeader), + async_socket_read_rel(sdkctl->as, &dispatcher->packet_header, sizeof(SDKCtlPacketHeader), _on_sdkctl_io_dispatcher_io, dispatcher, -1); return ASIO_ACTION_DONE; } @@ -1351,6 +1784,7 @@ _sdkctl_socket_disconnect_socket(SDKCtlSocket* sdkctl) } sdkctl->state = SDKCTL_SOCKET_DISCONNECTED; + sdkctl->port_status = SDKCTL_PORT_DISCONNECTED; } /* Frees SDKCtlSocket instance. */ @@ -1358,6 +1792,8 @@ static void _sdkctl_socket_free(SDKCtlSocket* sdkctl) { if (sdkctl != NULL) { + T("SDKCtl %s: descriptor is destroing.", sdkctl->service_name); + /* Disconnect, and release the socket. */ if (sdkctl->as != NULL) { async_socket_disconnect(sdkctl->as); @@ -1394,7 +1830,7 @@ _on_async_socket_connected(SDKCtlSocket* sdkctl) /* Notify the client that connection is established. */ const AsyncIOAction action = - sdkctl->on_connection(sdkctl->opaque, sdkctl, ASIO_STATE_SUCCEEDED); + sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, ASIO_STATE_SUCCEEDED); if (action == ASIO_ACTION_DONE) { /* Initialize, and start main I/O dispatcher. */ @@ -1418,8 +1854,8 @@ _on_async_socket_disconnected(SDKCtlSocket* sdkctl) _sdkctl_socket_disconnect_socket(sdkctl); - AsyncIOAction action = sdkctl->on_connection(sdkctl->opaque, sdkctl, - ASIO_STATE_FAILED); + AsyncIOAction action = sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, + ASIO_STATE_FAILED); if (action == ASIO_ACTION_DONE) { /* Default action for disconnect is to reestablish the connection. */ action = ASIO_ACTION_RETRY; @@ -1481,35 +1917,38 @@ _on_async_socket_connection(void* client_opaque, SDKCtlSocket* sdkctl_socket_new(int reconnect_to, const char* service_name, - on_sdkctl_connection_cb on_connection, - on_sdkctl_handshake_cb on_handshake, + on_sdkctl_socket_connection_cb on_socket_connection, + on_sdkctl_port_connection_cb on_port_connection, on_sdkctl_message_cb on_message, void* opaque) { SDKCtlSocket* sdkctl; ANEW0(sdkctl); - sdkctl->state = SDKCTL_SOCKET_DISCONNECTED; - sdkctl->opaque = opaque; - sdkctl->service_name = ASTRDUP(service_name); - sdkctl->on_connection = on_connection; - sdkctl->on_handshake = on_handshake; - sdkctl->on_message = on_message; - sdkctl->reconnect_to = reconnect_to; - sdkctl->as = NULL; - sdkctl->next_query_id = 0; - sdkctl->query_head = sdkctl->query_tail = NULL; - sdkctl->ref_count = 1; - sdkctl->recycler = NULL; - sdkctl->recycler_block_size = 0; - sdkctl->recycler_max = 0; - sdkctl->recycler_count = 0; + sdkctl->state = SDKCTL_SOCKET_DISCONNECTED; + sdkctl->port_status = SDKCTL_PORT_DISCONNECTED; + sdkctl->opaque = opaque; + sdkctl->service_name = ASTRDUP(service_name); + sdkctl->on_socket_connection = on_socket_connection; + sdkctl->on_port_connection = on_port_connection; + sdkctl->on_message = on_message; + sdkctl->reconnect_to = reconnect_to; + sdkctl->as = NULL; + sdkctl->next_query_id = 0; + sdkctl->query_head = sdkctl->query_tail = NULL; + sdkctl->ref_count = 1; + sdkctl->recycler = NULL; + sdkctl->recycler_block_size = 0; + sdkctl->recycler_max = 0; + sdkctl->recycler_count = 0; + + T("SDKCtl %s: descriptor is created.", sdkctl->service_name); sdkctl->looper = looper_newCore(); if (sdkctl->looper == NULL) { E("Unable to create I/O looper for SDKCtl socket '%s'", service_name); - on_connection(opaque, sdkctl, ASIO_STATE_FAILED); + on_socket_connection(opaque, sdkctl, ASIO_STATE_FAILED); _sdkctl_socket_free(sdkctl); return NULL; } @@ -1569,7 +2008,7 @@ sdkctl_socket_connect(SDKCtlSocket* sdkctl, int port, int retry_to) if (sdkctl->as == NULL) { E("Unable to allocate AsyncSocket for SDKCtl socket '%s'", sdkctl->service_name); - sdkctl->on_connection(sdkctl->opaque, sdkctl, ASIO_STATE_FAILED); + sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, ASIO_STATE_FAILED); } else { async_socket_connect(sdkctl->as, retry_to); } @@ -1599,11 +2038,54 @@ sdkctl_socket_disconnect(SDKCtlSocket* sdkctl) _sdkctl_socket_disconnect_socket(sdkctl); } +int +sdkctl_socket_is_connected(SDKCtlSocket* sdkctl) +{ + return (sdkctl->state == SDKCTL_SOCKET_CONNECTED) ? 1 : 0; +} + +int +sdkctl_socket_is_port_ready(SDKCtlSocket* sdkctl) +{ + return (sdkctl->port_status == SDKCTL_PORT_ENABLED) ? 1 : 0; +} + +SdkCtlPortStatus +sdkctl_socket_get_port_status(SDKCtlSocket* sdkctl) +{ + return sdkctl->port_status; +} + +int +sdkctl_socket_is_handshake_ok(SDKCtlSocket* sdkctl) +{ + switch (sdkctl->port_status) { + case SDKCTL_HANDSHAKE_DUP: + case SDKCTL_HANDSHAKE_UNKNOWN_QUERY: + case SDKCTL_HANDSHAKE_UNKNOWN_RESPONSE: + return 0; + default: + return 1; + } +} /******************************************************************************** * Handshake query *******************************************************************************/ +/* + * Handshake result values. + */ + +/* Handshake has succeeded completed, and service-side port is connected. */ +#define SDKCTL_HANDSHAKE_RESP_CONNECTED 0 +/* Handshake has succeeded completed, but service-side port is not connected. */ +#define SDKCTL_HANDSHAKE_RESP_NOPORT 1 +/* Handshake has failed due to duplicate connection request. */ +#define SDKCTL_HANDSHAKE_RESP_DUP -1 +/* Handshake has failed due to unknown query. */ +#define SDKCTL_HANDSHAKE_RESP_QUERY_UNKNOWN -2 + /* A callback that is ivoked on handshake I/O events. */ static AsyncIOAction _on_handshake_io(void* query_opaque, @@ -1613,12 +2095,41 @@ _on_handshake_io(void* query_opaque, SDKCtlSocket* const sdkctl = (SDKCtlSocket*)query_opaque; if (status == ASIO_STATE_SUCCEEDED) { - D("SDKCtl %s: %d bytes of handshake reply is received.", - sdkctl->service_name, *query->response_size); + const int* res = (const int*)(*query->response_buffer); + SdkCtlPortStatus handshake_status; + switch (*res) { + case SDKCTL_HANDSHAKE_RESP_CONNECTED: + D("SDKCtl %s: Handshake succeeded. Port is connected", + sdkctl->service_name); + handshake_status = SDKCTL_HANDSHAKE_CONNECTED; + break; + + case SDKCTL_HANDSHAKE_RESP_NOPORT: + D("SDKCtl %s: Handshake succeeded. Port is not connected", + sdkctl->service_name); + handshake_status = SDKCTL_HANDSHAKE_NO_PORT; + break; + + case SDKCTL_HANDSHAKE_RESP_DUP: + D("SDKCtl %s: Handshake failed: duplicate connection.", + sdkctl->service_name); + handshake_status = SDKCTL_HANDSHAKE_DUP; + break; - /* Handshake is received. Inform the client. */ - sdkctl->on_handshake(sdkctl->opaque, sdkctl, *query->response_buffer, - *query->response_size, status); + case SDKCTL_HANDSHAKE_RESP_QUERY_UNKNOWN: + D("SDKCtl %s: Handshake failed: unknown query.", + sdkctl->service_name); + handshake_status = SDKCTL_HANDSHAKE_UNKNOWN_QUERY; + break; + + default: + E("SDKCtl %s: Unknown handshake response: %d", + sdkctl->service_name, *res); + handshake_status = SDKCTL_HANDSHAKE_UNKNOWN_RESPONSE; + break; + } + sdkctl->port_status = handshake_status; + sdkctl->on_port_connection(sdkctl->opaque, sdkctl, handshake_status); } else { /* Something is going on with the handshake... */ switch (status) { @@ -1627,9 +2138,8 @@ _on_handshake_io(void* query_opaque, case ASIO_STATE_CANCELLED: D("SDKCtl %s: Handshake failed: I/O state %d. Error: %d -> %s", sdkctl->service_name, status, errno, strerror(errno)); - sdkctl->on_handshake(sdkctl->opaque, sdkctl, - *query->response_buffer, - *query->response_size, status); + sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, + ASIO_STATE_FAILED); break; default: @@ -1663,7 +2173,7 @@ _on_sdkctl_endianness_io(void* io_opaque, case ASIO_STATE_CANCELLED: D("SDKCtl %s: endianness failed: I/O state %d. Error: %d -> %s", sdkctl->service_name, status, errno, strerror(errno)); - sdkctl->on_handshake(sdkctl->opaque, sdkctl, NULL, 0, status); + sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, ASIO_STATE_FAILED); break; default: @@ -1682,7 +2192,7 @@ static const char _host_end = 0; static const char _host_end = 1; #endif - D("SDKCtl %s: Sending endianness: %d...", sdkctl->service_name, _host_end); + D("SDKCtl %s: Sending endianness: %d", sdkctl->service_name, _host_end); /* Before we can send any structured data to the SDK controller we need to * report endianness of the host. */ diff --git a/android/sdk-controller-socket.h b/android/sdk-controller-socket.h index 03b12f4..e58a4e2 100644 --- a/android/sdk-controller-socket.h +++ b/android/sdk-controller-socket.h @@ -18,6 +18,7 @@ #define ANDROID_SDKCONTROL_SOCKET_H_ #include "android/async-socket.h" +#include "android/async-utils.h" /* * Contains declaration of an API that encapsulates communication protocol with @@ -102,20 +103,29 @@ * interested in that particular descriptor. * * There are three types of data in the exchange protocol: - * - A packet - the simplest type of data that doesn't require any replies. + * - A message - the simplest type of data that doesn't require any replies. * - A query - A message that require a reply, and * - A query reply - A message that delivers query reply. */ +/* Default TCP port to use for connection with SDK controller. */ +#define SDKCTL_DEFAULT_TCP_PORT 1970 + /* Declares SDK controller socket descriptor. */ typedef struct SDKCtlSocket SDKCtlSocket; -/* Declares SDK controller data packet descriptor. */ -typedef struct SDKCtlPacket SDKCtlPacket; +/* Declares SDK controller message descriptor. */ +typedef struct SDKCtlMessage SDKCtlMessage; /* Declares SDK controller query descriptor. */ typedef struct SDKCtlQuery SDKCtlQuery; +/* Declares SDK controller direct packet descriptor. + * Direct packet (unlike message, or query packets) doesn't contain data buffer, + * but rather references message, or query data allocated by the client. + */ +typedef struct SDKCtlDirectPacket SDKCtlDirectPacket; + /* Defines client's callback set to monitor SDK controller socket connection. * * SDKCtlSocket will invoke this callback when connection to TCP port is @@ -137,30 +147,52 @@ typedef struct SDKCtlQuery SDKCtlQuery; * Return: * One of the AsyncIOAction values. */ -typedef AsyncIOAction (*on_sdkctl_connection_cb)(void* client_opaque, - SDKCtlSocket* sdkctl, - AsyncIOState status); +typedef AsyncIOAction (*on_sdkctl_socket_connection_cb)(void* client_opaque, + SDKCtlSocket* sdkctl, + AsyncIOState status); + +/* Enumerates port connection statuses passed to port connection callback. + */ +typedef enum SdkCtlPortStatus { + /* Service-side port has connected to the socket. */ + SDKCTL_PORT_DISCONNECTED, + /* Service-side port has disconnected from the socket. */ + SDKCTL_PORT_CONNECTED, + /* Service-side port has enabled emulation */ + SDKCTL_PORT_ENABLED, + /* Service-side port has disabled emulation */ + SDKCTL_PORT_DISABLED, + /* Handshake request has succeeded, and service-side port is connected. */ + SDKCTL_HANDSHAKE_CONNECTED, + /* Handshake request has succeeded, but service-side port is not connected. */ + SDKCTL_HANDSHAKE_NO_PORT, + /* Handshake request has failed due to port duplication. */ + SDKCTL_HANDSHAKE_DUP, + /* Handshake request has failed on an unknown query. */ + SDKCTL_HANDSHAKE_UNKNOWN_QUERY, + /* Handshake request has failed on an unknown response. */ + SDKCTL_HANDSHAKE_UNKNOWN_RESPONSE, +} SdkCtlPortStatus; -/* Defines client's callback set to receive handshake reply from the SdkController - * service running on the device. +/* Defines client's callback set to receive port connection status. * - * Successful handshake means that connection between the client and SDK - * controller service has been established. + * Port connection is different than socket connection, and indicates whether + * or not a service-side port that provides requested emulation functionality is + * hooked up with the connected socket. For instance, multi-touch port may be + * inactive at the time when socket is connected. So, there is a successful + * socket connection, but there is no service at the device end that provides + * multi-touch functionality. So, for multi-touch example, this callback will be + * invoked when multi-touch port at the device end becomes active, and hooks up + * with the socket that was connected before. * * Param: * client_opaque - An opaque pointer associated with the client. * sdkctl - Initialized SDKCtlSocket instance. - * handshake - Handshake message received from the SDK controller service. - * handshake_size - Size of the fandshake message received from the SDK - * controller service. - * status - Handshake status. Note that handshake, and handshake_size are valid - * only if this parameter is set to ASIO_STATE_SUCCEEDED. - */ -typedef void (*on_sdkctl_handshake_cb)(void* client_opaque, - SDKCtlSocket* sdkctl, - void* handshake, - uint32_t handshake_size, - AsyncIOState status); + * status - Port connection status. + */ +typedef void (*on_sdkctl_port_connection_cb)(void* client_opaque, + SDKCtlSocket* sdkctl, + SdkCtlPortStatus status); /* Defines a message notification callback. * Param: @@ -176,7 +208,7 @@ typedef void (*on_sdkctl_handshake_cb)(void* client_opaque, */ typedef void (*on_sdkctl_message_cb)(void* client_opaque, SDKCtlSocket* sdkctl, - SDKCtlPacket* message, + SDKCtlMessage* message, int msg_type, void* msg_data, int msg_size); @@ -203,27 +235,114 @@ typedef AsyncIOAction (*on_sdkctl_query_cb)(void* query_opaque, SDKCtlQuery* query, AsyncIOState status); +/* Defines direct packet completion callback. + * Param: + * opaque - An opaque pointer associated with the direct packet by the client. + * packet - Packet descriptor. Note that packet descriptor will be released + * upon exit from this callback (thus, could be freed). If the client is + * interested in working with that packet after the callback returns, it + * should reference the packet descriptor in this callback. + * status - Packet status. Can be one of these: + * - ASIO_STATE_SUCCEEDED : Packet has been successfully sent. + * - ASIO_STATE_FAILED : Packet has failed on an I/O. + * - ASIO_STATE_CANCELLED : Packet has been cancelled due to socket + * disconnection. + * Return: + * One of the AsyncIOAction values. + */ +typedef AsyncIOAction (*on_sdkctl_direct_cb)(void* opaque, + SDKCtlDirectPacket* packet, + AsyncIOState status); + /******************************************************************************** - * SDKCtlPacket API + * SDKCtlDirectPacket API ********************************************************************************/ -/* References SDKCtlPacket object. +/* Creates new SDKCtlDirectPacket descriptor. * Param: - * packet - Initialized SDKCtlPacket instance. + * sdkctl - Initialized SDKCtlSocket instance to create a direct packet for. + * Return: + * Referenced SDKCtlDirectPacket instance. + */ +extern SDKCtlDirectPacket* sdkctl_direct_packet_new(SDKCtlSocket* sdkctl); + +/* References SDKCtlDirectPacket object. + * Param: + * packet - Initialized SDKCtlDirectPacket instance. * Return: * Number of outstanding references to the object. */ -extern int sdkctl_packet_reference(SDKCtlPacket* packet); +extern int sdkctl_direct_packet_reference(SDKCtlDirectPacket* packet); -/* Releases SDKCtlPacket object. +/* Releases SDKCtlDirectPacket object. * Note that upon exiting from this routine the object might be destroyed, even * if this routine returns value other than zero. * Param: - * packet - Initialized SDKCtlPacket instance. + * packet - Initialized SDKCtlDirectPacket instance. * Return: * Number of outstanding references to the object. */ -extern int sdkctl_packet_release(SDKCtlPacket* packet); +extern int sdkctl_direct_packet_release(SDKCtlDirectPacket* packet); + +/* Sends direct packet. + * Param: + * packet - Packet descriptor for the direct packet to send. + * data - Data to send with the packet. Must be fully initialized message, or + * query header. + * cb, cb_opaque - Callback to invoke on packet transmission events. + */ +extern void sdkctl_direct_packet_send(SDKCtlDirectPacket* packet, + void* data, + on_sdkctl_direct_cb cb, + void* cb_opaque); + +/******************************************************************************** + * SDKCtlMessage API + ********************************************************************************/ + +/* References SDKCtlMessage object. + * Param: + * msg - Initialized SDKCtlMessage instance. + * Return: + * Number of outstanding references to the object. + */ +extern int sdkctl_message_reference(SDKCtlMessage* msg); + +/* Releases SDKCtlMessage object. + * Note that upon exiting from this routine the object might be destroyed, even + * if this routine returns value other than zero. + * Param: + * msg - Initialized SDKCtlMessage instance. + * Return: + * Number of outstanding references to the object. + */ +extern int sdkctl_message_release(SDKCtlMessage* msg); + +/* Builds and sends a message to the device. + * Param: + * sdkctl - SDKCtlSocket instance for the message. + * msg_type - Defines message type. + * data - Message data. Can be NULL if there is no data associated with the + * message. + * size - Byte size of the data buffer. + * Return: + * Referenced SDKCtlQuery descriptor. + */ +extern SDKCtlMessage* sdkctl_message_send(SDKCtlSocket* sdkctl, + int msg_type, + const void* data, + uint32_t size); + +/* Gets message header size */ +extern int sdkctl_message_get_header_size(void); + +/* Initializes message header. + * Param: + * msg - Beginning of the message packet. + * msg_type - Message type. + * msg_size - Message data size. + */ +extern void sdkctl_init_message_header(void* msg, int msg_type, int msg_size); /******************************************************************************** * SDKCtlQuery API @@ -349,6 +468,14 @@ extern int sdkctl_query_release(SDKCtlQuery* query); */ extern void* sdkctl_query_get_buffer_in(SDKCtlQuery* query); +/* Gets address of query's output data buffer (response data). + * Param: + * query - Query to get data buffer for. + * Return: + * Address of query's output data buffer. + */ +extern void* sdkctl_query_get_buffer_out(SDKCtlQuery* query); + /******************************************************************************** * SDKCtlSocket API ********************************************************************************/ @@ -359,8 +486,8 @@ extern void* sdkctl_query_get_buffer_in(SDKCtlQuery* query); * attempts after disconnection, or on connection failures. * service_name - Name of the SdkController service for this socket (such as * 'sensors', 'milti-touch', etc.) - * on_connection - A callback to invoke on socket connection events. - * on_handshake - A callback to invoke on handshake events. + * on_socket_connection - A callback to invoke on socket connection events. + * on_port_connection - A callback to invoke on port connection events. * on_message - A callback to invoke when a message is received from the SDK * controller. * opaque - An opaque pointer to associate with the socket. @@ -369,8 +496,8 @@ extern void* sdkctl_query_get_buffer_in(SDKCtlQuery* query); */ extern SDKCtlSocket* sdkctl_socket_new(int reconnect_to, const char* service_name, - on_sdkctl_connection_cb on_connection, - on_sdkctl_handshake_cb on_handshake, + on_sdkctl_socket_connection_cb on_socket_connection, + on_sdkctl_port_connection_cb on_port_connection, on_sdkctl_message_cb on_message, void* opaque); @@ -437,4 +564,36 @@ extern void sdkctl_socket_reconnect(SDKCtlSocket* sdkctl, int port, int retry_to */ extern void sdkctl_socket_disconnect(SDKCtlSocket* sdkctl); +/* Checks if SDK controller socket is connected. + * Param: + * sdkctl - Initialized SDKCtlSocket instance. + * Return: + * Boolean: 1 if socket is connected, 0 if socket is not connected. + */ +extern int sdkctl_socket_is_connected(SDKCtlSocket* sdkctl); + +/* Checks if SDK controller port is ready for emulation. + * Param: + * sdkctl - Initialized SDKCtlSocket instance. + * Return: + * Boolean: 1 if port is ready, 0 if port is not ready. + */ +extern int sdkctl_socket_is_port_ready(SDKCtlSocket* sdkctl); + +/* Gets status of the SDK controller port for this socket. + * Param: + * sdkctl - Initialized SDKCtlSocket instance. + * Return: + * Status of the SDK controller port for this socket. + */ +extern SdkCtlPortStatus sdkctl_socket_get_port_status(SDKCtlSocket* sdkctl); + +/* Checks if handshake was successful. + * Param: + * sdkctl - Initialized SDKCtlSocket instance. + * Return: + * Boolean: 1 if handshake was successful, 0 if handshake was not successful. + */ +extern int sdkctl_socket_is_handshake_ok(SDKCtlSocket* sdkctl); + #endif /* ANDROID_SDKCONTROL_SOCKET_H_ */ diff --git a/android/sensors-port.c b/android/sensors-port.c index 8620783..6831fe3 100644 --- a/android/sensors-port.c +++ b/android/sensors-port.c @@ -14,370 +14,474 @@ * limitations under the License. */ +#include "android/sdk-controller-socket.h" #include "android/sensors-port.h" #include "android/hw-sensors.h" +#include "android/utils/debug.h" #define E(...) derror(__VA_ARGS__) #define W(...) dwarning(__VA_ARGS__) #define D(...) VERBOSE_PRINT(sensors_port,__VA_ARGS__) #define D_ACTIVE VERBOSE_CHECK(sensors_port) -/* Maximum number of sensors supported. */ -#define ASP_MAX_SENSOR 12 +#define TRACE_ON 1 + +#if TRACE_ON +#define T(...) VERBOSE_PRINT(sensors_port,__VA_ARGS__) +#else +#define T(...) +#endif + +/* Timeout (millisec) to use when communicating with SDK controller. */ +#define SDKCTL_SENSORS_TIMEOUT 3000 -/* Maximum length of a sensor message. */ -#define ASP_MAX_SENSOR_MSG 1024 +/* + * Queries sent to sensors port of the SDK controller. + */ -/* Maximum length of a sensor event. */ -#define ASP_MAX_SENSOR_EVENT 256 +/* Queries the port for list of available sensors. */ +#define SDKCTL_SENSORS_QUERY_LIST 1 -/* Query timeout in milliseconds. */ -#define ASP_QUERY_TIMEOUT 3000 +/* + * Messages sent between the emuator, and sensors port of the SDK controller. + */ + +/* Starts sensor emulation. */ +#define SDKCTL_SENSORS_START 1 +/* Stops sensor emulation. */ +#define SENSOR_SENSORS_STOP 2 +/* Enables emulation for a sensor. */ +#define SDKCTL_SENSORS_ENABLE 3 +/* Disables emulation for a sensor. */ +#define SDKCTL_SENSORS_DISABLE 4 +/* This message delivers sensor values. */ +#define SDKCTL_SENSORS_SENSOR_EVENT 5 + + +/* Describes a sensor on the device. + * When SDK controller sensors port replies to a "list" query, it replies with + * a flat buffer containing entries of this type following each other. End of + * each entry is a zero-terminator for its 'sensor_name' field. The end of the + * entire list is marked with an entry, containing -1 at its 'sensor_id' field. + */ +typedef struct SensorEntry { + /* Identifies sensor on the device. Value -1 indicates list terminator, + * rather than a valid sensor descriptor. */ + int sensor_id; + /* Beginning of zero-terminated sensor name. */ + char sensor_name[1]; +} SensorEntry; + +/* Describes a sensor in the array of emulated sensors. */ +typedef struct SensorDescriptor { + /* Identifies sensor on the device. */ + int sensor_id; + /* Identifies sensor in emulator. */ + int emulator_id; + /* Sensor name. */ + char* sensor_name; +} SensorDescriptor; + +/* Sensor event message descriptor. + * Entries of this type are sent along with SDKCTL_SENSORS_SENSOR_EVENT message + */ +typedef struct SensorEvent { + /* Identifies a device sensor for which values have been delivered. */ + int sensor_id; + /* Sensor values. */ + float fvalues[3]; +} SensorEvent; /* Sensors port descriptor. */ struct AndroidSensorsPort { /* Caller identifier. */ - void* opaque; - /* Connected android device. */ - AndroidDevice* device; - /* String containing list of all available sensors. */ - char sensors[ASP_MAX_SENSOR * 64]; - /* Array of available sensor names. Note that each string in this array - * points inside the 'sensors' buffer. */ - const char* sensor_list[ASP_MAX_SENSOR]; - /* Number of available sensors. */ - int sensors_num; - /* Connection status: 1 connected, 0 - disconnected. */ - int is_connected; - /* Buffer where to receive sensor messages. */ - char sensor_msg[ASP_MAX_SENSOR_MSG]; - /* Buffer where to receive sensor events. */ - char events[ASP_MAX_SENSOR_EVENT]; + void* opaque; + /* Communication socket. */ + SDKCtlSocket* sdkctl; + /* Lists sensors available for emulation. */ + SensorDescriptor** sensors; + /* Number of sensors in 'sensors' list. */ + int sensors_count; }; +/******************************************************************************** + * Sensors port internals + *******************************************************************************/ + +/* Checks if sensor descriptor is the terminator. + * Return: + * Boolean, 1 if it is a terminator, 0 if it is not. + */ +static int +_sensor_entry_is_terminator(const SensorEntry* entry) +{ + return entry == NULL || entry->sensor_id == -1; +} + +/* Gets next sensor descriptor. + * Return: + * Next sensor desciptor, or NULL if there are no more descriptors in the list. + */ +static const SensorEntry* +_sensor_entry_next(const SensorEntry* entry) +{ + if (!_sensor_entry_is_terminator(entry)) { + /* Next descriptor begins right after zero-terminator for the sensor_name + * field of this descriptor. */ + entry = (const SensorEntry*)(entry->sensor_name + strlen(entry->sensor_name) + 1); + if (!_sensor_entry_is_terminator(entry)) { + return entry; + } + } + return NULL; +} + +/* Gets number of entries in the list. */ +static int +_sensor_entry_list_size(const SensorEntry* entry) { + int ret = 0; + while (!_sensor_entry_is_terminator(entry)) { + ret++; + entry = _sensor_entry_next(entry); + } + return ret; +} + +/* Discards sensors saved in AndroidSensorsPort's array. */ +static void +_sensors_port_discard_sensors(AndroidSensorsPort* asp) +{ + if (asp->sensors != NULL) { + int n; + for (n = 0; n < asp->sensors_count; n++) { + if (asp->sensors[n] != NULL) { + free(asp->sensors[n]->sensor_name); + AFREE(asp->sensors[n]); + } + } + free(asp->sensors); + asp->sensors = NULL; + } + asp->sensors_count = 0; +} + + /* Destroys and frees the descriptor. */ static void _sensors_port_free(AndroidSensorsPort* asp) { if (asp != NULL) { - if (asp->device != NULL) { - android_device_destroy(asp->device); + _sensors_port_discard_sensors(asp); + if (asp->sdkctl != NULL) { + sdkctl_socket_release(asp->sdkctl); } AFREE(asp); } } -/******************************************************************************** - * Sensors port callbacks - *******************************************************************************/ +/* Parses flat sensor list, and saves its entries into 'sensors' array filed of + * the AndroidSensorsPort descriptor. */ +static void +_sensors_port_save_sensors(AndroidSensorsPort* asp, const SensorEntry* list) +{ + const int count = _sensor_entry_list_size(list); + if (count != 0) { + int n; + /* Allocate array for sensor descriptors. */ + asp->sensors = malloc(sizeof(SensorDescriptor*) * count); + + /* Iterate through the flat sensor list, filling up array of emulated + * sensors. */ + const SensorEntry* entry = _sensor_entry_is_terminator(list) ? NULL : list; + for (n = 0; n < count && entry != NULL; n++) { + /* Get emulator-side ID for the sensor. < 0 value indicates that + * sensor is not supported by the emulator. */ + const int emulator_id = + android_sensors_get_id_from_name((char*)entry->sensor_name); + if (emulator_id >= 0) { + SensorDescriptor* desc; + ANEW0(desc); + desc->emulator_id = emulator_id; + desc->sensor_id = entry->sensor_id; + desc->sensor_name = ASTRDUP(entry->sensor_name); + + asp->sensors[asp->sensors_count++] = desc; + D("Sensors: Emulated sensor '%s': Device id = %d, Emulator id = %d", + desc->sensor_name, desc->sensor_id, desc->emulator_id); + } else { + D("Sensors: Sensor '%s' is not support by emulator", + entry->sensor_name); + } + entry = _sensor_entry_next(entry); + } + D("Sensors: Emulating %d sensors", asp->sensors_count); + } +} -/* A callback that invoked on sensor events. +/* Finds sensor descriptor for an SDK controller-side ID. */ +static const SensorDescriptor* +_sensor_from_sdkctl_id(AndroidSensorsPort* asp, int id) +{ + int n; + for (n = 0; n < asp->sensors_count; n++) { + if (asp->sensors[n]->sensor_id == id) { + return asp->sensors[n]; + } + } + return NULL; +} + +/* Initiates sensor emulation. * Param: - * opaque - AndroidSensorsPort instance. - * ad - Android device used by this sensors port. - * msg, msgsize - Sensor event message - * failure - Message receiving status. + * asp - Android sensors port instance returned from sensors_port_create. + * Return: + * Zero on success, failure otherwise. */ static void -_on_sensor_received(void* opaque, AndroidDevice* ad, char* msg, int msgsize) +_sensors_port_start(AndroidSensorsPort* asp) { - float fvalues[3] = {0, 0, 0}; - char sensor[ASP_MAX_SENSOR_MSG]; - char* value; - int id; - AndroidSensorsPort* asp = (AndroidSensorsPort*)opaque; - - if (errno) { - D("Sensors notification has failed on sensors port: %s", strerror(errno)); - return; - } + int n; - /* Parse notification, separating sensor name from parameters. */ - memcpy(sensor, msg, msgsize); - value = strchr(sensor, ':'); - if (value == NULL) { - W("Bad format for sensor notification: %s", msg); + if (!sdkctl_socket_is_port_ready(asp->sdkctl)) { + /* SDK controller side is not ready for emulation. Retreat... */ + D("Sensors: SDK controller side is not ready for emulation."); return; } - sensor[value-sensor] = '\0'; - value++; - - id = android_sensors_get_id_from_name(sensor); - if (id >= 0) { - /* Parse the value part to get the sensor values(a, b, c) */ - int i; - char* pnext; - char* pend = value + strlen(value); - for (i = 0; i < 3; i++, value = pnext + 1) { - pnext=strchr( value, ':' ); - if (pnext) { - *pnext = 0; - } else { - pnext = pend; - } - if (pnext > value) { - if (1 != sscanf( value,"%g", &fvalues[i] )) { - W("Bad parameters in sensor notification %s", msg); - return; - } - } + /* Disable all sensors, and reenable only those that are emulated by + * hardware. */ + sensors_port_disable_sensor(asp, "all"); + + /* Walk throuh the list of enabled sensors enabling them on the device. */ + for (n = 0; n < asp->sensors_count; n++) { + if (android_sensors_get_sensor_status(asp->sensors[n]->emulator_id) == 1) { + /* Reenable emulation for this sensor. */ + sensors_port_enable_sensor(asp, asp->sensors[n]->sensor_name); + D("Sensors: Sensor '%s' is enabled on SDK controller.", + asp->sensors[n]->sensor_name); } - android_sensors_set(id, fvalues[0], fvalues[1], fvalues[2]); - } else { - W("Unknown sensor name '%s' in '%s'", sensor, msg); } - /* Listen to the next event. */ - android_device_listen(ad, asp->events, sizeof(asp->events), _on_sensor_received); + /* Start the emulation. */ + SDKCtlMessage* const msg = + sdkctl_message_send(asp->sdkctl, SDKCTL_SENSORS_START, NULL, 0); + sdkctl_message_release(msg); + + D("Sensors: Emulation has been started."); } -/* A callback that is invoked when android device is connected (i.e. both, command - * and event channels have been stablished. - * Param: - * opaque - AndroidSensorsPort instance. - * ad - Android device used by this sensors port. - * failure - Connections status. - */ -static void -_on_device_connected(void* opaque, AndroidDevice* ad, int failure) +/******************************************************************************** + * Sensors port callbacks + *******************************************************************************/ + +/* Completion for the "list" query. */ +static AsyncIOAction +_on_sensor_list_query(void* query_opaque, + SDKCtlQuery* query, + AsyncIOState status) { - if (!failure) { - AndroidSensorsPort* asp = (AndroidSensorsPort*)opaque; - asp->is_connected = 1; - D("Sensor emulation has started"); - /* Initialize sensors on device. */ - sensors_port_init_sensors(asp); + AndroidSensorsPort* const asp = (AndroidSensorsPort*)(query_opaque); + if (status != ASIO_STATE_SUCCEEDED) { + /* We don't really care about failures at this point. They will + * eventually surface up in another place. */ + return ASIO_ACTION_DONE; } + + /* Parse query response which is a flat list of SensorEntry entries. */ + const SensorEntry* const list = + (const SensorEntry*)sdkctl_query_get_buffer_out(query); + D("Sensors: Sensor list received with %d sensors.", + _sensor_entry_list_size(list)); + _sensors_port_save_sensors(asp, list); + + /* At this point we are ready to statr sensor emulation. */ + _sensors_port_start(asp); + + return ASIO_ACTION_DONE; } -/* Invoked when an I/O failure occurs on a socket. - * Note that this callback will not be invoked on connection failures. +/* A callback that is invoked on sensor events. * Param: - * opaque - AndroidSensorsPort instance. - * ad - Android device instance - * ads - Connection socket where failure has occured. - * failure - Contains 'errno' indicating the reason for failure. + * asp - AndroidSensorsPort instance. + * event - Sensor event. */ static void -_on_io_failure(void* opaque, AndroidDevice* ad, int failure) +_on_sensor_event(AndroidSensorsPort* asp, const SensorEvent* event) { - AndroidSensorsPort* asp = (AndroidSensorsPort*)opaque; - E("Sensors port got disconnected: %s", strerror(failure)); - asp->is_connected = false; - android_device_disconnect(ad); - android_device_connect_async(ad, _on_device_connected); + /* Find corresponding server descriptor. */ + const SensorDescriptor* const desc = + _sensor_from_sdkctl_id(asp, event->sensor_id); + if (desc != NULL) { + T("Sensors: %s -> %f, %f, %f", desc->sensor_name, + event->fvalues[0], event->fvalues[1], + event->fvalues[2]); + /* Fire up sensor change in the guest. */ + android_sensors_set(desc->emulator_id, event->fvalues[0], + event->fvalues[1], event->fvalues[2]); + } else { + W("Sensors: No descriptor for sensor %d", event->sensor_id); + } } -/******************************************************************************** - * Sensors port API - *******************************************************************************/ - -#include "android/sdk-controller-socket.h" - +/* A callback that is invoked on SDK controller socket connection events. */ static AsyncIOAction -_on_sdkctl_connection(void* client_opaque, SDKCtlSocket* sdkctl, AsyncIOState status) +_on_sensors_socket_connection(void* client_opaque, + SDKCtlSocket* sdkctl, + AsyncIOState status) { + AndroidSensorsPort* const asp = (AndroidSensorsPort*)client_opaque; if (status == ASIO_STATE_FAILED) { - sdkctl_socket_reconnect(sdkctl, 1970, 20); + /* Disconnection could mean that user is swapping devices. New device may + * have different set of sensors, so we need to re-query sensor list on + * reconnection. */ + _sensors_port_discard_sensors(asp); + + /* Reconnect (after timeout delay) on failures */ + if (sdkctl_socket_is_handshake_ok(sdkctl)) { + sdkctl_socket_reconnect(sdkctl, SDKCTL_DEFAULT_TCP_PORT, + SDKCTL_SENSORS_TIMEOUT); + } } return ASIO_ACTION_DONE; } -void on_sdkctl_handshake(void* client_opaque, - SDKCtlSocket* sdkctl, - void* handshake, - uint32_t handshake_size, - AsyncIOState status) +/* A callback that is invoked on SDK controller port connection events. */ +static void +_on_sensors_port_connection(void* client_opaque, + SDKCtlSocket* sdkctl, + SdkCtlPortStatus status) { - if (status == ASIO_STATE_SUCCEEDED) { - printf("---------- Handshake %d bytes received.\n", handshake_size); - } else { - printf("!!!!!!!!!! Handshake failed with status %d: %d -> %s\n", - status, errno, strerror(errno)); - sdkctl_socket_reconnect(sdkctl, 1970, 20); + AndroidSensorsPort* const asp = (AndroidSensorsPort*)client_opaque; + switch (status) { + case SDKCTL_PORT_CONNECTED: { + D("Sensors: SDK Controller is connected."); + /* Query list of available sensors. */ + SDKCtlQuery* const query = + sdkctl_query_build_and_send(asp->sdkctl, SDKCTL_SENSORS_QUERY_LIST, + 0, NULL, NULL, NULL, + _on_sensor_list_query, asp, + SDKCTL_SENSORS_TIMEOUT); + sdkctl_query_release(query); + break; + } + + case SDKCTL_PORT_DISCONNECTED: + _sensors_port_discard_sensors(asp); + D("Sensors: SDK Controller is disconnected."); + break; + + case SDKCTL_PORT_ENABLED: + _sensors_port_start(asp); + D("Sensors: SDK Controller is enabled."); + break; + + case SDKCTL_PORT_DISABLED: + D("Sensors: SDK Controller is disabled."); + break; + + case SDKCTL_HANDSHAKE_CONNECTED: + D("Sensors: SDK Controller has succeeded handshake, and port is connected."); + break; + + case SDKCTL_HANDSHAKE_NO_PORT: + D("Sensors: SDK Controller has succeeded handshake, and port is not connected."); + break; + + case SDKCTL_HANDSHAKE_DUP: + E("Sensors: SDK Controller has failed the handshake due to port duplication."); + sdkctl_socket_disconnect(sdkctl); + break; + + case SDKCTL_HANDSHAKE_UNKNOWN_QUERY: + E("Sensors: SDK Controller has failed the handshake due to unknown query."); + sdkctl_socket_disconnect(sdkctl); + break; + + case SDKCTL_HANDSHAKE_UNKNOWN_RESPONSE: + default: + E("Sensors: Handshake has failed due to unknown reasons."); + sdkctl_socket_disconnect(sdkctl); + break; } } -void on_sdkctl_message(void* client_opaque, - SDKCtlSocket* sdkctl, - SDKCtlPacket* message, - int msg_type, - void* msg_data, - int msg_size) +/* A callback that is invoked when a message is received from SDK controller. */ +static void +_on_sensors_message(void* client_opaque, + SDKCtlSocket* sdkctl, + SDKCtlMessage* message, + int msg_type, + void* msg_data, + int msg_size) { - printf("########################################################\n"); + AndroidSensorsPort* const asp = (AndroidSensorsPort*)client_opaque; + switch (msg_type) { + case SDKCTL_SENSORS_SENSOR_EVENT: + _on_sensor_event(asp, (const SensorEvent*)msg_data); + break; + + default: + E("Sensors: Unknown message type %d", msg_type); + break; + } } +/******************************************************************************** + * Sensors port API + *******************************************************************************/ + AndroidSensorsPort* sensors_port_create(void* opaque) { AndroidSensorsPort* asp; - char* wrk; - int res; - - SDKCtlSocket* sdkctl = sdkctl_socket_new(20, "test", _on_sdkctl_connection, - on_sdkctl_handshake, on_sdkctl_message, - NULL); -// sdkctl_init_recycler(sdkctl, 64, 8); - sdkctl_socket_connect(sdkctl, 1970, 20); ANEW0(asp); asp->opaque = opaque; - asp->is_connected = 0; - - asp->device = android_device_init(asp, AD_SENSOR_PORT, _on_io_failure); - if (asp->device == NULL) { - _sensors_port_free(asp); - return NULL; - } - - res = android_device_connect_sync(asp->device, ASP_QUERY_TIMEOUT); - if (res != 0) { - sensors_port_destroy(asp); - return NULL; - } - - res = android_device_query(asp->device, "list", - asp->sensors, sizeof(asp->sensors), - ASP_QUERY_TIMEOUT); - if (res != 0) { - sensors_port_destroy(asp); - return NULL; - } - - /* Parse sensor list. */ - asp->sensors_num = 0; - wrk = asp->sensors; - - while (wrk != NULL && *wrk != '\0' && *wrk != '\n') { - asp->sensor_list[asp->sensors_num] = wrk; - asp->sensors_num++; - wrk = strchr(wrk, '\n'); - if (wrk != NULL) { - *wrk = '\0'; wrk++; - } - } - - android_device_listen(asp->device, asp->events, sizeof(asp->events), - _on_sensor_received); + asp->sensors = NULL; + asp->sensors_count = 0; + asp->sdkctl = sdkctl_socket_new(SDKCTL_SENSORS_TIMEOUT, "sensors", + _on_sensors_socket_connection, + _on_sensors_port_connection, + _on_sensors_message, asp); + sdkctl_init_recycler(asp->sdkctl, 76, 8); + sdkctl_socket_connect(asp->sdkctl, SDKCTL_DEFAULT_TCP_PORT, + SDKCTL_SENSORS_TIMEOUT); return asp; } -int -sensors_port_init_sensors(AndroidSensorsPort* asp) -{ - int res, id; - - /* Disable all sensors for now. Reenable only those that are emulated. */ - res = sensors_port_disable_sensor(asp, "all"); - if (res) { - return res; - } - - /* Start listening on sensor events. */ - res = android_device_listen(asp->device, asp->events, sizeof(asp->events), - _on_sensor_received); - if (res) { - return res; - } - - /* Walk throuh the list of enabled sensors enabling them on the device. */ - for (id = 0; id < MAX_SENSORS; id++) { - if (android_sensors_get_sensor_status(id) == 1) { - res = sensors_port_enable_sensor(asp, android_sensors_get_name_from_id(id)); - if (res == 0) { - D("Sensor '%s' is enabled on the device.", - android_sensors_get_name_from_id(id)); - } - } - } - - /* Start sensor events. */ - return sensors_port_start(asp); -} - void sensors_port_destroy(AndroidSensorsPort* asp) { + if (asp->sdkctl != NULL) { + sdkctl_socket_disconnect(asp->sdkctl); + } _sensors_port_free(asp); } int -sensors_port_is_connected(AndroidSensorsPort* asp) -{ - return asp->is_connected; -} - -int sensors_port_enable_sensor(AndroidSensorsPort* asp, const char* name) { - char query[1024]; - char qresp[1024]; - snprintf(query, sizeof(query), "enable:%s", name); - const int res = - android_device_query(asp->device, query, qresp, sizeof(qresp), - ASP_QUERY_TIMEOUT); - if (res) { - if (errno) { - D("Query '%s' failed on I/O: %s", query, strerror(errno)); - } else { - D("Query '%s' failed on device: %s", query, qresp); - } + if (asp->sdkctl != NULL && sdkctl_socket_is_port_ready(asp->sdkctl)) { + SDKCtlMessage* const msg = sdkctl_message_send(asp->sdkctl, + SDKCTL_SENSORS_ENABLE, + name, strlen(name)); + sdkctl_message_release(msg); + return 0; + } else { + return -1; } - return res; } int sensors_port_disable_sensor(AndroidSensorsPort* asp, const char* name) { - char query[1024]; - char qresp[1024]; - snprintf(query, sizeof(query), "disable:%s", name); - const int res = - android_device_query(asp->device, query, qresp, sizeof(qresp), - ASP_QUERY_TIMEOUT); - if (res) { - if (errno) { - D("Query '%s' failed on I/O: %s", query, strerror(errno)); - } else { - D("Query '%s' failed on device: %s", query, qresp); - } - } - return res; -} - -int -sensors_port_start(AndroidSensorsPort* asp) -{ - char qresp[ASP_MAX_SENSOR_MSG]; - const int res = - android_device_query(asp->device, "start", qresp, sizeof(qresp), - ASP_QUERY_TIMEOUT); - if (res) { - if (errno) { - D("Query 'start' failed on I/O: %s", strerror(errno)); - } else { - D("Query 'start' failed on device: %s", qresp); - } - } - return res; -} - -int -sensors_port_stop(AndroidSensorsPort* asp) -{ - char qresp[ASP_MAX_SENSOR_MSG]; - const int res = - android_device_query(asp->device, "stop", qresp, sizeof(qresp), - ASP_QUERY_TIMEOUT); - if (res) { - if (errno) { - D("Query 'stop' failed on I/O: %s", strerror(errno)); - } else { - D("Query 'stop' failed on device: %s", qresp); - } + if (asp->sdkctl != NULL && sdkctl_socket_is_port_ready(asp->sdkctl)) { + SDKCtlMessage* const msg = sdkctl_message_send(asp->sdkctl, + SDKCTL_SENSORS_DISABLE, + name, strlen(name)); + sdkctl_message_release(msg); + return 0; + } else { + return -1; } - - return res; } diff --git a/android/sensors-port.h b/android/sensors-port.h index dc5c966..a35d700 100644 --- a/android/sensors-port.h +++ b/android/sensors-port.h @@ -23,8 +23,6 @@ * the host via USB. */ -#include "android/android-device.h" - /* Declares sensors port descriptor. */ typedef struct AndroidSensorsPort AndroidSensorsPort; @@ -42,15 +40,6 @@ extern AndroidSensorsPort* sensors_port_create(void* opaque); /* Disconnects from the sensors port, and destroys the descriptor. */ extern void sensors_port_destroy(AndroidSensorsPort* asp); -/* Initializes sensors on the connected device. */ -extern int sensors_port_init_sensors(AndroidSensorsPort* asp); - -/* Checks if port is connected to a sensor reading application on the device. - * Note that connection can go out and then be restored at any time after - * sensors_port_create API succeeded. - */ -extern int sensors_port_is_connected(AndroidSensorsPort* asp); - /* Enables events from a particular sensor. * Param: * asp - Android sensors port instance returned from sensors_port_create. @@ -72,20 +61,4 @@ extern int sensors_port_enable_sensor(AndroidSensorsPort* asp, const char* name) */ extern int sensors_port_disable_sensor(AndroidSensorsPort* asp, const char* name); -/* Queries the connected application to start delivering sensor events. - * Param: - * asp - Android sensors port instance returned from sensors_port_create. - * Return: - * Zero on success, failure otherwise. - */ -extern int sensors_port_start(AndroidSensorsPort* asp); - -/* Queries the connected application to stop delivering sensor events. - * Param: - * asp - Android sensors port instance returned from sensors_port_create. - * Return: - * Zero on success, failure otherwise. - */ -extern int sensors_port_stop(AndroidSensorsPort* asp); - #endif /* ANDROID_SENSORS_PORT_H_ */ |