From c8aa2c570d30098da59f1967d5158024ed28570d Mon Sep 17 00:00:00 2001 From: Vladimir Chtchetkine Date: Thu, 5 Apr 2012 16:22:55 -0700 Subject: Implements SDKCtlSocket that implements communication protocol wih SdkController In addition, this CL contains some minor tweaks to async-socket, and async-socket-connector that improve tracebility. Change-Id: Ib1309b19dcd02e96379155fea7015019d93160e7 --- android/async-io-common.h | 6 + android/async-socket-connector.c | 93 ++- android/async-socket-connector.h | 5 +- android/async-socket.c | 134 ++- android/async-socket.h | 5 +- android/sdk-controller-socket.c | 1691 ++++++++++++++++++++++++++++++++++++++ android/sdk-controller-socket.h | 440 ++++++++++ android/sensors-port.c | 42 + android/utils/debug.h | 1 + 9 files changed, 2354 insertions(+), 63 deletions(-) create mode 100644 android/sdk-controller-socket.c create mode 100644 android/sdk-controller-socket.h (limited to 'android') diff --git a/android/async-io-common.h b/android/async-io-common.h index 02714a7..02b2c96 100644 --- a/android/async-io-common.h +++ b/android/async-io-common.h @@ -44,6 +44,12 @@ typedef enum AsyncIOState { /* Asynchronous I/O has been cancelled (due to disconnect, for * instance). (7) */ ASIO_STATE_CANCELLED, + /* Asynchronous I/O is finished and is about to be discarder. This state is + * useful in case there is an association between an I/O and some client's + * component, that holds a reference associated with this I/O. When callback + * is invoked with this state, it means that it's safe to drop that extra + * reference associated with the I/O (8) */ + ASIO_STATE_FINISHED, } AsyncIOState; /* Enumerates actions to perform with an I/O on state transition. diff --git a/android/async-socket-connector.c b/android/async-socket-connector.c index 80ec5bb..836016c 100644 --- a/android/async-socket-connector.c +++ b/android/async-socket-connector.c @@ -32,6 +32,14 @@ #define D(...) VERBOSE_PRINT(asconnector,__VA_ARGS__) #define D_ACTIVE VERBOSE_CHECK(asconnector) +#define TRACE_ON 0 + +#if TRACE_ON +#define T(...) VERBOSE_PRINT(asconnector,__VA_ARGS__) +#else +#define T(...) +#endif + /******************************************************************************** * Internals *******************************************************************************/ @@ -57,6 +65,8 @@ struct AsyncSocketConnector { int fd; /* Number of outstanding references to the connector. */ int ref_count; + /* Flags whether (1) or not (0) connector owns the looper. */ + int owns_looper; }; /* Asynchronous I/O looper callback invoked by the connector. @@ -81,24 +91,31 @@ static void _async_socket_connector_free(AsyncSocketConnector* connector) { if (connector != NULL) { - D("%s: Connector is destroyed.", _asc_socket_string(connector)); + T("ASC %s: Connector is destroying...", _asc_socket_string(connector)); + + /* Stop all activities. */ if (asyncConnector_stop(connector->connector) == 0) { /* Connection was in progress. We need to destroy I/O descriptor for * that connection. */ - D("%s: Stopped async connection in progress.", + D("ASC %s: Stopped async connection in progress.", _asc_socket_string(connector)); loopIo_done(connector->connector_io); } - if (connector->fd >= 0) { - socket_close(connector->fd); - } + /* Free allocated resources. */ if (connector->looper != NULL) { - loopTimer_stop(connector->connector_timer); loopTimer_done(connector->connector_timer); - looper_free(connector->looper); + if (connector->owns_looper) { + looper_free(connector->looper); + } } + if (connector->fd >= 0) { + socket_close(connector->fd); + } + + T("ASC %s: Connector is destroyed", _asc_socket_string(connector)); + sock_address_done(&connector->address); AFREE(connector); @@ -117,7 +134,7 @@ _async_socket_connector_open_socket(AsyncSocketConnector* connector) /* Open socket. */ connector->fd = socket_create_inet(SOCKET_STREAM); if (connector->fd < 0) { - D("%s: Unable to create socket: %d -> %s", + D("ASC %s: Unable to create socket: %d -> %s", _asc_socket_string(connector), errno, strerror(errno)); return -1; } @@ -125,6 +142,9 @@ _async_socket_connector_open_socket(AsyncSocketConnector* connector) /* Prepare for async I/O on the connector. */ socket_set_nonblock(connector->fd); + T("ASC %s: Connector socket is opened with FD = %d", + _asc_socket_string(connector), connector->fd); + return 0; } @@ -139,6 +159,8 @@ _async_socket_connector_close_socket(AsyncSocketConnector* connector) { if (connector->fd >= 0) { socket_close(connector->fd); + T("ASC %s: Connector socket FD = %d is closed.", + _asc_socket_string(connector), connector->fd); connector->fd = -1; } } @@ -177,15 +199,18 @@ _on_async_socket_connector_connecting(AsyncSocketConnector* connector, break; case ASYNC_NEED_MORE: + T("ASC %s: Waiting on connection to complete. Connector FD = %d", + _asc_socket_string(connector), connector->fd); return; } if (action == ASIO_ACTION_RETRY) { - D("%s: Retrying connection", _asc_socket_string(connector)); + D("ASC %s: Retrying connection. Connector FD = %d", + _asc_socket_string(connector), connector->fd); loopTimer_startRelative(connector->connector_timer, connector->retry_to); } else if (action == ASIO_ACTION_ABORT) { - D("%s: AsyncSocketConnector client for socket '%s' has aborted connection", - __FUNCTION__, _asc_socket_string(connector)); + D("ASC %s: Client has aborted connection. Connector FD = %d", + _asc_socket_string(connector), connector->fd); } } @@ -206,8 +231,8 @@ _on_async_socket_connector_io(void* opaque, int fd, unsigned events) const AsyncStatus status = asyncConnector_run(connector->connector); _on_async_socket_connector_connecting(connector, status); } else { - D("%s: AsyncSocketConnector client for socket '%s' has aborted connection", - __FUNCTION__, _asc_socket_string(connector)); + D("ASC %s: Client has aborted connection. Connector FD = %d", + _asc_socket_string(connector), connector->fd); } /* Release the connector after we're done with handing I/O. */ @@ -224,6 +249,9 @@ _on_async_socket_connector_retry(void* opaque) AsyncStatus status; AsyncSocketConnector* const connector = (AsyncSocketConnector*)opaque; + T("ASC %s: Reconnect timer expired. Connector FD = %d", + _asc_socket_string(connector), connector->fd); + /* Reference the connector while we're in callback. */ async_socket_connector_reference(connector); @@ -249,8 +277,8 @@ _on_async_socket_connector_retry(void* opaque) _on_async_socket_connector_connecting(connector, status); } else { - D("%s: AsyncSocketConnector client for socket '%s' has aborted connection", - __FUNCTION__, _asc_socket_string(connector)); + D("ASC %s: Client has aborted connection. Connector FD = %d", + _asc_socket_string(connector), connector->fd); } /* Release the connector after we're done with the callback. */ @@ -265,7 +293,8 @@ AsyncSocketConnector* async_socket_connector_new(const SockAddress* address, int retry_to, asc_event_cb cb, - void* cb_opaque) + void* cb_opaque, + Looper* looper) { AsyncSocketConnector* connector; @@ -292,19 +321,27 @@ async_socket_connector_new(const SockAddress* address, } /* Create a looper for asynchronous I/O. */ - connector->looper = looper_newCore(); - if (connector->looper == NULL) { - E("Unable to create I/O looper for AsyncSocketConnector for socket '%s'", - _asc_socket_string(connector)); - cb(cb_opaque, connector, ASIO_STATE_FAILED); - _async_socket_connector_free(connector); - return NULL; + if (looper == NULL) { + connector->looper = looper_newCore(); + if (connector->looper == NULL) { + E("Unable to create I/O looper for AsyncSocketConnector for socket '%s'", + _asc_socket_string(connector)); + cb(cb_opaque, connector, ASIO_STATE_FAILED); + _async_socket_connector_free(connector); + return NULL; + } + connector->owns_looper = 1; + } else { + connector->looper = looper; + connector->owns_looper = 0; } /* Create a timer that will be used for connection retries. */ loopTimer_init(connector->connector_timer, connector->looper, _on_async_socket_connector_retry, connector); + T("ASC %s: New connector object", _asc_socket_string(connector)); + return connector; } @@ -334,13 +371,16 @@ async_socket_connector_connect(AsyncSocketConnector* connector) { AsyncStatus status; + T("ASC %s: Handling connect request. Connector FD = %d", + _asc_socket_string(connector), connector->fd); + if (_async_socket_connector_open_socket(connector) == 0) { const AsyncIOAction action = connector->on_connected_cb(connector->on_connected_cb_opaque, connector, ASIO_STATE_STARTED); if (action == ASIO_ACTION_ABORT) { - D("%s: AsyncSocketConnector client for socket '%s' has aborted connection", - __FUNCTION__, _asc_socket_string(connector)); + D("ASC %s: Client has aborted connection. Connector FD = %d", + _asc_socket_string(connector), connector->fd); return; } else { loopIo_init(connector->connector_io, connector->looper, @@ -363,5 +403,8 @@ async_socket_connector_pull_fd(AsyncSocketConnector* connector) if (fd >= 0) { connector->fd = -1; } + + T("ASC %s: Client has pulled connector FD %d", _asc_socket_string(connector), fd); + return fd; } diff --git a/android/async-socket-connector.h b/android/async-socket-connector.h index 0769f56..bedc2df 100644 --- a/android/async-socket-connector.h +++ b/android/async-socket-connector.h @@ -89,6 +89,8 @@ typedef AsyncIOAction (*asc_event_cb)(void* opaque, * retry_to - Timeout to retry a failed connection attempt in milliseconds. * cb, cb_opaque - Callback to invoke on connection events. This callback is * required, and must not be NULL. + * looper - An optional (can be NULL) I/O looper to use for connection I/O. If + * this parameter is NULL, the connector will create its own looper. * Return: * Initialized AsyncSocketConnector instance. Note that AsyncSocketConnector * instance returned from this routine will be destroyed by the connector itself, @@ -99,7 +101,8 @@ typedef AsyncIOAction (*asc_event_cb)(void* opaque, extern AsyncSocketConnector* async_socket_connector_new(const SockAddress* address, int retry_to, asc_event_cb cb, - void* cb_opaque); + void* cb_opaque, + Looper* looper); /* References AsyncSocketConnector object. * Param: diff --git a/android/async-socket.c b/android/async-socket.c index a2577c9..5e2ae29 100644 --- a/android/async-socket.c +++ b/android/async-socket.c @@ -33,6 +33,14 @@ #define D(...) VERBOSE_PRINT(asyncsocket,__VA_ARGS__) #define D_ACTIVE VERBOSE_CHECK(asyncsocket) +#define TRACE_ON 0 + +#if TRACE_ON +#define T(...) VERBOSE_PRINT(asyncsocket,__VA_ARGS__) +#else +#define T(...) +#endif + /******************************************************************************** * Asynchronous Socket internal API declarations *******************************************************************************/ @@ -153,6 +161,9 @@ _async_socket_rw_new(AsyncSocket* as, /* Reference socket that is holding this I/O. */ async_socket_reference(as); + T("ASocket %s: %s I/O descriptor %p is created for %d bytes of data", + _async_socket_string(as), is_io_read ? "READ" : "WRITE", asio, len); + return asio; } @@ -162,7 +173,9 @@ _async_socket_io_free(AsyncSocketIO* asio) { AsyncSocket* const as = asio->as; - loopTimer_stop(asio->timer); + T("ASocket %s: %s I/O descriptor %p is destroyed.", + _async_socket_string(as), asio->is_io_read ? "READ" : "WRITE", asio); + loopTimer_done(asio->timer); /* Try to recycle it first, and free the memory if recycler is full. */ @@ -178,6 +191,14 @@ _async_socket_io_free(AsyncSocketIO* asio) async_socket_release(as); } +/* An I/O has been finished and its descriptor is about to be discarded. */ +static void +_async_socket_io_finished(AsyncSocketIO* asio) +{ + /* Notify the client of the I/O that I/O is finished. */ + asio->on_io(asio->io_opaque, asio, ASIO_STATE_FINISHED); +} + int async_socket_io_reference(AsyncSocketIO* asio) { @@ -192,6 +213,7 @@ async_socket_io_release(AsyncSocketIO* asio) assert(asio->ref_count > 0); asio->ref_count--; if (asio->ref_count == 0) { + _async_socket_io_finished(asio); /* Last reference has been dropped. Destroy this object. */ _async_socket_io_free(asio); return 0; @@ -253,7 +275,7 @@ _on_async_socket_io_timed_out(void* opaque) AsyncSocketIO* const asio = (AsyncSocketIO*)opaque; AsyncSocket* const as = asio->as; - D("%s: %s I/O (deadline = %lld) timed out at %lld", + D("ASocket %s: %s I/O with deadline %lld has timed out at %lld", _async_socket_string(as), asio->is_io_read ? "READ" : "WRITE", asio->deadline, async_socket_deadline(as, 0)); @@ -361,6 +383,8 @@ struct AsyncSocket { int reconnect_to; /* Number of outstanding references to the socket. */ int ref_count; + /* Flags whether (1) or not (0) socket owns the looper. */ + int owns_looper; }; static const char* @@ -523,8 +547,11 @@ _async_socket_advance_writer(AsyncSocket* as) static AsyncIOAction _async_socket_complete_io(AsyncSocket* as, AsyncSocketIO* asio) { + T("ASocket %s: %s I/O %p is completed.", + _async_socket_string(as), asio->is_io_read ? "READ" : "WRITE", asio); + /* Stop the timer. */ - loopTimer_stop(asio->timer); + async_socket_io_cancel_time_out(asio); return asio->on_io(asio->io_opaque, asio, ASIO_STATE_SUCCEEDED); } @@ -539,6 +566,10 @@ _async_socket_complete_io(AsyncSocket* as, AsyncSocketIO* asio) static AsyncIOAction _async_socket_io_timed_out(AsyncSocket* as, AsyncSocketIO* asio) { + T("ASocket %s: %s I/O %p with deadline %lld has timed out at %lld", + _async_socket_string(as), asio->is_io_read ? "READ" : "WRITE", asio, + asio->deadline, async_socket_deadline(as, 0)); + /* Report to the client. */ const AsyncIOAction action = asio->on_io(asio->io_opaque, asio, ASIO_STATE_TIMED_OUT); @@ -565,8 +596,11 @@ _async_socket_io_timed_out(AsyncSocket* as, AsyncSocketIO* asio) static AsyncIOAction _async_socket_cancel_io(AsyncSocket* as, AsyncSocketIO* asio) { + T("ASocket %s: %s I/O %p is cancelled.", + _async_socket_string(as), asio->is_io_read ? "READ" : "WRITE", asio); + /* Stop the timer. */ - loopTimer_stop(asio->timer); + async_socket_io_cancel_time_out(asio); return asio->on_io(asio->io_opaque, asio, ASIO_STATE_CANCELLED); } @@ -582,8 +616,12 @@ _async_socket_cancel_io(AsyncSocket* as, AsyncSocketIO* asio) static AsyncIOAction _async_socket_io_failure(AsyncSocket* as, AsyncSocketIO* asio, int failure) { + T("ASocket %s: %s I/O %p has failed: %d -> %s", + _async_socket_string(as), asio->is_io_read ? "READ" : "WRITE", asio, + failure, strerror(failure)); + /* Stop the timer. */ - loopTimer_stop(asio->timer); + async_socket_io_cancel_time_out(asio); errno = failure; return asio->on_io(asio->io_opaque, asio, ASIO_STATE_FAILED); @@ -647,6 +685,8 @@ _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); as->fd = -1; } } @@ -659,7 +699,7 @@ static void _async_socket_free(AsyncSocket* as) { if (as != NULL) { - D("AsyncSocket '%s' is destroyed", _async_socket_string(as)); + T("ASocket %s: Socket descriptor is destroyed.", _async_socket_string(as)); /* Close socket. */ _async_socket_close_socket(as); @@ -667,7 +707,9 @@ _async_socket_free(AsyncSocket* as) /* Free allocated resources. */ if (as->looper != NULL) { loopTimer_done(as->reconnect_timer); - looper_free(as->looper); + if (as->owns_looper) { + looper_free(as->looper); + } } sock_address_done(&as->address); AFREE(as); @@ -682,6 +724,8 @@ _async_socket_free(AsyncSocket* as) static void _async_socket_reconnect(AsyncSocket* as, int to) { + T("ASocket %s: reconnecting in %dms...", _async_socket_string(as), to); + /* Make sure that no I/O is active, and socket is closed before we * reconnect. */ _async_socket_cancel_all_io(as); @@ -705,8 +749,7 @@ _on_async_socket_disconnected(AsyncSocket* as) const int save_errno = errno; AsyncIOAction action = ASIO_ACTION_ABORT; - D("Async socket '%s' is disconnected. Error %d -> %s", - _async_socket_string(as), errno, strerror(errno)); + D("ASocket %s: Disconnected.", _async_socket_string(as)); /* Cancel all the I/O on this socket. */ _async_socket_cancel_all_io(as); @@ -732,7 +775,7 @@ _on_async_socket_disconnected(AsyncSocket* as) static AsyncIOAction _on_async_socket_failure(AsyncSocket* as, AsyncSocketIO* asio) { - D("Async socket '%s' %s I/O failure: %d -> %s", + D("ASocket %s: %s I/O failure: %d -> %s", _async_socket_string(as), asio->is_io_read ? "READ" : "WRITE", errno, strerror(errno)); @@ -755,8 +798,7 @@ _on_async_socket_recv(AsyncSocket* as) /* Get current reader. */ AsyncSocketIO* const asr = as->readers_head; if (asr == NULL) { - D("No async socket reader available on IO_READ for '%s'", - _async_socket_string(as)); + D("ASocket %s: No reader is available.", _async_socket_string(as)); loopIo_dontWantRead(as->io); return 0; } @@ -772,8 +814,7 @@ _on_async_socket_recv(AsyncSocket* as) } action = asr->on_io(asr->io_opaque, asr, asr->state); if (action == ASIO_ACTION_ABORT) { - D("Read is aborted by the client of async socket '%s'", - _async_socket_string(as)); + D("ASocket %s: Read is aborted by the client.", _async_socket_string(as)); /* Move on to the next reader. */ _async_socket_advance_reader(as); /* Lets see if there are still active readers, and enable, or disable @@ -813,9 +854,8 @@ _on_async_socket_recv(AsyncSocket* as) /* An I/O error. */ action = _on_async_socket_failure(as, asr); - if (action == ASIO_ACTION_ABORT) { - D("Read is aborted on failure by the client of async socket '%s'", - _async_socket_string(as)); + if (action != ASIO_ACTION_RETRY) { + D("ASocket %s: Read is aborted on failure.", _async_socket_string(as)); /* Move on to the next reader. */ _async_socket_advance_reader(as); /* Lets see if there are still active readers, and enable, or disable @@ -868,8 +908,7 @@ _on_async_socket_send(AsyncSocket* as) /* Get current writer. */ AsyncSocketIO* const asw = as->writers_head; if (asw == NULL) { - D("No async socket writer available on IO_WRITE for '%s'", - _async_socket_string(as)); + D("ASocket %s: No writer is available.", _async_socket_string(as)); loopIo_dontWantWrite(as->io); return 0; } @@ -885,8 +924,7 @@ _on_async_socket_send(AsyncSocket* as) } action = asw->on_io(asw->io_opaque, asw, asw->state); if (action == ASIO_ACTION_ABORT) { - D("Write is aborted by the client of async socket '%s'", - _async_socket_string(as)); + D("ASocket %s: Write is aborted by the client.", _async_socket_string(as)); /* Move on to the next writer. */ _async_socket_advance_writer(as); /* Lets see if there are still active writers, and enable, or disable @@ -926,9 +964,8 @@ _on_async_socket_send(AsyncSocket* as) /* An I/O error. */ action = _on_async_socket_failure(as, asw); - if (action == ASIO_ACTION_ABORT) { - D("Write is aborted on failure by the client of async socket '%s'", - _async_socket_string(as)); + if (action != ASIO_ACTION_RETRY) { + D("ASocket %s: Write is aborted on failure.", _async_socket_string(as)); /* Move on to the next writer. */ _async_socket_advance_writer(as); /* Lets see if there are still active writers, and enable, or disable @@ -1028,8 +1065,8 @@ _on_connector_events(void* opaque, if (event == ASIO_STATE_SUCCEEDED && action != ASIO_ACTION_DONE) { /* For whatever reason the client didn't want to keep this connection. * Close it. */ - D("Connection is discarded by a client of async socket '%s'", - _async_socket_string(as)); + D("ASocket %s: Connection is discarded by the client.", + _async_socket_string(as)); _async_socket_close_socket(as); } @@ -1066,7 +1103,8 @@ AsyncSocket* async_socket_new(int port, int reconnect_to, on_as_connection_cb client_cb, - void* client_opaque) + void* client_opaque, + Looper* looper) { AsyncSocket* as; @@ -1084,16 +1122,25 @@ async_socket_new(int port, as->reconnect_to = reconnect_to; as->ref_count = 1; sock_address_init_inet(&as->address, SOCK_ADDRESS_INET_LOOPBACK, port); - as->looper = looper_newCore(); - if (as->looper == NULL) { - E("Unable to create I/O looper for async socket '%s'", - _async_socket_string(as)); - client_cb(client_opaque, as, ASIO_STATE_FAILED); - _async_socket_free(as); - return NULL; + if (looper == NULL) { + as->looper = looper_newCore(); + if (as->looper == NULL) { + E("Unable to create I/O looper for async socket '%s'", + _async_socket_string(as)); + client_cb(client_opaque, as, ASIO_STATE_FAILED); + _async_socket_free(as); + return NULL; + } + as->owns_looper = 1; + } else { + as->looper = looper; + as->owns_looper = 0; } + loopTimer_init(as->reconnect_timer, as->looper, _on_async_socket_reconnect, as); + T("ASocket %s: Descriptor is created.", _async_socket_string(as)); + return as; } @@ -1122,8 +1169,12 @@ async_socket_release(AsyncSocket* as) void async_socket_connect(AsyncSocket* as, int retry_to) { + T("ASocket %s: Handling connection request for %dms...", + _async_socket_string(as), retry_to); + AsyncSocketConnector* const connector = - async_socket_connector_new(&as->address, retry_to, _on_connector_events, as); + async_socket_connector_new(&as->address, retry_to, _on_connector_events, + as, as->looper); if (connector != NULL) { async_socket_connector_connect(connector); } else { @@ -1134,6 +1185,8 @@ async_socket_connect(AsyncSocket* as, int retry_to) void async_socket_disconnect(AsyncSocket* as) { + T("ASocket %s: Handling disconnection request...", _async_socket_string(as)); + if (as != NULL) { _async_socket_cancel_all_io(as); _async_socket_close_socket(as); @@ -1143,9 +1196,12 @@ async_socket_disconnect(AsyncSocket* as) void async_socket_reconnect(AsyncSocket* as, int retry_to) { + T("ASocket %s: Handling reconnection request for %dms...", + _async_socket_string(as), retry_to); + _async_socket_cancel_all_io(as); _async_socket_close_socket(as); - async_socket_connect(as, retry_to); + _async_socket_reconnect(as, retry_to); } void @@ -1155,6 +1211,9 @@ async_socket_read_abs(AsyncSocket* as, void* reader_opaque, Duration deadline) { + T("ASocket %s: Handling read for %d bytes with deadline %lld...", + _async_socket_string(as), len, deadline); + AsyncSocketIO* const asr = _async_socket_reader_new(as, buffer, len, reader_cb, reader_opaque, deadline); @@ -1188,6 +1247,9 @@ async_socket_write_abs(AsyncSocket* as, void* writer_opaque, Duration deadline) { + T("ASocket %s: Handling write for %d bytes with deadline %lld...", + _async_socket_string(as), len, deadline); + AsyncSocketIO* const asw = _async_socket_writer_new(as, buffer, len, writer_cb, writer_opaque, deadline); diff --git a/android/async-socket.h b/android/async-socket.h index 661fae5..503907b 100644 --- a/android/async-socket.h +++ b/android/async-socket.h @@ -137,13 +137,16 @@ extern int async_socket_io_is_read(const AsyncSocketIO* asio); * reconnect_to - Timeout before trying to reconnect after disconnection. * connect_cb - Client callback to monitor connection state (must not be NULL). * client_opaque - An opaque pointer to associate with the socket client. + * looper - An optional (can be NULL) I/O looper to use for socket I/O. If + * this parameter is NULL, the socket will create its own looper. * Return: * Initialized AsyncSocket instance on success, or NULL on failure. */ extern AsyncSocket* async_socket_new(int port, int reconnect_to, on_as_connection_cb connect_cb, - void* client_opaque); + void* client_opaque, + Looper* looper); /* References AsyncSocket object. * Param: diff --git a/android/sdk-controller-socket.c b/android/sdk-controller-socket.c new file mode 100644 index 0000000..a300169 --- /dev/null +++ b/android/sdk-controller-socket.c @@ -0,0 +1,1691 @@ +/* + * Copyright (C) 2012 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 "qemu-common.h" +#include "android/async-utils.h" +#include "android/utils/debug.h" +#include "android/async-socket-connector.h" +#include "android/async-socket.h" +#include "android/sdk-controller-socket.h" +#include "utils/panic.h" +#include "iolooper.h" + +#define E(...) derror(__VA_ARGS__) +#define W(...) dwarning(__VA_ARGS__) +#define D(...) VERBOSE_PRINT(sdkctlsocket,__VA_ARGS__) +#define D_ACTIVE VERBOSE_CHECK(sdkctlsocket) + +#define TRACE_ON 1 + +#if TRACE_ON +#define T(...) VERBOSE_PRINT(sdkctlsocket,__VA_ARGS__) +#else +#define T(...) +#endif + +/* Recycling memory descriptor. */ +typedef struct SDKCtlRecycled SDKCtlRecycled; +struct SDKCtlRecycled { + union { + /* Next recycled descriptor (while listed in recycler). */ + SDKCtlRecycled* next; + /* Allocated memory size (while outside of the recycler). */ + uint32_t size; + }; +}; + +/******************************************************************************** + * SDKCtlPacket declarations + *******************************************************************************/ + +/* + * Types of the packets of data sent via SDK controller socket. + */ + +/* The packet is a message. */ +#define SDKCTL_PACKET_MESSAGE 1 +/* The packet is a query. */ +#define SDKCTL_PACKET_QUERY 2 +/* The packet is a response to a query. */ +#define SDKCTL_PACKET_QUERY_RESPONSE 3 + +/* 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 { + /* Total size of the data to transfer with this packet, including this + * header. The transferring data should immediatelly follow this header. */ + int size; + /* Encodes packet type. See SDKCTL_PACKET_XXX for the list of packet types + * used by SDK controller. */ + int type; +} SDKCtlPacketHeader; + +/* Packet descriptor, allocated by this API for data packets to be sent to SDK + * controller service on the device. + * + * 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 { + /* Supports recycling. Don't put anything in front: recycler expects this + * to be the first field in recyclable descriptor. */ + SDKCtlRecycled recycling; + + /* Next packet in the list of packets to send. */ + SDKCtlPacket* next; + /* SDK controller socket that transmits this packet. */ + SDKCtlSocket* sdkctl; + /* Number of outstanding references to the packet. */ + int ref_count; + + /* Common packet header. Packet data immediately follows this header, so it + * must be last field in SDKCtlPacket descriptor. */ + SDKCtlPacketHeader header; +}; + +/******************************************************************************** + * SDKCtlQuery declarations + *******************************************************************************/ + +/* + * Types of 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 + +/* Query packet descriptor. + * + * All queries, sent and received via SDK controller socket begin with this + * header, with query data immediately following this header. + */ +typedef struct SDKCtlQueryHeader { + /* Data packet header for this query. */ + SDKCtlPacketHeader packet; + /* 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. */ + int query_type; +} SDKCtlQueryHeader; + +/* Query descriptor, allocated by this API for queries to be sent to SDK + * controller service on the device. + * + * When query descriptors are allocated by this API, they are allocated large + * enough to contain this header, and query data to send to the service, + * immediately following this descriptor. + */ +struct SDKCtlQuery { + /* Supports recycling. Don't put anything in front: recycler expects this + * to be the first field in recyclable descriptor. */ + SDKCtlRecycled recycling; + + /* Next query in the list of active, or recycled 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. */ + Duration deadline; + /* SDK controller socket that owns the query. */ + SDKCtlSocket* sdkctl; + /* A callback to invoke on query state changes. */ + on_sdkctl_query_cb query_cb; + /* An opaque pointer associated with this query. */ + 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). */ + 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. */ + 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 + * must be last field in SDKCtlQuery descriptor. */ + SDKCtlQueryHeader header; +}; + +/* Query reply descriptor. + * + * All replies to a query, sent and received via SDK controller socket begin with + * this header, with query reply data immediately following this header. + */ +typedef struct SDKCtlQueryReplyHeader { + /* Data packet header for this reply. */ + SDKCtlPacketHeader packet; + + /* An identifier for the query that is addressed with this reply. */ + int query_id; +} SDKCtlQueryReplyHeader; + +/******************************************************************************** + * SDK Control Socket declarations + *******************************************************************************/ + +/* Enumerates SDKCtlSocket states. */ +typedef enum SDKCtlSocketState { + /* Socket is disconnected from SDK controller. */ + SDKCTL_SOCKET_DISCONNECTED, + /* Connection to SDK controller is in progress. */ + SDKCTL_SOCKET_CONNECTING, + /* Socket is connected to an SDK controller service. */ + SDKCTL_SOCKET_CONNECTED +} SDKCtlSocketState; + +/* Enumerates SDKCtlSocket I/O dispatcher states. */ +typedef enum SDKCtlIODispatcherState { + /* I/O dispatcher expects a packet header. */ + SDKCTL_IODISP_EXPECT_HEADER, + /* I/O dispatcher expects packet data. */ + SDKCTL_IODISP_EXPECT_DATA, + /* I/O dispatcher expects query response header. */ + SDKCTL_IODISP_EXPECT_QUERY_REPLY_HEADER, + /* I/O dispatcher expects query response data. */ + SDKCTL_IODISP_EXPECT_QUERY_REPLY_DATA, +} SDKCtlIODispatcherState; + +/* SDKCtlSocket I/O dispatcher descriptor. */ +typedef struct SDKCtlIODispatcher { + /* SDKCtlSocket instance for this dispatcher. */ + SDKCtlSocket* sdkctl; + /* Dispatcher state. */ + SDKCtlIODispatcherState state; + /* Unites all types of headers used in SDK controller data exchange. */ + union { + /* Common packet header. */ + SDKCtlPacketHeader header; + /* Header for a query packet. */ + SDKCtlQueryHeader query_header; + /* Header for a query response packet. */ + SDKCtlQueryReplyHeader query_reply_header; + }; + /* Descriptor of a packet packet received from SDK controller. */ + SDKCtlPacket* packet; + /* A query for which a reply is currently being received. */ + SDKCtlQuery* current_query; +} SDKCtlIODispatcher; + +/* SDK controller socket descriptor. */ +struct SDKCtlSocket { + /* SDK controller socket state */ + SDKCtlSocketState state; + /* I/O dispatcher for the socket. */ + SDKCtlIODispatcher io_dispatcher; + /* Asynchronous socket connected to SDK Controller on the device. */ + 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; + /* A callback to invoke when a message is received from the SDK controller. */ + 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; + /* I/O looper for timers. */ + Looper* looper; + /* Head of the active query list. */ + SDKCtlQuery* query_head; + /* Tail of the active query list. */ + SDKCtlQuery* query_tail; + /* Query ID generator that gets incremented for each new query. */ + int next_query_id; + /* Timeout before trying to reconnect after disconnection. */ + int reconnect_to; + /* Number of outstanding references to this descriptor. */ + int ref_count; + /* Head of the recycled memory */ + SDKCtlRecycled* recycler; + /* Recyclable block size. */ + uint32_t recycler_block_size; + /* Maximum number of blocks to recycle. */ + int recycler_max; + /* Number of blocs in the recycler. */ + int recycler_count; +}; + +/******************************************************************************** + * SDKCtlSocket recycling management + *******************************************************************************/ + +/* Gets a recycled block for a given SDKCtlSocket, or allocates new memory + * block. */ +static void* +_sdkctl_socket_alloc_recycler(SDKCtlSocket* sdkctl, uint32_t size) +{ + SDKCtlRecycled* block = NULL; + + if (sdkctl->recycler != NULL && size <= sdkctl->recycler_block_size) { + /* 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. */ + block = malloc(sdkctl->recycler_block_size); + if (block == NULL) { + APANIC("SDKCtl %s: Unable to allocate %d bytes block", + sdkctl->service_name, sdkctl->recycler_block_size); + } + block->size = sdkctl->recycler_block_size; + } else { + /* Requested size doesn't fit the recycler. */ + block = malloc(size); + if (block == NULL) { + APANIC("SDKCtl %s: Unable to allocate %d bytes block", + sdkctl->service_name, size); + } + block->size = size; + } + + return block; +} + +/* Recycles, or frees a block of memory for a given SDKCtlSocket. */ +static void +_sdkctl_socket_free_recycler(SDKCtlSocket* sdkctl, void* mem) +{ + SDKCtlRecycled* block = (SDKCtlRecycled*)mem; + + if (sdkctl->recycler_count == sdkctl->recycler_max || + block->size != sdkctl->recycler_block_size) { + /* Recycler is full, or block cannot be recycled. */ + free(mem); + return; + } + + block->next = sdkctl->recycler; + sdkctl->recycler = block; + sdkctl->recycler_count++; +} + +/* Empties the recycler for a given SDKCtlSocket. */ +static void +_sdkctl_socket_empty_recycler(SDKCtlSocket* sdkctl) +{ + SDKCtlRecycled* block = sdkctl->recycler; + while (block != NULL) { + void* to_free = block; + block = block->next; + free(to_free); + } + sdkctl->recycler = NULL; + sdkctl->recycler_count = 0; +} + +/******************************************************************************** + * SDKCtlSocket query list management + *******************************************************************************/ + +/* Adds a query to the list of active queries. + * Param: + * sdkctl - SDKCtlSocket instance for the query. + * query - Query to add to the list. + */ +static void +_sdkctl_socket_add_query(SDKCtlQuery* query) +{ + SDKCtlSocket* const sdkctl = query->sdkctl; + if (sdkctl->query_head == NULL) { + sdkctl->query_head = sdkctl->query_tail = query; + } else { + sdkctl->query_tail->next = query; + sdkctl->query_tail = query; + } + + /* Keep the query referenced while it's in the list. */ + sdkctl_query_reference(query); +} + +/* Removes a query from the list of active queries. + * Param: + * query - Query to remove from the list of active queries. + * Return: + * Boolean: 1 if query has been removed, or 0 if query has not been found in the + * list of active queries. + */ +static int +_sdkctl_socket_remove_query(SDKCtlQuery* query) +{ + SDKCtlSocket* const sdkctl = query->sdkctl; + SDKCtlQuery* prev = NULL; + SDKCtlQuery* head = sdkctl->query_head; + + /* Quick check: the query could be currently handled by dispatcher. */ + if (sdkctl->io_dispatcher.current_query == query) { + /* Release the query from dispatcher. */ + sdkctl_query_release(query); + sdkctl->io_dispatcher.current_query = NULL; + return 1; + } + + /* Remove query from the list. */ + while (head != NULL && query != head) { + 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; + + /* 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; + } else { + D("%s: Query %p is not found in the list.", sdkctl->service_name, query); + return 0; + } +} + +/* Removes a query (based on query ID) from the list of active queries. + * Param: + * sdkctl - SDKCtlSocket instance that owns the 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. + */ +static SDKCtlQuery* +_sdkctl_socket_remove_query_id(SDKCtlSocket* sdkctl, int query_id) +{ + SDKCtlQuery* prev = NULL; + SDKCtlQuery* head = sdkctl->query_head; + + /* Quick check: the query could be currently handled by dispatcher. */ + 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; + sdkctl->io_dispatcher.current_query = NULL; + return query; + } + + /* Remove query from the list. */ + while (head != NULL && head->header.query_id != 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.", + sdkctl->service_name, query_id); + return NULL; + } +} + +/* 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 + * list is empty. + */ +static SDKCtlQuery* +_sdkctl_socket_pull_first_query(SDKCtlSocket* sdkctl) +{ + SDKCtlQuery* const query = sdkctl->query_head; + + if (query != NULL) { + sdkctl->query_head = query->next; + if (sdkctl->query_head == NULL) { + sdkctl->query_tail = NULL; + } + } + return query; +} + +/* Generates new query ID for the given SDKCtl. */ +static int +_sdkctl_socket_next_query_id(SDKCtlSocket* sdkctl) +{ + return ++sdkctl->next_query_id; +} + +/******************************************************************************** + * SDKCtlPacket implementation + *******************************************************************************/ + +/* Alocates a packet. */ +static SDKCtlPacket* +_sdkctl_packet_new(SDKCtlSocket* sdkctl, int size, int type) +{ + const uint32_t total_size = sizeof(SDKCtlPacket) + size; + SDKCtlPacket* const packet = _sdkctl_socket_alloc_recycler(sdkctl, total_size); + + packet->sdkctl = sdkctl; + packet->ref_count = 1; + packet->header.size = size; + packet->header.type = type; + + /* Refence SDKCTlSocket that owns this packet. */ + sdkctl_socket_reference(sdkctl); + + return packet; +} + +/* Frees a packet. */ +static void +_sdkctl_packet_free(SDKCtlPacket* packet) +{ + SDKCtlSocket* const sdkctl = packet->sdkctl; + + /* Free allocated resources. */ + _sdkctl_socket_free_recycler(packet->sdkctl, packet); + + /* Release SDKCTlSocket that owned this packet. */ + sdkctl_socket_release(sdkctl); +} + +int +sdkctl_packet_reference(SDKCtlPacket* packet) +{ + assert(packet->ref_count > 0); + packet->ref_count++; + return packet->ref_count; +} + +int +sdkctl_packet_release(SDKCtlPacket* packet) +{ + assert(packet->ref_count > 0); + packet->ref_count--; + if (packet->ref_count == 0) { + /* Last reference has been dropped. Destroy this object. */ + _sdkctl_packet_free(packet); + return 0; + } + return packet->ref_count; +} + +/******************************************************************************** + * SDKCtlQuery implementation + *******************************************************************************/ + +/* Frees query descriptor. */ +static void +_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)) { + free(query->internal_resp_buffer); + } + + loopTimer_done(query->timer); + _sdkctl_socket_free_recycler(sdkctl, query); + + /* Release socket that owned this query. */ + sdkctl_socket_release(sdkctl); + } +} + +/* Cancels timeout for the query. + * + * For the simplicity of implementation, the dispatcher will cancel query timer + * when query response data begins to flow in. If we let the timer to expire at + * that stage, we will end up with data flowing in without real place to + * accomodate it. + */ +static void +_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); +} + +/* + * Query I/O callbacks. + */ + +/* Callback that is invoked by the I/O dispatcher when query is successfuly + * completed (i.e. response to the query is received). + */ +static void +_on_sdkctl_query_completed(SDKCtlQuery* query) +{ + T("SDKCtl %s: Query %p ID %d is completed.", + query->sdkctl->service_name, query, query->header.query_id); + + /* Cancel deadline, and inform the client about query completion. */ + _sdkctl_query_cancel_timeout(query); + query->query_cb(query->query_opaque, query, ASIO_STATE_SUCCEEDED); +} + +/* A callback that is invoked on query cancellation. */ +static void +_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. + */ + + /* Cancel deadline, and inform the client about query cancellation. */ + _sdkctl_query_cancel_timeout(query); + query->query_cb(query->query_opaque, query, ASIO_STATE_CANCELLED); +} + +/* A timer callback that is invoked on query timeout. + * Param: + * opaque - SDKCtlQuery instance. + */ +static void +_on_skdctl_query_timeout(void* opaque) +{ + SDKCtlQuery* const query = (SDKCtlQuery*)opaque; + + D("SDKCtl %s: Query %p ID %d with deadline %lld has timed out at %lld", + query->sdkctl->service_name, query, query->header.query_id, + query->deadline, async_socket_deadline(query->sdkctl->as, 0)); + + /* Reference the query while we're in this callback. */ + sdkctl_query_reference(query); + + /* Inform the client about deadline expiration. Note that client may + * extend the deadline, and retry the query. */ + const AsyncIOAction action = + query->query_cb(query->query_opaque, query, ASIO_STATE_TIMED_OUT); + + /* For actions other than retry we will destroy the query. */ + if (action != ASIO_ACTION_RETRY) { + _sdkctl_socket_remove_query(query); + } + + sdkctl_query_release(query); +} + +/* A callback that is invoked when query has been sent to the SDK controller + * service. */ +static void +_on_sdkctl_query_sent(SDKCtlQuery* query) +{ + 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); + + /* Inform the client about the event. */ + query->query_cb(query->query_opaque, query, ASIO_STATE_CONTINUES); + + /* Set a timer to expire at query's deadline, and let the response to come + * through the dispatcher loop. */ + loopTimer_startAbsolute(query->timer, query->deadline); +} + +/* An I/O callback invoked on query transmission. + * Param: + * io_opaque SDKCtlQuery instance of the query that's being sent with this I/O. + * asio - Write I/O descriptor. + * status - I/O status. + */ +static AsyncIOAction +_on_sdkctl_query_send_io(void* io_opaque, + AsyncSocketIO* asio, + AsyncIOState status) +{ + SDKCtlQuery* const query = (SDKCtlQuery*)io_opaque; + AsyncIOAction action = ASIO_ACTION_DONE; + + /* 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_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"); + /* 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", + 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)); + /* Invoke query's callback. */ + action = query->query_cb(query->query_opaque, query, status); + /* For actions other than retry we need to stop the query. */ + if (action != ASIO_ACTION_RETRY) { + _sdkctl_socket_remove_query(query); + } + break; + + case ASIO_STATE_FAILED: + T("SDKCtl %s: Query %p ID %d failed in %s I/O: %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. */ + action = query->query_cb(query->query_opaque, query, status); + /* For actions other than retry we need to stop the query. */ + if (action != ASIO_ACTION_RETRY) { + _sdkctl_socket_remove_query(query); + } + break; + + case ASIO_STATE_FINISHED: + /* Time to disassociate with the I/O. */ + sdkctl_query_release(query); + break; + + default: + /* Transitional state. */ + break; + } + + sdkctl_query_release(query); + + return action; +} + +/******************************************************************************** + * SDKCtlQuery public API implementation + ********************************************************************************/ + +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; + + /* Initialize timer to fire up on query deadline expiration. */ + loopTimer_init(query->timer, sdkctl->looper, _on_skdctl_query_timeout, query); + + /* Reference socket that owns this query. */ + sdkctl_socket_reference(sdkctl); + + T("SDKCtl %s: Query %p ID %d type %d is created for %d bytes of data.", + query->sdkctl->service_name, query, query->header.query_id, + query_type, in_data_size); + + return query; +} + +SDKCtlQuery* +sdkctl_query_new_ex(SDKCtlSocket* sdkctl, + int query_type, + uint32_t in_data_size, + const void* in_data, + void** response_buffer, + uint32_t* response_size, + on_sdkctl_query_cb query_cb, + void* query_opaque) +{ + SDKCtlQuery* const query = sdkctl_query_new(sdkctl, query_type, in_data_size); + + 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; + 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; + /* Init query's input buffer. */ + if (in_data_size != 0 && in_data != NULL) { + memcpy(query + 1, in_data, in_data_size); + } + + return query; +} + +void +sdkctl_query_send(SDKCtlQuery* query, int to) +{ + SDKCtlSocket* const sdkctl = query->sdkctl; + + /* Initialize the deadline. */ + query->deadline = async_socket_deadline(query->sdkctl->as, to); + + /* List the query in the list of active queries. */ + _sdkctl_socket_add_query(query); + + /* Reference query associated with write I/O. */ + sdkctl_query_reference(query); + + /* 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", + query->sdkctl->service_name, query, query->header.query_id, + query->header.query_type, query->deadline); +} + +SDKCtlQuery* +sdkctl_query_build_and_send(SDKCtlSocket* sdkctl, + int query_type, + uint32_t in_data_size, + const void* in_data, + void** response_buffer, + uint32_t* response_size, + on_sdkctl_query_cb query_cb, + void* query_opaque, + int to) +{ + SDKCtlQuery* const query = + sdkctl_query_new_ex(sdkctl, query_type, in_data_size, in_data, + response_buffer, response_size, query_cb, + query_opaque); + sdkctl_query_send(query, to); + return query; +} + +int +sdkctl_query_reference(SDKCtlQuery* query) +{ + assert(query->ref_count > 0); + query->ref_count++; + return query->ref_count; +} + +int +sdkctl_query_release(SDKCtlQuery* query) +{ + assert(query->ref_count > 0); + query->ref_count--; + if (query->ref_count == 0) { + /* Last reference has been dropped. Destroy this object. */ + _sdkctl_query_free(query); + return 0; + } + return query->ref_count; +} + +/******************************************************************************** + * SDKCtlPacket implementation + *******************************************************************************/ + +/* A packet has been received from SDK controller. */ +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)); +} + +/******************************************************************************** + * SDKCtlIODispatcher implementation + *******************************************************************************/ + +/* An I/O callback invoked when data gets received from the socket. + * Param: + * io_opaque SDKCtlIODispatcher instance associated with the reader. + * asio - Read I/O descriptor. + * status - I/O status. + */ +static AsyncIOAction _on_sdkctl_io_dispatcher_io(void* io_opaque, + AsyncSocketIO* asio, + AsyncIOState status); + +/* Starts I/O dispatcher for SDK controller socket. */ +static void +_sdkctl_io_dispatcher_start(SDKCtlSocket* sdkctl) { + SDKCtlIODispatcher* const dispatcher = &sdkctl->io_dispatcher; + + dispatcher->state = SDKCTL_IODISP_EXPECT_HEADER; + dispatcher->sdkctl = sdkctl; + dispatcher->packet = NULL; + dispatcher->current_query = NULL; + + /* Register a packet header reader with the socket. */ + async_socket_read_rel(dispatcher->sdkctl->as, &dispatcher->header, + sizeof(SDKCtlPacketHeader), _on_sdkctl_io_dispatcher_io, + dispatcher, -1); +} + +/* Resets I/O dispatcher for SDK controller socket. */ +static void +_sdkctl_io_dispatcher_reset(SDKCtlSocket* sdkctl) { + SDKCtlIODispatcher* const dispatcher = &sdkctl->io_dispatcher; + + /* Cancel current query. */ + if (dispatcher->current_query != NULL) { + SDKCtlQuery* const query = dispatcher->current_query; + dispatcher->current_query = NULL; + _on_sdkctl_query_cancelled(query); + sdkctl_query_release(query); + } + + /* Free packet data buffer. */ + if (dispatcher->packet != NULL) { + sdkctl_packet_release(dispatcher->packet); + dispatcher->packet = NULL; + } + + /* Reset dispatcher state. */ + dispatcher->state = SDKCTL_IODISP_EXPECT_HEADER; +} + +/* + * I/O dispatcher callbacks. + */ + +/* A callback that is invoked when a failure occurred while dispatcher was + * reading data from the socket. + */ +static void +_on_io_dispatcher_io_failure(SDKCtlIODispatcher* dispatcher, + AsyncSocketIO* asio) +{ + SDKCtlSocket* const sdkctl = dispatcher->sdkctl; + + D("SDKCtl %s: Dispatcher I/O failure: %d -> %s", + sdkctl->service_name, errno, strerror(errno)); + + /* We treat all I/O failures same way we treat disconnection. Just cancel + * everything, disconnect, and let the client to decide what to do next. */ + sdkctl_socket_disconnect(sdkctl); + + /* Report disconnection to the client, and let it restore connection in this + * callback. */ + sdkctl->on_connection(sdkctl->opaque, sdkctl, ASIO_STATE_FAILED); +} + +/* A callback that is invoked when dispatcher's reader has been cancelled. */ +static void +_on_io_dispatcher_io_cancelled(SDKCtlIODispatcher* dispatcher, + AsyncSocketIO* asio) +{ + T("SDKCtl %s: Dispatcher I/O cancelled.", dispatcher->sdkctl->service_name); + + /* If we're in the middle of receiving query reply we need to cancel the + * query. */ + if (dispatcher->current_query != NULL) { + SDKCtlQuery* const query = dispatcher->current_query; + dispatcher->current_query = NULL; + _on_sdkctl_query_cancelled(query); + sdkctl_query_release(query); + } + + /* Discard packet data we've received so far. */ + if (dispatcher->packet != NULL) { + sdkctl_packet_release(dispatcher->packet); + dispatcher->packet = NULL; + } +} + +/* A generic packet header has been received by I/O dispatcher. */ +static AsyncIOAction +_on_io_dispatcher_packet_header(SDKCtlIODispatcher* dispatcher, + AsyncSocketIO* asio) +{ + 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); + + /* Here we have three choices for the packet, that define the rest of + * the data that follow it: + * - Regular packet, + * - Response to a query that has been sent to SDK controller, + * - A query from SDK controller. + * Update the state accordingly, and initiate reading of the + * remaining of the packet. + */ + if (dispatcher->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 + * reading of the remaining part of SDKCtlQueryReplyHeader. */ + dispatcher->state = SDKCTL_IODISP_EXPECT_QUERY_REPLY_HEADER; + async_socket_read_rel(sdkctl->as, &dispatcher->query_reply_header.query_id, + sizeof(SDKCtlQueryReplyHeader) - sizeof(SDKCtlPacketHeader), + _on_sdkctl_io_dispatcher_io, dispatcher, -1); + } else { + /* For regular packets, as well as queries, we simply allocate buffer, + * that fits the entire packet, and read the remainder of the data in + * there. */ + dispatcher->state = SDKCTL_IODISP_EXPECT_DATA; + dispatcher->packet = + _sdkctl_packet_new(sdkctl, dispatcher->header.size, + dispatcher->header.type); + /* Initiate reading of the packet data. */ + async_socket_read_rel(sdkctl->as, dispatcher->packet + 1, + dispatcher->header.size - sizeof(SDKCtlPacketHeader), + _on_sdkctl_io_dispatcher_io, dispatcher, -1); + } + + return ASIO_ACTION_DONE; +} + +/* A generic packet has been received by I/O dispatcher. */ +static AsyncIOAction +_on_io_dispatcher_packet(SDKCtlIODispatcher* dispatcher, AsyncSocketIO* asio) +{ + SDKCtlSocket* const sdkctl = dispatcher->sdkctl; + + T("SDKCtl %s: Packet type %d, size %d is received.", + dispatcher->sdkctl->service_name, dispatcher->header.type, + dispatcher->header.size); + + _on_sdkctl_packet_received(sdkctl, 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), + _on_sdkctl_io_dispatcher_io, dispatcher, -1); + return ASIO_ACTION_DONE; +} + +/* A query reply header has been received by I/O dispatcher. */ +static AsyncIOAction +_on_io_dispatcher_query_reply_header(SDKCtlIODispatcher* dispatcher, + AsyncSocketIO* asio) +{ + SDKCtlSocket* const sdkctl = dispatcher->sdkctl; + SDKCtlQuery* query; + + T("SDKCtl %s: Query reply header is received for query ID %d", + dispatcher->sdkctl->service_name, dispatcher->query_reply_header.query_id); + + /* Pull the query out of the list of active queries. It's the dispatcher that + * owns this query now. */ + dispatcher->current_query = + _sdkctl_socket_remove_query_id(sdkctl, dispatcher->query_reply_header.query_id); + query = dispatcher->current_query; + + if (query == NULL) { + D("%s: Query #%d is not found by dispatcher", + dispatcher->sdkctl->service_name, dispatcher->query_reply_header.query_id); + + /* Query is not found. Just read the remainder of reply up in the air, + * 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); + /* 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), + _on_sdkctl_io_dispatcher_io, dispatcher, -1); + } else { + /* Prepare to receive query reply. For the simplicity sake, cancel query + * time out, so it doesn't expire on us while we're in the middle of + * 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) { + APANIC("%s: Unable to allocate %d bytes for query response", + sdkctl->service_name, query_data_size); + } + } + /* Save the actual query response size. */ + *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); + } + + return ASIO_ACTION_DONE; +} + +/* A query reply header has been received by I/O dispatcher. */ +static AsyncIOAction +_on_io_dispatcher_query_reply(SDKCtlIODispatcher* dispatcher, AsyncSocketIO* asio) +{ + SDKCtlSocket* const sdkctl = dispatcher->sdkctl; + SDKCtlQuery* const query = dispatcher->current_query; + dispatcher->current_query = NULL; + + if (query != NULL) { + _ANDROID_ASSERT(query->header.query_id == dispatcher->query_reply_header.query_id, + "SDKCtl %s: Query ID mismatch in I/O dispatcher", + sdkctl->service_name); + T("SDKCtl %s: Query reply is received for query %p ID %d. Reply size is %d", + dispatcher->sdkctl->service_name, query, query->header.query_id, + *query->response_size); + + /* Complete the query, and release it from the dispatcher. */ + _on_sdkctl_query_completed(query); + sdkctl_query_release(query); + } else { + /* 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); + 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), + _on_sdkctl_io_dispatcher_io, dispatcher, -1); + return ASIO_ACTION_DONE; +} + +/* An I/O callback invoked when data gets received from the socket. + * This is main I/O dispatcher loop. + * Param: + * io_opaque SDKCtlIODispatcher instance associated with the reader. + * asio - Read I/O descriptor. + * status - I/O status. + */ +static AsyncIOAction +_on_sdkctl_io_dispatcher_io(void* io_opaque, + AsyncSocketIO* asio, + AsyncIOState status) +{ + AsyncIOAction action = ASIO_ACTION_DONE; + SDKCtlIODispatcher* const dispatcher = (SDKCtlIODispatcher*)io_opaque; + SDKCtlSocket* const sdkctl = dispatcher->sdkctl; + + /* Reference SDKCtlSocket while we're in this callback. */ + sdkctl_socket_reference(sdkctl); + + if (status != ASIO_STATE_SUCCEEDED) { + /* Something going on with I/O other than receiving data.. */ + switch (status) { + case ASIO_STATE_STARTED: + /* Data has started flowing in. Cancel timeout on I/O that has + * started, so we can complete the current state of the + * dispatcher without interruptions other than I/O failures. */ + async_socket_io_cancel_time_out(asio); + break; + + case ASIO_STATE_FAILED: + /* I/O failure has occurred. Handle the failure. */ + _on_io_dispatcher_io_failure(dispatcher, asio); + break; + + case ASIO_STATE_TIMED_OUT: + /* The way I/O dispatcher is implemented, this should never + * happen, because dispatcher doesn't set I/O expiration time + * when registering its readers. */ + _ANDROID_ASSERT(0, + "SDKCtl %s: We should never receive ASIO_STATE_TIMED_OUT in SDKCtl I/O dispatcher.", + sdkctl->service_name); + break; + + case ASIO_STATE_CANCELLED: + /* Cancellation means that we're in the middle of handling + * disconnection. Sooner or later, this dispatcher will be reset, + * so we don't really care about keeping its state at this point. + */ + _on_io_dispatcher_io_cancelled(dispatcher, asio); + break; + + case ASIO_STATE_FINISHED: + break; + + default: + _ANDROID_ASSERT(0, "SDKCtl %s: Unexpected I/O status %d in the dispatcher", + sdkctl->service_name, status); + /* Handle this as protocol failure. */ + errno = EINVAL; + _on_io_dispatcher_io_failure(dispatcher, asio); + action = ASIO_ACTION_ABORT; + break; + } + + sdkctl_socket_release(sdkctl); + + return action; + } + + /* Requested data has been read. Handle the chunk depending on dispatcher's + * state. */ + switch (dispatcher->state) { + case SDKCTL_IODISP_EXPECT_HEADER: + /* A generic packet header is received. */ + action = _on_io_dispatcher_packet_header(dispatcher, asio); + break; + + case SDKCTL_IODISP_EXPECT_QUERY_REPLY_HEADER: + /* Query reply header is received. */ + action = _on_io_dispatcher_query_reply_header(dispatcher, asio); + break; + + case SDKCTL_IODISP_EXPECT_QUERY_REPLY_DATA: + /* Query reply is received. Complete the query. */ + action = _on_io_dispatcher_query_reply(dispatcher, asio); + break; + + case SDKCTL_IODISP_EXPECT_DATA: + /* A generic packet is received. */ + action = _on_io_dispatcher_packet(dispatcher, asio); + break; + + default: + _ANDROID_ASSERT(0, "SDKCtl %s: Unexpected I/O dispacher state %d", + sdkctl->service_name, dispatcher->state); + break; + } + + sdkctl_socket_release(sdkctl); + + return action; +} + +/******************************************************************************** + * SDKCtlSocket internals. + *******************************************************************************/ + +/* Cancels all queries that is active on this socket. */ +static void +_sdkctl_socket_cancel_all_queries(SDKCtlSocket* sdkctl) +{ + SDKCtlIODispatcher* const dispatcher = &sdkctl->io_dispatcher; + SDKCtlQuery* query; + + /* Cancel query that is being completed in dispatcher. */ + if (dispatcher->current_query != NULL) { + SDKCtlQuery* const query = dispatcher->current_query; + dispatcher->current_query = NULL; + _on_sdkctl_query_cancelled(query); + sdkctl_query_release(query); + } + + /* One by one empty query list cancelling pulled queries. */ + query = _sdkctl_socket_pull_first_query(sdkctl); + while (query != NULL) { + _sdkctl_query_cancel_timeout(query); + query->query_cb(query->query_opaque, query, ASIO_STATE_CANCELLED); + sdkctl_query_release(query); + query = _sdkctl_socket_pull_first_query(sdkctl); + } +} + +/* Cancels all packets that is active on this socket. */ +static void +_sdkctl_socket_cancel_all_packets(SDKCtlSocket* sdkctl) +{ +} + +/* Cancels all I/O that is active on this socket. */ +static void +_sdkctl_socket_cancel_all_io(SDKCtlSocket* sdkctl) +{ + /* Cancel all queries, and packets that are active for this I/O. */ + _sdkctl_socket_cancel_all_queries(sdkctl); + _sdkctl_socket_cancel_all_packets(sdkctl); +} + +/* Disconnects AsyncSocket for SDKCtlSocket. */ +static void +_sdkctl_socket_disconnect_socket(SDKCtlSocket* sdkctl) +{ + if (sdkctl->as != NULL) { + /* Disconnect the socket. This will trigger I/O cancellation callbacks. */ + async_socket_disconnect(sdkctl->as); + + /* Cancel all I/O that is active on this socket. */ + _sdkctl_socket_cancel_all_io(sdkctl); + + /* Reset I/O dispatcher. */ + _sdkctl_io_dispatcher_reset(sdkctl); + } + + sdkctl->state = SDKCTL_SOCKET_DISCONNECTED; +} + +/* Frees SDKCtlSocket instance. */ +static void +_sdkctl_socket_free(SDKCtlSocket* sdkctl) +{ + if (sdkctl != NULL) { + /* Disconnect, and release the socket. */ + if (sdkctl->as != NULL) { + async_socket_disconnect(sdkctl->as); + async_socket_release(sdkctl->as); + } + + /* Free allocated resources. */ + if (sdkctl->looper != NULL) { + looper_free(sdkctl->looper); + } + if (sdkctl->service_name != NULL) { + free(sdkctl->service_name); + } + _sdkctl_socket_empty_recycler(sdkctl); + + AFREE(sdkctl); + } +} + +/******************************************************************************** + * SDK Control Socket connection callbacks. + *******************************************************************************/ + +/* Initiates handshake query when SDK controller socket is connected. */ +static void _sdkctl_do_handshake(SDKCtlSocket* sdkctl); + +/* A socket connection is established. + * Here we will start I/O dispatcher, and will initiate a handshake with + * the SdkController service for this socket. */ +static AsyncIOAction +_on_async_socket_connected(SDKCtlSocket* sdkctl) +{ + D("SDKCtl %s: Socket is connected.", sdkctl->service_name); + + /* Notify the client that connection is established. */ + const AsyncIOAction action = + sdkctl->on_connection(sdkctl->opaque, sdkctl, ASIO_STATE_SUCCEEDED); + + if (action == ASIO_ACTION_DONE) { + /* Initialize, and start main I/O dispatcher. */ + _sdkctl_io_dispatcher_start(sdkctl); + + /* Initiate handshake. */ + _sdkctl_do_handshake(sdkctl); + + return action; + } else { + /* Client didn't like something about this connection. */ + return action; + } +} + +/* Handles lost connection with SdkController service. */ +static AsyncIOAction +_on_async_socket_disconnected(SDKCtlSocket* sdkctl) +{ + D("SDKCtl %s: Socket has been disconnected.", sdkctl->service_name); + + _sdkctl_socket_disconnect_socket(sdkctl); + + AsyncIOAction action = sdkctl->on_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; + } + if (action == ASIO_ACTION_RETRY) { + sdkctl->state = SDKCTL_SOCKET_CONNECTING; + } + return action; +} + +/* An entry point for all socket connection events. + * Here we will dispatch connection events to appropriate handlers. + * Param: + * client_opaque - SDKCtlSocket isntance. + */ +static AsyncIOAction +_on_async_socket_connection(void* client_opaque, + AsyncSocket* as, + AsyncIOState status) +{ + AsyncIOAction action = ASIO_ACTION_DONE; + SDKCtlSocket* const sdkctl = (SDKCtlSocket*)client_opaque; + + /* Reference the socket while in this callback. */ + sdkctl_socket_reference(sdkctl); + + switch (status) { + case ASIO_STATE_SUCCEEDED: + sdkctl->state = SDKCTL_SOCKET_CONNECTED; + _on_async_socket_connected(sdkctl); + break; + + case ASIO_STATE_FAILED: + if (sdkctl->state == SDKCTL_SOCKET_CONNECTED) { + /* This is disconnection condition. */ + action = _on_async_socket_disconnected(sdkctl); + } else { + /* An error has occurred while attempting to connect to socket. + * Lets try again... */ + action = ASIO_ACTION_RETRY; + } + break; + + case ASIO_STATE_RETRYING: + default: + action = ASIO_ACTION_RETRY; + break; + } + + sdkctl_socket_release(sdkctl); + + return action; +} + +/******************************************************************************** + * SDK Control Socket public API + *******************************************************************************/ + +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_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->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); + _sdkctl_socket_free(sdkctl); + return NULL; + } + + return sdkctl; +} + +int sdkctl_socket_reference(SDKCtlSocket* sdkctl) +{ + assert(sdkctl->ref_count > 0); + sdkctl->ref_count++; + return sdkctl->ref_count; +} + +int +sdkctl_socket_release(SDKCtlSocket* sdkctl) +{ + assert(sdkctl->ref_count > 0); + sdkctl->ref_count--; + if (sdkctl->ref_count == 0) { + /* Last reference has been dropped. Destroy this object. */ + _sdkctl_socket_free(sdkctl); + return 0; + } + return sdkctl->ref_count; +} + +void +sdkctl_init_recycler(SDKCtlSocket* sdkctl, + uint32_t data_size, + int max_recycled_num) +{ + if (sdkctl->recycler != NULL) { + D("SDKCtl %s: Recycler is already initialized. Ignoring recycler init.", + sdkctl->service_name); + return; + } + + /* SDKCtlQuery is max descriptor sizeof. */ + data_size += sizeof(SDKCtlQuery); + + sdkctl->recycler_block_size = data_size; + sdkctl->recycler_max = max_recycled_num; + sdkctl->recycler_count = 0; +} + +void +sdkctl_socket_connect(SDKCtlSocket* sdkctl, int port, int retry_to) +{ + T("SDKCtl %s: Handling connect request to port %d, retrying in %dms...", + sdkctl->service_name, port, retry_to); + + sdkctl->state = SDKCTL_SOCKET_CONNECTING; + sdkctl->as = async_socket_new(port, sdkctl->reconnect_to, + _on_async_socket_connection, sdkctl, + sdkctl->looper); + 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); + } else { + async_socket_connect(sdkctl->as, retry_to); + } +} + +void +sdkctl_socket_reconnect(SDKCtlSocket* sdkctl, int port, int retry_to) +{ + T("SDKCtl %s: Handling reconnection request to port %d, retrying in %dms...", + sdkctl->service_name, port, retry_to); + + _sdkctl_socket_disconnect_socket(sdkctl); + + if (sdkctl->as == NULL) { + sdkctl_socket_connect(sdkctl, port, retry_to); + } else { + sdkctl->state = SDKCTL_SOCKET_CONNECTING; + async_socket_reconnect(sdkctl->as, retry_to); + } +} + +void +sdkctl_socket_disconnect(SDKCtlSocket* sdkctl) +{ + T("SDKCtl %s: Handling disconnect request.", sdkctl->service_name); + + _sdkctl_socket_disconnect_socket(sdkctl); +} + + +/******************************************************************************** + * Handshake query + *******************************************************************************/ + +/* A callback that is ivoked on handshake I/O events. */ +static AsyncIOAction +_on_handshake_io(void* query_opaque, + SDKCtlQuery* query, + AsyncIOState status) +{ + 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); + + /* Handshake is received. Inform the client. */ + sdkctl->on_handshake(sdkctl->opaque, sdkctl, *query->response_buffer, + *query->response_size, status); + } else { + /* Something is going on with the handshake... */ + switch (status) { + case ASIO_STATE_FAILED: + case ASIO_STATE_TIMED_OUT: + 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); + break; + + default: + break; + } + } + return ASIO_ACTION_DONE; +} + +static AsyncIOAction +_on_sdkctl_endianness_io(void* io_opaque, + AsyncSocketIO* asio, + AsyncIOState status) { + SDKCtlSocket* const sdkctl = (SDKCtlSocket*)io_opaque; + + if (status == ASIO_STATE_SUCCEEDED) { + /* Now it's time to initiate handshake message. */ + D("SDKCtl %s: Sending handshake query...", sdkctl->service_name); + SDKCtlQuery* query = + sdkctl_query_build_and_send(sdkctl, SDKCTL_QUERY_HANDSHAKE, + strlen(sdkctl->service_name), + sdkctl->service_name, NULL, NULL, + _on_handshake_io, sdkctl, 3000); + sdkctl_query_release(query); + return ASIO_ACTION_DONE; + } else { + /* Something is going on with the endianness... */ + switch (status) { + case ASIO_STATE_FAILED: + case ASIO_STATE_TIMED_OUT: + 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); + break; + + default: + break; + } + } + return ASIO_ACTION_DONE; +} + +static void +_sdkctl_do_handshake(SDKCtlSocket* sdkctl) +{ +#ifndef HOST_WORDS_BIGENDIAN +static const char _host_end = 0; +#else +static const char _host_end = 1; +#endif + + 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. */ + async_socket_write_rel(sdkctl->as, &_host_end, 1, + _on_sdkctl_endianness_io, sdkctl, 3000); +} diff --git a/android/sdk-controller-socket.h b/android/sdk-controller-socket.h new file mode 100644 index 0000000..03b12f4 --- /dev/null +++ b/android/sdk-controller-socket.h @@ -0,0 +1,440 @@ +/* + * Copyright (C) 2012 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_SDKCONTROL_SOCKET_H_ +#define ANDROID_SDKCONTROL_SOCKET_H_ + +#include "android/async-socket.h" + +/* + * Contains declaration of an API that encapsulates communication protocol with + * SdkController running on an Android device. + * + * SdkController is used to provide: + * + * - Realistic sensor emulation. + * - Multi-touch emulation. + * - Open for other types of emulation. + * + * The idea behind this type of emulation is such that there is an actual + * Android device that is connected via USB to the host running the emulator. + * On the device there is an SdkController service running that enables + * communication between an Android application that gathers information required + * by the emulator, and transmits that info to the emulator. + * + * SdkController service on the device, and SDKCtlSocket API implemented here + * implement the exchange protocol between an Android application, and emulation + * engine running inside the emulator. + * + * In turn, the exchange protocol is implemented on top of asynchronous socket + * communication (abstracted in AsyncSocket protocol implemented in + * android/async-socket.*). It means that connection, and all data transfer + * (both, in, and out) are completely asynchronous, and results of each operation + * are reported through callbacks. + * + * Essentially, this entire API implements two types of protocols: + * + * - Connection protocol. + * - Data exchange protocol. + * + * 1. Connection protocol. + * + * Connecting to SdkController service on the attached device can be broken down + * into two phases: + * - Connecting to a TCP socket. + * - Sending a "handshake" query to the SdkController. + * + * 1.1. Connecting to the socket. + * + * TCP socket connection with SdkController is enabled by using adb port + * forwarding. SdkController is always listening to a local abstract socket named + * 'android.sdk.controller', so to enable connecting to it from the host, run + * + * adb forward tcp: localabstract: android.sdk.controller + * + * After that TCP socket for the requested port can be used to connect to + * SdkController, and connecting to it is no different than connecting to any + * socket server. Except for one thing: adb port forwarding is implemented in + * such a way, that socket_connect will always succeed, even if there is no + * server listening to that port on the other side of connection. Moreover, + * even socked_send will succeed in this case, so the only way to ensure that + * SdkController in deed is listening is to exchange a handshake with it: + * Fortunatelly, an attempt to read from forwarded TCP port on condition that + * there is no listener on the oher side will fail. + * + * 1.2. Handshake query. + * + * Handshake query is a special type of query that SDKCtlSocket sends to the + * SdkController upon successful socket connection. This query served two + * purposes: + * - Informs the SdkController about host endianness. This information is + * important, because SdkController needs it in order to format its messages + * with proper endianness. + * - Ensures that SdkController is in deed listening on the other side of the + * connected socket. + * + * Connection with SdkController is considered to be successfuly established when + * SdkController responds to the handshake query, thus, completing the connection. + * + * 2. Data exchange protocol. + * + * As it was mentioned above, all data transfer in this API is completely + * asynchronous, and result of each data transfer is reported via callbacks. + * This also complicates destruction of data involved in exchange, since in an + * asynchronous environment it's hard to control the lifespan of an object, its + * owner, and who and when is responsible to free resources allocated for the + * transfer. To address this issue, all the descriptors that this API operates + * with are referenced on use / released after use, and get freed when reference + * counter for them drops to zero, indicating that there is no component that is + * 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 query - A message that require a reply, and + * - A query reply - A message that delivers query reply. + */ + +/* Declares SDK controller socket descriptor. */ +typedef struct SDKCtlSocket SDKCtlSocket; + +/* Declares SDK controller data packet descriptor. */ +typedef struct SDKCtlPacket SDKCtlPacket; + +/* Declares SDK controller query descriptor. */ +typedef struct SDKCtlQuery SDKCtlQuery; + +/* Defines client's callback set to monitor SDK controller socket connection. + * + * SDKCtlSocket will invoke this callback when connection to TCP port is + * established, but before handshake query is processed. The client should use + * on_sdkctl_handshake_cb to receive notification about an operational connection + * with SdkController. + * + * The main purpose of this callback for the client is to monitor connection + * state: in addition to TCP port connection, this callback will be invoked when + * connection with the port is lost. + * + * Param: + * client_opaque - An opaque pointer associated with the client. + * sdkctl - Initialized SDKCtlSocket instance. + * status - Socket connection status. Can be one of these: + * - ASIO_STATE_SUCCEEDED : Socket is connected to the port. + * - ASIO_STATE_FAILED : Connection attempt has failed, or connection with + * the port is lost. + * Return: + * One of the AsyncIOAction values. + */ +typedef AsyncIOAction (*on_sdkctl_connection_cb)(void* client_opaque, + SDKCtlSocket* sdkctl, + AsyncIOState status); + +/* Defines client's callback set to receive handshake reply from the SdkController + * service running on the device. + * + * Successful handshake means that connection between the client and SDK + * controller service has been established. + * + * 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); + +/* Defines a message notification callback. + * Param: + * client_opaque - An opaque pointer associated with the client. + * sdkctl - Initialized SDKCtlSocket instance. + * message - Descriptor for received message. Note that message descriptor will + * be released upon exit from this callback (thus, could be freed along + * with message data). If the client is interested in working with that + * message after the callback returns, it should reference the message + * descriptor in this callback. + * msg_type - Message type. + * msg_data, msg_size - Message data. + */ +typedef void (*on_sdkctl_message_cb)(void* client_opaque, + SDKCtlSocket* sdkctl, + SDKCtlPacket* message, + int msg_type, + void* msg_data, + int msg_size); + +/* Defines query completion callback. + * Param: + * query_opaque - An opaque pointer associated with the query by the client. + * query - Query descriptor. Note that query descriptor will be released upon + * exit from this callback (thus, could be freed along with query data). If + * the client is interested in working with that query after the callback + * returns, it should reference the query descriptor in this callback. + * status - Query status. Can be one of these: + * - ASIO_STATE_CONTINUES : Query data has been transmitted to the service, + * and query is now waiting for response. + * - ASIO_STATE_SUCCEEDED : Query has been successfully completed. + * - ASIO_STATE_FAILED : Query has failed on an I/O. + * - ASIO_STATE_TIMED_OUT : Deadline set for the query has expired. + * - ASIO_STATE_CANCELLED : Query has been cancelled due to socket + * disconnection. + * Return: + * One of the AsyncIOAction values. + */ +typedef AsyncIOAction (*on_sdkctl_query_cb)(void* query_opaque, + SDKCtlQuery* query, + AsyncIOState status); + +/******************************************************************************** + * SDKCtlPacket API + ********************************************************************************/ + +/* References SDKCtlPacket object. + * Param: + * packet - Initialized SDKCtlPacket instance. + * Return: + * Number of outstanding references to the object. + */ +extern int sdkctl_packet_reference(SDKCtlPacket* packet); + +/* Releases SDKCtlPacket 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. + * Return: + * Number of outstanding references to the object. + */ +extern int sdkctl_packet_release(SDKCtlPacket* packet); + +/******************************************************************************** + * SDKCtlQuery API + ********************************************************************************/ + +/* Creates, and partially initializes query descriptor. + * Note that returned descriptor is referenced, and it must be eventually + * released with a call to sdkctl_query_release. + * Param: + * sdkctl - SDKCtlSocket instance for the query. + * query_type - Defines query type. + * in_data_size Size of the query's input buffer (data to be sent with this + * query). Note that buffer for query data will be allocated along with the + * query descriptor. Use sdkctl_query_get_buffer_in to get address of data + * buffer for this query. + * Return: + * Referenced SDKCtlQuery descriptor. + */ +extern SDKCtlQuery* sdkctl_query_new(SDKCtlSocket* sdkctl, + int query_type, + uint32_t in_data_size); + +/* Creates, and fully initializes query descriptor. + * Note that returned descriptor is referenced, and it must be eventually + * released with a call to sdkctl_query_release. + * Param: + * sdkctl - SDKCtlSocket instance for the query. + * query_type - Defines query type. + * in_data_size Size of the query's input buffer (data to be sent with this + * query). Note that buffer for query data will be allocated along with the + * query descriptor. Use sdkctl_query_get_buffer_in to get address of data + * buffer for this query. + * in_data - Data to initialize query's input buffer with. + * response_buffer - Contains address of the buffer addressing preallocated + * response buffer on the way in, and address of the buffer containing query + * response on query completion. If this parameter is NULL, the API will + * allocate its own query response buffer, which is going to be freed after + * query completion. + * response_size - Contains size of preallocated response buffer on the way in, + * and size of the received response on query completion. Can be NULL. + * query_cb - A callback to monitor query state. + * query_opaque - An opaque pointer associated with the query. + * Return: + * Referenced SDKCtlQuery descriptor. + */ +extern SDKCtlQuery* sdkctl_query_new_ex(SDKCtlSocket* sdkctl, + int query_type, + uint32_t in_data_size, + const void* in_data, + void** response_buffer, + uint32_t* response_size, + on_sdkctl_query_cb query_cb, + void* query_opaque); + +/* Sends query to SDK controller. + * Param: + * query - Query to send. Note that this must be a fully initialized query + * descriptor. + * to - Milliseconds to allow for the query to complete. Negative value means + * "forever". + */ +extern void sdkctl_query_send(SDKCtlQuery* query, int to); + +/* Creates, fully initializes query descriptor, and sends the query to SDK + * controller. + * Note that returned descriptor is referenced, and it must be eventually + * released with a call to sdkctl_query_release. + * Param: + * sdkctl - SDKCtlSocket instance for the query. + * query_type - Defines query type. + * in_data_size Size of the query's input buffer (data to be sent with this + * query). Note that buffer for query data will be allocated along with the + * query descriptor. Use sdkctl_query_get_buffer_in to get address of data + * buffer for this query. + * in_data - Data to initialize query's input buffer with. + * response_buffer - Contains address of the buffer addressing preallocated + * response buffer on the way in, and address of the buffer containing query + * response on query completion. If this parameter is NULL, the API will + * allocate its own query response buffer, which is going to be freed after + * query completion. + * response_size - Contains size of preallocated response buffer on the way in, + * and size of the received response on query completion. Can be NULL. + * query_cb - A callback to monitor query state. + * query_opaque - An opaque pointer associated with the query. + * to - Milliseconds to allow for the query to complete. Negative value means + * "forever". + * Return: + * Referenced SDKCtlQuery descriptor for the query that has been sent. + */ +extern SDKCtlQuery* sdkctl_query_build_and_send(SDKCtlSocket* sdkctl, + int query_type, + uint32_t in_data_size, + const void* in_data, + void** response_buffer, + uint32_t* response_size, + on_sdkctl_query_cb query_cb, + void* query_opaque, + int to); + +/* References SDKCtlQuery object. + * Param: + * query - Initialized SDKCtlQuery instance. + * Return: + * Number of outstanding references to the object. + */ +extern int sdkctl_query_reference(SDKCtlQuery* query); + +/* Releases SDKCtlQuery object. + * Note that upon exit from this routine the object might be destroyed, even if + * this routine returns value other than zero. + * Param: + * query - Initialized SDKCtlQuery instance. + * Return: + * Number of outstanding references to the object. + */ +extern int sdkctl_query_release(SDKCtlQuery* query); + +/* Gets address of query's input data buffer (data to be send). + * Param: + * query - Query to get data buffer for. + * Return: + * Address of query's input data buffer. + */ +extern void* sdkctl_query_get_buffer_in(SDKCtlQuery* query); + +/******************************************************************************** + * SDKCtlSocket API + ********************************************************************************/ + +/* Creates an SDK controller socket descriptor. + * Param: + * reconnect_to - Timeout before trying to reconnect, or retry connection + * 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_message - A callback to invoke when a message is received from the SDK + * controller. + * opaque - An opaque pointer to associate with the socket. + * Return: + * Initialized SDKCtlSocket instance on success, or NULL on failure. + */ +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_message_cb on_message, + void* opaque); + +/* Improves throughput by recycling memory allocated for buffers transferred via + * this API. + * + * In many cases data exchanged between SDK controller sides are small, and, + * although may come quite often, are coming in a sequential manner. For instance, + * sensor service on the device may send us sensor updates every 20ms, one after + * another. For such data traffic it makes perfect sense to recycle memory + * allocated for the previous sensor update, rather than to free it just to + * reallocate same buffer in 20ms. This routine sets properties of the recycler + * for the given SDK controller connection. Recycling includes memory allocated + * for all types of data transferred in this API: packets, and queries. + * Param: + * sdkctl - Initialized SDKCtlSocket instance. + * data_size - Size of buffer to allocate for each data block. + * max_recycled_num - Maximum number of allocated buffers to keep in the + * recycler. + */ +extern void sdkctl_init_recycler(SDKCtlSocket* sdkctl, + uint32_t data_size, + int max_recycled_num); + +/* References SDKCtlSocket object. + * Param: + * sdkctl - Initialized SDKCtlSocket instance. + * Return: + * Number of outstanding references to the object. + */ +extern int sdkctl_socket_reference(SDKCtlSocket* sdkctl); + +/* Releases SDKCtlSocket object. + * Note that upon exit from this routine the object might be destroyed, even if + * this routine returns value other than zero. + * Param: + * sdkctl - Initialized SDKCtlSocket instance. + * Return: + * Number of outstanding references to the object. + */ +extern int sdkctl_socket_release(SDKCtlSocket* sdkctl); + +/* Asynchronously connects to SDK controller. + * Param: + * sdkctl - Initialized SDKCtlSocket instance. + * port - TCP port to connect the socket to. + * retry_to - Number of milliseconds to wait before retrying a failed + * connection attempt. + */ +extern void sdkctl_socket_connect(SDKCtlSocket* sdkctl, int port, int retry_to); + +/* Asynchronously reconnects to SDK controller. + * Param: + * sdkctl - Initialized SDKCtlSocket instance. + * port - TCP port to reconnect the socket to. + * retry_to - Number of milliseconds to wait before reconnecting. Same timeout + * will be used for retrying a failed connection attempt. + */ +extern void sdkctl_socket_reconnect(SDKCtlSocket* sdkctl, int port, int retry_to); + +/* Disconnects from SDK controller. + * Param: + * sdkctl - Initialized SDKCtlSocket instance. + */ +extern void sdkctl_socket_disconnect(SDKCtlSocket* sdkctl); + +#endif /* ANDROID_SDKCONTROL_SOCKET_H_ */ diff --git a/android/sensors-port.c b/android/sensors-port.c index 9e9351d..8620783 100644 --- a/android/sensors-port.c +++ b/android/sensors-port.c @@ -173,6 +173,42 @@ _on_io_failure(void* opaque, AndroidDevice* ad, int failure) * Sensors port API *******************************************************************************/ +#include "android/sdk-controller-socket.h" + +static AsyncIOAction +_on_sdkctl_connection(void* client_opaque, SDKCtlSocket* sdkctl, AsyncIOState status) +{ + if (status == ASIO_STATE_FAILED) { + sdkctl_socket_reconnect(sdkctl, 1970, 20); + } + return ASIO_ACTION_DONE; +} + +void on_sdkctl_handshake(void* client_opaque, + SDKCtlSocket* sdkctl, + void* handshake, + uint32_t handshake_size, + AsyncIOState 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); + } +} + +void on_sdkctl_message(void* client_opaque, + SDKCtlSocket* sdkctl, + SDKCtlPacket* message, + int msg_type, + void* msg_data, + int msg_size) +{ + printf("########################################################\n"); +} + AndroidSensorsPort* sensors_port_create(void* opaque) { @@ -180,6 +216,12 @@ sensors_port_create(void* opaque) 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; diff --git a/android/utils/debug.h b/android/utils/debug.h index d6fd60c..5aa52ba 100644 --- a/android/utils/debug.h +++ b/android/utils/debug.h @@ -46,6 +46,7 @@ _VERBOSE_TAG(adb, "ADB debugger") \ _VERBOSE_TAG(asconnector, "Asynchronous socket connector") \ _VERBOSE_TAG(asyncsocket, "Asynchronous socket") \ + _VERBOSE_TAG(sdkctlsocket, "Socket tethering to SdkControl server") \ #define _VERBOSE_TAG(x,y) VERBOSE_##x, typedef enum { -- cgit v1.1