Introduce asynchronous operation helpers.

<android/async-utils.h> contains generic helpers to read, write
and connect to sockets.

<android/async-console.h> contains a helper class to connect
to Android console port asynchronously.

Change-Id: I5d0a49a770ad974c5d4382438d75e9eb624368d1
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 */