/* * 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; }