diff options
Diffstat (limited to 'apps/SdkController/src/com/android/tools/sdkcontroller/lib/Connection.java')
-rw-r--r-- | apps/SdkController/src/com/android/tools/sdkcontroller/lib/Connection.java | 412 |
1 files changed, 0 insertions, 412 deletions
diff --git a/apps/SdkController/src/com/android/tools/sdkcontroller/lib/Connection.java b/apps/SdkController/src/com/android/tools/sdkcontroller/lib/Connection.java deleted file mode 100644 index cb5086905..000000000 --- a/apps/SdkController/src/com/android/tools/sdkcontroller/lib/Connection.java +++ /dev/null @@ -1,412 +0,0 @@ -/* - * Copyright (C) 2012 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. - */ - -package com.android.tools.sdkcontroller.lib; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.List; - -import android.util.Log; -import android.net.LocalServerSocket; -import android.net.LocalSocket; - -import com.android.tools.sdkcontroller.lib.Channel; -import com.android.tools.sdkcontroller.service.ControllerService; - -/** - * Encapsulates a connection between SdkController service and the emulator. On - * the device side, the connection is bound to the UNIX-domain socket named - * 'android.sdk.controller'. On the emulator side the connection is established - * via TCP port that is used to forward I/O traffic on the host machine to - * 'android.sdk.controller' socket on the device. Typically, the port forwarding - * can be enabled using adb command: - * <p/> - * 'adb forward tcp:<TCP port number> localabstract:android.sdk.controller' - * <p/> - * The way communication between the emulator and SDK controller service works - * is as follows: - * <p/> - * 1. Both sides, emulator and the service have components that implement a particular - * type of emulation. For instance, AndroidSensorsPort in the emulator, and - * SensorChannel in the application implement sensors emulation. - * Emulation channels are identified by unique names. For instance, sensor emulation - * is done via "sensors" channel, multi-touch emulation is done via "multi-touch" - * channel, etc. - * <p/> - * 2. Channels are connected to emulator via separate socket instance (though all - * of the connections share the same socket address). - * <p/> - * 3. Connection is initiated by the emulator side, while the service provides - * its side (a channel) that implement functionality and exchange protocol required - * by the requested type of emulation. - * <p/> - * Given that, the main responsibilities of this class are: - * <p/> - * 1. Bind to "android.sdk.controller" socket, listening to emulator connections. - * <p/> - * 2. Maintain a list of service-side channels registered by the application. - * <p/> - * 3. Bind emulator connection with service-side channel via port name, provided by - * the emulator. - * <p/> - * 4. Monitor connection state with the emulator, and automatically restore the - * connection once it is lost. - */ -public class Connection { - /** UNIX-domain name reserved for SDK controller. */ - public static final String SDK_CONTROLLER_PORT = "android.sdk.controller"; - /** Tag for logging messages. */ - private static final String TAG = "SdkControllerConnection"; - /** Controls debug logging */ - private static final boolean DEBUG = false; - - /** Server socket used to listen to emulator connections. */ - private LocalServerSocket mServerSocket = null; - /** Service that has created this object. */ - private ControllerService mService; - /** - * List of connected emulator sockets, pending for a channel to be registered. - * <p/> - * Emulator may connect to SDK controller before the app registers a channel - * for that connection. In this case (when app-side channel is not registered - * with this class) we will keep emulator connection in this list, pending - * for the app-side channel to register. - */ - private List<Socket> mPendingSockets = new ArrayList<Socket>(); - /** - * List of registered app-side channels. - * <p/> - * Channels that are kept in this list may be disconnected from (or pending - * connection with) the emulator, or they may be connected with the - * emulator. - */ - private List<Channel> mChannels = new ArrayList<Channel>(); - - /** - * Constructs Connection instance. - */ - public Connection(ControllerService service) { - mService = service; - if (DEBUG) Log.d(TAG, "SdkControllerConnection is constructed."); - } - - /** - * Binds to the socket, and starts the listening thread. - */ - public void connect() { - if (DEBUG) Log.d(TAG, "SdkControllerConnection is connecting..."); - // Start connection listener. - new Thread(new Runnable() { - @Override - public void run() { - runIOLooper(); - } - }, "SdkControllerConnectionIoLoop").start(); - } - - /** - * Stops the listener, and closes the socket. - * - * @return true if connection has been stopped in this call, or false if it - * has been already stopped when this method has been called. - */ - public boolean disconnect() { - // This is the only place in this class where we will null the - // socket object. Since this method can be called concurrently from - // different threads, lets do this under the lock. - LocalServerSocket socket; - synchronized (this) { - socket = mServerSocket; - mServerSocket = null; - } - if (socket != null) { - if (DEBUG) Log.d(TAG, "SdkControllerConnection is stopping I/O looper..."); - // Stop accepting new connections. - wakeIOLooper(socket); - try { - socket.close(); - } catch (Exception e) { - } - - // Close all the pending sockets, and clear pending socket list. - if (DEBUG) Log.d(TAG, "SdkControllerConnection is closing pending sockets..."); - for (Socket pending_socket : mPendingSockets) { - pending_socket.close(); - } - mPendingSockets.clear(); - - // Disconnect all the emualtors. - if (DEBUG) Log.d(TAG, "SdkControllerConnection is disconnecting channels..."); - for (Channel channel : mChannels) { - if (channel.disconnect()) { - channel.onEmulatorDisconnected(); - } - } - if (DEBUG) Log.d(TAG, "SdkControllerConnection is disconnected."); - } - return socket != null; - } - - /** - * Registers SDK controller channel. - * - * @param channel SDK controller emulator to register. - * @return true if channel has been registered successfully, or false if channel - * with the same name is already registered. - */ - public boolean registerChannel(Channel channel) { - for (Channel check_channel : mChannels) { - if (check_channel.getChannelName().equals(channel.getChannelName())) { - Loge("Registering a duplicate Channel " + channel.getChannelName()); - return false; - } - } - if (DEBUG) Log.d(TAG, "Registering Channel " + channel.getChannelName()); - mChannels.add(channel); - - // Lets see if there is a pending socket for this channel. - for (Socket pending_socket : mPendingSockets) { - if (pending_socket.getChannelName().equals(channel.getChannelName())) { - // Remove the socket from the pending list, and connect the registered channel with it. - if (DEBUG) Log.d(TAG, "Found pending Socket for registering Channel " - + channel.getChannelName()); - mPendingSockets.remove(pending_socket); - channel.connect(pending_socket); - } - } - return true; - } - - /** - * Checks if at least one socket connection exists with channel. - * - * @return true if at least one socket connection exists with channel. - */ - public boolean isEmulatorConnected() { - for (Channel channel : mChannels) { - if (channel.isConnected()) { - return true; - } - } - return !mPendingSockets.isEmpty(); - } - - /** - * Gets Channel instance for the given channel name. - * - * @param name Channel name to get Channel instance for. - * @return Channel instance for the given channel name, or NULL if no - * channel has been registered for that name. - */ - public Channel getChannel(String name) { - for (Channel channel : mChannels) { - if (channel.getChannelName().equals(name)) { - return channel; - } - } - return null; - } - - /** - * Gets connected emulator socket that is pending for service-side channel - * registration. - * - * @param name Channel name to lookup Socket for. - * @return Connected emulator socket that is pending for service-side channel - * registration, or null if no socket is pending for service-size - * channel registration. - */ - private Socket getPendingSocket(String name) { - for (Socket socket : mPendingSockets) { - if (socket.getChannelName().equals(name)) { - return socket; - } - } - return null; - } - - /** - * Wakes I/O looper waiting on connection with the emulator. - * - * @param socket Server socket waiting on connection. - */ - private void wakeIOLooper(LocalServerSocket socket) { - // We wake the looper by connecting to the socket. - LocalSocket waker = new LocalSocket(); - try { - waker.connect(socket.getLocalSocketAddress()); - } catch (IOException e) { - Loge("Exception " + e + " in SdkControllerConnection while waking up the I/O looper."); - } - } - - /** - * Loops on the local socket, handling emulator connection attempts. - */ - private void runIOLooper() { - if (DEBUG) Log.d(TAG, "In SdkControllerConnection I/O looper."); - do { - try { - // Create non-blocking server socket that would listen for connections, - // and bind it to the given port on the local host. - mServerSocket = new LocalServerSocket(SDK_CONTROLLER_PORT); - LocalServerSocket socket = mServerSocket; - while (socket != null) { - final LocalSocket sk = socket.accept(); - if (mServerSocket != null) { - onAccept(sk); - } else { - break; - } - socket = mServerSocket; - } - } catch (IOException e) { - Loge("Exception " + e + "SdkControllerConnection I/O looper."); - } - if (DEBUG) Log.d(TAG, "Exiting SdkControllerConnection I/O looper."); - - // If we're exiting the internal loop for reasons other than an explicit - // disconnect request, we should reconnect again. - } while (disconnect()); - } - - /** - * Accepts new connection from the emulator. - * - * @param sock Connecting socket. - * @throws IOException - */ - private void onAccept(LocalSocket sock) throws IOException { - final ByteBuffer handshake = ByteBuffer.allocate(ProtocolConstants.QUERY_HEADER_SIZE); - - // By protocol, first byte received from newly connected emulator socket - // indicates host endianness. - Socket.receive(sock, handshake.array(), 1); - final ByteOrder endian = (handshake.getChar() == 0) ? ByteOrder.LITTLE_ENDIAN : - ByteOrder.BIG_ENDIAN; - handshake.order(endian); - - // Right after that follows the handshake query header. - handshake.position(0); - Socket.receive(sock, handshake.array(), handshake.array().length); - - // First int - signature - final int signature = handshake.getInt(); - assert signature == ProtocolConstants.PACKET_SIGNATURE; - // Second int - total query size (including fixed query header) - final int remains = handshake.getInt() - ProtocolConstants.QUERY_HEADER_SIZE; - // After that - header type (which must be SDKCTL_PACKET_TYPE_QUERY) - final int msg_type = handshake.getInt(); - assert msg_type == ProtocolConstants.PACKET_TYPE_QUERY; - // After that - query ID. - final int query_id = handshake.getInt(); - // And finally, query type (which must be ProtocolConstants.QUERY_HANDSHAKE for - // handshake query) - final int query_type = handshake.getInt(); - assert query_type == ProtocolConstants.QUERY_HANDSHAKE; - // Verify that received is a query. - if (msg_type != ProtocolConstants.PACKET_TYPE_QUERY) { - // Message type is not a query. Lets read and discard the remainder - // of the message. - if (remains > 0) { - Loge("Unexpected handshake message type: " + msg_type); - byte[] discard = new byte[remains]; - Socket.receive(sock, discard, discard.length); - } - return; - } - - // Receive query data. - final byte[] name_array = new byte[remains]; - Socket.receive(sock, name_array, name_array.length); - - // Prepare response header. - handshake.position(0); - handshake.putInt(ProtocolConstants.PACKET_SIGNATURE); - // Handshake reply is just one int. - handshake.putInt(ProtocolConstants.QUERY_RESP_HEADER_SIZE + 4); - handshake.putInt(ProtocolConstants.PACKET_TYPE_QUERY_RESPONSE); - handshake.putInt(query_id); - - // Verify that received query is in deed a handshake query. - if (query_type != ProtocolConstants.QUERY_HANDSHAKE) { - // Query is not a handshake. Reply with failure. - Loge("Unexpected handshake query type: " + query_type); - handshake.putInt(ProtocolConstants.HANDSHAKE_RESP_QUERY_UNKNOWN); - sock.getOutputStream().write(handshake.array()); - return; - } - - // Handshake query data consist of SDK controller channel name. - final String channel_name = new String(name_array); - if (DEBUG) Log.d(TAG, "Handshake received for channel " + channel_name); - - // Respond to query depending on service-side channel availability - final Channel channel = getChannel(channel_name); - Socket sk = null; - - if (channel != null) { - if (channel.isConnected()) { - // This is a duplicate connection. - Loge("Duplicate connection to a connected Channel " + channel_name); - handshake.putInt(ProtocolConstants.HANDSHAKE_RESP_DUP); - } else { - // Connecting to a registered channel. - if (DEBUG) Log.d(TAG, "Emulator is connected to a registered Channel " + channel_name); - handshake.putInt(ProtocolConstants.HANDSHAKE_RESP_CONNECTED); - } - } else { - // Make sure that there are no other channel connections for this - // channel name. - if (getPendingSocket(channel_name) != null) { - // This is a duplicate. - Loge("Duplicate connection to a pending Socket " + channel_name); - handshake.putInt(ProtocolConstants.HANDSHAKE_RESP_DUP); - } else { - // Connecting to a channel that has not been registered yet. - if (DEBUG) Log.d(TAG, "Emulator is connected to a pending Socket " + channel_name); - handshake.putInt(ProtocolConstants.HANDSHAKE_RESP_NOPORT); - sk = new Socket(sock, channel_name, endian); - mPendingSockets.add(sk); - } - } - - // Send handshake reply. - sock.getOutputStream().write(handshake.array()); - - // If a disconnected channel for emulator connection has been found, - // connect it. - if (channel != null && !channel.isConnected()) { - if (DEBUG) Log.d(TAG, "Connecting Channel " + channel_name + " with emulator."); - sk = new Socket(sock, channel_name, endian); - channel.connect(sk); - } - - mService.notifyStatusChanged(); - } - - /*************************************************************************** - * Logging wrappers - **************************************************************************/ - - private void Loge(String log) { - mService.addError(log); - Log.e(TAG, log); - } -} |