aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.common2
-rw-r--r--android/android-device.c1281
-rw-r--r--android/android-device.h239
-rw-r--r--android/async-utils.c19
-rw-r--r--android/async-utils.h12
-rw-r--r--android/avd/hardware-properties.ini21
-rw-r--r--android/hw-sensors.c53
-rw-r--r--android/sensors-port.c341
-rw-r--r--android/sensors-port.h91
-rw-r--r--android/utils/debug.h2
10 files changed, 2048 insertions, 13 deletions
diff --git a/Makefile.common b/Makefile.common
index 71100f3..45be256 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -423,6 +423,8 @@ CORE_MISC_SOURCES = \
android/hw-pipe-net.c \
android/qemu-setup.c \
android/snapshot.c \
+ android/android-device.c \
+ android/sensors-port.c \
android/utils/timezone.c \
android/camera/camera-format-converters.c \
android/camera/camera-service.c
diff --git a/android/android-device.c b/android/android-device.c
new file mode 100644
index 0000000..093879d
--- /dev/null
+++ b/android/android-device.c
@@ -0,0 +1,1281 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Encapsulates exchange protocol between the emulator, and an Android device
+ * that is connected to the host via USB. The communication is established over
+ * a TCP port forwarding, enabled by ADB.
+ */
+
+#include "android/android-device.h"
+#include "iolooper.h"
+
+#define E(...) derror(__VA_ARGS__)
+#define W(...) dwarning(__VA_ARGS__)
+#define D(...) VERBOSE_PRINT(adevice,__VA_ARGS__)
+#define D_ACTIVE VERBOSE_CHECK(adevice)
+
+/********************************************************************************
+ * Common android device socket
+ *******************************************************************************/
+
+/* Milliseconds between retrying asynchronous connections to the device. */
+#define ADS_RETRY_CONNECTION_TIMEOUT 3000
+
+/* Socket type. */
+typedef enum ADSType {
+ /* Query socket. */
+ ADS_TYPE_QUERY = 0,
+ /* Events socket. */
+ ADS_TYPE_EVENT = 1
+} ADSType;
+
+/* Status of the socket. */
+typedef enum ADSStatus {
+ /* Socket is disconnected. */
+ ADS_DISCONNECTED,
+ /* Connection process has been started. */
+ ADS_CONNECTING,
+ /* Connection has been established. */
+ ADS_CONNECTED,
+ /* Socket has been registered with the server. */
+ ADS_REGISTERED,
+} ADSStatus;
+
+/* Identifies socket as a "query" socket with the server. */
+static const char* _ads_query_socket_id = "query";
+/* Identifies socket as an "event" socket with the server. */
+static const char* _ads_event_socket_id = "event";
+
+/* Android device socket descriptor. */
+typedef struct AndroidDevSocket AndroidDevSocket;
+
+/*
+ * Callback routines.
+ */
+
+/* Callback routine that is called when a socket is connected.
+ * Param:
+ * opaque - Opaque pointer associated with the socket. Typicaly it's the same
+ * pointer that is associated with AndroidDevice instance.
+ * ads - Connection socket.
+ * failure - If zero, indicates that socket has been successuly connected. If a
+ * connection error has occured, this parameter contains the error code (as
+ * in 'errno).
+ */
+typedef void (*ads_socket_connected_cb)(void* opaque,
+ struct AndroidDevSocket* ads,
+ int failure);
+
+/* Android device socket descriptor. */
+struct AndroidDevSocket {
+ /* Socket type. */
+ ADSType type;
+ /* Socket status. */
+ ADSStatus socket_status;
+ /* TCP address for the socket. */
+ SockAddress address;
+ /* Android device descriptor that owns the socket. */
+ AndroidDevice* ad;
+ /* Opaque pointer associated with the socket. Typicaly it's the same
+ * pointer that is associated with AndroidDevice instance.*/
+ void* opaque;
+ /* Deadline for current I/O performed on the socket. */
+ Duration deadline;
+ /* Socket's file descriptor. */
+ int fd;
+};
+
+/* Query socket descriptor. */
+typedef struct AndroidQuerySocket {
+ /* Common device socket. */
+ AndroidDevSocket dev_socket;
+} AndroidQuerySocket;
+
+/* Event socket descriptor. */
+typedef struct AndroidEventSocket {
+ /* Common socket descriptor. */
+ AndroidDevSocket dev_socket;
+ /* Asynchronous connector to the device. */
+ AsyncConnector connector[1];
+ /* I/O port for asynchronous I/O on this socket. */
+ LoopIo io[1];
+ /* Asynchronous string reader. */
+ AsyncLineReader alr;
+ /* Callback to call at the end of the asynchronous connection to this socket.
+ * Can be NULL. */
+ ads_socket_connected_cb on_connected;
+ /* Callback to call when an event is received on this socket. Can be NULL. */
+ event_cb on_event;
+} AndroidEventSocket;
+
+/* Android device descriptor. */
+struct AndroidDevice {
+ /* Query socket for the device. */
+ AndroidQuerySocket query_socket;
+ /* Event socket for the device. */
+ AndroidEventSocket event_socket;
+ /* An opaque pointer associated with this descriptor. */
+ void* opaque;
+ /* I/O looper for synchronous I/O on the sockets for this device. */
+ IoLooper* io_looper;
+ /* Timer that is used to retry asynchronous connections. */
+ LoopTimer timer[1];
+ /* I/O looper for asynchronous I/O. */
+ Looper* looper;
+ /* Callback to call when device is fully connected. */
+ device_connected_cb on_connected;
+ /* I/O failure callback .*/
+ io_failure_cb on_io_failure;
+};
+
+/********************************************************************************
+ * Common socket declarations
+ *******************************************************************************/
+
+/* Initializes common device socket.
+ * Param:
+ * ads - Socket descriptor to initialize.
+ * opaque - An opaque pointer to associate with the socket. Typicaly it's the
+ * same pointer that is associated with AndroidDevice instance.
+ * ad - Android device descriptor that owns the socket.
+ * port - Socket's TCP port.
+ * type - Socket type (query, or event).
+ */
+static int _android_dev_socket_init(AndroidDevSocket* ads,
+ void* opaque,
+ AndroidDevice* ad,
+ int port,
+ ADSType type);
+
+/* Destroys socket descriptor. */
+static void _android_dev_socket_destroy(AndroidDevSocket* ads);
+
+/* Synchronously connects to the socket, and registers it with the server.
+ * Param:
+ * ads - Socket to connect. Must have 'deadline' field properly setup.
+ * Return:
+ * 0 on success, -1 on failure with errno containing the reason for failure.
+ */
+static int _android_dev_socket_connect(AndroidDevSocket* ads);
+
+/* Synchronously registers a connected socket with the server.
+ * Param:
+ * ads - Socket to register. Must be connected, and must have 'deadline' field
+ * properly setup.
+ * Return:
+ * 0 on success, -1 on failure with errno containing the reason for failure.
+ */
+static int _android_dev_socket_register(AndroidDevSocket* ads);
+
+/* Disconnects the socket (if it was connected) */
+static void _android_dev_socket_disconnect(AndroidDevSocket* ads);
+
+/* Synchronously sends data to the socket.
+ * Param:
+ * ads - Socket to send the data to. Must be connected, and must have 'deadline'
+ * field properly setup.
+ * buff, buffsize - Buffer to send.
+ * Return:
+ * Number of bytes sent on success, or -1 on failure with errno containing the
+ * reason for failure.
+ */
+static int _android_dev_socket_send(AndroidDevSocket* ads,
+ const char* buff,
+ int buffsize);
+
+/* Synchronously receives data from the socket.
+ * Param:
+ * ads - Socket to receive the data from. Must be connected, and must have
+ * 'deadline' field properly setup.
+ * buff, buffsize - Buffer where to receive data.
+ * Return:
+ * Number of bytes received on success, or -1 on failure with errno containing
+ * the reason for failure.
+ */
+static int _android_dev_socket_recv(AndroidDevSocket* ads,
+ char* buf,
+ int bufsize);
+
+/* Synchronously reads zero-terminated string from the socket.
+ * Param:
+ * ads - Socket to read the string from. Must be connected, and must have
+ * 'deadline' field properly setup.
+ * str, strsize - Buffer where to read the string.
+ * Return:
+ * Number of charactes read into the string buffer (including zero-terminator)
+ * on success, or -1 on failure with 'errno' containing the reason for failure.
+ * If this routine returns -1, and errno contains ENOMEM, this is an indicator
+ * that supplied string buffer was too small for the receiving string.
+ */
+static int _android_dev_socket_read_string(AndroidDevSocket* ads,
+ char* str,
+ int strsize);
+
+/* Synchronously reads zero-terminated query response from the socket.
+ * All queries respond with an 'ok', or 'ko' prefix, indicating a success, or
+ * failure. Prefix can be followed by more query response data, separated from
+ * the prefix with a ':' character. This routine helps separating prefix from the
+ * data, by placing only the query response data into provided buffer. 'ko' or
+ * 'ok' will be encoded in the return value.
+ * Param:
+ * ads - Socket to read the response from. Must be connected, and must have
+ * 'deadline' field properly setup.
+ * data, datasize - Buffer where to read the query response data.
+ * Return:
+ * Number of charactes read into the data buffer (including zero-terminator) on
+ * success, or -1 on failure with errno containing the reason for failure.
+ * If the query has been completed with 'ko', this routine will return -1, with
+ * errno set to 0. If this routine returned -1, and errno is set to EINVAL, this
+ * indicates that reply string didn't match expected query reply format.
+ */
+static int _android_dev_socket_read_response(AndroidDevSocket* ads,
+ char* str,
+ int strsize);
+
+/* Gets ID string for the channel. */
+AINLINED const char*
+_ads_id_str(AndroidDevSocket* ads)
+{
+ return (ads->type == ADS_TYPE_QUERY) ? _ads_query_socket_id :
+ _ads_event_socket_id;
+}
+
+/* Gets socket's TCP port. */
+AINLINED int
+_ads_port(AndroidDevSocket* ads)
+{
+ return sock_address_get_port(&ads->address);
+}
+
+/* Gets synchronous I/O looper for the socket. */
+AINLINED IoLooper*
+_ads_io_looper(AndroidDevSocket* ads)
+{
+ return ads->ad->io_looper;
+}
+
+/* Sets deadline on a socket operation, given relative timeout.
+ * Param:
+ * ads - Socket descriptor to set deadline for.
+ * to - Relative timeout (in millisec) for the operation.
+ * AD_INFINITE_WAIT passed in this parameter means "no deadline".
+ */
+AINLINED void
+_ads_set_deadline(AndroidDevSocket* ads, int to)
+{
+ ads->deadline = (to == AD_INFINITE_WAIT) ? DURATION_INFINITE :
+ iolooper_now() + to;
+}
+
+/********************************************************************************
+ * Common socket implementation
+ *******************************************************************************/
+
+static int
+_android_dev_socket_init(AndroidDevSocket* ads,
+ void* opaque,
+ AndroidDevice* ad,
+ int port,
+ ADSType type)
+{
+ ads->type = type;
+ ads->socket_status = ADS_DISCONNECTED;
+ ads->opaque = opaque;
+ ads->ad = ad;
+ ads->fd = -1;
+ sock_address_init_inet(&ads->address, SOCK_ADDRESS_INET_LOOPBACK, port);
+
+ return 0;
+}
+
+static void
+_android_dev_socket_destroy(AndroidDevSocket* ads)
+{
+ /* Make sure it's disconnected. */
+ _android_dev_socket_disconnect(ads);
+
+ /* Finalize socket address. */
+ sock_address_done(&ads->address);
+ memset(&ads->address, 0, sizeof(ads->address));
+}
+
+static int
+_android_dev_socket_connect(AndroidDevSocket* ads)
+{
+ int res;
+
+ /* Create communication socket. */
+ ads->fd = socket_create_inet(SOCKET_STREAM);
+ if (ads->fd < 0) {
+ D("Unable to create socket for channel '%s'@%d: %s",
+ _ads_id_str(ads), _ads_port(ads), strerror(errno));
+ return -1;
+ }
+ socket_set_nonblock(ads->fd);
+
+ /* Synchronously connect to it. */
+ ads->socket_status = ADS_CONNECTING;
+ iolooper_add_write(_ads_io_looper(ads), ads->fd);
+ res = socket_connect(ads->fd, &ads->address);
+ while (res < 0 && errno == EINTR) {
+ res = socket_connect(ads->fd, &ads->address);
+ }
+
+ if (res && (errno == EINPROGRESS || errno == EWOULDBLOCK || errno == EAGAIN)) {
+ /* Connection is delayed. Wait for it until timeout expires. */
+ res = iolooper_wait_absolute(_ads_io_looper(ads), ads->deadline);
+ if (res > 0) {
+ /* Pick up on possible connection error. */
+ errno = socket_get_error(ads->fd);
+ res = (errno == 0) ? 0 : -1;
+ } else {
+ res = -1;
+ }
+ }
+ iolooper_del_write(_ads_io_looper(ads), ads->fd);
+
+ if (res == 0) {
+ D("Channel '%s'@%d is connected", _ads_id_str(ads), _ads_port(ads));
+ /* Socket is connected. Now register it with the server. */
+ ads->socket_status = ADS_CONNECTED;
+ res = _android_dev_socket_register(ads);
+ } else {
+ D("Unable to connect channel '%s' to port %d: %s",
+ _ads_id_str(ads), _ads_port(ads), strerror(errno));
+ }
+
+ if (res) {
+ _android_dev_socket_disconnect(ads);
+ }
+
+ return res;
+}
+
+static int
+_android_dev_socket_register(AndroidDevSocket* ads)
+{
+ /* Make sure that socket is connected. */
+ if (ads->socket_status < ADS_CONNECTED) {
+ D("Attempt to register a disconnected channel '%s'@%d",
+ _ads_id_str(ads), _ads_port(ads));
+ errno = ECONNRESET;
+ return -1;
+ }
+
+ /* Register this socket accordingly to its type. */
+ const char* reg_str = _ads_id_str(ads);
+ int res = _android_dev_socket_send(ads, reg_str, strlen(reg_str) + 1);
+ if (res > 0) {
+ /* Receive reply. Note that according to the protocol, the server should
+ * reply to channel registration with 'ok', or 'ko' (just like with queries),
+ * so we can use query reply reader here. */
+ char reply[256];
+ res = _android_dev_socket_read_response(ads, reply, sizeof(reply));
+ if (res >= 0) {
+ /* Socket is now registered. */
+ ads->socket_status = ADS_REGISTERED;
+ D("Channel '%s'@%d is registered", _ads_id_str(ads), _ads_port(ads));
+ res = 0;
+ } else {
+ if (errno == 0) {
+ /* 'ko' condition */
+ D("Device failed registration of channel '%s'@%d: %s",
+ _ads_id_str(ads), _ads_port(ads), reply);
+ errno = EINVAL;
+ } else {
+ D("I/O failure while registering channel '%s'@%d: %s",
+ _ads_id_str(ads), _ads_port(ads), strerror(errno));
+ }
+ res = -1;
+ }
+ } else {
+ D("Unable to send registration query for channel '%s'@%d: %s",
+ _ads_id_str(ads), _ads_port(ads), strerror(errno));
+ res = -1;
+ }
+
+ return res;
+}
+
+static void
+_android_dev_socket_disconnect(AndroidDevSocket* ads)
+{
+ /* Preserve errno */
+ const int save_error = errno;
+ if (ads->socket_status != ADS_DISCONNECTED) {
+ /* Reset I/O looper for this socket. */
+ iolooper_modify(_ads_io_looper(ads), ads->fd,
+ IOLOOPER_READ | IOLOOPER_WRITE, 0);
+
+ /* Mark as disconnected. */
+ ads->socket_status = ADS_DISCONNECTED;
+
+ /* Close socket. */
+ if (ads->fd >= 0) {
+ socket_close(ads->fd);
+ ads->fd = -1;
+ }
+ }
+ errno = save_error;
+}
+
+static int
+_android_dev_socket_send(AndroidDevSocket* ads, const char* buff, int to_send)
+{
+ int sent = 0;
+
+ /* Make sure that socket is connected. */
+ if (ads->socket_status < ADS_CONNECTED) {
+ D("Attempt to send via disconnected channel '%s'@%d",
+ _ads_id_str(ads), _ads_port(ads));
+ errno = ECONNRESET;
+ return -1;
+ }
+
+ iolooper_add_write(_ads_io_looper(ads), ads->fd);
+ do {
+ int res = socket_send(ads->fd, buff + sent, to_send - sent);
+ if (res == 0) {
+ /* Disconnection. */
+ errno = ECONNRESET;
+ sent = -1;
+ break;
+ }
+
+ if (res < 0) {
+ if (errno == EINTR) {
+ /* loop on EINTR */
+ continue;
+ }
+
+ if (errno == EWOULDBLOCK || errno == EAGAIN) {
+ res = iolooper_wait_absolute(_ads_io_looper(ads), ads->deadline);
+ if (res > 0) {
+ /* Ready to write. */
+ continue;
+ }
+ }
+ sent = -1;
+ break;
+ }
+ sent += res;
+ } while (sent < to_send);
+ iolooper_del_write(_ads_io_looper(ads), ads->fd);
+
+ /* In case of an I/O failure we have to invoke failure callback. Note that we
+ * report I/O failures only on registered sockets. */
+ if (sent < 0) {
+ D("I/O error while sending data via channel '%s'@%d: %s",
+ _ads_id_str(ads), _ads_port(ads), strerror(errno));
+
+ if (ads->ad->on_io_failure != NULL && ads->socket_status > ADS_CONNECTED) {
+ const char save_error = errno;
+ ads->ad->on_io_failure(ads->opaque, ads->ad, save_error);
+ errno = save_error;
+ }
+ }
+
+ return sent;
+}
+
+static int
+_android_dev_socket_recv(AndroidDevSocket* ads, char* buf, int bufsize)
+{
+ int recvd = 0;
+
+ /* Make sure that socket is connected. */
+ if (ads->socket_status < ADS_CONNECTED) {
+ D("Attempt to receive from disconnected channel '%s'@%d",
+ _ads_id_str(ads), _ads_port(ads));
+ errno = ECONNRESET;
+ return -1;
+ }
+
+ iolooper_add_read(_ads_io_looper(ads), ads->fd);
+ do {
+ int res = socket_recv(ads->fd, buf + recvd, bufsize - recvd);
+ if (res == 0) {
+ /* Disconnection. */
+ errno = ECONNRESET;
+ recvd = -1;
+ break;
+ }
+
+ if (res < 0) {
+ if (errno == EINTR) {
+ /* loop on EINTR */
+ continue;
+ }
+
+ if (errno == EWOULDBLOCK || errno == EAGAIN) {
+ res = iolooper_wait_absolute(_ads_io_looper(ads), ads->deadline);
+ if (res > 0) {
+ /* Ready to read. */
+ continue;
+ }
+ }
+ recvd = -1;
+ break;
+ }
+ recvd += res;
+ } while (recvd < bufsize);
+ iolooper_del_read(_ads_io_looper(ads), ads->fd);
+
+ /* In case of an I/O failure we have to invoke failure callback. Note that we
+ * report I/O failures only on registered sockets. */
+ if (recvd < 0) {
+ D("I/O error while receiving from channel '%s'@%d: %s",
+ _ads_id_str(ads), _ads_port(ads), strerror(errno));
+
+ if (ads->ad->on_io_failure != NULL && ads->socket_status > ADS_CONNECTED) {
+ const char save_error = errno;
+ ads->ad->on_io_failure(ads->opaque, ads->ad, save_error);
+ errno = save_error;
+ }
+ }
+
+ return recvd;
+}
+
+static int
+_android_dev_socket_read_string(AndroidDevSocket* ads, char* str, int strsize)
+{
+ int n;
+
+ /* Char by char read from the socket, until zero-terminator is read. */
+ for (n = 0; n < strsize; n++) {
+ if (_android_dev_socket_recv(ads, str + n, 1) > 0) {
+ if (str[n] == '\0') {
+ /* Done. */
+ return n + 1; /* Including zero-terminator. */
+ }
+ } else {
+ /* I/O error. */
+ return -1;
+ }
+ }
+
+ /* Buffer was too small. Report that by setting errno to ENOMEM. */
+ D("Buffer %d is too small to receive a string from channel '%s'@%d",
+ strsize, _ads_id_str(ads), _ads_port(ads));
+ errno = ENOMEM;
+ return -1;
+}
+
+static int
+_android_dev_socket_read_response(AndroidDevSocket* ads, char* data, int datasize)
+{
+ int n, res;
+ int success = 0;
+ int failure = 0;
+ int bad_format = 0;
+ char ok[4];
+
+ *data = '\0';
+
+ /* Char by char read from the socket, until ok/ko is read. */
+ for (n = 0; n < 2; n++) {
+ res = _android_dev_socket_recv(ads, ok + n, 1);
+ if (res > 0) {
+ if (ok[n] == '\0') {
+ /* EOS is unexpected here! */
+ D("Bad query reply format on channel '%s'@%d: '%s' is too short.",
+ _ads_id_str(ads), _ads_port(ads), ok);
+ errno = EINVAL;
+ return -1;
+ }
+ } else {
+ /* I/O error. */
+ return -1;
+ }
+ }
+
+ /* Next character must be either ':', or '\0' */
+ res = _android_dev_socket_recv(ads, ok + n, 1);
+ if (res <= 0) {
+ /* I/O error. */
+ return -1;
+ }
+
+ /*
+ * Verify format.
+ */
+
+ /* Check ok / ko */
+ success = memcmp(ok, "ok", 2) == 0;
+ failure = memcmp(ok, "ko", 2) == 0;
+
+ /* Check the prefix: 'ok'|'ko' & ':'|'\0' */
+ if ((success || failure) && (ok[n] == '\0' || ok[n] == ':')) {
+ /* Format is good. */
+ if (ok[n] == '\0') {
+ /* We're done: no extra data in response. */
+ errno = 0;
+ return success ? 0 : -1;
+ }
+ /* Reset buffer offset, so we will start to read the remaining query
+ * data to the beginning of the supplied buffer. */
+ n = 0;
+ } else {
+ /* Bad format. Lets move what we've read to the main buffer, and
+ * continue filling it in. */
+ bad_format = 1;
+ n++;
+ memcpy(data, ok, n);
+ }
+
+ /* Read the remainder of reply to the supplied data buffer. */
+ res = _android_dev_socket_read_string(ads, data + n, datasize - n);
+ if (res < 0) {
+ return res;
+ }
+
+ /* Lets see if format was bad */
+ if (bad_format) {
+ D("Bad query reply format on channel '%s'@%d: %s.",
+ _ads_id_str(ads), _ads_port(ads), data);
+ errno = EINVAL;
+ return -1;
+ } else {
+ errno = 0;
+ return success ? n : -1;
+ }
+}
+
+/********************************************************************************
+ * Query socket declarations
+ *******************************************************************************/
+
+/* Initializes query socket descriptor.
+ * Param:
+ * adsquery - Socket descriptor to initialize.
+ * opaque - An opaque pointer to associate with the socket. Typicaly it's the
+ * same pointer that is associated with AndroidDevice instance.
+ * ad - Android device descriptor that owns the socket.
+ * port - TCP socket port.
+ */
+static int _android_query_socket_init(AndroidQuerySocket* adsquery,
+ void* opaque,
+ AndroidDevice* ad,
+ int port);
+
+/* Destroys query socket descriptor. */
+static void _android_query_socket_destroy(AndroidQuerySocket* adsquery);
+
+/* Synchronously connects the query socket, and registers it with the server.
+ * Param:
+ * adsquery - Descriptor for the query socket to connect. Must have 'deadline'
+ * field properly setup.
+ * cb - Callback to invoke when socket connection is completed. Can be NULL.
+ * Return:
+ * Zero on success, or non-zero on failure.
+ */
+static int _android_query_socket_connect(AndroidQuerySocket* adsquery);
+
+/* Disconnects the query socket. */
+static void _android_query_socket_disconnect(AndroidQuerySocket* adsquery);
+
+/********************************************************************************
+ * Query socket implementation
+ *******************************************************************************/
+
+static int
+_android_query_socket_init(AndroidQuerySocket* adsquery,
+ void* opaque,
+ AndroidDevice* ad,
+ int port)
+{
+ return _android_dev_socket_init(&adsquery->dev_socket, opaque, ad, port,
+ ADS_TYPE_QUERY);
+}
+
+static void
+_android_query_socket_destroy(AndroidQuerySocket* adsquery)
+{
+ _android_query_socket_disconnect(adsquery);
+ _android_dev_socket_destroy(&adsquery->dev_socket);
+}
+
+static int
+_android_query_socket_connect(AndroidQuerySocket* adsquery)
+{
+ return _android_dev_socket_connect(&adsquery->dev_socket);
+}
+
+static void
+_android_query_socket_disconnect(AndroidQuerySocket* adsquery)
+{
+ _android_dev_socket_disconnect(&adsquery->dev_socket);
+}
+
+/********************************************************************************
+ * Events socket declarations
+ *******************************************************************************/
+
+/* Initializes event socket descriptor.
+ * Param:
+ * adsevent - Socket descriptor to initialize.
+ * opaque - An opaque pointer to associate with the socket. Typicaly it's the
+ * same pointer that is associated with AndroidDevice instance.
+ * ad - Android device descriptor that owns the socket.
+ * port - TCP socket port.
+ */
+static int _android_event_socket_init(AndroidEventSocket* adsevent,
+ void* opaque,
+ AndroidDevice* ad,
+ int port);
+
+/* Destroys the event socket descriptor. */
+static void _android_event_socket_destroy(AndroidEventSocket* adsevent);
+
+/* Synchronously connects event socket.
+ * Param:
+ * adsevent - Descriptor for the event socket to connect. Must have 'deadline'
+ * field properly setup.
+ * Return:
+ * Zero on success, or non-zero on failure.
+ */
+static int _android_event_socket_connect_sync(AndroidEventSocket* adsevent);
+
+/* Initiates asynchronous event socket connection.
+ * Param:
+ * adsevent - Descriptor for the event socket to connect. Must have 'deadline'
+ * field properly setup.
+ * cb - Callback to invoke when socket connection is completed. Can be NULL.
+ * Return:
+ * Zero on success, or non-zero on failure.
+ */
+static int _android_event_socket_connect_async(AndroidEventSocket* adsevent,
+ ads_socket_connected_cb cb);
+
+/* Disconnects the event socket. */
+static void _android_event_socket_disconnect(AndroidEventSocket* adsevent);
+
+/* Initiates listening on the event socket.
+ * Param:
+ * adsevent - Descriptor for the event socket to listen on.
+ * str, strsize - Buffer where to read the string.
+ * cb - A callback to call when the event string is read. Can be NULL.
+ * Return:
+ * Zero on success, or non-zero on failure.
+ */
+static int _android_event_socket_listen(AndroidEventSocket* adsevent,
+ char* str,
+ int strsize,
+ event_cb cb);
+
+/* Event socket's asynchronous I/O looper callback.
+ * Param:
+ * opaque - AndroidEventSocket instance.
+ * fd - Socket's FD.
+ * events - I/O type bitsmask (read | write).
+ */
+static void _on_event_socket_io(void* opaque, int fd, unsigned events);
+
+/* Callback that is invoked when asynchronous event socket connection is
+ * completed. */
+static void _on_event_socket_connected(AndroidEventSocket* adsevent, int failure);
+
+/* Callback that is invoked when an event is received from the device. */
+static void _on_event_received(AndroidEventSocket* adsevent);
+
+/* Gets I/O looper for asynchronous I/O on event socket. */
+AINLINED Looper*
+_aes_looper(AndroidEventSocket* adsevent)
+{
+ return adsevent->dev_socket.ad->looper;
+}
+
+/********************************************************************************
+ * Events socket implementation
+ *******************************************************************************/
+
+static int
+_android_event_socket_init(AndroidEventSocket* adsevent,
+ void* opaque,
+ AndroidDevice* ad,
+ int port)
+{
+ return _android_dev_socket_init(&adsevent->dev_socket, opaque, ad, port,
+ ADS_TYPE_EVENT);
+}
+
+static void
+_android_event_socket_destroy(AndroidEventSocket* adsevent)
+{
+ _android_event_socket_disconnect(adsevent);
+ _android_dev_socket_destroy(&adsevent->dev_socket);
+}
+
+
+static int
+_android_event_socket_connect_sync(AndroidEventSocket* adsevent)
+{
+ AndroidDevSocket* ads = &adsevent->dev_socket;
+ const int res = _android_dev_socket_connect(&adsevent->dev_socket);
+ if (res == 0) {
+ /* Prepare for async I/O on the event socket. */
+ loopIo_init(adsevent->io, _aes_looper(adsevent), ads->fd,
+ _on_event_socket_io, adsevent);
+ }
+ return res;
+}
+
+static int
+_android_event_socket_connect_async(AndroidEventSocket* adsevent,
+ ads_socket_connected_cb cb)
+{
+ AsyncStatus status;
+ AndroidDevSocket* ads = &adsevent->dev_socket;
+
+ /* Create asynchronous socket. */
+ ads->fd = socket_create_inet(SOCKET_STREAM);
+ if (ads->fd < 0) {
+ D("Unable to create socket for channel '%s'@%d: %s",
+ _ads_id_str(ads), _ads_port(ads), strerror(errno));
+ if (cb != NULL) {
+ cb(ads->opaque, ads, errno);
+ }
+ return -1;
+ }
+ socket_set_nonblock(ads->fd);
+
+ /* Prepare for async I/O on the event socket. */
+ loopIo_init(adsevent->io, _aes_looper(adsevent), ads->fd,
+ _on_event_socket_io, adsevent);
+
+ /* Try to connect. */
+ ads->socket_status = ADS_CONNECTING;
+ adsevent->on_connected = cb;
+ status = asyncConnector_init(adsevent->connector, &ads->address, adsevent->io);
+ switch (status) {
+ case ASYNC_COMPLETE:
+ /* We're connected to the device socket. */
+ ads->socket_status = ADS_CONNECTED;
+ _on_event_socket_connected(adsevent, 0);
+ break;
+ case ASYNC_ERROR:
+ _on_event_socket_connected(adsevent, errno);
+ break;
+ case ASYNC_NEED_MORE:
+ /* Attempt to connect would block, so connection competion is
+ * delegates to the looper's I/O routine. */
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void
+_android_event_socket_disconnect(AndroidEventSocket* adsevent)
+{
+ AndroidDevSocket* ads = &adsevent->dev_socket;
+
+ if (ads->socket_status != ADS_DISCONNECTED) {
+ /* Stop all async I/O. */
+ loopIo_done(adsevent->io);
+
+ /* Disconnect common socket. */
+ _android_dev_socket_disconnect(ads);
+ }
+}
+
+static int
+_android_event_socket_listen(AndroidEventSocket* adsevent,
+ char* str,
+ int strsize,
+ event_cb cb)
+{
+ AsyncStatus status;
+ AndroidDevSocket* ads = &adsevent->dev_socket;
+
+ /* Make sure that device is connected. */
+ if (ads->socket_status < ADS_CONNECTED) {
+ D("Attempt to listen on a disconnected channel '%s'@%d",
+ _ads_id_str(ads), _ads_port(ads));
+ errno = ECONNRESET;
+ return -1;
+ }
+
+ /* NOTE: only one reader at any given time! */
+ adsevent->on_event = cb;
+ asyncLineReader_init(&adsevent->alr, str, strsize, adsevent->io);
+ /* Default EOL for the line reader was '\n'. */
+ asyncLineReader_setEOL(&adsevent->alr, '\0');
+ status = asyncLineReader_read(&adsevent->alr);
+ if (status == ASYNC_COMPLETE) {
+ /* Data has been transferred immediately. Do the callback here. */
+ _on_event_received(adsevent);
+ } else if (status == ASYNC_ERROR) {
+ D("Error while listening on channel '%s'@%d: %s",
+ _ads_id_str(ads), _ads_port(ads), strerror(errno));
+ /* There is one special failure here, when buffer was too small to
+ * contain the entire string. This is not an I/O, but rather a
+ * protocol error. So we don't report it to the I/O failure
+ * callback. */
+ if (errno == ENOMEM) {
+ _on_event_received(adsevent);
+ } else {
+ if (ads->ad->on_io_failure != NULL) {
+ ads->ad->on_io_failure(ads->ad->opaque, ads->ad, errno);
+ }
+ }
+ return -1;
+ }
+ return 0;
+}
+
+static void
+_on_event_socket_io(void* opaque, int fd, unsigned events)
+{
+ AsyncStatus status;
+ AndroidEventSocket* adsevent = (AndroidEventSocket*)opaque;
+ AndroidDevSocket* ads = &adsevent->dev_socket;
+
+ /* Lets see if we're still wating on a connection to occur. */
+ if (ads->socket_status == ADS_CONNECTING) {
+ /* Complete socket connection. */
+ status = asyncConnector_run(adsevent->connector);
+ if (status == ASYNC_COMPLETE) {
+ /* We're connected to the device socket. */
+ ads->socket_status = ADS_CONNECTED;
+ D("Channel '%s'@%d is connected asynchronously",
+ _ads_id_str(ads), _ads_port(ads));
+ _on_event_socket_connected(adsevent, 0);
+ } else if (status == ASYNC_ERROR) {
+ _on_event_socket_connected(adsevent, adsevent->connector->error);
+ }
+ return;
+ }
+
+ /*
+ * Device is connected. Continue with the data transfer. We don't expect
+ * any writes here, since we always write to the event socket synchronously.
+ */
+
+ if ((events & LOOP_IO_READ) != 0) {
+ /* Continue reading data. */
+ status = asyncLineReader_read(&adsevent->alr);
+ if (status == ASYNC_COMPLETE) {
+ _on_event_received(adsevent);
+ } else if (status == ASYNC_ERROR) {
+ D("I/O failure while reading from channel '%s'@%d: %s",
+ _ads_id_str(ads), _ads_port(ads), strerror(errno));
+ /* There is one special failure here, when buffer was too small to
+ * contain the entire string. This is not an I/O, but rather a
+ * protocol error. So we don't report it to the I/O failure
+ * callback. */
+ if (errno == ENOMEM) {
+ _on_event_received(adsevent);
+ } else {
+ if (ads->ad->on_io_failure != NULL) {
+ ads->ad->on_io_failure(ads->ad->opaque, ads->ad, errno);
+ }
+ }
+ }
+ }
+}
+
+static void
+_on_event_socket_connected(AndroidEventSocket* adsevent, int failure)
+{
+ int res;
+ AndroidDevSocket* ads = &adsevent->dev_socket;
+
+ if (failure) {
+ _android_event_socket_disconnect(adsevent);
+ if (adsevent->on_connected != NULL) {
+ adsevent->on_connected(ads->opaque, ads, failure);
+ }
+ return;
+ }
+
+ /* Complete event socket connection by identifying it as "event" socket with
+ * the application. */
+ res = _android_dev_socket_register(ads);
+ if (res) {
+ const int save_error = errno;
+ _android_event_socket_disconnect(adsevent);
+ errno = save_error;
+ }
+
+ /* Notify callback about connection completion. */
+ if (adsevent->on_connected != NULL) {
+ if (res) {
+ adsevent->on_connected(ads->opaque, ads, errno);
+ } else {
+ adsevent->on_connected(ads->opaque, ads, 0);
+ }
+ }
+}
+
+static void
+_on_event_received(AndroidEventSocket* adsevent)
+{
+ if (adsevent->on_event != NULL) {
+ AndroidDevice* ad = adsevent->dev_socket.ad;
+ adsevent->on_event(ad->opaque, ad, (char*)adsevent->alr.buffer,
+ adsevent->alr.pos);
+ }
+}
+
+/********************************************************************************
+ * Android device connection
+ *******************************************************************************/
+
+/* Callback that is invoked when event socket is connected and registered as part
+ * of the _android_device_connect_async API.
+ * Param:
+ * opaque - Opaque pointer associated with AndroidDevice instance.
+ * ads - Common socket descriptor for the event socket.
+ * failure - If zero connection has succeeded, otherwise contains 'errno'-reason
+ * for connection failure.
+ */
+static void
+_on_android_device_connected_async(void* opaque,
+ AndroidDevSocket* ads,
+ int failure)
+{
+ int res;
+ AndroidDevice* ad = ads->ad;
+
+ if (failure) {
+ /* Depending on the failure code we will either retry, or bail out. */
+ switch (failure) {
+ case EPIPE:
+ case EAGAIN:
+ case EINPROGRESS:
+ case EALREADY:
+ case EHOSTUNREACH:
+ case EHOSTDOWN:
+ case ECONNREFUSED:
+ case ESHUTDOWN:
+ case ENOTCONN:
+ case ECONNRESET:
+ case ECONNABORTED:
+ case ENETRESET:
+ case ENETUNREACH:
+ case ENETDOWN:
+ case EBUSY:
+#ifndef _DARWIN_C_SOURCE
+ case ERESTART:
+ case ECOMM:
+ case ENONET:
+#endif /* _DARWIN_C_SOURCE */
+ /* Device is not available / reachable at the moment.
+ * Retry connection later. */
+ loopTimer_startRelative(ad->timer, ADS_RETRY_CONNECTION_TIMEOUT);
+ return;
+ default:
+ D("Failed to asynchronously connect channel '%s':%d %s",
+ _ads_id_str(ads), _ads_port(ads), strerror(errno));
+ if (ad->on_connected != NULL) {
+ ad->on_connected(ad->opaque, ad, failure);
+ }
+ break;
+ }
+ return;
+ }
+
+ /* Event socket is connected. Connect the query socket now. Give it 5
+ * seconds to connect. */
+ _ads_set_deadline(&ad->query_socket.dev_socket, 5000);
+ res = _android_query_socket_connect(&ad->query_socket);
+ if (res == 0) {
+ /* Query socket is connected. */
+ if (ad->on_connected != NULL) {
+ ad->on_connected(ad->opaque, ad, 0);
+ }
+ } else {
+ /* If connection completion has failed - disconnect the sockets. */
+ _android_event_socket_disconnect(&ad->event_socket);
+ _android_query_socket_disconnect(&ad->query_socket);
+
+ if (ad->on_connected != NULL) {
+ ad->on_connected(ad->opaque, ad, errno);
+ }
+ }
+}
+
+static void
+_on_timer(void* opaque)
+{
+ /* Retry the connection. */
+ AndroidDevice* ad = (AndroidDevice*)opaque;
+ android_device_connect_async(ad, ad->on_connected);
+}
+
+/* Destroys and frees the descriptor. */
+static void
+_android_device_free(AndroidDevice* ad)
+{
+ if (ad != NULL) {
+ _android_event_socket_destroy(&ad->event_socket);
+ _android_query_socket_destroy(&ad->query_socket);
+
+ /* Delete asynchronous I/O looper. */
+ if (ad->looper != NULL ) {
+ loopTimer_done(ad->timer);
+ looper_free(ad->looper);
+ }
+
+ /* Delete synchronous I/O looper. */
+ if (ad->io_looper != NULL) {
+ iolooper_reset(ad->io_looper);
+ iolooper_free(ad->io_looper);
+ }
+
+ AFREE(ad);
+ }
+}
+
+/********************************************************************************
+ * Android device API
+ *******************************************************************************/
+
+AndroidDevice*
+android_device_init(void* opaque, int port, io_failure_cb on_io_failure)
+{
+ int res;
+ AndroidDevice* ad;
+
+ ANEW0(ad);
+
+ ad->opaque = opaque;
+ ad->on_io_failure = on_io_failure;
+
+ /* Create I/O looper for synchronous I/O on the device. */
+ ad->io_looper = iolooper_new();
+ if (ad->io_looper == NULL) {
+ E("Unable to create synchronous I/O looper for android device.");
+ _android_device_free(ad);
+ return NULL;
+ }
+
+ /* Create a looper for asynchronous I/O on the device. */
+ ad->looper = looper_newCore();
+ if (ad->looper != NULL) {
+ /* Create a timer that will be used for connection retries. */
+ loopTimer_init(ad->timer, ad->looper, _on_timer, ad);
+ } else {
+ E("Unable to create asynchronous I/O looper for android device.");
+ _android_device_free(ad);
+ return NULL;
+ }
+
+ /* Init query socket. */
+ res = _android_query_socket_init(&ad->query_socket, opaque, ad, port);
+ if (res) {
+ _android_device_free(ad);
+ return NULL;
+ }
+
+ /* Init event socket. */
+ res = _android_event_socket_init(&ad->event_socket, opaque, ad, port);
+ if (res) {
+ _android_device_free(ad);
+ return NULL;
+ }
+
+ return ad;
+}
+
+void
+android_device_destroy(AndroidDevice* ad)
+{
+ if (ad != NULL) {
+ _android_device_free(ad);
+ }
+}
+
+int
+android_device_connect_sync(AndroidDevice* ad, int to)
+{
+ int res;
+
+ /* Setup deadline for the connections. */
+ _ads_set_deadline(&ad->query_socket.dev_socket, to);
+ ad->event_socket.dev_socket.deadline = ad->query_socket.dev_socket.deadline;
+
+ /* Connect the query socket first. */
+ res = _android_query_socket_connect(&ad->query_socket);
+ if (!res) {
+ /* Connect to the event socket next. */
+ res = _android_event_socket_connect_sync(&ad->event_socket);
+ }
+
+ return res;
+}
+
+int
+android_device_connect_async(AndroidDevice* ad, device_connected_cb on_connected)
+{
+ /* No deadline for async connections. */
+ ad->query_socket.dev_socket.deadline = DURATION_INFINITE;
+ ad->event_socket.dev_socket.deadline = DURATION_INFINITE;
+
+ /* Connect to the event socket first, and delegate query socket connection
+ * into callback invoked when event socket is connected. NOTE: In case of
+ * failure 'on_connected' callback has already been called from
+ * _on_android_device_connected_async routine. */
+ ad->on_connected = on_connected;
+ return _android_event_socket_connect_async(&ad->event_socket,
+ _on_android_device_connected_async);
+}
+
+void
+android_device_disconnect(AndroidDevice* ad)
+{
+ _android_event_socket_disconnect(&ad->event_socket);
+ _android_query_socket_disconnect(&ad->query_socket);
+}
+
+int
+android_device_query(AndroidDevice* ad,
+ const char* query,
+ char* buff,
+ size_t buffsize,
+ int to)
+{
+ int res;
+
+ /* Setup deadline for the query. */
+ _ads_set_deadline(&ad->query_socket.dev_socket, to);
+
+ /* Send the query. */
+ res = _android_dev_socket_send(&ad->query_socket.dev_socket, query,
+ strlen(query) + 1);
+ if (res > 0) {
+ /* Receive the response. */
+ res = _android_dev_socket_read_response(&ad->query_socket.dev_socket,
+ buff, buffsize);
+ return (res >= 0) ? 0 : -1;
+ }
+
+ return -1;
+}
+
+int
+android_device_listen(AndroidDevice* ad,
+ char* buff,
+ int buffsize,
+ event_cb on_event)
+{
+ return _android_event_socket_listen(&ad->event_socket, buff, buffsize,
+ on_event);
+}
diff --git a/android/android-device.h b/android/android-device.h
new file mode 100644
index 0000000..9fde906
--- /dev/null
+++ b/android/android-device.h
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_ANDROID_DEVICE_H_
+#define ANDROID_ANDROID_DEVICE_H_
+
+/*
+ * Encapsulates an exchange protocol between the emulator, and an Android device
+ * that is connected to the host via USB. The communication is established over
+ * a TCP port forwarding, enabled by ADB (always use 'adb -d forward ...' variant
+ * of this command, so ADB will know to enable port forwarding on the connected
+ * device, and not on the emulator's guest system).
+ *
+ * Exchange protocol contains two channel:
+ *
+ * - Query channel.
+ * - Event channel.
+ *
+ * Both channels are implemented on top of TCP sockets that are connected to the
+ * same port.
+ *
+ * I QUERY CHANNEL.
+ * Query channel is intended to send queries to and receive responses from the
+ * connected device. It is implemented on top of iolooper_xxx API (see iolooper.h)
+ * because it must work outside of the main event loop. This is required to enable
+ * proper initialization of components (such as sensors) that must be set up
+ * before emulator enters the main loop.
+ *
+ * II EVENT CHANNEL.
+ * Event channel is intended to listen on events sent from the device, and
+ * asynchronously report them back to the client of this API by invoking an event
+ * callback that was registered by the client. Event channel is implemented on
+ * top of asyncXxx API (see android/async-utils.*). Note that using of asyncXxx
+ * API limits the use of event channel to the time after the emulator has entered
+ * its main event loop. The only exception is if event channel is connected from
+ * android_device_connect_sync API, in which case iolooper_xxx API is used to
+ * establish the connection. However, even in this case listening for events will
+ * not be available until after the emulator enters its event loop, since event
+ * listening always uses asyncXxx API.
+ *
+ * III. ESTABLISHING CONNECTION.
+ * ADB port forwarding requires that the server socket is to be run on the device,
+ * while emulator must use a client socket for communication. Thus, it's the
+ * emulator that initiates the connection.
+ *
+ * There are two ways how emulator can initiate the connection:
+ *
+ * - Synchronous connection.
+ * - Asynchronous connection.
+ *
+ * III.I SYNCHROUNOUS CONNECTION.
+ * Synchronous connection is initiated via android_device_connect_sync API, and
+ * completes synchronously.
+ *
+ * This API should be used when connection with the device is required at the time
+ * of the call. For instance, when initializing sensor emulation, connection with
+ * the device is required to properly set up the emulator before the guest system
+ * starts, and before emulator enters its main event loop.
+ *
+ * III.II ASYNCHRONOUS CONNECTION.
+ * Asynchronous connection is initiated via android_device_connect_async API. The
+ * main difference with the synchronous connection is that this API will not fail
+ * if connection is not immediately available. If connection is not available at
+ * the time of the call, the API will schedule a retry (based on a timer), and
+ * will continue reprying untill connection becomes available, or until an error
+ * occurs that prevent further retries.
+ *
+ * This API should be used when ... Well, whenever appropriate. For instance,
+ * sensor emulation will use this API to restore lost connection with the device.
+ *
+ * NOTE: Asynchronous connection will complete no sooner than the emulator enters
+ * its main loop.
+ *
+ * IV EXCHANGE PROTOCOL.
+ * Obviously, there must be some application running on the device, that implements
+ * a socket server listening on the forwarded TCP port, and accepting the clients.
+ *
+ * IV.I Query vs. event channel.
+ * The exchange protocol assumes, that when a channel is connected, it will
+ * identify itself by sending a string containing channel type. Only after such
+ * identification has been made the channel becomes available for use.
+ *
+ * IV.II Message format.
+ * All data that is transferred in both directions over both channels are zero-
+ * terminated strings.
+ */
+
+#include "qemu-common.h"
+#include "android/async-utils.h"
+#include "android/utils/debug.h"
+
+/* TCP port reserved for sensor emulation. */
+#define AD_SENSOR_PORT 1968
+
+/* Definis infinite timeout. */
+#define AD_INFINITE_WAIT -1
+
+/* Android device descriptor. */
+typedef struct AndroidDevice AndroidDevice;
+
+/********************************************************************************
+ * Callback declarations
+ *******************************************************************************/
+
+/* Callback routine that is invoked when android device is connected, or failed
+ * to connect. As discussed above, this callback is called when both, query and
+ * event channels have been connected. This callback is used only for asynchronous
+ * connections.
+ * Param:
+ * opaque - Opaque pointer that was passed to android_device_init API.
+ * ad - Androd device descriptor for the connection.
+ * failure - Zero indicates that connection with the device has been successfuly
+ * established. Non-zero vaule passed in this parameter indicates a failure,
+ * and contains 'errno'-reason for failure.
+ */
+typedef void (*device_connected_cb)(void* opaque, AndroidDevice* ad, int failure);
+
+/* Callback routine that is invoked on an event received in the event channel.
+ * NOTE: It's important to check 'errno' in this callback. If 'errno' is set to
+ * ENOMEM, this signals that buffer passed to android_device_listen was too small
+ * to contain the entire event message.
+ * Param:
+ * opaque - Opaque pointer that was passed to android_device_init API.
+ * ad - Androd device descriptor for the connection.
+ * msg - Event message (a zero-terminated string) received from the device.
+ * msgsize - Event message size (including zero-terminator).
+ */
+typedef void (*event_cb)(void* opaque, AndroidDevice* ad, char* msg, int msgsize);
+
+/* Callback routine that is invoked when an I/O failure occurs on a channel.
+ * Note that this callback will not be invoked on connection failures.
+ * Param:
+ * opaque - Opaque pointer that was passed to android_device_init API.
+ * ad - Android device instance
+ * failure - Contains 'errno' indicating the reason for failure.
+ */
+typedef void (*io_failure_cb)(void* opaque, AndroidDevice* ad, int failure);
+
+/********************************************************************************
+ * Android Device API.
+ *******************************************************************************/
+
+/* Initializes android device descriptor.
+ * Param:
+ * opaque - An opaque pointer to associate with the descriptor. This pointer
+ * will be passed to all callbacks (see above) that were invoked by the
+ * initializing android device instance.
+ * port - TCP port to use for connection.
+ * on_io_failure - Callback to invoke when an I/O failure occurs on a channel
+ * used by the initializing android device instance. Can be NULL.
+ * Return:
+ * Initialized android device descriptor on success, or NULL on failure.
+ */
+extern AndroidDevice* android_device_init(void* opaque,
+ int port,
+ io_failure_cb on_io_failure);
+
+/* Disconnects and destroys android device descriptor.
+ * Param:
+ * ad - Android device descriptor, returned from android_device_init API.
+ * Note that memory allocated for this descriptor will be freed in this
+ * routine.
+ */
+extern void android_device_destroy(AndroidDevice* ad);
+
+/* Synchronously connects to the device. See notes above for more details.
+ * Param:
+ * ad - Android device descriptor, returned from android_device_init API.
+ * to - Milliseconds to wait for connection to be established.
+ * Return:
+ * Zero on success, or non-zero value on failure with 'errno' properly set.
+ */
+extern int android_device_connect_sync(AndroidDevice* ad, int to);
+
+/* Asynchronously connects to the device. See notes above for more details.
+ * Param:
+ * ad - Android device descriptor, returned from android_device_init API.
+ * on_connected - Callback to invoke when connection is completed (i,e, both,
+ * event, and query channels have been connected). This parameter can be
+ * NULL. Note that connection errors will be also reported through this
+ * callback. Also note that this callback will be invoked even if this
+ * routine returns with a failure.
+ * Return:
+ * Zero on success, or non-zero value on failure with 'errno' properly set.
+ */
+extern int android_device_connect_async(AndroidDevice* ad,
+ device_connected_cb on_connected);
+
+/* Disconnects from the android device.
+ * Param:
+ * ad - Android device descriptor, returned from android_device_init API.
+ */
+extern void android_device_disconnect(AndroidDevice* ad);
+
+/* Queries the device via query channel.
+ * Param:
+ * ad - Android device descriptor, returned from android_device_init API.
+ * query - Zero-terminated query string.
+ * buff, buffsize - Buffer where to receive the response to the query.
+ * to - Milliseconds to wait for the entire query to complete.
+ * Return:
+ * Zero on success, or non-zero value on failure with 'errno' properly set:
+ * - 0 Indicates that the server has failed the query.
+ * - Anything else indicates an I/O error.
+ */
+extern int android_device_query(AndroidDevice* ad,
+ const char* query,
+ char* buff,
+ size_t buffsize,
+ int to);
+
+/* Start listening on the event channel.
+ * Param:
+ * ad - Android device descriptor, returned from android_device_init API.
+ * buff, buffsize - Buffer where to receive the event message.
+ * on_event - Callback to invoke on event. Note that this callback will be
+ * invoked even if this routine returns with a failure.
+ * Return:
+ * Zero on success, or non-zero value on failure with 'errno' properly set.
+ */
+extern int android_device_listen(AndroidDevice* ad,
+ char* buff,
+ int buffsize,
+ event_cb on_event);
+
+#endif /* ANDROID_ANDROID_DEVICE_H_ */
diff --git a/android/async-utils.c b/android/async-utils.c
index 35a144a..e410de0 100644
--- a/android/async-utils.c
+++ b/android/async-utils.c
@@ -25,6 +25,7 @@ asyncReader_init(AsyncReader* ar,
ar->buffer = buffer;
ar->buffsize = buffsize;
ar->pos = 0;
+ ar->io = io;
if (buffsize > 0)
loopIo_wantRead(io);
}
@@ -71,6 +72,7 @@ asyncWriter_init(AsyncWriter* aw,
aw->buffer = buffer;
aw->buffsize = buffsize;
aw->pos = 0;
+ aw->io = io;
if (buffsize > 0)
loopIo_wantWrite(io);
}
@@ -118,6 +120,7 @@ asyncLineReader_init(AsyncLineReader* alr,
alr->buffsize = buffsize;
alr->pos = 0;
alr->io = io;
+ alr->eol = '\n';
if (buffsize > 0)
loopIo_wantRead(io);
}
@@ -150,7 +153,7 @@ asyncLineReader_read(AsyncLineReader* alr)
return ASYNC_ERROR;
}
alr->buffer[alr->pos++] = (uint8_t)ch;
- if (ch == '\n') {
+ if (ch == alr->eol) {
loopIo_dontWantRead(alr->io);
return ASYNC_COMPLETE;
}
@@ -187,14 +190,16 @@ asyncLineReader_getLine(AsyncLineReader* alr)
pos--;
/* Check that we have a proper terminator, and replace it with 0 */
- if (buffer[pos] != '\n')
- return NULL;
-
- buffer[pos] = '\0';
+ if (alr->eol == '\n') {
+ if (buffer[pos] != '\n')
+ return NULL;
- /* Also strip \r\n */
- if (pos > 0 && buffer[--pos] == '\r') {
buffer[pos] = '\0';
+
+ /* Also strip \r\n */
+ if (pos > 0 && buffer[--pos] == '\r') {
+ buffer[pos] = '\0';
+ }
}
return (const char*) buffer;
diff --git a/android/async-utils.h b/android/async-utils.h
index 93148c1..6d460c2 100644
--- a/android/async-utils.h
+++ b/android/async-utils.h
@@ -137,6 +137,7 @@ typedef struct {
size_t buffsize;
size_t pos;
LoopIo* io;
+ char eol;
} AsyncLineReader;
/* Setup an AsyncLineReader to read at most 'buffsize' characters (bytes)
@@ -155,6 +156,17 @@ void asyncLineReader_init(AsyncLineReader* alr,
size_t buffsize,
LoopIo* io);
+/* Sets line terminator character for the reader.
+ * By default, asyncLineReader_init will set EOL to be '\n'. Sometimes it's more
+ * convenient to have '\0' as line terminator, so "line" reader easily becomes
+ * a "string" reader.
+ */
+AINLINED void
+asyncLineReader_setEOL(AsyncLineReader* alr, char eol)
+{
+ alr->eol = eol;
+}
+
/* Try to read line characters from 'io'.
* Returns:
* ASYNC_COMPLETE: An end-of-line was detected, call asyncLineReader_getLine
diff --git a/android/avd/hardware-properties.ini b/android/avd/hardware-properties.ini
index a2b05df..83a9300 100644
--- a/android/avd/hardware-properties.ini
+++ b/android/avd/hardware-properties.ini
@@ -367,6 +367,27 @@ default = yes
abstract = Proximity support
description = Whether there is an proximity in the device.
+# Magnetic field sensor
+name = hw.sensors.magnetic_field
+type = boolean
+default = yes
+abstract = Magnetic field support
+description = Provides magnetic field senosr values.
+
+# Orientation sensor
+name = hw.sensors.orientation
+type = boolean
+default = yes
+abstract = Orientation support
+description = Provides orientation sensor values.
+
+# Temperature sensor
+name = hw.sensors.temperature
+type = boolean
+default = yes
+abstract = Temperature support
+description = Provides temperatore sensor values.
+
# Kernel image.
#
# kernel.path specified the path to the kernel image
diff --git a/android/hw-sensors.c b/android/hw-sensors.c
index 9c39196..6a7721c 100644
--- a/android/hw-sensors.c
+++ b/android/hw-sensors.c
@@ -20,7 +20,10 @@
#include "hw/hw.h"
#include "qemu-char.h"
#include "qemu-timer.h"
+#include "android/sensors-port.h"
+#define E(...) derror(__VA_ARGS__)
+#define W(...) dwarning(__VA_ARGS__)
#define D(...) VERBOSE_PRINT(sensors,__VA_ARGS__)
/* define T_ACTIVE to 1 to debug transport communications */
@@ -165,9 +168,10 @@ typedef struct {
typedef struct HwSensorClient HwSensorClient;
typedef struct {
- QemudService* service;
- Sensor sensors[MAX_SENSORS];
- HwSensorClient* clients;
+ QemudService* service;
+ Sensor sensors[MAX_SENSORS];
+ HwSensorClient* clients;
+ AndroidSensorsPort* sensors_port;
} HwSensors;
struct HwSensorClient {
@@ -301,7 +305,8 @@ _hwSensorClient_tick( void* opaque )
if (_hwSensorClient_enabled(cl, ANDROID_SENSOR_MAGNETIC_FIELD)) {
sensor = &hw->sensors[ANDROID_SENSOR_MAGNETIC_FIELD];
- snprintf(buffer, sizeof buffer, "magnetic-field:%g:%g:%g",
+ /* NOTE: sensors HAL expects "magnetic", not "magnetic-field" name here. */
+ snprintf(buffer, sizeof buffer, "magnetic:%g:%g:%g",
sensor->u.magnetic.x,
sensor->u.magnetic.y,
sensor->u.magnetic.z);
@@ -430,6 +435,16 @@ _hwSensorClient_receive( HwSensorClient* cl, uint8_t* msg, int msglen )
D("%s: %s %s sensor", __FUNCTION__,
(cl->enabledMask & (1 << id)) ? "enabling" : "disabling", msg);
}
+
+ /* If emulating device is connected update sensor state there too. */
+ if (hw->sensors_port != NULL && sensors_port_is_connected(hw->sensors_port)) {
+ if (enabled) {
+ sensors_port_enable_sensor(hw->sensors_port, (const char*)msg);
+ } else {
+ sensors_port_disable_sensor(hw->sensors_port, (const char*)msg);
+ }
+ }
+
_hwSensorClient_tick(cl);
return;
}
@@ -645,14 +660,40 @@ _hwSensors_setCoarseOrientation( HwSensors* h, AndroidCoarseOrientation orient
static void
_hwSensors_init( HwSensors* h )
{
+ /* Try to see if there is a device attached that can be used for
+ * sensor emulation. */
+ h->sensors_port = sensors_port_create(h);
+ if (h->sensors_port == NULL) {
+ W("Unable to create sensors port: %s", strerror(errno));
+ }
+
h->service = qemud_service_register("sensors", 0, h, _hwSensors_connect,
_hwSensors_save, _hwSensors_load);
- if (android_hw->hw_accelerometer)
+ if (android_hw->hw_accelerometer) {
h->sensors[ANDROID_SENSOR_ACCELERATION].enabled = 1;
+ }
- if (android_hw->hw_sensors_proximity)
+ if (android_hw->hw_sensors_proximity) {
h->sensors[ANDROID_SENSOR_PROXIMITY].enabled = 1;
+ }
+
+ if (android_hw->hw_sensors_magnetic_field) {
+ h->sensors[ANDROID_SENSOR_MAGNETIC_FIELD].enabled = 1;
+ }
+
+ if (android_hw->hw_sensors_orientation) {
+ h->sensors[ANDROID_SENSOR_ORIENTATION].enabled = 1;
+ }
+
+ if (android_hw->hw_sensors_temperature) {
+ h->sensors[ANDROID_SENSOR_TEMPERATURE].enabled = 1;
+ }
+
+ if (h->sensors_port != NULL) {
+ /* Init sensors on the attached device. */
+ sensors_port_init_sensors(h->sensors_port);
+ }
/* XXX: TODO: Add other tests when we add the corresponding
* properties to hardware-properties.ini et al. */
diff --git a/android/sensors-port.c b/android/sensors-port.c
new file mode 100644
index 0000000..7422f96
--- /dev/null
+++ b/android/sensors-port.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android/sensors-port.h"
+#include "android/hw-sensors.h"
+
+#define E(...) derror(__VA_ARGS__)
+#define W(...) dwarning(__VA_ARGS__)
+#define D(...) VERBOSE_PRINT(sensors_port,__VA_ARGS__)
+#define D_ACTIVE VERBOSE_CHECK(camera)
+
+/* Maximum number of sensors supported. */
+#define ASP_MAX_SENSOR 12
+
+/* Maximum length of a sensor message. */
+#define ASP_MAX_SENSOR_MSG 1024
+
+/* Maximum length of a sensor event. */
+#define ASP_MAX_SENSOR_EVENT 256
+
+/* Query timeout in milliseconds. */
+#define ASP_QUERY_TIMEOUT 3000
+
+/* Sensors port descriptor. */
+struct AndroidSensorsPort {
+ /* Caller identifier. */
+ void* opaque;
+ /* Connected android device. */
+ AndroidDevice* device;
+ /* String containing list of all available sensors. */
+ char sensors[ASP_MAX_SENSOR * 64];
+ /* Array of available sensor names. Note that each string in this array
+ * points inside the 'sensors' buffer. */
+ const char* sensor_list[ASP_MAX_SENSOR];
+ /* Number of available sensors. */
+ int sensors_num;
+ /* Connection status: 1 connected, 0 - disconnected. */
+ int is_connected;
+ /* Buffer where to receive sensor messages. */
+ char sensor_msg[ASP_MAX_SENSOR_MSG];
+ /* Buffer where to receive sensor events. */
+ char events[ASP_MAX_SENSOR_EVENT];
+};
+
+/* Destroys and frees the descriptor. */
+static void
+_sensors_port_free(AndroidSensorsPort* asp)
+{
+ if (asp != NULL) {
+ if (asp->device != NULL) {
+ android_device_destroy(asp->device);
+ }
+ AFREE(asp);
+ }
+}
+
+/********************************************************************************
+ * Sensors port callbacks
+ *******************************************************************************/
+
+/* A callback that invoked on sensor events.
+ * Param:
+ * opaque - AndroidSensorsPort instance.
+ * ad - Android device used by this sensors port.
+ * msg, msgsize - Sensor event message
+ * failure - Message receiving status.
+ */
+static void
+_on_sensor_received(void* opaque, AndroidDevice* ad, char* msg, int msgsize)
+{
+ float fvalues[3] = {0, 0, 0};
+ char sensor[ASP_MAX_SENSOR_MSG];
+ char* value;
+ int id;
+ AndroidSensorsPort* asp = (AndroidSensorsPort*)opaque;
+
+ if (errno) {
+ D("Sensors notification has failed on sensors port: %s", strerror(errno));
+ return;
+ }
+
+ /* Parse notification, separating sensor name from parameters. */
+ memcpy(sensor, msg, msgsize);
+ value = strchr(sensor, ':');
+ if (value == NULL) {
+ W("Bad format for sensor notification: %s", msg);
+ return;
+ }
+ sensor[value-sensor] = '\0';
+ value++;
+
+ id = android_sensors_get_id_from_name(sensor);
+ if (id >= 0) {
+ /* Parse the value part to get the sensor values(a, b, c) */
+ int i;
+ char* pnext;
+ char* pend = value + strlen(value);
+ for (i = 0; i < 3; i++, value = pnext + 1) {
+ pnext=strchr( value, ':' );
+ if (pnext) {
+ *pnext = 0;
+ } else {
+ pnext = pend;
+ }
+
+ if (pnext > value) {
+ if (1 != sscanf( value,"%g", &fvalues[i] )) {
+ W("Bad parameters in sensor notification %s", msg);
+ return;
+ }
+ }
+ }
+ android_sensors_set(id, fvalues[0], fvalues[1], fvalues[2]);
+ } else {
+ W("Unknown sensor name '%s' in '%s'", sensor, msg);
+ }
+
+ /* Listen to the next event. */
+ android_device_listen(ad, asp->events, sizeof(asp->events), _on_sensor_received);
+}
+
+/* A callback that is invoked when android device is connected (i.e. both, command
+ * and event channels have been stablished.
+ * Param:
+ * opaque - AndroidSensorsPort instance.
+ * ad - Android device used by this sensors port.
+ * failure - Connections status.
+ */
+static void
+_on_device_connected(void* opaque, AndroidDevice* ad, int failure)
+{
+ if (!failure) {
+ AndroidSensorsPort* asp = (AndroidSensorsPort*)opaque;
+ asp->is_connected = 1;
+ D("Sensor emulation has started");
+ /* Initialize sensors on device. */
+ sensors_port_init_sensors(asp);
+ }
+}
+
+/* Invoked when an I/O failure occurs on a socket.
+ * Note that this callback will not be invoked on connection failures.
+ * Param:
+ * opaque - AndroidSensorsPort instance.
+ * ad - Android device instance
+ * ads - Connection socket where failure has occured.
+ * failure - Contains 'errno' indicating the reason for failure.
+ */
+static void
+_on_io_failure(void* opaque, AndroidDevice* ad, int failure)
+{
+ AndroidSensorsPort* asp = (AndroidSensorsPort*)opaque;
+ E("Sensors port got disconnected: %s", strerror(failure));
+ asp->is_connected = false;
+ android_device_disconnect(ad);
+ android_device_connect_async(ad, _on_device_connected);
+}
+
+/********************************************************************************
+ * Sensors port API
+ *******************************************************************************/
+
+AndroidSensorsPort*
+sensors_port_create(void* opaque)
+{
+ AndroidSensorsPort* asp;
+ char* wrk;
+ int res;
+
+ ANEW0(asp);
+ asp->opaque = opaque;
+ asp->is_connected = 0;
+
+ asp->device = android_device_init(asp, AD_SENSOR_PORT, _on_io_failure);
+ if (asp->device == NULL) {
+ _sensors_port_free(asp);
+ return NULL;
+ }
+
+ res = android_device_connect_sync(asp->device, ASP_QUERY_TIMEOUT);
+ if (res != 0) {
+ sensors_port_destroy(asp);
+ return NULL;
+ }
+
+ res = android_device_query(asp->device, "list",
+ asp->sensors, sizeof(asp->sensors),
+ ASP_QUERY_TIMEOUT);
+ if (res != 0) {
+ sensors_port_destroy(asp);
+ return NULL;
+ }
+
+ /* Parse sensor list. */
+ asp->sensors_num = 0;
+ wrk = asp->sensors;
+
+ while (wrk != NULL && *wrk != '\0' && *wrk != '\n') {
+ asp->sensor_list[asp->sensors_num] = wrk;
+ asp->sensors_num++;
+ wrk = strchr(wrk, '\n');
+ if (wrk != NULL) {
+ *wrk = '\0'; wrk++;
+ }
+ }
+
+ android_device_listen(asp->device, asp->events, sizeof(asp->events),
+ _on_sensor_received);
+ return asp;
+}
+
+int
+sensors_port_init_sensors(AndroidSensorsPort* asp)
+{
+ int res, id;
+
+ /* Disable all sensors for now. Reenable only those that are emulated. */
+ res = sensors_port_disable_sensor(asp, "all");
+ if (res) {
+ return res;
+ }
+
+ /* Start listening on sensor events. */
+ res = android_device_listen(asp->device, asp->events, sizeof(asp->events),
+ _on_sensor_received);
+ if (res) {
+ return res;
+ }
+
+ /* Walk throuh the list of enabled sensors enabling them on the device. */
+ for (id = 0; id < MAX_SENSORS; id++) {
+ if (android_sensors_get_sensor_status(id) == 1) {
+ res = sensors_port_enable_sensor(asp, android_sensors_get_name_from_id(id));
+ if (res == 0) {
+ D("Sensor '%s' is enabled on the device.",
+ android_sensors_get_name_from_id(id));
+ }
+ }
+ }
+
+ /* Start sensor events. */
+ return sensors_port_start(asp);
+}
+
+void
+sensors_port_destroy(AndroidSensorsPort* asp)
+{
+ _sensors_port_free(asp);
+}
+
+int
+sensors_port_is_connected(AndroidSensorsPort* asp)
+{
+ return asp->is_connected;
+}
+
+int
+sensors_port_enable_sensor(AndroidSensorsPort* asp, const char* name)
+{
+ char query[1024];
+ char qresp[1024];
+ snprintf(query, sizeof(query), "enable:%s", name);
+ const int res =
+ android_device_query(asp->device, query, qresp, sizeof(qresp),
+ ASP_QUERY_TIMEOUT);
+ if (res) {
+ if (errno) {
+ D("Query '%s' failed on I/O: %s", query, strerror(errno));
+ } else {
+ D("Query '%s' failed on device: %s", query, qresp);
+ }
+ }
+ return res;
+}
+
+int
+sensors_port_disable_sensor(AndroidSensorsPort* asp, const char* name)
+{
+ char query[1024];
+ char qresp[1024];
+ snprintf(query, sizeof(query), "disable:%s", name);
+ const int res =
+ android_device_query(asp->device, query, qresp, sizeof(qresp),
+ ASP_QUERY_TIMEOUT);
+ if (res) {
+ if (errno) {
+ D("Query '%s' failed on I/O: %s", query, strerror(errno));
+ } else {
+ D("Query '%s' failed on device: %s", query, qresp);
+ }
+ }
+ return res;
+}
+
+int
+sensors_port_start(AndroidSensorsPort* asp)
+{
+ char qresp[ASP_MAX_SENSOR_MSG];
+ const int res =
+ android_device_query(asp->device, "start", qresp, sizeof(qresp),
+ ASP_QUERY_TIMEOUT);
+ if (res) {
+ if (errno) {
+ D("Query 'start' failed on I/O: %s", strerror(errno));
+ } else {
+ D("Query 'start' failed on device: %s", qresp);
+ }
+ }
+ return res;
+}
+
+int
+sensors_port_stop(AndroidSensorsPort* asp)
+{
+ char qresp[ASP_MAX_SENSOR_MSG];
+ const int res =
+ android_device_query(asp->device, "stop", qresp, sizeof(qresp),
+ ASP_QUERY_TIMEOUT);
+ if (res) {
+ if (errno) {
+ D("Query 'stop' failed on I/O: %s", strerror(errno));
+ } else {
+ D("Query 'stop' failed on device: %s", qresp);
+ }
+ }
+
+ return res;
+}
diff --git a/android/sensors-port.h b/android/sensors-port.h
new file mode 100644
index 0000000..dc5c966
--- /dev/null
+++ b/android/sensors-port.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SENSORS_PORT_H_
+#define ANDROID_SENSORS_PORT_H_
+
+/*
+ * Encapsulates exchange protocol between the sensor emulator, and an application
+ * running on an Android device that provides sensor values, and is connected to
+ * the host via USB.
+ */
+
+#include "android/android-device.h"
+
+/* Declares sensors port descriptor. */
+typedef struct AndroidSensorsPort AndroidSensorsPort;
+
+/* Creates sensors port, and connects it to the device.
+ * Param:
+ * opaque - An opaque pointer that is passed back to the callback routines.
+ * Return:
+ * Initialized device descriptor on success, or NULL on failure. If failure is
+ * returned from this routine, 'errno' indicates the reason for failure. If this
+ * routine successeds, a connection is established with the sensor reading
+ * application on the device.
+ */
+extern AndroidSensorsPort* sensors_port_create(void* opaque);
+
+/* Disconnects from the sensors port, and destroys the descriptor. */
+extern void sensors_port_destroy(AndroidSensorsPort* asp);
+
+/* Initializes sensors on the connected device. */
+extern int sensors_port_init_sensors(AndroidSensorsPort* asp);
+
+/* Checks if port is connected to a sensor reading application on the device.
+ * Note that connection can go out and then be restored at any time after
+ * sensors_port_create API succeeded.
+ */
+extern int sensors_port_is_connected(AndroidSensorsPort* asp);
+
+/* Enables events from a particular sensor.
+ * Param:
+ * asp - Android sensors port instance returned from sensors_port_create.
+ * name - Name of the sensor to enable events on. If this parameter is "all",
+ * then events on all sensors will be enabled.
+ * Return:
+ * Zero on success, failure otherwise.
+ */
+extern int sensors_port_enable_sensor(AndroidSensorsPort* asp, const char* name);
+
+
+/* Disables events from a particular sensor.
+ * Param:
+ * asp - Android sensors port instance returned from sensors_port_create.
+ * name - Name of the sensor to disable events on. If this parameter is "all",
+ * then events on all sensors will be disable.
+ * Return:
+ * Zero on success, failure otherwise.
+ */
+extern int sensors_port_disable_sensor(AndroidSensorsPort* asp, const char* name);
+
+/* Queries the connected application to start delivering sensor events.
+ * Param:
+ * asp - Android sensors port instance returned from sensors_port_create.
+ * Return:
+ * Zero on success, failure otherwise.
+ */
+extern int sensors_port_start(AndroidSensorsPort* asp);
+
+/* Queries the connected application to stop delivering sensor events.
+ * Param:
+ * asp - Android sensors port instance returned from sensors_port_create.
+ * Return:
+ * Zero on success, failure otherwise.
+ */
+extern int sensors_port_stop(AndroidSensorsPort* asp);
+
+#endif /* ANDROID_SENSORS_PORT_H_ */
diff --git a/android/utils/debug.h b/android/utils/debug.h
index 096b002..ea5a6b0 100644
--- a/android/utils/debug.h
+++ b/android/utils/debug.h
@@ -36,6 +36,8 @@
_VERBOSE_TAG(sensors, "emulated sensors") \
_VERBOSE_TAG(memcheck, "memory checker") \
_VERBOSE_TAG(camera, "camera") \
+ _VERBOSE_TAG(adevice, "android device connected via port forwarding") \
+ _VERBOSE_TAG(sensors_port, "sensors emulator connected to android device") \
_VERBOSE_TAG(gles, "hardware OpenGLES emulation") \
#define _VERBOSE_TAG(x,y) VERBOSE_##x,