| /* |
| * Copyright (C) 2011 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. |
| */ |
| |
| /* |
| * Encapsulates exchange protocol between the emulator, and an Android device |
| * that is connected to the host via USB. The communication is established over |
| * a TCP port forwarding, enabled by ADB. |
| */ |
| |
| #include "qemu-common.h" |
| #include "android/async-utils.h" |
| #include "android/utils/debug.h" |
| #include "android/async-socket-connector.h" |
| #include "utils/panic.h" |
| #include "iolooper.h" |
| |
| #define E(...) derror(__VA_ARGS__) |
| #define W(...) dwarning(__VA_ARGS__) |
| #define D(...) VERBOSE_PRINT(asconnector,__VA_ARGS__) |
| #define D_ACTIVE VERBOSE_CHECK(asconnector) |
| |
| /******************************************************************************** |
| * Internals |
| *******************************************************************************/ |
| |
| struct AsyncSocketConnector { |
| /* TCP address for the connection. */ |
| SockAddress address; |
| /* I/O looper for asynchronous I/O. */ |
| Looper* looper; |
| /* I/O port for asynchronous connection. */ |
| LoopIo connector_io[1]; |
| /* Timer that is used to retry asynchronous connections. */ |
| LoopTimer connector_timer[1]; |
| /* Asynchronous connector to the socket. */ |
| AsyncConnector connector[1]; |
| /* Callback to invoke on connection / connection error. */ |
| asc_event_cb on_connected_cb; |
| /* An opaque parameter to pass to the connection callback. */ |
| void* on_connected_cb_opaque; |
| /* Retry timeout in milliseconds. */ |
| int retry_to; |
| /* Socket descriptor for the connection. */ |
| int fd; |
| }; |
| |
| /* Asynchronous I/O looper callback invoked by the connector. |
| * Param: |
| * opaque - AsyncSocketConnector instance. |
| * fd, events - Standard I/O callback parameters. |
| */ |
| static void _on_async_socket_connector_io(void* opaque, int fd, unsigned events); |
| |
| /* Gets socket's address string. */ |
| AINLINED const char* |
| _asc_socket_string(AsyncSocketConnector* connector) |
| { |
| return sock_address_to_string(&connector->address); |
| } |
| |
| /* Destroys AsyncSocketConnector instance. |
| * Param: |
| * connector - Initialized AsyncSocketConnector instance. |
| */ |
| static void |
| _async_socket_connector_free(AsyncSocketConnector* connector) |
| { |
| if (connector != NULL) { |
| if (connector->fd >= 0) { |
| socket_close(connector->fd); |
| } |
| |
| if (connector->looper != NULL) { |
| loopTimer_done(connector->connector_timer); |
| looper_free(connector->looper); |
| } |
| |
| sock_address_done(&connector->address); |
| |
| AFREE(connector); |
| } |
| } |
| |
| /* Opens connection socket. |
| * Param: |
| * connector - Initialized AsyncSocketConnector instance. |
| * Return: |
| * 0 on success, or -1 on failure. |
| */ |
| static int |
| _async_socket_connector_open_socket(AsyncSocketConnector* connector) |
| { |
| /* Open socket. */ |
| connector->fd = socket_create_inet(SOCKET_STREAM); |
| if (connector->fd < 0) { |
| D("Unable to create connector socket for %s. Error: %s", |
| _asc_socket_string(connector), strerror(errno)); |
| return -1; |
| } |
| |
| /* Prepare for async I/O on the connector. */ |
| socket_set_nonblock(connector->fd); |
| loopIo_init(connector->connector_io, connector->looper, connector->fd, |
| _on_async_socket_connector_io, connector); |
| |
| return 0; |
| } |
| |
| /* Closes connection socket. |
| * Param: |
| * connector - Initialized AsyncSocketConnector instance. |
| * Return: |
| * 0 on success, or -1 on failure. |
| */ |
| static void |
| _async_socket_connector_close_socket(AsyncSocketConnector* connector) |
| { |
| if (connector->fd >= 0) { |
| socket_close(connector->fd); |
| connector->fd = -1; |
| } |
| } |
| |
| /* Asynchronous connector (AsyncConnector instance) has completed connection |
| * attempt. |
| * |
| * NOTE: Upon exit from this routine AsyncSocketConnector instance might be |
| * destroyed. So, once this routine is called, there must be no further |
| * references to AsyncSocketConnector instance passed to this routine. |
| * Param: |
| * connector - Initialized AsyncSocketConnector instance. |
| * status - Status of the connection attempt. |
| */ |
| static void |
| _on_async_socket_connector_connecting(AsyncSocketConnector* connector, |
| AsyncStatus status) |
| { |
| ASCCbRes action; |
| int do_retry = 0; |
| |
| switch (status) { |
| case ASYNC_COMPLETE: |
| loopIo_done(connector->connector_io); |
| D("Socket %s is connected", _asc_socket_string(connector)); |
| /* Invoke "on connected" callback */ |
| action = connector->on_connected_cb(connector->on_connected_cb_opaque, |
| connector, ASC_CONNECTION_SUCCEEDED); |
| if (action == ASC_CB_RETRY) { |
| do_retry = 1; |
| } else if (action == ASC_CB_ABORT) { |
| _async_socket_connector_close_socket(connector); |
| } |
| break; |
| |
| case ASYNC_ERROR: |
| loopIo_done(connector->connector_io); |
| D("Error %d while connecting to socket %s: %s", |
| errno, _asc_socket_string(connector), strerror(errno)); |
| /* Invoke "on connected" callback */ |
| action = connector->on_connected_cb(connector->on_connected_cb_opaque, |
| connector, ASC_CONNECTION_FAILED); |
| if (action == ASC_CB_RETRY) { |
| do_retry = 1; |
| } else if (action == ASC_CB_ABORT) { |
| _async_socket_connector_close_socket(connector); |
| } |
| break; |
| |
| case ASYNC_NEED_MORE: |
| return; |
| } |
| |
| if (do_retry) { |
| D("Retrying connection to socket %s", _asc_socket_string(connector)); |
| loopTimer_startRelative(connector->connector_timer, connector->retry_to); |
| } else { |
| _async_socket_connector_free(connector); |
| } |
| } |
| |
| static void |
| _on_async_socket_connector_io(void* opaque, int fd, unsigned events) |
| { |
| AsyncSocketConnector* const connector = (AsyncSocketConnector*)opaque; |
| |
| /* Complete socket connection. */ |
| const AsyncStatus status = asyncConnector_run(connector->connector); |
| _on_async_socket_connector_connecting(connector, status); |
| } |
| |
| /* Retry connection timer callback. |
| * Param: |
| * opaque - AsyncSocketConnector instance. |
| */ |
| static void |
| _on_async_socket_connector_retry(void* opaque) |
| { |
| AsyncSocketConnector* const connector = (AsyncSocketConnector*)opaque; |
| |
| /* Invoke the callback to notify about a connection retry attempt. */ |
| const ASCCbRes action = |
| connector->on_connected_cb(connector->on_connected_cb_opaque, |
| connector, ASC_CONNECTION_RETRY); |
| |
| if (action == ASC_CB_RETRY) { |
| AsyncStatus status; |
| |
| /* Close handle opened for the previous (failed) attempt. */ |
| _async_socket_connector_close_socket(connector); |
| |
| /* Retry connection attempt. */ |
| if (_async_socket_connector_open_socket(connector) == 0) { |
| status = asyncConnector_init(connector->connector, &connector->address, |
| connector->connector_io); |
| } else { |
| status = ASYNC_ERROR; |
| } |
| |
| _on_async_socket_connector_connecting(connector, status); |
| } else { |
| _async_socket_connector_free(connector); |
| } |
| } |
| |
| /******************************************************************************** |
| * Async connector implementation |
| *******************************************************************************/ |
| |
| AsyncSocketConnector* |
| async_socket_connector_new(const SockAddress* address, |
| int retry_to, |
| asc_event_cb cb, |
| void* cb_opaque) |
| { |
| AsyncSocketConnector* connector; |
| |
| if (cb == NULL) { |
| W("No callback for AsyncSocketConnector for %s", |
| sock_address_to_string(address)); |
| errno = EINVAL; |
| return NULL; |
| } |
| |
| ANEW0(connector); |
| |
| connector->fd = -1; |
| connector->retry_to = retry_to; |
| connector->on_connected_cb = cb; |
| connector->on_connected_cb_opaque = cb_opaque; |
| |
| /* Copy socket address. */ |
| if (sock_address_get_family(address) == SOCKET_UNIX) { |
| sock_address_init_unix(&connector->address, sock_address_get_path(address)); |
| } else { |
| connector->address = *address; |
| } |
| |
| /* Create a looper for asynchronous I/O. */ |
| connector->looper = looper_newCore(); |
| if (connector->looper != NULL) { |
| /* Create a timer that will be used for connection retries. */ |
| loopTimer_init(connector->connector_timer, connector->looper, |
| _on_async_socket_connector_retry, connector); |
| } else { |
| E("Unable to create I/O looper for asynchronous connector to socket %s", |
| _asc_socket_string(connector)); |
| _async_socket_connector_free(connector); |
| return NULL; |
| } |
| |
| return connector; |
| } |
| |
| ASCConnectRes |
| async_socket_connector_connect(AsyncSocketConnector* connector) |
| { |
| AsyncStatus status; |
| |
| if (_async_socket_connector_open_socket(connector) == 0) { |
| status = asyncConnector_init(connector->connector, &connector->address, |
| connector->connector_io); |
| } else { |
| status = ASYNC_ERROR; |
| } |
| |
| _on_async_socket_connector_connecting(connector, status); |
| |
| switch (status) { |
| case ASYNC_COMPLETE: |
| return ASC_CONNECT_SUCCEEDED; |
| |
| case ASYNC_ERROR: |
| return ASC_CONNECT_FAILED; |
| |
| case ASYNC_NEED_MORE: |
| default: |
| return ASC_CONNECT_IN_PROGRESS; |
| } |
| } |
| |
| int |
| async_socket_connector_pull_fd(AsyncSocketConnector* connector) |
| { |
| const int fd = connector->fd; |
| if (fd >= 0) { |
| connector->fd = -1; |
| } |
| return fd; |
| } |