blob: 41a13e061407df898caee33d8117f7f6b6927e60 [file] [log] [blame]
Vladimir Chtchetkine9d36fe72012-03-26 10:29:20 -07001/*
2 * Copyright (C) 2011 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
17/*
18 * Encapsulates exchange protocol between the emulator, and an Android device
19 * that is connected to the host via USB. The communication is established over
20 * a TCP port forwarding, enabled by ADB.
21 */
22
23#include "qemu-common.h"
24#include "android/async-utils.h"
25#include "android/utils/debug.h"
26#include "android/async-socket-connector.h"
27#include "utils/panic.h"
28#include "iolooper.h"
29
30#define E(...) derror(__VA_ARGS__)
31#define W(...) dwarning(__VA_ARGS__)
32#define D(...) VERBOSE_PRINT(asconnector,__VA_ARGS__)
33#define D_ACTIVE VERBOSE_CHECK(asconnector)
34
35/********************************************************************************
36 * Internals
37 *******************************************************************************/
38
39struct AsyncSocketConnector {
40 /* TCP address for the connection. */
41 SockAddress address;
42 /* I/O looper for asynchronous I/O. */
43 Looper* looper;
44 /* I/O port for asynchronous connection. */
45 LoopIo connector_io[1];
46 /* Timer that is used to retry asynchronous connections. */
47 LoopTimer connector_timer[1];
48 /* Asynchronous connector to the socket. */
49 AsyncConnector connector[1];
Vladimir Chtchetkine6dc5c2c2012-04-02 07:48:19 -070050 /* Callback to invoke on connection events. */
Vladimir Chtchetkine9d36fe72012-03-26 10:29:20 -070051 asc_event_cb on_connected_cb;
52 /* An opaque parameter to pass to the connection callback. */
53 void* on_connected_cb_opaque;
54 /* Retry timeout in milliseconds. */
55 int retry_to;
56 /* Socket descriptor for the connection. */
57 int fd;
58};
59
60/* Asynchronous I/O looper callback invoked by the connector.
61 * Param:
62 * opaque - AsyncSocketConnector instance.
63 * fd, events - Standard I/O callback parameters.
64 */
65static void _on_async_socket_connector_io(void* opaque, int fd, unsigned events);
66
67/* Gets socket's address string. */
68AINLINED const char*
69_asc_socket_string(AsyncSocketConnector* connector)
70{
71 return sock_address_to_string(&connector->address);
72}
73
74/* Destroys AsyncSocketConnector instance.
75 * Param:
76 * connector - Initialized AsyncSocketConnector instance.
77 */
78static void
79_async_socket_connector_free(AsyncSocketConnector* connector)
80{
81 if (connector != NULL) {
82 if (connector->fd >= 0) {
83 socket_close(connector->fd);
84 }
85
86 if (connector->looper != NULL) {
87 loopTimer_done(connector->connector_timer);
88 looper_free(connector->looper);
89 }
90
91 sock_address_done(&connector->address);
92
93 AFREE(connector);
94 }
95}
96
97/* Opens connection socket.
98 * Param:
99 * connector - Initialized AsyncSocketConnector instance.
100 * Return:
101 * 0 on success, or -1 on failure.
102 */
103static int
104_async_socket_connector_open_socket(AsyncSocketConnector* connector)
105{
106 /* Open socket. */
107 connector->fd = socket_create_inet(SOCKET_STREAM);
108 if (connector->fd < 0) {
Vladimir Chtchetkine6dc5c2c2012-04-02 07:48:19 -0700109 D("Unable to create AsyncSocketConnector socket for '%s'. Error: %d -> %s",
110 _asc_socket_string(connector), errno, strerror(errno));
Vladimir Chtchetkine9d36fe72012-03-26 10:29:20 -0700111 return -1;
112 }
113
114 /* Prepare for async I/O on the connector. */
115 socket_set_nonblock(connector->fd);
116 loopIo_init(connector->connector_io, connector->looper, connector->fd,
117 _on_async_socket_connector_io, connector);
118
119 return 0;
120}
121
122/* Closes connection socket.
123 * Param:
124 * connector - Initialized AsyncSocketConnector instance.
125 * Return:
126 * 0 on success, or -1 on failure.
127 */
128static void
129_async_socket_connector_close_socket(AsyncSocketConnector* connector)
130{
131 if (connector->fd >= 0) {
132 socket_close(connector->fd);
133 connector->fd = -1;
134 }
135}
136
137/* Asynchronous connector (AsyncConnector instance) has completed connection
138 * attempt.
139 *
140 * NOTE: Upon exit from this routine AsyncSocketConnector instance might be
141 * destroyed. So, once this routine is called, there must be no further
142 * references to AsyncSocketConnector instance passed to this routine.
143 * Param:
144 * connector - Initialized AsyncSocketConnector instance.
145 * status - Status of the connection attempt.
146 */
147static void
148_on_async_socket_connector_connecting(AsyncSocketConnector* connector,
149 AsyncStatus status)
150{
Vladimir Chtchetkine6dc5c2c2012-04-02 07:48:19 -0700151 AsyncIOAction action = ASIO_ACTION_DONE;
Vladimir Chtchetkine9d36fe72012-03-26 10:29:20 -0700152
153 switch (status) {
154 case ASYNC_COMPLETE:
155 loopIo_done(connector->connector_io);
Vladimir Chtchetkine6dc5c2c2012-04-02 07:48:19 -0700156 D("Socket '%s' is connected", _asc_socket_string(connector));
Vladimir Chtchetkine9d36fe72012-03-26 10:29:20 -0700157 /* Invoke "on connected" callback */
158 action = connector->on_connected_cb(connector->on_connected_cb_opaque,
Vladimir Chtchetkine6dc5c2c2012-04-02 07:48:19 -0700159 connector, ASIO_STATE_SUCCEEDED);
Vladimir Chtchetkine9d36fe72012-03-26 10:29:20 -0700160 break;
161
162 case ASYNC_ERROR:
163 loopIo_done(connector->connector_io);
Vladimir Chtchetkine6dc5c2c2012-04-02 07:48:19 -0700164 D("Error while connecting to socket '%s': %d -> %s",
165 _asc_socket_string(connector), errno, strerror(errno));
Vladimir Chtchetkine9d36fe72012-03-26 10:29:20 -0700166 /* Invoke "on connected" callback */
167 action = connector->on_connected_cb(connector->on_connected_cb_opaque,
Vladimir Chtchetkine6dc5c2c2012-04-02 07:48:19 -0700168 connector, ASIO_STATE_FAILED);
Vladimir Chtchetkine9d36fe72012-03-26 10:29:20 -0700169 break;
170
171 case ASYNC_NEED_MORE:
172 return;
173 }
174
Vladimir Chtchetkine6dc5c2c2012-04-02 07:48:19 -0700175 if (action == ASIO_ACTION_RETRY) {
176 D("Retrying connection to socket '%s'", _asc_socket_string(connector));
Vladimir Chtchetkine9d36fe72012-03-26 10:29:20 -0700177 loopTimer_startRelative(connector->connector_timer, connector->retry_to);
178 } else {
Vladimir Chtchetkine6dc5c2c2012-04-02 07:48:19 -0700179 if (action == ASIO_ACTION_ABORT) {
180 D("%s: AsyncSocketConnector client for socket '%s' has aborted connection",
181 __FUNCTION__, _asc_socket_string(connector));
182 }
Vladimir Chtchetkine9d36fe72012-03-26 10:29:20 -0700183 _async_socket_connector_free(connector);
184 }
185}
186
187static void
188_on_async_socket_connector_io(void* opaque, int fd, unsigned events)
189{
190 AsyncSocketConnector* const connector = (AsyncSocketConnector*)opaque;
191
Vladimir Chtchetkine6dc5c2c2012-04-02 07:48:19 -0700192 /* Notify the client that another connection attempt is about to start. */
193 const AsyncIOAction action =
194 connector->on_connected_cb(connector->on_connected_cb_opaque,
195 connector, ASIO_STATE_CONTINUES);
196 if (action != ASIO_ACTION_ABORT) {
197 /* Complete socket connection. */
198 const AsyncStatus status = asyncConnector_run(connector->connector);
199 _on_async_socket_connector_connecting(connector, status);
200 } else {
201 D("%s: AsyncSocketConnector client for socket '%s' has aborted connection",
202 __FUNCTION__, _asc_socket_string(connector));
203 _async_socket_connector_free(connector);
204 }
Vladimir Chtchetkine9d36fe72012-03-26 10:29:20 -0700205}
206
207/* Retry connection timer callback.
208 * Param:
209 * opaque - AsyncSocketConnector instance.
210 */
211static void
212_on_async_socket_connector_retry(void* opaque)
213{
Vladimir Chtchetkine6dc5c2c2012-04-02 07:48:19 -0700214 AsyncStatus status;
Vladimir Chtchetkine9d36fe72012-03-26 10:29:20 -0700215 AsyncSocketConnector* const connector = (AsyncSocketConnector*)opaque;
216
217 /* Invoke the callback to notify about a connection retry attempt. */
Vladimir Chtchetkine6dc5c2c2012-04-02 07:48:19 -0700218 AsyncIOAction action =
Vladimir Chtchetkine9d36fe72012-03-26 10:29:20 -0700219 connector->on_connected_cb(connector->on_connected_cb_opaque,
Vladimir Chtchetkine6dc5c2c2012-04-02 07:48:19 -0700220 connector, ASIO_STATE_RETRYING);
Vladimir Chtchetkine9d36fe72012-03-26 10:29:20 -0700221
Vladimir Chtchetkine6dc5c2c2012-04-02 07:48:19 -0700222 if (action == ASIO_ACTION_ABORT) {
223 D("%s: AsyncSocketConnector client for socket '%s' has aborted connection",
224 __FUNCTION__, _asc_socket_string(connector));
Vladimir Chtchetkine9d36fe72012-03-26 10:29:20 -0700225 _async_socket_connector_free(connector);
Vladimir Chtchetkine6dc5c2c2012-04-02 07:48:19 -0700226 return;
Vladimir Chtchetkine9d36fe72012-03-26 10:29:20 -0700227 }
Vladimir Chtchetkine6dc5c2c2012-04-02 07:48:19 -0700228
229 /* Close handle opened for the previous (failed) attempt. */
230 _async_socket_connector_close_socket(connector);
231
232 /* Retry connection attempt. */
233 if (_async_socket_connector_open_socket(connector) == 0) {
234 status = asyncConnector_init(connector->connector, &connector->address,
235 connector->connector_io);
236 } else {
237 status = ASYNC_ERROR;
238 }
239
240 _on_async_socket_connector_connecting(connector, status);
Vladimir Chtchetkine9d36fe72012-03-26 10:29:20 -0700241}
242
243/********************************************************************************
244 * Async connector implementation
245 *******************************************************************************/
246
247AsyncSocketConnector*
248async_socket_connector_new(const SockAddress* address,
249 int retry_to,
250 asc_event_cb cb,
251 void* cb_opaque)
252{
253 AsyncSocketConnector* connector;
254
255 if (cb == NULL) {
Vladimir Chtchetkine6dc5c2c2012-04-02 07:48:19 -0700256 W("No callback for AsyncSocketConnector for socket '%s'",
Vladimir Chtchetkine9d36fe72012-03-26 10:29:20 -0700257 sock_address_to_string(address));
258 errno = EINVAL;
259 return NULL;
260 }
261
262 ANEW0(connector);
263
264 connector->fd = -1;
265 connector->retry_to = retry_to;
266 connector->on_connected_cb = cb;
267 connector->on_connected_cb_opaque = cb_opaque;
268
269 /* Copy socket address. */
270 if (sock_address_get_family(address) == SOCKET_UNIX) {
271 sock_address_init_unix(&connector->address, sock_address_get_path(address));
272 } else {
273 connector->address = *address;
274 }
275
276 /* Create a looper for asynchronous I/O. */
277 connector->looper = looper_newCore();
278 if (connector->looper != NULL) {
279 /* Create a timer that will be used for connection retries. */
280 loopTimer_init(connector->connector_timer, connector->looper,
281 _on_async_socket_connector_retry, connector);
282 } else {
Vladimir Chtchetkine6dc5c2c2012-04-02 07:48:19 -0700283 E("Unable to create I/O looper for AsyncSocketConnector for socket '%s'",
Vladimir Chtchetkine9d36fe72012-03-26 10:29:20 -0700284 _asc_socket_string(connector));
Vladimir Chtchetkine6dc5c2c2012-04-02 07:48:19 -0700285 cb(cb_opaque, connector, ASIO_STATE_FAILED);
Vladimir Chtchetkine9d36fe72012-03-26 10:29:20 -0700286 _async_socket_connector_free(connector);
287 return NULL;
288 }
289
290 return connector;
291}
292
Vladimir Chtchetkine6dc5c2c2012-04-02 07:48:19 -0700293void
Vladimir Chtchetkine9d36fe72012-03-26 10:29:20 -0700294async_socket_connector_connect(AsyncSocketConnector* connector)
295{
296 AsyncStatus status;
297
298 if (_async_socket_connector_open_socket(connector) == 0) {
Vladimir Chtchetkine6dc5c2c2012-04-02 07:48:19 -0700299 const AsyncIOAction action =
300 connector->on_connected_cb(connector->on_connected_cb_opaque,
301 connector, ASIO_STATE_STARTED);
302 if (action == ASIO_ACTION_ABORT) {
303 D("%s: AsyncSocketConnector client for socket '%s' has aborted connection",
304 __FUNCTION__, _asc_socket_string(connector));
305 _async_socket_connector_free(connector);
306 return;
307 } else {
308 status = asyncConnector_init(connector->connector,
309 &connector->address,
310 connector->connector_io);
311 }
Vladimir Chtchetkine9d36fe72012-03-26 10:29:20 -0700312 } else {
313 status = ASYNC_ERROR;
314 }
315
316 _on_async_socket_connector_connecting(connector, status);
Vladimir Chtchetkine9d36fe72012-03-26 10:29:20 -0700317}
318
319int
320async_socket_connector_pull_fd(AsyncSocketConnector* connector)
321{
322 const int fd = connector->fd;
323 if (fd >= 0) {
324 connector->fd = -1;
325 }
326 return fd;
327}