1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#include "android/async-console.h"
17#include <string.h>
18
19/*
20 * State diagram, ommitting the ERROR state
21 *
22 *  INITIAL -->--+
23 *     |        |
24 *     |     CONNECTING
25 *     |       |
26 *     |<-----+
27 *     v
28 *  READ_BANNER_1
29 *     |
30 *     v
31 *  READ_BANNER_2
32 *     |
33 *     v
34 *  COMPLETE
35 */
36
37enum {
38    STATE_INITIAL,
39    STATE_CONNECTING,
40    STATE_ERROR,
41    STATE_READ_BANNER_1,
42    STATE_READ_BANNER_2,
43    STATE_COMPLETE
44};
45
46/* A helper function to prepare the line reader and switch to a new state */
47static AsyncStatus
48_acc_prepareLineReader(AsyncConsoleConnector* acc, int newState)
49{
50    acc->state = newState;
51    asyncLineReader_init(acc->lreader, acc->lbuff, sizeof(acc->lbuff), acc->io);
52    return ASYNC_NEED_MORE;
53}
54
55AsyncStatus
56asyncConsoleConnector_connect(AsyncConsoleConnector* acc,
57                              const SockAddress*     address,
58                              LoopIo*                io)
59{
60    acc->state = STATE_INITIAL;
61    acc->address = address[0];
62    acc->io = io;
63    return asyncConsoleConnector_run(acc);
64}
65
66
67AsyncStatus
68asyncConsoleConnector_run(AsyncConsoleConnector* acc)
69{
70    AsyncStatus  status = ASYNC_NEED_MORE;
71
72    for (;;) {
73        switch (acc->state)
74        {
75        case STATE_ERROR: /* reporting previous error */
76            errno = acc->error;
77            return ASYNC_ERROR;
78
79        case STATE_INITIAL: /* initial connection attempt */
80            acc->state = STATE_CONNECTING;
81            status = asyncConnector_init(acc->connector, &acc->address, acc->io);
82            if (status == ASYNC_ERROR)
83                goto SET_ERROR;
84
85            if (status == ASYNC_COMPLETE) { /* immediate connection */
86                _acc_prepareLineReader(acc, STATE_READ_BANNER_1);
87                continue;
88            }
89            break;
90
91        case STATE_CONNECTING: /* still trying to connect */
92            status = asyncConnector_run(acc->connector);
93            if (status == ASYNC_ERROR)
94                goto SET_ERROR;
95
96            if (status == ASYNC_COMPLETE) {
97                _acc_prepareLineReader(acc, STATE_READ_BANNER_1);
98                continue;
99            }
100            break;
101
102        case STATE_READ_BANNER_1: /* reading the first banner line */
103            status = asyncLineReader_read(acc->lreader);
104            if (status == ASYNC_ERROR)
105                goto SET_ERROR;
106
107            if (status == ASYNC_COMPLETE) {
108                /* Check that first line starts with "Android Console:",
109                 * otherwise we're not talking to the right program. */
110                const char* line = asyncLineReader_getLine(acc->lreader);
111                if (line == NULL || memcmp(line, "Android Console:", 16)) {
112                    goto BAD_BANNER;
113                }
114                /* ok, fine, prepare for the next banner line then */
115                _acc_prepareLineReader(acc, STATE_READ_BANNER_2);
116                continue;
117            }
118            break;
119
120        case STATE_READ_BANNER_2: /* reading the second banner line */
121            status = asyncLineReader_read(acc->lreader);
122            if (status == ASYNC_ERROR)
123                goto SET_ERROR;
124
125            if (status == ASYNC_COMPLETE) {
126                const char* line = asyncLineReader_getLine(acc->lreader);
127                if (line == NULL) {
128                    goto BAD_BANNER;
129                }
130                /* ok, we're done !*/
131                acc->state = STATE_COMPLETE;
132                return ASYNC_COMPLETE;
133            }
134            break;
135
136        case STATE_COMPLETE:
137            status = ASYNC_COMPLETE;
138        }
139        return status;
140    }
141BAD_BANNER:
142    errno = ENOPROTOOPT;
143SET_ERROR:
144    acc->state = STATE_ERROR;
145    acc->error = errno;
146    return ASYNC_ERROR;
147}
148