aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.android2
-rw-r--r--android/async-console.c147
-rw-r--r--android/async-console.h63
-rw-r--r--android/async-utils.c266
-rw-r--r--android/async-utils.h187
5 files changed, 665 insertions, 0 deletions
diff --git a/Makefile.android b/Makefile.android
index 942b03d..65d56c0 100644
--- a/Makefile.android
+++ b/Makefile.android
@@ -754,6 +754,8 @@ UI_AND_CORE_SOURCES = \
sockets.c \
android/keycode-array.c \
android/charmap.c \
+ android/async-utils.c \
+ android/async-console.c \
android/utils/assert.c \
android/utils/bufprint.c \
android/utils/debug.c \
diff --git a/android/async-console.c b/android/async-console.c
new file mode 100644
index 0000000..f486df0
--- /dev/null
+++ b/android/async-console.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2010 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/async-console.h"
+#include <string.h>
+
+/*
+ * State diagram, ommitting the ERROR state
+ *
+ * INITIAL -->--+
+ * | |
+ * | CONNECTING
+ * | |
+ * |<-----+
+ * v
+ * READ_BANNER_1
+ * |
+ * v
+ * READ_BANNER_2
+ * |
+ * v
+ * COMPLETE
+ */
+
+enum {
+ STATE_INITIAL,
+ STATE_CONNECTING,
+ STATE_ERROR,
+ STATE_READ_BANNER_1,
+ STATE_READ_BANNER_2,
+ STATE_COMPLETE
+};
+
+/* A helper function to prepare the line reader and switch to a new state */
+static AsyncStatus
+_acc_prepareLineReader(AsyncConsoleConnector* acc, LoopIo* io, int newState)
+{
+ acc->state = newState;
+ asyncLineReader_init(acc->lreader, acc->lbuff, sizeof(acc->lbuff), io);
+ return ASYNC_NEED_MORE;
+}
+
+AsyncStatus
+asyncConsoleConnector_connect(AsyncConsoleConnector* acc,
+ const SockAddress* address,
+ LoopIo* io)
+{
+ acc->state = STATE_INITIAL;
+ acc->address = address[0];
+ return asyncConsoleConnector_run(acc, io);
+}
+
+
+AsyncStatus
+asyncConsoleConnector_run(AsyncConsoleConnector* acc,
+ LoopIo* io)
+{
+ AsyncStatus status = ASYNC_NEED_MORE;
+
+ for (;;) {
+ switch (acc->state)
+ {
+ case STATE_ERROR: /* reporting previous error */
+ errno = acc->error;
+ return ASYNC_ERROR;
+
+ case STATE_INITIAL: /* initial connection attempt */
+ acc->state = STATE_CONNECTING;
+ status = asyncConnector_init(acc->connector, &acc->address, io);
+ if (status == ASYNC_ERROR)
+ goto SET_ERROR;
+
+ if (status == ASYNC_COMPLETE) { /* immediate connection */
+ _acc_prepareLineReader(acc, io, STATE_READ_BANNER_1);
+ continue;
+ }
+ break;
+
+ case STATE_CONNECTING: /* still trying to connect */
+ status = asyncConnector_run(acc->connector, io);
+ if (status == ASYNC_ERROR)
+ goto SET_ERROR;
+
+ if (status == ASYNC_COMPLETE) {
+ _acc_prepareLineReader(acc, io, STATE_READ_BANNER_1);
+ continue;
+ }
+ break;
+
+ case STATE_READ_BANNER_1: /* reading the first banner line */
+ status = asyncLineReader_read(acc->lreader, io);
+ if (status == ASYNC_ERROR)
+ goto SET_ERROR;
+
+ if (status == ASYNC_COMPLETE) {
+ /* Check that first line starts with "Android Console:",
+ * otherwise we're not talking to the right program. */
+ const char* line = asyncLineReader_getLine(acc->lreader);
+ if (line == NULL || memcmp(line, "Android Console:", 16)) {
+ goto BAD_BANNER;
+ }
+ /* ok, fine, prepare for the next banner line then */
+ _acc_prepareLineReader(acc, io, STATE_READ_BANNER_2);
+ continue;
+ }
+ break;
+
+ case STATE_READ_BANNER_2: /* reading the second banner line */
+ status = asyncLineReader_read(acc->lreader, io);
+ if (status == ASYNC_ERROR)
+ goto SET_ERROR;
+
+ if (status == ASYNC_COMPLETE) {
+ const char* line = asyncLineReader_getLine(acc->lreader);
+ if (line == NULL) {
+ goto BAD_BANNER;
+ }
+ /* ok, we're done !*/
+ acc->state = STATE_COMPLETE;
+ return ASYNC_COMPLETE;
+ }
+ break;
+
+ case STATE_COMPLETE:
+ status = ASYNC_COMPLETE;
+ }
+ return status;
+ }
+BAD_BANNER:
+ errno = ENOPROTOOPT;
+SET_ERROR:
+ acc->state = STATE_ERROR;
+ acc->error = errno;
+ return ASYNC_ERROR;
+}
diff --git a/android/async-console.h b/android/async-console.h
new file mode 100644
index 0000000..6779ae2
--- /dev/null
+++ b/android/async-console.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 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_ASYNC_CONSOLE_H
+#define ANDROID_ASYNC_CONSOLE_H
+
+#include "android/async-utils.h"
+
+/* An AsyncConsoleConnector allows you to asynchronously connect to an
+ * Android console port.
+ */
+typedef struct {
+ int state;
+ int error;
+ LoopIo* io;
+ SockAddress address;
+ AsyncConnector connector[1];
+ AsyncLineReader lreader[1];
+ uint8_t lbuff[128];
+} AsyncConsoleConnector;
+
+/* Initialize the console connector. This attempts to connect to the address
+ * provided through 'io'. Use asyncConsoleConnect_run() after that.
+ */
+AsyncStatus
+asyncConsoleConnector_connect(AsyncConsoleConnector* acc,
+ const SockAddress* address,
+ LoopIo* io);
+
+/* Asynchronous console connection management. Returns:
+ *
+ * ASYNC_COMPLETE:
+ * Connection was complete, and the console banner was properly read/eaten.
+ * you can now send/write commands through the console with 'io'.
+ *
+ * ASYNC_ERROR:
+ * An error occured, either during the connection itself, or when
+ * reading the content. This sets errno to ENOPROTOOPT if the connector
+ * detects that you're not connected to a proper Android emulator console
+ * port (i.e. if the banner was incorrect). Other errors are possible
+ * (e.g. in case of early connection termination).
+ *
+ * ASYNC_NEED_MORE:
+ * Not enough data was exchanged, call this function later.
+ */
+AsyncStatus
+asyncConsoleConnector_run(AsyncConsoleConnector* acc,
+ LoopIo* io);
+
+
+#endif /* ANDROID_ASYNC_CONSOLE_H */
diff --git a/android/async-utils.c b/android/async-utils.c
new file mode 100644
index 0000000..678b0b4
--- /dev/null
+++ b/android/async-utils.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2010 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/async-utils.h"
+#include "unistd.h"
+
+void
+asyncReader_init(AsyncReader* ar,
+ void* buffer,
+ size_t buffsize,
+ LoopIo* io)
+{
+ ar->buffer = buffer;
+ ar->buffsize = buffsize;
+ ar->pos = 0;
+ if (buffsize > 0)
+ loopIo_wantRead(io);
+}
+
+AsyncStatus
+asyncReader_read(AsyncReader* ar,
+ LoopIo* io)
+{
+ int ret;
+
+ if (ar->pos >= ar->buffsize) {
+ return ASYNC_COMPLETE;
+ }
+
+ do {
+ ret = read(io->fd, ar->buffer + ar->pos, ar->buffsize - ar->pos);
+ if (ret == 0) {
+ /* disconnection ! */
+ errno = ECONNRESET;
+ return ASYNC_ERROR;
+ }
+ if (ret < 0) {
+ if (errno == EINTR) /* loop on EINTR */
+ continue;
+ if (errno == EWOULDBLOCK || errno == EAGAIN) {
+ loopIo_wantRead(io);
+ return ASYNC_NEED_MORE;
+ }
+ return ASYNC_ERROR;
+ }
+ ar->pos += ret;
+
+ } while (ar->pos < ar->buffsize);
+
+ loopIo_dontWantRead(io);
+ return ASYNC_COMPLETE;
+}
+
+void
+asyncWriter_init(AsyncWriter* aw,
+ const void* buffer,
+ size_t buffsize,
+ LoopIo* io)
+{
+ aw->buffer = buffer;
+ aw->buffsize = buffsize;
+ aw->pos = 0;
+ if (buffsize > 0)
+ loopIo_wantWrite(io);
+}
+
+AsyncStatus
+asyncWriter_write(AsyncWriter* aw,
+ LoopIo* io)
+{
+ int ret;
+
+ if (aw->pos >= aw->buffsize) {
+ return ASYNC_COMPLETE;
+ }
+
+ do {
+ ret = write(io->fd, aw->buffer + aw->pos, aw->buffsize - aw->pos);
+ if (ret == 0) {
+ /* disconnection ! */
+ errno = ECONNRESET;
+ return ASYNC_ERROR;
+ }
+ if (ret < 0) {
+ if (errno == EINTR) /* loop on EINTR */
+ continue;
+ if (errno == EWOULDBLOCK || errno == EAGAIN) {
+ return ASYNC_NEED_MORE;
+ }
+ return ASYNC_ERROR;
+ }
+ aw->pos += ret;
+
+ } while (aw->pos < aw->buffsize);
+
+ loopIo_dontWantWrite(io);
+ return ASYNC_COMPLETE;
+}
+
+
+void
+asyncLineReader_init(AsyncLineReader* alr,
+ void* buffer,
+ size_t buffsize,
+ LoopIo* io)
+{
+ alr->buffer = buffer;
+ alr->buffsize = buffsize;
+ alr->pos = 0;
+ if (buffsize > 0)
+ loopIo_wantRead(io);
+}
+
+AsyncStatus
+asyncLineReader_read(AsyncLineReader* alr,
+ LoopIo* io)
+{
+ int ret;
+
+ if (alr->pos >= alr->buffsize) {
+ errno = ENOMEM;
+ return ASYNC_ERROR;
+ }
+
+ do {
+ char ch;
+ ret = read(io->fd, &ch, 1);
+ if (ret == 0) {
+ /* disconnection ! */
+ errno = ECONNRESET;
+ return ASYNC_ERROR;
+ }
+ if (ret < 0) {
+ if (errno == EINTR) /* loop on EINTR */
+ continue;
+ if (errno == EWOULDBLOCK || errno == EAGAIN) {
+ loopIo_wantRead(io);
+ return ASYNC_NEED_MORE;
+ }
+ return ASYNC_ERROR;
+ }
+ alr->buffer[alr->pos++] = (uint8_t)ch;
+ if (ch == '\n') {
+ loopIo_dontWantRead(io);
+ return ASYNC_COMPLETE;
+ }
+ } while (alr->pos < alr->buffsize);
+
+ /* Not enough room in the input buffer!*/
+ loopIo_dontWantRead(io);
+ errno = ENOMEM;
+ return ASYNC_ERROR;
+}
+
+const char*
+asyncLineReader_getLineRaw(AsyncLineReader* alr, int *pLength)
+{
+ if (alr->pos == 0 || alr->pos > alr->buffsize)
+ return NULL;
+
+ if (pLength != 0)
+ *pLength = alr->pos;
+
+ return (const char*) alr->buffer;
+}
+
+const char*
+asyncLineReader_getLine(AsyncLineReader* alr)
+{
+ /* Strip trailing \n if any */
+ size_t pos = alr->pos;
+ char* buffer = (char*) alr->buffer;
+
+ if (pos == 0 || pos > alr->buffsize)
+ return NULL;
+
+ pos--;
+
+ /* Check that we have a proper terminator, and replace it with 0 */
+ if (buffer[pos] != '\n')
+ return NULL;
+
+ buffer[pos] = '\0';
+
+ /* Also strip \r\n */
+ if (pos > 0 && buffer[--pos] == '\r') {
+ buffer[pos] = '\0';
+ }
+
+ return (const char*) buffer;
+}
+
+
+enum {
+ CONNECT_ERROR = 0,
+ CONNECT_CONNECTING,
+ CONNECT_COMPLETED
+};
+
+AsyncStatus
+asyncConnector_init(AsyncConnector* ac,
+ const SockAddress* address,
+ LoopIo* io)
+{
+ int ret;
+ ac->error = 0;
+ ret = socket_connect(io->fd, address);
+ if (ret == 0) {
+ ac->state = CONNECT_COMPLETED;
+ return ASYNC_COMPLETE;
+ }
+ if (errno == EINPROGRESS || errno == EWOULDBLOCK || errno == EAGAIN) {
+ ac->state = CONNECT_CONNECTING;
+ /* The socket will be marked writable for select() when the
+ * connection is established, or when it is definitely
+ * refused / timed-out, for any reason. */
+ loopIo_wantWrite(io);
+ return ASYNC_NEED_MORE;
+ }
+ ac->error = errno;
+ ac->state = CONNECT_ERROR;
+ return ASYNC_ERROR;
+}
+
+AsyncStatus
+asyncConnector_run(AsyncConnector* ac, LoopIo* io)
+{
+ switch (ac->state) {
+ case CONNECT_ERROR:
+ errno = ac->error;
+ return ASYNC_ERROR;
+
+ case CONNECT_CONNECTING:
+ loopIo_dontWantWrite(io);
+ /* We need to read the socket error to determine if
+ * the connection was really succesful or not. This
+ * is optional, because in case of error a future
+ * read() or write() will fail anyway, but this
+ * allows us to get a better error value as soon as
+ * possible.
+ */
+ ac->error = socket_get_error(io->fd);
+ if (ac->error == 0) {
+ ac->state = CONNECT_COMPLETED;
+ return ASYNC_COMPLETE;
+ }
+ ac->state = CONNECT_ERROR;
+ errno = ac->error;
+ return ASYNC_ERROR;
+
+ default:
+ return ASYNC_COMPLETE;
+ }
+}
diff --git a/android/async-utils.h b/android/async-utils.h
new file mode 100644
index 0000000..e34e1bb
--- /dev/null
+++ b/android/async-utils.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2010 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_ASYNC_UTILS_H
+#define ANDROID_ASYNC_UTILS_H
+
+#include "android/looper.h"
+#include "sockets.h"
+
+/* A set of useful data types to perform asynchronous operations.
+ *
+ * IMPORTANT NOTE:
+ * In case of network disconnection, read() and write() just return 0
+ * the first time they are called. As a convenience, these functions
+ * will return ASYNC_ERROR and set 'errno' to ECONNRESET instead.
+ */
+typedef enum {
+ ASYNC_COMPLETE = 0, /* asynchronous operation completed */
+ ASYNC_ERROR, /* an error occurred, look at errno */
+ ASYNC_NEED_MORE /* more data is needed, try again later */
+} AsyncStatus;
+
+/* An AsyncReader makes it easier to read a given number of bytes into
+ * a target buffer asynchronously. Usage is the following:
+ *
+ * 1/ setup the reader with asyncReader_init(ar, buffer, buffsize,io);
+ * 2/ call asyncReader_read(ar, io), where 'io' is a LoopIo whenever
+ * you can receive data, i.e. just after the init() or in your
+ * own callback.
+ */
+typedef struct {
+ uint8_t* buffer;
+ size_t buffsize;
+ size_t pos;
+} AsyncReader;
+
+/* Setup an ASyncReader, by giving the address of the read buffer,
+ * and the number of bytes we want to read.
+ *
+ * This also calls loopIo_wantRead(io) for you.
+ */
+void asyncReader_init(AsyncReader* ar,
+ void* buffer,
+ size_t buffsize,
+ LoopIo* io);
+
+/* Try to read data from 'io' and return the state of the read operation.
+ *
+ * Returns:
+ * ASYNC_COMPLETE: If the read operation was complete. This will also
+ * call loopIo_dontWantRead(io) for you.
+ *
+ * ASYNC_ERROR: If an error occured (see errno). The error will be
+ * ECONNRESET in case of disconnection.
+ *
+ * ASYNC_NEED_MORE: If there was not enough incoming data to complete
+ * the read (or if 'events' doesn't contain LOOP_IO_READ).
+ */
+AsyncStatus asyncReader_read(AsyncReader* ar,
+ LoopIo* io);
+
+/* An AsyncWriter is the counterpart of an AsyncReader, but for writing
+ * data to a file descriptor asynchronously.
+ */
+typedef struct {
+ const uint8_t* buffer;
+ size_t buffsize;
+ size_t pos;
+} AsyncWriter;
+
+/* Setup an ASyncReader, by giving the address of the read buffer,
+ * and the number of bytes we want to read.
+ *
+ * This also calls loopIo_wantWrite(io) for you.
+ */
+void asyncWriter_init(AsyncWriter* aw,
+ const void* buffer,
+ size_t buffsize,
+ LoopIo* io);
+
+/* Try to write data to 'io' and return the state of the write operation.
+ *
+ * Returns:
+ * ASYNC_COMPLETE: If the write operation was complete. This will also
+ * call loopIo_dontWantWrite(io) for you.
+ *
+ * ASYNC_ERROR: If an error occured (see errno). The error will be
+ * ECONNRESET in case of disconnection.
+ *
+ * ASYNC_NEED_MORE: If not all bytes could be sent yet (or if 'events'
+ * doesn't contain LOOP_IO_READ).
+ */
+AsyncStatus asyncWriter_write(AsyncWriter* aw,
+ LoopIo* io);
+
+
+/* An AsyncLineReader allows you to read one line of text asynchronously.
+ * The biggest difference with AsyncReader is that you don't know the line
+ * size in advance, so the object will read data byte-by-byte until it
+ * encounters a '\n'.
+ */
+typedef struct {
+ uint8_t* buffer;
+ size_t buffsize;
+ size_t pos;
+} AsyncLineReader;
+
+/* Setup an AsyncLineReader to read at most 'buffsize' characters (bytes)
+ * into 'buffer'. The reader will stop when it finds a '\n' which will be
+ * part of the buffer by default.
+ *
+ * NOTE: buffsize must be > 0. If not, asyncLineReader_getLine will return
+ * ASYNC_ERROR with errno == ENOMEM.
+ *
+ * buffsize must also sufficiently big to hold the final '\n'.
+ *
+ * Also calls loopIo_wantRead(io) for you.
+ */
+void asyncLineReader_init(AsyncLineReader* alr,
+ void* buffer,
+ size_t buffsize,
+ LoopIo* io);
+
+/* Try to read line characters from 'io'.
+ * Returns:
+ * ASYNC_COMPLETE: An end-of-line was detected, call asyncLineReader_getLine
+ * to extract the line content.
+ *
+ * ASYNC_ERROR: An error occured. Note that in case of disconnection,
+ * errno will be set to ECONNRESET, but you should be able
+ * to call asyncLineReader_getLine to read the partial line
+ * that was read.
+ *
+ * In case of overflow, errno will be set to ENOMEM.
+ *
+ * ASYNC_NEED_MORE: If there was not enough incoming data (or events
+ * does not contain LOOP_IO_READ).
+ */
+AsyncStatus asyncLineReader_read(AsyncLineReader* alr,
+ LoopIo* io);
+
+/* Return a pointer to the NON-ZERO-TERMINATED line characters, if any.
+ * If 'pLength" is not NULL, the function sets '*pLength' to the length
+ * in bytes of the line.
+ *
+ * Returns:
+ * NULL if 'buffsize' was initially 0, otherwise, a pointer to 'buffer'
+ * as passed in asyncLineReader_setup().
+ *
+ * NOTE: The data is *not* zero terminated, but its last character
+ * should be '\n' unless an error occured.
+ */
+const char* asyncLineReader_getLineRaw(AsyncLineReader* alr, int *pLength);
+
+/* Return a pointer to the ZERO-TERMINATED line, with final '\n' or '\r\n'
+ * stripped. This will be NULL in case of error though.
+ */
+const char* asyncLineReader_getLine(AsyncLineReader* alr);
+
+/* Asynchronous connection to a socket
+ */
+typedef struct {
+ int error;
+ int state;
+} AsyncConnector;
+
+AsyncStatus
+asyncConnector_init(AsyncConnector* ac,
+ const SockAddress* address,
+ LoopIo* io);
+
+AsyncStatus
+asyncConnector_run(AsyncConnector* ac, LoopIo* io);
+
+#endif /* ANDROID_ASYNC_UTILS_H */