| /* |
| * 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 "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) |
| |
| #define TRACE_ON 0 |
| |
| #if TRACE_ON |
| #define T(...) VERBOSE_PRINT(asconnector,__VA_ARGS__) |
| #else |
| #define T(...) |
| #endif |
| |
| /******************************************************************************** |
| * 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 events. */ |
| 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; |
| /* Number of outstanding references to the connector. */ |
| int ref_count; |
| /* Flags whether (1) or not (0) connector owns the looper. */ |
| int owns_looper; |
| }; |
| |
| /* 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) { |
| T("ASC %s: Connector is destroying...", _asc_socket_string(connector)); |
| |
| /* Stop all activities. */ |
| if (asyncConnector_stop(connector->connector) == 0) { |
| /* Connection was in progress. We need to destroy I/O descriptor for |
| * that connection. */ |
| D("ASC %s: Stopped async connection in progress.", |
| _asc_socket_string(connector)); |
| loopIo_done(connector->connector_io); |
| } |
| |
| /* Free allocated resources. */ |
| if (connector->looper != NULL) { |
| loopTimer_done(connector->connector_timer); |
| if (connector->owns_looper) { |
| looper_free(connector->looper); |
| } |
| } |
| |
| if (connector->fd >= 0) { |
| socket_close(connector->fd); |
| } |
| |
| T("ASC %s: Connector is destroyed", _asc_socket_string(connector)); |
| |
| 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("ASC %s: Unable to create socket: %d -> %s", |
| _asc_socket_string(connector), errno, strerror(errno)); |
| return -1; |
| } |
| |
| /* Prepare for async I/O on the connector. */ |
| socket_set_nonblock(connector->fd); |
| |
| T("ASC %s: Connector socket is opened with FD = %d", |
| _asc_socket_string(connector), connector->fd); |
| |
| 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); |
| T("ASC %s: Connector socket FD = %d is closed.", |
| _asc_socket_string(connector), connector->fd); |
| connector->fd = -1; |
| } |
| } |
| |
| /* Asynchronous connector (AsyncConnector instance) has completed connection |
| * attempt. |
| * Param: |
| * connector - Initialized AsyncSocketConnector instance. Note: When this |
| * callback is called, the caller has referenced passed connector object, |
| * So, it's guaranteed that this connector is not going to be destroyed |
| * while this routine executes. |
| * status - Status of the connection attempt. |
| */ |
| static void |
| _on_async_socket_connector_connecting(AsyncSocketConnector* connector, |
| AsyncStatus status) |
| { |
| AsyncIOAction action = ASIO_ACTION_DONE; |
| |
| 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, ASIO_STATE_SUCCEEDED); |
| break; |
| |
| case ASYNC_ERROR: |
| loopIo_done(connector->connector_io); |
| D("Error while connecting to socket '%s': %d -> %s", |
| _asc_socket_string(connector), errno, strerror(errno)); |
| /* Invoke "on connected" callback */ |
| action = connector->on_connected_cb(connector->on_connected_cb_opaque, |
| connector, ASIO_STATE_FAILED); |
| break; |
| |
| case ASYNC_NEED_MORE: |
| T("ASC %s: Waiting on connection to complete. Connector FD = %d", |
| _asc_socket_string(connector), connector->fd); |
| return; |
| } |
| |
| if (action == ASIO_ACTION_RETRY) { |
| D("ASC %s: Retrying connection. Connector FD = %d", |
| _asc_socket_string(connector), connector->fd); |
| loopTimer_startRelative(connector->connector_timer, connector->retry_to); |
| } else if (action == ASIO_ACTION_ABORT) { |
| D("ASC %s: Client has aborted connection. Connector FD = %d", |
| _asc_socket_string(connector), connector->fd); |
| } |
| } |
| |
| static void |
| _on_async_socket_connector_io(void* opaque, int fd, unsigned events) |
| { |
| AsyncSocketConnector* const connector = (AsyncSocketConnector*)opaque; |
| |
| /* Reference the connector while we're handing I/O. */ |
| async_socket_connector_reference(connector); |
| |
| /* Notify the client that another connection attempt is about to start. */ |
| const AsyncIOAction action = |
| connector->on_connected_cb(connector->on_connected_cb_opaque, |
| connector, ASIO_STATE_CONTINUES); |
| if (action != ASIO_ACTION_ABORT) { |
| /* Complete socket connection. */ |
| const AsyncStatus status = asyncConnector_run(connector->connector); |
| _on_async_socket_connector_connecting(connector, status); |
| } else { |
| D("ASC %s: Client has aborted connection. Connector FD = %d", |
| _asc_socket_string(connector), connector->fd); |
| } |
| |
| /* Release the connector after we're done with handing I/O. */ |
| async_socket_connector_release(connector); |
| } |
| |
| /* Retry connection timer callback. |
| * Param: |
| * opaque - AsyncSocketConnector instance. |
| */ |
| static void |
| _on_async_socket_connector_retry(void* opaque) |
| { |
| AsyncStatus status; |
| AsyncSocketConnector* const connector = (AsyncSocketConnector*)opaque; |
| |
| T("ASC %s: Reconnect timer expired. Connector FD = %d", |
| _asc_socket_string(connector), connector->fd); |
| |
| /* Reference the connector while we're in callback. */ |
| async_socket_connector_reference(connector); |
| |
| /* Invoke the callback to notify about a connection retry attempt. */ |
| AsyncIOAction action = |
| connector->on_connected_cb(connector->on_connected_cb_opaque, |
| connector, ASIO_STATE_RETRYING); |
| |
| if (action != ASIO_ACTION_ABORT) { |
| /* 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) { |
| loopIo_init(connector->connector_io, connector->looper, |
| connector->fd, _on_async_socket_connector_io, connector); |
| status = asyncConnector_init(connector->connector, |
| &connector->address, |
| connector->connector_io); |
| } else { |
| status = ASYNC_ERROR; |
| } |
| |
| _on_async_socket_connector_connecting(connector, status); |
| } else { |
| D("ASC %s: Client has aborted connection. Connector FD = %d", |
| _asc_socket_string(connector), connector->fd); |
| } |
| |
| /* Release the connector after we're done with the callback. */ |
| async_socket_connector_release(connector); |
| } |
| |
| /******************************************************************************** |
| * Async connector implementation |
| *******************************************************************************/ |
| |
| AsyncSocketConnector* |
| async_socket_connector_new(const SockAddress* address, |
| int retry_to, |
| asc_event_cb cb, |
| void* cb_opaque, |
| Looper* looper) |
| { |
| AsyncSocketConnector* connector; |
| |
| if (cb == NULL) { |
| W("No callback for AsyncSocketConnector for socket '%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; |
| connector->ref_count = 1; |
| |
| /* Copy socket address. */ |
| #ifdef _WIN32 |
| connector->address = *address; |
| #else |
| if (sock_address_get_family(address) == SOCKET_UNIX) { |
| sock_address_init_unix(&connector->address, sock_address_get_path(address)); |
| } else { |
| connector->address = *address; |
| } |
| #endif |
| |
| /* Create a looper for asynchronous I/O. */ |
| if (looper == NULL) { |
| connector->looper = looper_newCore(); |
| if (connector->looper == NULL) { |
| E("Unable to create I/O looper for AsyncSocketConnector for socket '%s'", |
| _asc_socket_string(connector)); |
| cb(cb_opaque, connector, ASIO_STATE_FAILED); |
| _async_socket_connector_free(connector); |
| return NULL; |
| } |
| connector->owns_looper = 1; |
| } else { |
| connector->looper = looper; |
| connector->owns_looper = 0; |
| } |
| |
| /* Create a timer that will be used for connection retries. */ |
| loopTimer_init(connector->connector_timer, connector->looper, |
| _on_async_socket_connector_retry, connector); |
| |
| T("ASC %s: New connector object", _asc_socket_string(connector)); |
| |
| return connector; |
| } |
| |
| int |
| async_socket_connector_reference(AsyncSocketConnector* connector) |
| { |
| assert(connector->ref_count > 0); |
| connector->ref_count++; |
| return connector->ref_count; |
| } |
| |
| int |
| async_socket_connector_release(AsyncSocketConnector* connector) |
| { |
| assert(connector->ref_count > 0); |
| connector->ref_count--; |
| if (connector->ref_count == 0) { |
| /* Last reference has been dropped. Destroy this object. */ |
| _async_socket_connector_free(connector); |
| return 0; |
| } |
| return connector->ref_count; |
| } |
| |
| void |
| async_socket_connector_connect(AsyncSocketConnector* connector) |
| { |
| AsyncStatus status; |
| |
| T("ASC %s: Handling connect request. Connector FD = %d", |
| _asc_socket_string(connector), connector->fd); |
| |
| if (_async_socket_connector_open_socket(connector) == 0) { |
| const AsyncIOAction action = |
| connector->on_connected_cb(connector->on_connected_cb_opaque, |
| connector, ASIO_STATE_STARTED); |
| if (action == ASIO_ACTION_ABORT) { |
| D("ASC %s: Client has aborted connection. Connector FD = %d", |
| _asc_socket_string(connector), connector->fd); |
| return; |
| } else { |
| loopIo_init(connector->connector_io, connector->looper, |
| connector->fd, _on_async_socket_connector_io, connector); |
| status = asyncConnector_init(connector->connector, |
| &connector->address, |
| connector->connector_io); |
| } |
| } else { |
| status = ASYNC_ERROR; |
| } |
| |
| _on_async_socket_connector_connecting(connector, status); |
| } |
| |
| int |
| async_socket_connector_pull_fd(AsyncSocketConnector* connector) |
| { |
| const int fd = connector->fd; |
| if (fd >= 0) { |
| connector->fd = -1; |
| } |
| |
| T("ASC %s: Client has pulled connector FD %d", _asc_socket_string(connector), fd); |
| |
| return fd; |
| } |