Roll NDK to pick std::deque patch.
[android_tools.git] / sdk / tools / apps / SdkController / src / com / android / tools / sdkcontroller / lib / Channel.java
blob639f4cfd4c408fd3ffc2c14b947b925e495faa56
1 /*
2 * Copyright (C) 2012 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
17 package com.android.tools.sdkcontroller.lib;
19 import android.os.Message;
20 import android.util.Log;
22 import com.android.tools.sdkcontroller.service.ControllerService;
24 import java.io.IOException;
25 import java.nio.ByteBuffer;
26 import java.nio.ByteOrder;
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.concurrent.BlockingQueue;
30 import java.util.concurrent.LinkedBlockingQueue;
31 import java.util.concurrent.atomic.AtomicInteger;
33 /**
34 * Encapsulates basics of a connection with the emulator.
35 * This class must be used as a base class for all the channelss that provide
36 * particular type of emulation (such as sensors, multi-touch, etc.)
37 * <p/>
38 * Essentially, Channel is an implementation of a particular emulated functionality,
39 * that defines logical format of the data transferred between the emulator and
40 * SDK controller. For instance, "sensors" is a channel that emulates sensors,
41 * and transfers sensor value changes from the device to the emulator. "Multi-touch"
42 * is a channel that supports multi-touch emulation, and transfers multi-touch
43 * events to the emulator, while receiving frame buffer updates from the emulator.
44 * <p/>
45 * Besides connection with the emulator, each channel may contain one or more UI
46 * components associated with it. This class provides some basics for UI support,
47 * including:
48 * <p/>
49 * - Providing a way to register / unregister a UI component with the channel.
50 * <p/>
51 * - Implementing posting of messages to emulator in opposite to direct message
52 * sent. This is due to requirement that UI threads are prohibited from doing
53 * network I/O.
55 public abstract class Channel {
57 /**
58 * Encapsulates a message posted to be sent to the emulator from a worker
59 * thread. This class is used to describe a message that is posted in UI
60 * thread, and then picked up in the worker thread.
62 private class SdkControllerMessage {
63 /** Message type. */
64 private int mMessageType;
65 /** Message data (can be null). */
66 private byte[] mMessage;
67 /** Message data size */
68 private int mMessageSize;
70 /**
71 * Construct message from an array.
73 * @param type Message type.
74 * @param message Message data. Message data size is defined by size of
75 * the array.
77 public SdkControllerMessage(int type, byte[] message) {
78 mMessageType = type;
79 mMessage = message;
80 mMessageSize = (message != null) ? message.length : 0;
83 /**
84 * Construct message from a ByteBuffer.
86 * @param type Message type.
87 * @param message Message data. Message data size is defined by
88 * position() property of the ByteBuffer.
90 public SdkControllerMessage(int type, ByteBuffer message) {
91 mMessageType = type;
92 if (message != null) {
93 mMessage = message.array();
94 mMessageSize = message.position();
95 } else {
96 mMessage = null;
97 mMessageSize = 0;
102 * Gets message type.
105 * @return Message type.
107 public int getMessageType() {
108 return mMessageType;
112 * Gets message buffer.
114 * @return Message buffer.
116 public byte[] getMessage() {
117 return mMessage;
121 * Gets message buffer size.
123 * @return Message buffer size.
125 public int getMessageSize() {
126 return mMessageSize;
128 } // SdkControllerMessage
131 * Names for currently implemented SDK controller channels.
134 /** Name for a channel that handles sensors emulation */
135 public static final String SENSOR_CHANNEL = "sensors";
136 /** Name for a channel that handles multi-touch emulation */
137 public static final String MULTITOUCH_CHANNEL = "multi-touch";
140 * Types of messages internally used by Channel class.
143 /** Service-side emulator is connected. */
144 private static final int MSG_CONNECTED = -1;
145 /** Service-side emulator is disconnected. */
146 private static final int MSG_DISCONNECTED = -2;
147 /** Service-side emulator is enabled. */
148 private static final int MSG_ENABLED = -3;
149 /** Service-side emulator is disabled. */
150 private static final int MSG_DISABLED = -4;
152 /** Tag for logging messages. */
153 private static final String TAG = "SdkControllerChannel";
154 /** Controls debug log. */
155 private static final boolean DEBUG = false;
157 /** Service that has created this object. */
158 protected ControllerService mService;
161 * Socket stuff.
164 /** Socket to use to to communicate with the emulator. */
165 private Socket mSocket = null;
166 /** Channel name ("sensors", "multi-touch", etc.) */
167 private String mChannelName;
168 /** Endianness of data transferred in this channel. */
169 private ByteOrder mEndian;
172 * Message posting support.
175 /** Total number of messages posted in this channel */
176 private final AtomicInteger mMsgCount = new AtomicInteger(0);
177 /** Flags whether or not message thread is running. */
178 private volatile boolean mRunMsgQueue = true;
179 /** Queue of messages pending transmission. */
180 private final BlockingQueue<SdkControllerMessage>
181 mMsgQueue = new LinkedBlockingQueue<SdkControllerMessage>();
182 /** Message thread */
183 private final Thread mMsgThread;
186 * UI support.
189 /** Lists UI handlers attached to this channel. */
190 private final List<android.os.Handler> mUiHandlers = new ArrayList<android.os.Handler>();
193 * Abstract methods.
197 * This method is invoked when this channel is fully connected with its
198 * counterpart in the emulator.
200 public abstract void onEmulatorConnected();
203 * This method is invoked when this channel loses connection with its
204 * counterpart in the emulator.
206 public abstract void onEmulatorDisconnected();
209 * A message has been received from the emulator.
211 * @param msg_type Message type.
212 * @param msg_data Message data. Message data size is defined by the length
213 * of the array wrapped by the ByteBuffer.
215 public abstract void onEmulatorMessage(int msg_type, ByteBuffer msg_data);
218 * A query has been received from the emulator.
220 * @param query_id Identifies the query. This ID must be used when replying
221 * to the query.
222 * @param query_type Query type.
223 * @param query_data Query data. Query data size is defined by the length of
224 * the array wrapped by the ByteBuffer.
226 public abstract void onEmulatorQuery(int query_id, int query_type, ByteBuffer query_data);
229 * Channel implementation.
233 * Constructs Channel instance.
235 * @param name Channel name.
237 public Channel(ControllerService service, String name) {
238 mService = service;
239 mChannelName = name;
240 // Start the worker thread for posted messages.
241 mMsgThread = new Thread(new Runnable() {
242 @Override
243 public void run() {
244 if (DEBUG) Log.d(TAG, "MsgThread.started-" + mChannelName);
245 while (mRunMsgQueue) {
246 try {
247 SdkControllerMessage msg = mMsgQueue.take();
248 if (msg != null) {
249 sendMessage(
250 msg.getMessageType(), msg.getMessage(), msg.getMessageSize());
251 mMsgCount.incrementAndGet();
253 } catch (InterruptedException e) {
254 Log.e(TAG, "MsgThread-" + mChannelName, e);
257 if (DEBUG) Log.d(TAG, "MsgThread.terminate-" + mChannelName);
259 }, "MsgThread-" + name);
260 mMsgThread.start();
261 if (DEBUG) Log.d(TAG, "Channel is constructed for " + mChannelName);
265 * Gets name for this channel.
267 * @return Emulator name.
269 public String getChannelName() {
270 return mChannelName;
274 * Gets endianness for this channel.
276 * @return Channel endianness.
278 public ByteOrder getEndian() {
279 return mEndian;
283 * Gets number of messages sent via postMessage method.
285 * @return Number of messages sent via postMessage method.
287 public int getMsgSentCount() {
288 return mMsgCount.get();
292 * Checks if this channel is connected with the emulator.
294 * @return true if this channel is connected with the emulator, or false if it is
295 * not connected.
297 public boolean isConnected() {
298 // Use local copy of the socket, ensuring it's not going to NULL while
299 // we're working with it. If it gets closed, while we're in the middle
300 // of data transfer - it's OK, since it will produce an exception, and
301 // the caller will gracefully handle it.
303 // Same technique is used everywhere in this class where mSocket member
304 // is touched.
305 Socket socket = mSocket;
306 return socket != null && socket.isConnected();
310 * Establishes connection with the emulator. This method is called by Connection
311 * object when emulator successfully connects to this channel, or this channel
312 * gets registered, and there is a pending socket connection for it.
314 * @param socket Channel connection socket.
316 public void connect(Socket socket) {
317 mSocket = socket;
318 mEndian = socket.getEndian();
319 Logv("Channel " + mChannelName + " is now connected with the emulator.");
320 // Notify the emulator that connection is established.
321 sendMessage(MSG_CONNECTED, (byte[]) null);
323 // Let the derived class know that emulator is connected, and start the
324 // I/O loop in which we will receive data from the emulator. Note that
325 // we start the loop after onEmulatorConnected call, since we don't want
326 // to start dispatching messages before the derived class could set
327 // itself up for receiving them.
328 onEmulatorConnected();
329 new Thread(new Runnable() {
330 @Override
331 public void run() {
332 runIOLooper();
334 }, "ChannelIoLoop").start();
335 mService.notifyStatusChanged();
339 * Disconnects this channel from the emulator.
341 * @return true if this channel has been disconnected in this call, or false if
342 * channel has been already disconnected when this method has been called.
344 public boolean disconnect() {
345 // This is the only place in this class where we will null the
346 // socket object. Since this method can be called concurrently from
347 // different threads, lets do this under the lock.
348 Socket socket;
349 synchronized (this) {
350 socket = mSocket;
351 mSocket = null;
353 if (socket != null) {
354 // Notify the emulator about channel disconnection before we close
355 // the communication socket.
356 try {
357 sendMessage(socket, MSG_DISCONNECTED, null, 0);
358 } catch (IOException e) {
359 // Ignore I/O exception at this point. We don't care about
360 // it, since the socket is being closed anyways.
362 // This will eventually stop I/O looper thread.
363 socket.close();
364 mService.notifyStatusChanged();
366 return socket != null;
370 * Enables the emulation. Typically, this method is called for channels that are
371 * dependent on UI to handle the emulation. For instance, multi-touch emulation is
372 * disabled until at least one UI component is attached to the channel. So, for
373 * multi-touch emulation this method is called when UI gets attached to the channel.
375 public void enable() {
376 postMessage(MSG_ENABLED, (byte[]) null);
377 mService.notifyStatusChanged();
381 * Disables the emulation. Just the opposite to enable(). For multi-touch this
382 * method is called when UI detaches from the channel.
384 public void disable() {
385 postMessage(MSG_DISABLED, (byte[]) null);
386 mService.notifyStatusChanged();
390 * Sends message to the emulator.
392 * @param socket Socket to send the message to.
393 * @param msg_type Message type.
394 * @param msg Message data to send.
395 * @param len Byte size of message data.
396 * @throws IOException
398 private void sendMessage(Socket socket, int msg_type, byte[] msg, int len)
399 throws IOException {
400 // In async environment we must have message header and message data in
401 // one block to prevent messages from other threads getting between the
402 // header and the data. So, we can't sent header, and then the data. We
403 // must combine them in one data block instead.
404 ByteBuffer bb = ByteBuffer.allocate(ProtocolConstants.MESSAGE_HEADER_SIZE + len);
405 bb.order(mEndian);
407 // Initialize message header.
408 bb.putInt(ProtocolConstants.PACKET_SIGNATURE);
409 bb.putInt(ProtocolConstants.MESSAGE_HEADER_SIZE + len);
410 bb.putInt(ProtocolConstants.PACKET_TYPE_MESSAGE);
411 bb.putInt(msg_type);
413 // Save message data (if there is any).
414 if (len != 0) {
415 bb.put(msg, 0, len);
418 socket.send(bb.array());
422 * Sends message to the emulator.
424 * @param msg_type Message type.
425 * @param msg Message data to send. Message size is defined by the size of
426 * the array.
427 * @return true on success, or false if data transmission has failed.
429 public boolean sendMessage(int msg_type, byte[] msg, int msg_len) {
430 try {
431 Socket socket = mSocket;
432 if (socket != null) {
433 sendMessage(socket, msg_type, msg, msg_len);
434 return true;
435 } else {
436 Logw("sendMessage is called on disconnected Channel " + mChannelName);
438 } catch (IOException e) {
439 Loge("Exception " + e + " in sendMessage for Channel " + mChannelName);
440 onIoFailure();
442 return false;
446 * Sends message to the emulator.
448 * @param msg_type Message type.
449 * @param msg Message data to send. Message size is defined by the size of
450 * the array.
451 * @return true on success, or false if data transmission has failed.
453 public boolean sendMessage(int msg_type, byte[] msg) {
454 try {
455 Socket socket = mSocket;
456 if (socket != null) {
457 if (msg != null) {
458 sendMessage(socket, msg_type, msg, msg.length);
459 } else {
460 sendMessage(socket, msg_type, null, 0);
462 return true;
463 } else {
464 Logw("sendMessage is called on disconnected Channel " + mChannelName);
466 } catch (IOException e) {
467 Loge("Exception " + e + " in sendMessage for Channel " + mChannelName);
468 onIoFailure();
470 return false;
474 * Sends message to the emulator.
476 * @param msg_type Message type.
477 * @param msg Message data to send. Message size is defined by the
478 * position() property of the ByteBuffer.
479 * @return true on success, or false if data transmission has failed.
481 public boolean sendMessage(int msg_type, ByteBuffer msg) {
482 try {
483 Socket socket = mSocket;
484 if (socket != null) {
485 if (msg != null) {
486 sendMessage(socket, msg_type, msg.array(), msg.position());
487 } else {
488 sendMessage(socket, msg_type, null, 0);
490 return true;
491 } else {
492 Logw("sendMessage is called on disconnected Channel " + mChannelName);
494 } catch (IOException e) {
495 Loge("Exception " + e + " in sendMessage for Channel " + mChannelName);
496 onIoFailure();
498 return false;
502 * Posts message to the emulator.
504 * @param msg_type Message type.
505 * @param msg Message data to post. Message size is defined by the size of
506 * the array.
508 public void postMessage(int msg_type, byte[] msg) {
509 try {
510 mMsgQueue.put(new SdkControllerMessage(msg_type, msg));
511 } catch (InterruptedException e) {
512 Log.e(TAG, "mMessageQueue.put", e);
517 * Posts message to the emulator.
519 * @param msg_type Message type.
520 * @param msg Message data to post. Message size is defined by the
521 * position() property of the ByteBuffer.
523 public void postMessage(int msg_type, ByteBuffer msg) {
524 try {
525 mMsgQueue.put(new SdkControllerMessage(msg_type, msg));
526 } catch (InterruptedException e) {
527 Log.e(TAG, "mMessageQueue.put", e);
532 * Sends query response to the emulator.
534 * @param query_id Query identifier.
535 * @param qresp Response to the query.
536 * @param len Byte size of query response data.
537 * @return true on success, or false if data transmission has failed.
539 public boolean sendQueryResponse(int query_id, byte[] qresp, int len) {
540 // Just like with messages, we must combine header and data in a single
541 // transmitting block.
542 ByteBuffer bb = ByteBuffer.allocate(ProtocolConstants.QUERY_RESP_HEADER_SIZE + len);
543 bb.order(mEndian);
545 // Initialize response header.
546 bb.putInt(ProtocolConstants.PACKET_SIGNATURE);
547 bb.putInt(ProtocolConstants.QUERY_RESP_HEADER_SIZE + len);
548 bb.putInt(ProtocolConstants.PACKET_TYPE_QUERY_RESPONSE);
549 bb.putInt(query_id);
551 // Save response data (if there is any).
552 if (qresp != null && len != 0) {
553 bb.put(qresp, 0, len);
556 // Send the response.
557 try {
558 Socket socket = mSocket;
559 if (socket != null) {
560 socket.send(bb.array());
561 return true;
562 } else {
563 Logw("sendQueryResponse is called on disconnected Channel "
564 + mChannelName);
566 } catch (IOException e) {
567 Loge("Exception " + e + " in sendQueryResponse for Channel " + mChannelName);
568 onIoFailure();
570 return false;
574 * Sends query response to the emulator.
576 * @param query_id Query identifier.
577 * @param qresp Response to the query. Query response size is defined by the
578 * size of the array.
579 * @return true on success, or false if data transmission has failed.
581 public boolean sendQueryResponse(int query_id, byte[] qresp) {
582 return (qresp != null) ? sendQueryResponse(query_id, qresp, qresp.length) :
583 sendQueryResponse(query_id, null, 0);
587 * Sends query response to the emulator.
589 * @param query_id Query identifier.
590 * @param qresp Response to the query. Query response size is defined by the
591 * position() property of the ByteBuffer.
592 * @return true on success, or false if data transmission has failed.
594 public boolean sendQueryResponse(int query_id, ByteBuffer qresp) {
595 return (qresp != null) ? sendQueryResponse(query_id, qresp.array(), qresp.position()) :
596 sendQueryResponse(query_id, null, 0);
600 * Handles an I/O failure occurred in the channel.
602 private void onIoFailure() {
603 // All I/O failures cause disconnection.
604 if (disconnect()) {
605 // Success of disconnect() indicates that I/O failure is not the
606 // result of a disconnection request, but is in deed an I/O
607 // failure. Report lost connection to the derived class.
608 Loge("Connection with the emulator has been lost in Channel " + mChannelName);
609 onEmulatorDisconnected();
614 * Loops on the local socket, handling connection attempts.
616 private void runIOLooper() {
617 if (DEBUG) Log.d(TAG, "In I/O looper for Channel " + mChannelName);
618 // Initialize byte buffer large enough to receive packet header.
619 ByteBuffer header = ByteBuffer.allocate(ProtocolConstants.PACKET_HEADER_SIZE);
620 header.order(mEndian);
621 try {
622 // Since disconnection (which will null the mSocket) can be
623 // requested from outside of this thread, it's simpler just to make
624 // a copy of mSocket here, and work with that copy. Otherwise we
625 // will have to go through a complex synchronization algorithm that
626 // would decrease performance on normal runs. If socket gets closed
627 // while we're in the middle of transfer, an exception will occur,
628 // which we will catch and handle properly.
629 Socket socket = mSocket;
630 while (socket != null) {
631 // Reset header position.
632 header.position(0);
633 // This will receive total packet size + packet type.
634 socket.receive(header.array());
635 // First - signature.
636 final int signature = header.getInt();
637 assert signature == ProtocolConstants.PACKET_SIGNATURE;
638 // Next - packet size (including header).
639 int remains = header.getInt() - ProtocolConstants.PACKET_HEADER_SIZE;
640 // After the size comes packet type.
641 final int packet_type = header.getInt();
643 // Get the remainder of the data, and dispatch the packet to
644 // an appropriate handler.
645 switch (packet_type) {
646 case ProtocolConstants.PACKET_TYPE_MESSAGE:
647 // Read message header (one int: message type).
648 final int ext = ProtocolConstants.MESSAGE_HEADER_SIZE - ProtocolConstants.PACKET_HEADER_SIZE;
649 header.position(0);
650 socket.receive(header.array(), ext);
651 final int msg_type = header.getInt();
653 // Read message data.
654 remains -= ext;
655 final ByteBuffer msg_data = ByteBuffer.allocate(remains);
656 msg_data.order(mEndian);
657 socket.receive(msg_data.array());
659 // Dispatch message for handling.
660 onEmulatorMessage(msg_type, msg_data);
661 break;
663 case ProtocolConstants.PACKET_TYPE_QUERY:
664 // Read query ID and query type.
665 final int extq = ProtocolConstants.QUERY_HEADER_SIZE - ProtocolConstants.PACKET_HEADER_SIZE;
666 header.position(0);
667 socket.receive(header.array(), extq);
668 final int query_id = header.getInt();
669 final int query_type = header.getInt();
671 // Read query data.
672 remains -= extq;
673 final ByteBuffer query_data = ByteBuffer.allocate(remains);
674 query_data.order(mEndian);
675 socket.receive(query_data.array());
677 // Dispatch query for handling.
678 onEmulatorQuery(query_id, query_type, query_data);
679 break;
681 default:
682 // Unknown packet type. Just discard the remainder
683 // of the packet
684 Loge("Unknown packet type " + packet_type + " in Channel "
685 + mChannelName);
686 final byte[] discard_data = new byte[remains];
687 socket.receive(discard_data);
688 break;
690 socket = mSocket;
692 } catch (IOException e) {
693 Loge("Exception " + e + " in I/O looper for Channel " + mChannelName);
694 onIoFailure();
696 if (DEBUG) Log.d(TAG, "Exiting I/O looper for Channel " + mChannelName);
700 * Indicates any UI handler is currently registered with the channel. If no UI
701 * is displaying the channel's state, maybe the channel can skip UI related tasks.
703 * @return True if there's at least one UI handler registered.
705 public boolean hasUiHandler() {
706 return !mUiHandlers.isEmpty();
710 * Registers a new UI handler.
712 * @param uiHandler A non-null UI handler to register. Ignored if the UI
713 * handler is null or already registered.
715 public void addUiHandler(android.os.Handler uiHandler) {
716 assert uiHandler != null;
717 if (uiHandler != null) {
718 if (!mUiHandlers.contains(uiHandler)) {
719 mUiHandlers.add(uiHandler);
725 * Unregisters an UI handler.
727 * @param uiHandler A non-null UI listener to unregister. Ignored if the
728 * listener is null or already registered.
730 public void removeUiHandler(android.os.Handler uiHandler) {
731 assert uiHandler != null;
732 mUiHandlers.remove(uiHandler);
736 * Protected method to be used by handlers to send an event to all UI
737 * handlers.
739 * @param event An integer event code with no specific parameters. To be
740 * defined by the handler itself.
742 protected void notifyUiHandlers(int event) {
743 for (android.os.Handler uiHandler : mUiHandlers) {
744 uiHandler.sendEmptyMessage(event);
749 * Protected method to be used by handlers to send an event to all UI
750 * handlers.
752 * @param msg An event with parameters. To be defined by the handler itself.
754 protected void notifyUiHandlers(Message msg) {
755 for (android.os.Handler uiHandler : mUiHandlers) {
756 uiHandler.sendMessage(msg);
761 * A helper routine that expands ByteBuffer to contain given number of extra
762 * bytes.
764 * @param buff Buffer to expand.
765 * @param extra Number of bytes that are required to be available in the
766 * buffer after current position()
767 * @return ByteBuffer, containing required number of available bytes.
769 public ByteBuffer ExpandIf(ByteBuffer buff, int extra) {
770 if (extra <= buff.remaining()) {
771 return buff;
773 ByteBuffer ret = ByteBuffer.allocate(buff.position() + extra);
774 ret.order(buff.order());
775 ret.put(buff.array(), 0, buff.position());
776 return ret;
779 /***************************************************************************
780 * Logging wrappers
781 **************************************************************************/
783 private void Loge(String log) {
784 mService.addError(log);
785 Log.e(TAG, log);
788 private void Logw(String log) {
789 Log.w(TAG, log);
792 private void Logv(String log) {
793 Log.v(TAG, log);