1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 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 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 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. 15 */ 16 17 package com.android.tools.sdkcontroller.lib; 18 19 import android.os.Message; 20 import android.util.Log; 21 22 import com.android.tools.sdkcontroller.service.ControllerService; 23 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; 32 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. 54 */ 55 public abstract class Channel { 56 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. 61 */ 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; 69 70 /** 71 * Construct message from an array. 72 * 73 * @param type Message type. 74 * @param message Message data. Message data size is defined by size of 75 * the array. 76 */ SdkControllerMessage(int type, byte[] message)77 public SdkControllerMessage(int type, byte[] message) { 78 mMessageType = type; 79 mMessage = message; 80 mMessageSize = (message != null) ? message.length : 0; 81 } 82 83 /** 84 * Construct message from a ByteBuffer. 85 * 86 * @param type Message type. 87 * @param message Message data. Message data size is defined by 88 * position() property of the ByteBuffer. 89 */ SdkControllerMessage(int type, ByteBuffer message)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; 98 } 99 } 100 101 /** 102 * Gets message type. 103 104 * 105 * @return Message type. 106 */ getMessageType()107 public int getMessageType() { 108 return mMessageType; 109 } 110 111 /** 112 * Gets message buffer. 113 * 114 * @return Message buffer. 115 */ getMessage()116 public byte[] getMessage() { 117 return mMessage; 118 } 119 120 /** 121 * Gets message buffer size. 122 * 123 * @return Message buffer size. 124 */ getMessageSize()125 public int getMessageSize() { 126 return mMessageSize; 127 } 128 } // SdkControllerMessage 129 130 /* 131 * Names for currently implemented SDK controller channels. 132 */ 133 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"; 138 139 /* 140 * Types of messages internally used by Channel class. 141 */ 142 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; 151 152 /** Tag for logging messages. */ 153 private static final String TAG = "SdkControllerChannel"; 154 /** Controls debug log. */ 155 private static final boolean DEBUG = false; 156 157 /** Service that has created this object. */ 158 protected ControllerService mService; 159 160 /* 161 * Socket stuff. 162 */ 163 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; 170 171 /* 172 * Message posting support. 173 */ 174 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; 184 185 /* 186 * UI support. 187 */ 188 189 /** Lists UI handlers attached to this channel. */ 190 private final List<android.os.Handler> mUiHandlers = new ArrayList<android.os.Handler>(); 191 192 /* 193 * Abstract methods. 194 */ 195 196 /** 197 * This method is invoked when this channel is fully connected with its 198 * counterpart in the emulator. 199 */ onEmulatorConnected()200 public abstract void onEmulatorConnected(); 201 202 /** 203 * This method is invoked when this channel loses connection with its 204 * counterpart in the emulator. 205 */ onEmulatorDisconnected()206 public abstract void onEmulatorDisconnected(); 207 208 /** 209 * A message has been received from the emulator. 210 * 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. 214 */ onEmulatorMessage(int msg_type, ByteBuffer msg_data)215 public abstract void onEmulatorMessage(int msg_type, ByteBuffer msg_data); 216 217 /** 218 * A query has been received from the emulator. 219 * 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. 225 */ onEmulatorQuery(int query_id, int query_type, ByteBuffer query_data)226 public abstract void onEmulatorQuery(int query_id, int query_type, ByteBuffer query_data); 227 228 /* 229 * Channel implementation. 230 */ 231 232 /** 233 * Constructs Channel instance. 234 * 235 * @param name Channel name. 236 */ Channel(ControllerService service, String 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(); 252 } 253 } catch (InterruptedException e) { 254 Log.e(TAG, "MsgThread-" + mChannelName, e); 255 } 256 } 257 if (DEBUG) Log.d(TAG, "MsgThread.terminate-" + mChannelName); 258 } 259 }, "MsgThread-" + name); 260 mMsgThread.start(); 261 if (DEBUG) Log.d(TAG, "Channel is constructed for " + mChannelName); 262 } 263 264 /** 265 * Gets name for this channel. 266 * 267 * @return Emulator name. 268 */ getChannelName()269 public String getChannelName() { 270 return mChannelName; 271 } 272 273 /** 274 * Gets endianness for this channel. 275 * 276 * @return Channel endianness. 277 */ getEndian()278 public ByteOrder getEndian() { 279 return mEndian; 280 } 281 282 /** 283 * Gets number of messages sent via postMessage method. 284 * 285 * @return Number of messages sent via postMessage method. 286 */ getMsgSentCount()287 public int getMsgSentCount() { 288 return mMsgCount.get(); 289 } 290 291 /** 292 * Checks if this channel is connected with the emulator. 293 * 294 * @return true if this channel is connected with the emulator, or false if it is 295 * not connected. 296 */ isConnected()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. 302 // 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(); 307 } 308 309 /** 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. 313 * 314 * @param socket Channel connection socket. 315 */ connect(Socket 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); 322 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(); 333 } 334 }, "ChannelIoLoop").start(); 335 mService.notifyStatusChanged(); 336 } 337 338 /** 339 * Disconnects this channel from the emulator. 340 * 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. 343 */ disconnect()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; 352 } 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. 361 } 362 // This will eventually stop I/O looper thread. 363 socket.close(); 364 mService.notifyStatusChanged(); 365 } 366 return socket != null; 367 } 368 369 /** 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. 374 */ enable()375 public void enable() { 376 postMessage(MSG_ENABLED, (byte[]) null); 377 mService.notifyStatusChanged(); 378 } 379 380 /** 381 * Disables the emulation. Just the opposite to enable(). For multi-touch this 382 * method is called when UI detaches from the channel. 383 */ disable()384 public void disable() { 385 postMessage(MSG_DISABLED, (byte[]) null); 386 mService.notifyStatusChanged(); 387 } 388 389 /** 390 * Sends message to the emulator. 391 * 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 397 */ sendMessage(Socket socket, int msg_type, byte[] msg, int len)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); 406 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); 412 413 // Save message data (if there is any). 414 if (len != 0) { 415 bb.put(msg, 0, len); 416 } 417 418 socket.send(bb.array()); 419 } 420 421 /** 422 * Sends message to the emulator. 423 * 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. 428 */ sendMessage(int msg_type, byte[] msg, int msg_len)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); 437 } 438 } catch (IOException e) { 439 Loge("Exception " + e + " in sendMessage for Channel " + mChannelName); 440 onIoFailure(); 441 } 442 return false; 443 } 444 445 /** 446 * Sends message to the emulator. 447 * 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. 452 */ sendMessage(int msg_type, byte[] msg)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); 461 } 462 return true; 463 } else { 464 Logw("sendMessage is called on disconnected Channel " + mChannelName); 465 } 466 } catch (IOException e) { 467 Loge("Exception " + e + " in sendMessage for Channel " + mChannelName); 468 onIoFailure(); 469 } 470 return false; 471 } 472 473 /** 474 * Sends message to the emulator. 475 * 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. 480 */ sendMessage(int msg_type, ByteBuffer msg)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); 489 } 490 return true; 491 } else { 492 Logw("sendMessage is called on disconnected Channel " + mChannelName); 493 } 494 } catch (IOException e) { 495 Loge("Exception " + e + " in sendMessage for Channel " + mChannelName); 496 onIoFailure(); 497 } 498 return false; 499 } 500 501 /** 502 * Posts message to the emulator. 503 * 504 * @param msg_type Message type. 505 * @param msg Message data to post. Message size is defined by the size of 506 * the array. 507 */ postMessage(int msg_type, byte[] msg)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); 513 } 514 } 515 516 /** 517 * Posts message to the emulator. 518 * 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. 522 */ postMessage(int msg_type, ByteBuffer msg)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); 528 } 529 } 530 531 /** 532 * Sends query response to the emulator. 533 * 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. 538 */ sendQueryResponse(int query_id, byte[] qresp, int len)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); 544 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); 550 551 // Save response data (if there is any). 552 if (qresp != null && len != 0) { 553 bb.put(qresp, 0, len); 554 } 555 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); 565 } 566 } catch (IOException e) { 567 Loge("Exception " + e + " in sendQueryResponse for Channel " + mChannelName); 568 onIoFailure(); 569 } 570 return false; 571 } 572 573 /** 574 * Sends query response to the emulator. 575 * 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. 580 */ sendQueryResponse(int query_id, byte[] qresp)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); 584 } 585 586 /** 587 * Sends query response to the emulator. 588 * 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. 593 */ sendQueryResponse(int query_id, ByteBuffer qresp)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); 597 } 598 599 /** 600 * Handles an I/O failure occurred in the channel. 601 */ onIoFailure()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(); 610 } 611 } 612 613 /** 614 * Loops on the local socket, handling connection attempts. 615 */ runIOLooper()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(); 642 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(); 652 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()); 658 659 // Dispatch message for handling. 660 onEmulatorMessage(msg_type, msg_data); 661 break; 662 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(); 670 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()); 676 677 // Dispatch query for handling. 678 onEmulatorQuery(query_id, query_type, query_data); 679 break; 680 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; 689 } 690 socket = mSocket; 691 } 692 } catch (IOException e) { 693 Loge("Exception " + e + " in I/O looper for Channel " + mChannelName); 694 onIoFailure(); 695 } 696 if (DEBUG) Log.d(TAG, "Exiting I/O looper for Channel " + mChannelName); 697 } 698 699 /** 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. 702 * 703 * @return True if there's at least one UI handler registered. 704 */ hasUiHandler()705 public boolean hasUiHandler() { 706 return !mUiHandlers.isEmpty(); 707 } 708 709 /** 710 * Registers a new UI handler. 711 * 712 * @param uiHandler A non-null UI handler to register. Ignored if the UI 713 * handler is null or already registered. 714 */ addUiHandler(android.os.Handler uiHandler)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); 720 } 721 } 722 } 723 724 /** 725 * Unregisters an UI handler. 726 * 727 * @param uiHandler A non-null UI listener to unregister. Ignored if the 728 * listener is null or already registered. 729 */ removeUiHandler(android.os.Handler uiHandler)730 public void removeUiHandler(android.os.Handler uiHandler) { 731 assert uiHandler != null; 732 mUiHandlers.remove(uiHandler); 733 } 734 735 /** 736 * Protected method to be used by handlers to send an event to all UI 737 * handlers. 738 * 739 * @param event An integer event code with no specific parameters. To be 740 * defined by the handler itself. 741 */ notifyUiHandlers(int event)742 protected void notifyUiHandlers(int event) { 743 for (android.os.Handler uiHandler : mUiHandlers) { 744 uiHandler.sendEmptyMessage(event); 745 } 746 } 747 748 /** 749 * Protected method to be used by handlers to send an event to all UI 750 * handlers. 751 * 752 * @param msg An event with parameters. To be defined by the handler itself. 753 */ notifyUiHandlers(Message msg)754 protected void notifyUiHandlers(Message msg) { 755 for (android.os.Handler uiHandler : mUiHandlers) { 756 uiHandler.sendMessage(msg); 757 } 758 } 759 760 /** 761 * A helper routine that expands ByteBuffer to contain given number of extra 762 * bytes. 763 * 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. 768 */ ExpandIf(ByteBuffer buff, int extra)769 public ByteBuffer ExpandIf(ByteBuffer buff, int extra) { 770 if (extra <= buff.remaining()) { 771 return buff; 772 } 773 ByteBuffer ret = ByteBuffer.allocate(buff.position() + extra); 774 ret.order(buff.order()); 775 ret.put(buff.array(), 0, buff.position()); 776 return ret; 777 } 778 779 /*************************************************************************** 780 * Logging wrappers 781 **************************************************************************/ 782 Loge(String log)783 private void Loge(String log) { 784 mService.addError(log); 785 Log.e(TAG, log); 786 } 787 Logw(String log)788 private void Logw(String log) { 789 Log.w(TAG, log); 790 } 791 Logv(String log)792 private void Logv(String log) { 793 Log.v(TAG, log); 794 } 795 } 796