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;
+}