diff options
Diffstat (limited to 'apps/SdkController/src/com/android/tools/sdkcontroller/lib/Channel.java')
-rw-r--r-- | apps/SdkController/src/com/android/tools/sdkcontroller/lib/Channel.java | 795 |
1 files changed, 0 insertions, 795 deletions
diff --git a/apps/SdkController/src/com/android/tools/sdkcontroller/lib/Channel.java b/apps/SdkController/src/com/android/tools/sdkcontroller/lib/Channel.java deleted file mode 100644 index 639f4cfd4..000000000 --- a/apps/SdkController/src/com/android/tools/sdkcontroller/lib/Channel.java +++ /dev/null @@ -1,795 +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 android.os.Message; -import android.util.Log; - -import com.android.tools.sdkcontroller.service.ControllerService; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Encapsulates basics of a connection with the emulator. - * This class must be used as a base class for all the channelss that provide - * particular type of emulation (such as sensors, multi-touch, etc.) - * <p/> - * Essentially, Channel is an implementation of a particular emulated functionality, - * that defines logical format of the data transferred between the emulator and - * SDK controller. For instance, "sensors" is a channel that emulates sensors, - * and transfers sensor value changes from the device to the emulator. "Multi-touch" - * is a channel that supports multi-touch emulation, and transfers multi-touch - * events to the emulator, while receiving frame buffer updates from the emulator. - * <p/> - * Besides connection with the emulator, each channel may contain one or more UI - * components associated with it. This class provides some basics for UI support, - * including: - * <p/> - * - Providing a way to register / unregister a UI component with the channel. - * <p/> - * - Implementing posting of messages to emulator in opposite to direct message - * sent. This is due to requirement that UI threads are prohibited from doing - * network I/O. - */ -public abstract class Channel { - - /** - * Encapsulates a message posted to be sent to the emulator from a worker - * thread. This class is used to describe a message that is posted in UI - * thread, and then picked up in the worker thread. - */ - private class SdkControllerMessage { - /** Message type. */ - private int mMessageType; - /** Message data (can be null). */ - private byte[] mMessage; - /** Message data size */ - private int mMessageSize; - - /** - * Construct message from an array. - * - * @param type Message type. - * @param message Message data. Message data size is defined by size of - * the array. - */ - public SdkControllerMessage(int type, byte[] message) { - mMessageType = type; - mMessage = message; - mMessageSize = (message != null) ? message.length : 0; - } - - /** - * Construct message from a ByteBuffer. - * - * @param type Message type. - * @param message Message data. Message data size is defined by - * position() property of the ByteBuffer. - */ - public SdkControllerMessage(int type, ByteBuffer message) { - mMessageType = type; - if (message != null) { - mMessage = message.array(); - mMessageSize = message.position(); - } else { - mMessage = null; - mMessageSize = 0; - } - } - - /** - * Gets message type. - - * - * @return Message type. - */ - public int getMessageType() { - return mMessageType; - } - - /** - * Gets message buffer. - * - * @return Message buffer. - */ - public byte[] getMessage() { - return mMessage; - } - - /** - * Gets message buffer size. - * - * @return Message buffer size. - */ - public int getMessageSize() { - return mMessageSize; - } - } // SdkControllerMessage - - /* - * Names for currently implemented SDK controller channels. - */ - - /** Name for a channel that handles sensors emulation */ - public static final String SENSOR_CHANNEL = "sensors"; - /** Name for a channel that handles multi-touch emulation */ - public static final String MULTITOUCH_CHANNEL = "multi-touch"; - - /* - * Types of messages internally used by Channel class. - */ - - /** Service-side emulator is connected. */ - private static final int MSG_CONNECTED = -1; - /** Service-side emulator is disconnected. */ - private static final int MSG_DISCONNECTED = -2; - /** Service-side emulator is enabled. */ - private static final int MSG_ENABLED = -3; - /** Service-side emulator is disabled. */ - private static final int MSG_DISABLED = -4; - - /** Tag for logging messages. */ - private static final String TAG = "SdkControllerChannel"; - /** Controls debug log. */ - private static final boolean DEBUG = false; - - /** Service that has created this object. */ - protected ControllerService mService; - - /* - * Socket stuff. - */ - - /** Socket to use to to communicate with the emulator. */ - private Socket mSocket = null; - /** Channel name ("sensors", "multi-touch", etc.) */ - private String mChannelName; - /** Endianness of data transferred in this channel. */ - private ByteOrder mEndian; - - /* - * Message posting support. - */ - - /** Total number of messages posted in this channel */ - private final AtomicInteger mMsgCount = new AtomicInteger(0); - /** Flags whether or not message thread is running. */ - private volatile boolean mRunMsgQueue = true; - /** Queue of messages pending transmission. */ - private final BlockingQueue<SdkControllerMessage> - mMsgQueue = new LinkedBlockingQueue<SdkControllerMessage>(); - /** Message thread */ - private final Thread mMsgThread; - - /* - * UI support. - */ - - /** Lists UI handlers attached to this channel. */ - private final List<android.os.Handler> mUiHandlers = new ArrayList<android.os.Handler>(); - - /* - * Abstract methods. - */ - - /** - * This method is invoked when this channel is fully connected with its - * counterpart in the emulator. - */ - public abstract void onEmulatorConnected(); - - /** - * This method is invoked when this channel loses connection with its - * counterpart in the emulator. - */ - public abstract void onEmulatorDisconnected(); - - /** - * A message has been received from the emulator. - * - * @param msg_type Message type. - * @param msg_data Message data. Message data size is defined by the length - * of the array wrapped by the ByteBuffer. - */ - public abstract void onEmulatorMessage(int msg_type, ByteBuffer msg_data); - - /** - * A query has been received from the emulator. - * - * @param query_id Identifies the query. This ID must be used when replying - * to the query. - * @param query_type Query type. - * @param query_data Query data. Query data size is defined by the length of - * the array wrapped by the ByteBuffer. - */ - public abstract void onEmulatorQuery(int query_id, int query_type, ByteBuffer query_data); - - /* - * Channel implementation. - */ - - /** - * Constructs Channel instance. - * - * @param name Channel name. - */ - public Channel(ControllerService service, String name) { - mService = service; - mChannelName = name; - // Start the worker thread for posted messages. - mMsgThread = new Thread(new Runnable() { - @Override - public void run() { - if (DEBUG) Log.d(TAG, "MsgThread.started-" + mChannelName); - while (mRunMsgQueue) { - try { - SdkControllerMessage msg = mMsgQueue.take(); - if (msg != null) { - sendMessage( - msg.getMessageType(), msg.getMessage(), msg.getMessageSize()); - mMsgCount.incrementAndGet(); - } - } catch (InterruptedException e) { - Log.e(TAG, "MsgThread-" + mChannelName, e); - } - } - if (DEBUG) Log.d(TAG, "MsgThread.terminate-" + mChannelName); - } - }, "MsgThread-" + name); - mMsgThread.start(); - if (DEBUG) Log.d(TAG, "Channel is constructed for " + mChannelName); - } - - /** - * Gets name for this channel. - * - * @return Emulator name. - */ - public String getChannelName() { - return mChannelName; - } - - /** - * Gets endianness for this channel. - * - * @return Channel endianness. - */ - public ByteOrder getEndian() { - return mEndian; - } - - /** - * Gets number of messages sent via postMessage method. - * - * @return Number of messages sent via postMessage method. - */ - public int getMsgSentCount() { - return mMsgCount.get(); - } - - /** - * Checks if this channel is connected with the emulator. - * - * @return true if this channel is connected with the emulator, or false if it is - * not connected. - */ - public boolean isConnected() { - // Use local copy of the socket, ensuring it's not going to NULL while - // we're working with it. If it gets closed, while we're in the middle - // of data transfer - it's OK, since it will produce an exception, and - // the caller will gracefully handle it. - // - // Same technique is used everywhere in this class where mSocket member - // is touched. - Socket socket = mSocket; - return socket != null && socket.isConnected(); - } - - /** - * Establishes connection with the emulator. This method is called by Connection - * object when emulator successfully connects to this channel, or this channel - * gets registered, and there is a pending socket connection for it. - * - * @param socket Channel connection socket. - */ - public void connect(Socket socket) { - mSocket = socket; - mEndian = socket.getEndian(); - Logv("Channel " + mChannelName + " is now connected with the emulator."); - // Notify the emulator that connection is established. - sendMessage(MSG_CONNECTED, (byte[]) null); - - // Let the derived class know that emulator is connected, and start the - // I/O loop in which we will receive data from the emulator. Note that - // we start the loop after onEmulatorConnected call, since we don't want - // to start dispatching messages before the derived class could set - // itself up for receiving them. - onEmulatorConnected(); - new Thread(new Runnable() { - @Override - public void run() { - runIOLooper(); - } - }, "ChannelIoLoop").start(); - mService.notifyStatusChanged(); - } - - /** - * Disconnects this channel from the emulator. - * - * @return true if this channel has been disconnected in this call, or false if - * channel has been already disconnected 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. - Socket socket; - synchronized (this) { - socket = mSocket; - mSocket = null; - } - if (socket != null) { - // Notify the emulator about channel disconnection before we close - // the communication socket. - try { - sendMessage(socket, MSG_DISCONNECTED, null, 0); - } catch (IOException e) { - // Ignore I/O exception at this point. We don't care about - // it, since the socket is being closed anyways. - } - // This will eventually stop I/O looper thread. - socket.close(); - mService.notifyStatusChanged(); - } - return socket != null; - } - - /** - * Enables the emulation. Typically, this method is called for channels that are - * dependent on UI to handle the emulation. For instance, multi-touch emulation is - * disabled until at least one UI component is attached to the channel. So, for - * multi-touch emulation this method is called when UI gets attached to the channel. - */ - public void enable() { - postMessage(MSG_ENABLED, (byte[]) null); - mService.notifyStatusChanged(); - } - - /** - * Disables the emulation. Just the opposite to enable(). For multi-touch this - * method is called when UI detaches from the channel. - */ - public void disable() { - postMessage(MSG_DISABLED, (byte[]) null); - mService.notifyStatusChanged(); - } - - /** - * Sends message to the emulator. - * - * @param socket Socket to send the message to. - * @param msg_type Message type. - * @param msg Message data to send. - * @param len Byte size of message data. - * @throws IOException - */ - private void sendMessage(Socket socket, int msg_type, byte[] msg, int len) - throws IOException { - // In async environment we must have message header and message data in - // one block to prevent messages from other threads getting between the - // header and the data. So, we can't sent header, and then the data. We - // must combine them in one data block instead. - ByteBuffer bb = ByteBuffer.allocate(ProtocolConstants.MESSAGE_HEADER_SIZE + len); - bb.order(mEndian); - - // Initialize message header. - bb.putInt(ProtocolConstants.PACKET_SIGNATURE); - bb.putInt(ProtocolConstants.MESSAGE_HEADER_SIZE + len); - bb.putInt(ProtocolConstants.PACKET_TYPE_MESSAGE); - bb.putInt(msg_type); - - // Save message data (if there is any). - if (len != 0) { - bb.put(msg, 0, len); - } - - socket.send(bb.array()); - } - - /** - * Sends message to the emulator. - * - * @param msg_type Message type. - * @param msg Message data to send. Message size is defined by the size of - * the array. - * @return true on success, or false if data transmission has failed. - */ - public boolean sendMessage(int msg_type, byte[] msg, int msg_len) { - try { - Socket socket = mSocket; - if (socket != null) { - sendMessage(socket, msg_type, msg, msg_len); - return true; - } else { - Logw("sendMessage is called on disconnected Channel " + mChannelName); - } - } catch (IOException e) { - Loge("Exception " + e + " in sendMessage for Channel " + mChannelName); - onIoFailure(); - } - return false; - } - - /** - * Sends message to the emulator. - * - * @param msg_type Message type. - * @param msg Message data to send. Message size is defined by the size of - * the array. - * @return true on success, or false if data transmission has failed. - */ - public boolean sendMessage(int msg_type, byte[] msg) { - try { - Socket socket = mSocket; - if (socket != null) { - if (msg != null) { - sendMessage(socket, msg_type, msg, msg.length); - } else { - sendMessage(socket, msg_type, null, 0); - } - return true; - } else { - Logw("sendMessage is called on disconnected Channel " + mChannelName); - } - } catch (IOException e) { - Loge("Exception " + e + " in sendMessage for Channel " + mChannelName); - onIoFailure(); - } - return false; - } - - /** - * Sends message to the emulator. - * - * @param msg_type Message type. - * @param msg Message data to send. Message size is defined by the - * position() property of the ByteBuffer. - * @return true on success, or false if data transmission has failed. - */ - public boolean sendMessage(int msg_type, ByteBuffer msg) { - try { - Socket socket = mSocket; - if (socket != null) { - if (msg != null) { - sendMessage(socket, msg_type, msg.array(), msg.position()); - } else { - sendMessage(socket, msg_type, null, 0); - } - return true; - } else { - Logw("sendMessage is called on disconnected Channel " + mChannelName); - } - } catch (IOException e) { - Loge("Exception " + e + " in sendMessage for Channel " + mChannelName); - onIoFailure(); - } - return false; - } - - /** - * Posts message to the emulator. - * - * @param msg_type Message type. - * @param msg Message data to post. Message size is defined by the size of - * the array. - */ - public void postMessage(int msg_type, byte[] msg) { - try { - mMsgQueue.put(new SdkControllerMessage(msg_type, msg)); - } catch (InterruptedException e) { - Log.e(TAG, "mMessageQueue.put", e); - } - } - - /** - * Posts message to the emulator. - * - * @param msg_type Message type. - * @param msg Message data to post. Message size is defined by the - * position() property of the ByteBuffer. - */ - public void postMessage(int msg_type, ByteBuffer msg) { - try { - mMsgQueue.put(new SdkControllerMessage(msg_type, msg)); - } catch (InterruptedException e) { - Log.e(TAG, "mMessageQueue.put", e); - } - } - - /** - * Sends query response to the emulator. - * - * @param query_id Query identifier. - * @param qresp Response to the query. - * @param len Byte size of query response data. - * @return true on success, or false if data transmission has failed. - */ - public boolean sendQueryResponse(int query_id, byte[] qresp, int len) { - // Just like with messages, we must combine header and data in a single - // transmitting block. - ByteBuffer bb = ByteBuffer.allocate(ProtocolConstants.QUERY_RESP_HEADER_SIZE + len); - bb.order(mEndian); - - // Initialize response header. - bb.putInt(ProtocolConstants.PACKET_SIGNATURE); - bb.putInt(ProtocolConstants.QUERY_RESP_HEADER_SIZE + len); - bb.putInt(ProtocolConstants.PACKET_TYPE_QUERY_RESPONSE); - bb.putInt(query_id); - - // Save response data (if there is any). - if (qresp != null && len != 0) { - bb.put(qresp, 0, len); - } - - // Send the response. - try { - Socket socket = mSocket; - if (socket != null) { - socket.send(bb.array()); - return true; - } else { - Logw("sendQueryResponse is called on disconnected Channel " - + mChannelName); - } - } catch (IOException e) { - Loge("Exception " + e + " in sendQueryResponse for Channel " + mChannelName); - onIoFailure(); - } - return false; - } - - /** - * Sends query response to the emulator. - * - * @param query_id Query identifier. - * @param qresp Response to the query. Query response size is defined by the - * size of the array. - * @return true on success, or false if data transmission has failed. - */ - public boolean sendQueryResponse(int query_id, byte[] qresp) { - return (qresp != null) ? sendQueryResponse(query_id, qresp, qresp.length) : - sendQueryResponse(query_id, null, 0); - } - - /** - * Sends query response to the emulator. - * - * @param query_id Query identifier. - * @param qresp Response to the query. Query response size is defined by the - * position() property of the ByteBuffer. - * @return true on success, or false if data transmission has failed. - */ - public boolean sendQueryResponse(int query_id, ByteBuffer qresp) { - return (qresp != null) ? sendQueryResponse(query_id, qresp.array(), qresp.position()) : - sendQueryResponse(query_id, null, 0); - } - - /** - * Handles an I/O failure occurred in the channel. - */ - private void onIoFailure() { - // All I/O failures cause disconnection. - if (disconnect()) { - // Success of disconnect() indicates that I/O failure is not the - // result of a disconnection request, but is in deed an I/O - // failure. Report lost connection to the derived class. - Loge("Connection with the emulator has been lost in Channel " + mChannelName); - onEmulatorDisconnected(); - } - } - - /** - * Loops on the local socket, handling connection attempts. - */ - private void runIOLooper() { - if (DEBUG) Log.d(TAG, "In I/O looper for Channel " + mChannelName); - // Initialize byte buffer large enough to receive packet header. - ByteBuffer header = ByteBuffer.allocate(ProtocolConstants.PACKET_HEADER_SIZE); - header.order(mEndian); - try { - // Since disconnection (which will null the mSocket) can be - // requested from outside of this thread, it's simpler just to make - // a copy of mSocket here, and work with that copy. Otherwise we - // will have to go through a complex synchronization algorithm that - // would decrease performance on normal runs. If socket gets closed - // while we're in the middle of transfer, an exception will occur, - // which we will catch and handle properly. - Socket socket = mSocket; - while (socket != null) { - // Reset header position. - header.position(0); - // This will receive total packet size + packet type. - socket.receive(header.array()); - // First - signature. - final int signature = header.getInt(); - assert signature == ProtocolConstants.PACKET_SIGNATURE; - // Next - packet size (including header). - int remains = header.getInt() - ProtocolConstants.PACKET_HEADER_SIZE; - // After the size comes packet type. - final int packet_type = header.getInt(); - - // Get the remainder of the data, and dispatch the packet to - // an appropriate handler. - switch (packet_type) { - case ProtocolConstants.PACKET_TYPE_MESSAGE: - // Read message header (one int: message type). - final int ext = ProtocolConstants.MESSAGE_HEADER_SIZE - ProtocolConstants.PACKET_HEADER_SIZE; - header.position(0); - socket.receive(header.array(), ext); - final int msg_type = header.getInt(); - - // Read message data. - remains -= ext; - final ByteBuffer msg_data = ByteBuffer.allocate(remains); - msg_data.order(mEndian); - socket.receive(msg_data.array()); - - // Dispatch message for handling. - onEmulatorMessage(msg_type, msg_data); - break; - - case ProtocolConstants.PACKET_TYPE_QUERY: - // Read query ID and query type. - final int extq = ProtocolConstants.QUERY_HEADER_SIZE - ProtocolConstants.PACKET_HEADER_SIZE; - header.position(0); - socket.receive(header.array(), extq); - final int query_id = header.getInt(); - final int query_type = header.getInt(); - - // Read query data. - remains -= extq; - final ByteBuffer query_data = ByteBuffer.allocate(remains); - query_data.order(mEndian); - socket.receive(query_data.array()); - - // Dispatch query for handling. - onEmulatorQuery(query_id, query_type, query_data); - break; - - default: - // Unknown packet type. Just discard the remainder - // of the packet - Loge("Unknown packet type " + packet_type + " in Channel " - + mChannelName); - final byte[] discard_data = new byte[remains]; - socket.receive(discard_data); - break; - } - socket = mSocket; - } - } catch (IOException e) { - Loge("Exception " + e + " in I/O looper for Channel " + mChannelName); - onIoFailure(); - } - if (DEBUG) Log.d(TAG, "Exiting I/O looper for Channel " + mChannelName); - } - - /** - * Indicates any UI handler is currently registered with the channel. If no UI - * is displaying the channel's state, maybe the channel can skip UI related tasks. - * - * @return True if there's at least one UI handler registered. - */ - public boolean hasUiHandler() { - return !mUiHandlers.isEmpty(); - } - - /** - * Registers a new UI handler. - * - * @param uiHandler A non-null UI handler to register. Ignored if the UI - * handler is null or already registered. - */ - public void addUiHandler(android.os.Handler uiHandler) { - assert uiHandler != null; - if (uiHandler != null) { - if (!mUiHandlers.contains(uiHandler)) { - mUiHandlers.add(uiHandler); - } - } - } - - /** - * Unregisters an UI handler. - * - * @param uiHandler A non-null UI listener to unregister. Ignored if the - * listener is null or already registered. - */ - public void removeUiHandler(android.os.Handler uiHandler) { - assert uiHandler != null; - mUiHandlers.remove(uiHandler); - } - - /** - * Protected method to be used by handlers to send an event to all UI - * handlers. - * - * @param event An integer event code with no specific parameters. To be - * defined by the handler itself. - */ - protected void notifyUiHandlers(int event) { - for (android.os.Handler uiHandler : mUiHandlers) { - uiHandler.sendEmptyMessage(event); - } - } - - /** - * Protected method to be used by handlers to send an event to all UI - * handlers. - * - * @param msg An event with parameters. To be defined by the handler itself. - */ - protected void notifyUiHandlers(Message msg) { - for (android.os.Handler uiHandler : mUiHandlers) { - uiHandler.sendMessage(msg); - } - } - - /** - * A helper routine that expands ByteBuffer to contain given number of extra - * bytes. - * - * @param buff Buffer to expand. - * @param extra Number of bytes that are required to be available in the - * buffer after current position() - * @return ByteBuffer, containing required number of available bytes. - */ - public ByteBuffer ExpandIf(ByteBuffer buff, int extra) { - if (extra <= buff.remaining()) { - return buff; - } - ByteBuffer ret = ByteBuffer.allocate(buff.position() + extra); - ret.order(buff.order()); - ret.put(buff.array(), 0, buff.position()); - return ret; - } - - /*************************************************************************** - * Logging wrappers - **************************************************************************/ - - private void Loge(String log) { - mService.addError(log); - Log.e(TAG, log); - } - - private void Logw(String log) { - Log.w(TAG, log); - } - - private void Logv(String log) { - Log.v(TAG, log); - } -} |