1/** 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of 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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.internal.util; 18 19import android.content.ComponentName; 20import android.content.Context; 21import android.content.Intent; 22import android.content.ServiceConnection; 23import android.os.Handler; 24import android.os.HandlerThread; 25import android.os.IBinder; 26import android.os.Looper; 27import android.os.Message; 28import android.os.Messenger; 29import android.os.RemoteException; 30import android.util.Slog; 31 32import java.util.Stack; 33 34/** 35 * <p>An asynchronous channel between two handlers.</p> 36 * 37 * <p>The handlers maybe in the same process or in another process. There 38 * are two protocol styles that can be used with an AysncChannel. The 39 * first is a simple request/reply protocol where the server does 40 * not need to know which client is issuing the request.</p> 41 * 42 * <p>In a simple request/reply protocol the client/source sends requests to the 43 * server/destination. And the server uses the replyToMessage methods. 44 * In this usage model there is no need for the destination to 45 * use the connect methods. The typical sequence of operations is:</p> 46 *<ol> 47 * <li>Client calls AsyncChannel#connectSync or Asynchronously:</li> 48 * <ol>For an asynchronous half connection client calls AsyncChannel#connect.</ol> 49 * <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li> 50 * </ol> 51 * <li><code>comm-loop:</code></li> 52 * <li>Client calls AsyncChannel#sendMessage</li> 53 * <li>Server processes messages and optionally replies using AsyncChannel#replyToMessage 54 * <li>Loop to <code>comm-loop</code> until done</li> 55 * <li>When done Client calls {@link AsyncChannel#disconnect}</li> 56 * <li>Client/Server receives CMD_CHANNEL_DISCONNECTED from AsyncChannel</li> 57 *</ol> 58 *<br/> 59 * <p>A second usage model is where the server/destination needs to know 60 * which client it's connected too. For example the server needs to 61 * send unsolicited messages back to the client. Or the server keeps 62 * different state for each client. In this model the server will also 63 * use the connect methods. The typical sequence of operation is:</p> 64 *<ol> 65 * <li>Client calls AsyncChannel#fullyConnectSync or Asynchronously:<li> 66 * <ol>For an asynchronous full connection it calls AsyncChannel#connect</li> 67 * <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li> 68 * <li>Client calls AsyncChannel#sendMessage(CMD_CHANNEL_FULL_CONNECTION)</li> 69 * </ol> 70 * <li>Server receives CMD_CHANNEL_FULL_CONNECTION</li> 71 * <li>Server calls AsyncChannel#connected</li> 72 * <li>Server sends AsyncChannel#sendMessage(CMD_CHANNEL_FULLY_CONNECTED)</li> 73 * <li>Client receives CMD_CHANNEL_FULLY_CONNECTED</li> 74 * <li><code>comm-loop:</code></li> 75 * <li>Client/Server uses AsyncChannel#sendMessage/replyToMessage 76 * to communicate and perform work</li> 77 * <li>Loop to <code>comm-loop</code> until done</li> 78 * <li>When done Client/Server calls {@link AsyncChannel#disconnect}</li> 79 * <li>Client/Server receives CMD_CHANNEL_DISCONNECTED from AsyncChannel</li> 80 *</ol> 81 * 82 * TODO: Consider simplifying where we have connect and fullyConnect with only one response 83 * message RSP_CHANNEL_CONNECT instead of two, CMD_CHANNEL_HALF_CONNECTED and 84 * CMD_CHANNEL_FULLY_CONNECTED. We'd also change CMD_CHANNEL_FULL_CONNECTION to REQ_CHANNEL_CONNECT. 85 */ 86public class AsyncChannel { 87 /** Log tag */ 88 private static final String TAG = "AsyncChannel"; 89 90 /** Enable to turn on debugging */ 91 private static final boolean DBG = false; 92 93 private static final int BASE = Protocol.BASE_SYSTEM_ASYNC_CHANNEL; 94 95 /** 96 * Command sent when the channel is half connected. Half connected 97 * means that the channel can be used to send commends to the destination 98 * but the destination is unaware that the channel exists. The first 99 * command sent to the destination is typically CMD_CHANNEL_FULL_CONNECTION if 100 * it is desired to establish a long term connection, but any command maybe 101 * sent. 102 * 103 * msg.arg1 == 0 : STATUS_SUCCESSFUL 104 * 1 : STATUS_BINDING_UNSUCCESSFUL 105 * msg.obj == the AsyncChannel 106 * msg.replyTo == dstMessenger if successful 107 */ 108 public static final int CMD_CHANNEL_HALF_CONNECTED = BASE + 0; 109 110 /** 111 * Command typically sent when after receiving the CMD_CHANNEL_HALF_CONNECTED. 112 * This is used to initiate a long term connection with the destination and 113 * typically the destination will reply with CMD_CHANNEL_FULLY_CONNECTED. 114 * 115 * msg.replyTo = srcMessenger. 116 */ 117 public static final int CMD_CHANNEL_FULL_CONNECTION = BASE + 1; 118 119 /** 120 * Command typically sent after the destination receives a CMD_CHANNEL_FULL_CONNECTION. 121 * This signifies the acceptance or rejection of the channel by the sender. 122 * 123 * msg.arg1 == 0 : Accept connection 124 * : All other values signify the destination rejected the connection 125 * and {@link AsyncChannel#disconnect} would typically be called. 126 */ 127 public static final int CMD_CHANNEL_FULLY_CONNECTED = BASE + 2; 128 129 /** 130 * Command sent when one side or the other wishes to disconnect. The sender 131 * may or may not be able to receive a reply depending upon the protocol and 132 * the state of the connection. The receiver should call {@link AsyncChannel#disconnect} 133 * to close its side of the channel and it will receive a CMD_CHANNEL_DISCONNECTED 134 * when the channel is closed. 135 * 136 * msg.replyTo = messenger that is disconnecting 137 */ 138 public static final int CMD_CHANNEL_DISCONNECT = BASE + 3; 139 140 /** 141 * Command sent when the channel becomes disconnected. This is sent when the 142 * channel is forcibly disconnected by the system or as a reply to CMD_CHANNEL_DISCONNECT. 143 * 144 * msg.arg1 == 0 : STATUS_SUCCESSFUL 145 * 1 : STATUS_BINDING_UNSUCCESSFUL 146 * 2 : STATUS_SEND_UNSUCCESSFUL 147 * : All other values signify failure and the channel state is indeterminate 148 * msg.obj == the AsyncChannel 149 * msg.replyTo = messenger disconnecting or null if it was never connected. 150 */ 151 public static final int CMD_CHANNEL_DISCONNECTED = BASE + 4; 152 153 private static final int CMD_TO_STRING_COUNT = CMD_CHANNEL_DISCONNECTED - BASE + 1; 154 private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT]; 155 static { 156 sCmdToString[CMD_CHANNEL_HALF_CONNECTED - BASE] = "CMD_CHANNEL_HALF_CONNECTED"; 157 sCmdToString[CMD_CHANNEL_FULL_CONNECTION - BASE] = "CMD_CHANNEL_FULL_CONNECTION"; 158 sCmdToString[CMD_CHANNEL_FULLY_CONNECTED - BASE] = "CMD_CHANNEL_FULLY_CONNECTED"; 159 sCmdToString[CMD_CHANNEL_DISCONNECT - BASE] = "CMD_CHANNEL_DISCONNECT"; 160 sCmdToString[CMD_CHANNEL_DISCONNECTED - BASE] = "CMD_CHANNEL_DISCONNECTED"; 161 } 162 protected static String cmdToString(int cmd) { 163 cmd -= BASE; 164 if ((cmd >= 0) && (cmd < sCmdToString.length)) { 165 return sCmdToString[cmd]; 166 } else { 167 return null; 168 } 169 } 170 171 /** Successful status always 0, !0 is an unsuccessful status */ 172 public static final int STATUS_SUCCESSFUL = 0; 173 174 /** Error attempting to bind on a connect */ 175 public static final int STATUS_BINDING_UNSUCCESSFUL = 1; 176 177 /** Error attempting to send a message */ 178 public static final int STATUS_SEND_UNSUCCESSFUL = 2; 179 180 /** CMD_FULLY_CONNECTED refused because a connection already exists*/ 181 public static final int STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED = 3; 182 183 /** Service connection */ 184 private AsyncChannelConnection mConnection; 185 186 /** Context for source */ 187 private Context mSrcContext; 188 189 /** Handler for source */ 190 private Handler mSrcHandler; 191 192 /** Messenger for source */ 193 private Messenger mSrcMessenger; 194 195 /** Messenger for destination */ 196 private Messenger mDstMessenger; 197 198 /** 199 * AsyncChannel constructor 200 */ 201 public AsyncChannel() { 202 } 203 204 /** 205 * Connect handler to named package/class synchronously. 206 * 207 * @param srcContext is the context of the source 208 * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED 209 * messages 210 * @param dstPackageName is the destination package name 211 * @param dstClassName is the fully qualified class name (i.e. contains 212 * package name) 213 * 214 * @return STATUS_SUCCESSFUL on success any other value is an error. 215 */ 216 public int connectSrcHandlerToPackageSync( 217 Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName) { 218 if (DBG) log("connect srcHandler to dst Package & class E"); 219 220 mConnection = new AsyncChannelConnection(); 221 222 /* Initialize the source information */ 223 mSrcContext = srcContext; 224 mSrcHandler = srcHandler; 225 mSrcMessenger = new Messenger(srcHandler); 226 227 /* 228 * Initialize destination information to null they will 229 * be initialized when the AsyncChannelConnection#onServiceConnected 230 * is called 231 */ 232 mDstMessenger = null; 233 234 /* Send intent to create the connection */ 235 Intent intent = new Intent(Intent.ACTION_MAIN); 236 intent.setClassName(dstPackageName, dstClassName); 237 boolean result = srcContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 238 if (DBG) log("connect srcHandler to dst Package & class X result=" + result); 239 return result ? STATUS_SUCCESSFUL : STATUS_BINDING_UNSUCCESSFUL; 240 } 241 242 /** 243 * Connect a handler to Messenger synchronously. 244 * 245 * @param srcContext is the context of the source 246 * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED 247 * messages 248 * @param dstMessenger is the hander to send messages to. 249 * 250 * @return STATUS_SUCCESSFUL on success any other value is an error. 251 */ 252 public int connectSync(Context srcContext, Handler srcHandler, Messenger dstMessenger) { 253 if (DBG) log("halfConnectSync srcHandler to the dstMessenger E"); 254 255 // We are connected 256 connected(srcContext, srcHandler, dstMessenger); 257 258 if (DBG) log("halfConnectSync srcHandler to the dstMessenger X"); 259 return STATUS_SUCCESSFUL; 260 } 261 262 /** 263 * connect two local Handlers synchronously. 264 * 265 * @param srcContext is the context of the source 266 * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED 267 * messages 268 * @param dstHandler is the hander to send messages to. 269 * 270 * @return STATUS_SUCCESSFUL on success any other value is an error. 271 */ 272 public int connectSync(Context srcContext, Handler srcHandler, Handler dstHandler) { 273 return connectSync(srcContext, srcHandler, new Messenger(dstHandler)); 274 } 275 276 /** 277 * Fully connect two local Handlers synchronously. 278 * 279 * @param srcContext is the context of the source 280 * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED 281 * messages 282 * @param dstHandler is the hander to send messages to. 283 * 284 * @return STATUS_SUCCESSFUL on success any other value is an error. 285 */ 286 public int fullyConnectSync(Context srcContext, Handler srcHandler, Handler dstHandler) { 287 int status = connectSync(srcContext, srcHandler, dstHandler); 288 if (status == STATUS_SUCCESSFUL) { 289 Message response = sendMessageSynchronously(CMD_CHANNEL_FULL_CONNECTION); 290 status = response.arg1; 291 } 292 return status; 293 } 294 295 /** 296 * Connect handler to named package/class. 297 * 298 * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete. 299 * msg.arg1 = status 300 * msg.obj = the AsyncChannel 301 * 302 * @param srcContext is the context of the source 303 * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED 304 * messages 305 * @param dstPackageName is the destination package name 306 * @param dstClassName is the fully qualified class name (i.e. contains 307 * package name) 308 */ 309 public void connect(Context srcContext, Handler srcHandler, String dstPackageName, 310 String dstClassName) { 311 if (DBG) log("connect srcHandler to dst Package & class E"); 312 313 final class ConnectAsync implements Runnable { 314 Context mSrcCtx; 315 Handler mSrcHdlr; 316 String mDstPackageName; 317 String mDstClassName; 318 319 ConnectAsync(Context srcContext, Handler srcHandler, String dstPackageName, 320 String dstClassName) { 321 mSrcCtx = srcContext; 322 mSrcHdlr = srcHandler; 323 mDstPackageName = dstPackageName; 324 mDstClassName = dstClassName; 325 } 326 327 @Override 328 public void run() { 329 int result = connectSrcHandlerToPackageSync(mSrcCtx, mSrcHdlr, mDstPackageName, 330 mDstClassName); 331 replyHalfConnected(result); 332 } 333 } 334 335 ConnectAsync ca = new ConnectAsync(srcContext, srcHandler, dstPackageName, dstClassName); 336 new Thread(ca).start(); 337 338 if (DBG) log("connect srcHandler to dst Package & class X"); 339 } 340 341 /** 342 * Connect handler to a class 343 * 344 * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete. 345 * msg.arg1 = status 346 * msg.obj = the AsyncChannel 347 * 348 * @param srcContext 349 * @param srcHandler 350 * @param klass is the class to send messages to. 351 */ 352 public void connect(Context srcContext, Handler srcHandler, Class<?> klass) { 353 connect(srcContext, srcHandler, klass.getPackage().getName(), klass.getName()); 354 } 355 356 /** 357 * Connect handler and messenger. 358 * 359 * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete. 360 * msg.arg1 = status 361 * msg.obj = the AsyncChannel 362 * 363 * @param srcContext 364 * @param srcHandler 365 * @param dstMessenger 366 */ 367 public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger) { 368 if (DBG) log("connect srcHandler to the dstMessenger E"); 369 370 // We are connected 371 connected(srcContext, srcHandler, dstMessenger); 372 373 // Tell source we are half connected 374 replyHalfConnected(STATUS_SUCCESSFUL); 375 376 if (DBG) log("connect srcHandler to the dstMessenger X"); 377 } 378 379 /** 380 * Connect handler to messenger. This method is typically called 381 * when a server receives a CMD_CHANNEL_FULL_CONNECTION request 382 * and initializes the internal instance variables to allow communication 383 * with the dstMessenger. 384 * 385 * @param srcContext 386 * @param srcHandler 387 * @param dstMessenger 388 */ 389 public void connected(Context srcContext, Handler srcHandler, Messenger dstMessenger) { 390 if (DBG) log("connected srcHandler to the dstMessenger E"); 391 392 // Initialize source fields 393 mSrcContext = srcContext; 394 mSrcHandler = srcHandler; 395 mSrcMessenger = new Messenger(mSrcHandler); 396 397 // Initialize destination fields 398 mDstMessenger = dstMessenger; 399 400 if (DBG) log("connected srcHandler to the dstMessenger X"); 401 } 402 403 /** 404 * Connect two local Handlers. 405 * 406 * @param srcContext is the context of the source 407 * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED 408 * messages 409 * @param dstHandler is the hander to send messages to. 410 */ 411 public void connect(Context srcContext, Handler srcHandler, Handler dstHandler) { 412 connect(srcContext, srcHandler, new Messenger(dstHandler)); 413 } 414 415 /** 416 * Connect service and messenger. 417 * 418 * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcAsyncService when complete. 419 * msg.arg1 = status 420 * msg.obj = the AsyncChannel 421 * 422 * @param srcAsyncService 423 * @param dstMessenger 424 */ 425 public void connect(AsyncService srcAsyncService, Messenger dstMessenger) { 426 connect(srcAsyncService, srcAsyncService.getHandler(), dstMessenger); 427 } 428 429 /** 430 * To close the connection call when handler receives CMD_CHANNEL_DISCONNECTED 431 */ 432 public void disconnected() { 433 mSrcContext = null; 434 mSrcHandler = null; 435 mSrcMessenger = null; 436 mDstMessenger = null; 437 mConnection = null; 438 } 439 440 /** 441 * Disconnect 442 */ 443 public void disconnect() { 444 if ((mConnection != null) && (mSrcContext != null)) { 445 mSrcContext.unbindService(mConnection); 446 } 447 try { 448 // Send the DISCONNECTED, although it may not be received 449 // but its the best we can do. 450 Message msg = Message.obtain(); 451 msg.what = CMD_CHANNEL_DISCONNECTED; 452 msg.replyTo = mSrcMessenger; 453 mDstMessenger.send(msg); 454 } catch(Exception e) { 455 } 456 // Tell source we're disconnected. 457 if (mSrcHandler != null) { 458 replyDisconnected(STATUS_SUCCESSFUL); 459 } 460 } 461 462 /** 463 * Send a message to the destination handler. 464 * 465 * @param msg 466 */ 467 public void sendMessage(Message msg) { 468 msg.replyTo = mSrcMessenger; 469 try { 470 mDstMessenger.send(msg); 471 } catch (RemoteException e) { 472 replyDisconnected(STATUS_SEND_UNSUCCESSFUL); 473 } 474 } 475 476 /** 477 * Send a message to the destination handler 478 * 479 * @param what 480 */ 481 public void sendMessage(int what) { 482 Message msg = Message.obtain(); 483 msg.what = what; 484 sendMessage(msg); 485 } 486 487 /** 488 * Send a message to the destination handler 489 * 490 * @param what 491 * @param arg1 492 */ 493 public void sendMessage(int what, int arg1) { 494 Message msg = Message.obtain(); 495 msg.what = what; 496 msg.arg1 = arg1; 497 sendMessage(msg); 498 } 499 500 /** 501 * Send a message to the destination handler 502 * 503 * @param what 504 * @param arg1 505 * @param arg2 506 */ 507 public void sendMessage(int what, int arg1, int arg2) { 508 Message msg = Message.obtain(); 509 msg.what = what; 510 msg.arg1 = arg1; 511 msg.arg2 = arg2; 512 sendMessage(msg); 513 } 514 515 /** 516 * Send a message to the destination handler 517 * 518 * @param what 519 * @param arg1 520 * @param arg2 521 * @param obj 522 */ 523 public void sendMessage(int what, int arg1, int arg2, Object obj) { 524 Message msg = Message.obtain(); 525 msg.what = what; 526 msg.arg1 = arg1; 527 msg.arg2 = arg2; 528 msg.obj = obj; 529 sendMessage(msg); 530 } 531 532 /** 533 * Send a message to the destination handler 534 * 535 * @param what 536 * @param obj 537 */ 538 public void sendMessage(int what, Object obj) { 539 Message msg = Message.obtain(); 540 msg.what = what; 541 msg.obj = obj; 542 sendMessage(msg); 543 } 544 545 /** 546 * Reply to srcMsg sending dstMsg 547 * 548 * @param srcMsg 549 * @param dstMsg 550 */ 551 public void replyToMessage(Message srcMsg, Message dstMsg) { 552 try { 553 dstMsg.replyTo = mSrcMessenger; 554 srcMsg.replyTo.send(dstMsg); 555 } catch (RemoteException e) { 556 log("TODO: handle replyToMessage RemoteException" + e); 557 e.printStackTrace(); 558 } 559 } 560 561 /** 562 * Reply to srcMsg 563 * 564 * @param srcMsg 565 * @param what 566 */ 567 public void replyToMessage(Message srcMsg, int what) { 568 Message msg = Message.obtain(); 569 msg.what = what; 570 replyToMessage(srcMsg, msg); 571 } 572 573 /** 574 * Reply to srcMsg 575 * 576 * @param srcMsg 577 * @param what 578 * @param arg1 579 */ 580 public void replyToMessage(Message srcMsg, int what, int arg1) { 581 Message msg = Message.obtain(); 582 msg.what = what; 583 msg.arg1 = arg1; 584 replyToMessage(srcMsg, msg); 585 } 586 587 /** 588 * Reply to srcMsg 589 * 590 * @param srcMsg 591 * @param what 592 * @param arg1 593 * @param arg2 594 */ 595 public void replyToMessage(Message srcMsg, int what, int arg1, int arg2) { 596 Message msg = Message.obtain(); 597 msg.what = what; 598 msg.arg1 = arg1; 599 msg.arg2 = arg2; 600 replyToMessage(srcMsg, msg); 601 } 602 603 /** 604 * Reply to srcMsg 605 * 606 * @param srcMsg 607 * @param what 608 * @param arg1 609 * @param arg2 610 * @param obj 611 */ 612 public void replyToMessage(Message srcMsg, int what, int arg1, int arg2, Object obj) { 613 Message msg = Message.obtain(); 614 msg.what = what; 615 msg.arg1 = arg1; 616 msg.arg2 = arg2; 617 msg.obj = obj; 618 replyToMessage(srcMsg, msg); 619 } 620 621 /** 622 * Reply to srcMsg 623 * 624 * @param srcMsg 625 * @param what 626 * @param obj 627 */ 628 public void replyToMessage(Message srcMsg, int what, Object obj) { 629 Message msg = Message.obtain(); 630 msg.what = what; 631 msg.obj = obj; 632 replyToMessage(srcMsg, msg); 633 } 634 635 /** 636 * Send the Message synchronously. 637 * 638 * @param msg to send 639 * @return reply message or null if an error. 640 */ 641 public Message sendMessageSynchronously(Message msg) { 642 Message resultMsg = SyncMessenger.sendMessageSynchronously(mDstMessenger, msg); 643 return resultMsg; 644 } 645 646 /** 647 * Send the Message synchronously. 648 * 649 * @param what 650 * @return reply message or null if an error. 651 */ 652 public Message sendMessageSynchronously(int what) { 653 Message msg = Message.obtain(); 654 msg.what = what; 655 Message resultMsg = sendMessageSynchronously(msg); 656 return resultMsg; 657 } 658 659 /** 660 * Send the Message synchronously. 661 * 662 * @param what 663 * @param arg1 664 * @return reply message or null if an error. 665 */ 666 public Message sendMessageSynchronously(int what, int arg1) { 667 Message msg = Message.obtain(); 668 msg.what = what; 669 msg.arg1 = arg1; 670 Message resultMsg = sendMessageSynchronously(msg); 671 return resultMsg; 672 } 673 674 /** 675 * Send the Message synchronously. 676 * 677 * @param what 678 * @param arg1 679 * @param arg2 680 * @return reply message or null if an error. 681 */ 682 public Message sendMessageSynchronously(int what, int arg1, int arg2) { 683 Message msg = Message.obtain(); 684 msg.what = what; 685 msg.arg1 = arg1; 686 msg.arg2 = arg2; 687 Message resultMsg = sendMessageSynchronously(msg); 688 return resultMsg; 689 } 690 691 /** 692 * Send the Message synchronously. 693 * 694 * @param what 695 * @param arg1 696 * @param arg2 697 * @param obj 698 * @return reply message or null if an error. 699 */ 700 public Message sendMessageSynchronously(int what, int arg1, int arg2, Object obj) { 701 Message msg = Message.obtain(); 702 msg.what = what; 703 msg.arg1 = arg1; 704 msg.arg2 = arg2; 705 msg.obj = obj; 706 Message resultMsg = sendMessageSynchronously(msg); 707 return resultMsg; 708 } 709 710 /** 711 * Send the Message synchronously. 712 * 713 * @param what 714 * @param obj 715 * @return reply message or null if an error. 716 */ 717 public Message sendMessageSynchronously(int what, Object obj) { 718 Message msg = Message.obtain(); 719 msg.what = what; 720 msg.obj = obj; 721 Message resultMsg = sendMessageSynchronously(msg); 722 return resultMsg; 723 } 724 725 /** 726 * Helper class to send messages synchronously 727 */ 728 private static class SyncMessenger { 729 /** A stack of SyncMessengers */ 730 private static Stack<SyncMessenger> sStack = new Stack<SyncMessenger>(); 731 /** A number of SyncMessengers created */ 732 private static int sCount = 0; 733 /** The handler thread */ 734 private HandlerThread mHandlerThread; 735 /** The handler that will receive the result */ 736 private SyncHandler mHandler; 737 /** The messenger used to send the message */ 738 private Messenger mMessenger; 739 740 /** private constructor */ 741 private SyncMessenger() { 742 } 743 744 /** Synchronous Handler class */ 745 private class SyncHandler extends Handler { 746 /** The object used to wait/notify */ 747 private Object mLockObject = new Object(); 748 /** The resulting message */ 749 private Message mResultMsg; 750 751 /** Constructor */ 752 private SyncHandler(Looper looper) { 753 super(looper); 754 } 755 756 /** Handle of the reply message */ 757 @Override 758 public void handleMessage(Message msg) { 759 mResultMsg = Message.obtain(); 760 mResultMsg.copyFrom(msg); 761 synchronized(mLockObject) { 762 mLockObject.notify(); 763 } 764 } 765 } 766 767 /** 768 * @return the SyncMessenger 769 */ 770 private static SyncMessenger obtain() { 771 SyncMessenger sm; 772 synchronized (sStack) { 773 if (sStack.isEmpty()) { 774 sm = new SyncMessenger(); 775 sm.mHandlerThread = new HandlerThread("SyncHandler-" + sCount++); 776 sm.mHandlerThread.start(); 777 sm.mHandler = sm.new SyncHandler(sm.mHandlerThread.getLooper()); 778 sm.mMessenger = new Messenger(sm.mHandler); 779 } else { 780 sm = sStack.pop(); 781 } 782 } 783 return sm; 784 } 785 786 /** 787 * Recycle this object 788 */ 789 private void recycle() { 790 synchronized (sStack) { 791 sStack.push(this); 792 } 793 } 794 795 /** 796 * Send a message synchronously. 797 * 798 * @param msg to send 799 * @return result message or null if an error occurs 800 */ 801 private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) { 802 SyncMessenger sm = SyncMessenger.obtain(); 803 try { 804 if (dstMessenger != null && msg != null) { 805 msg.replyTo = sm.mMessenger; 806 synchronized (sm.mHandler.mLockObject) { 807 dstMessenger.send(msg); 808 sm.mHandler.mLockObject.wait(); 809 } 810 } else { 811 sm.mHandler.mResultMsg = null; 812 } 813 } catch (InterruptedException e) { 814 sm.mHandler.mResultMsg = null; 815 } catch (RemoteException e) { 816 sm.mHandler.mResultMsg = null; 817 } 818 Message resultMsg = sm.mHandler.mResultMsg; 819 sm.recycle(); 820 return resultMsg; 821 } 822 } 823 824 /** 825 * Reply to the src handler that we're half connected. 826 * see: CMD_CHANNEL_HALF_CONNECTED for message contents 827 * 828 * @param status to be stored in msg.arg1 829 */ 830 private void replyHalfConnected(int status) { 831 Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED); 832 msg.arg1 = status; 833 msg.obj = this; 834 msg.replyTo = mDstMessenger; 835 mSrcHandler.sendMessage(msg); 836 } 837 838 /** 839 * Reply to the src handler that we are disconnected 840 * see: CMD_CHANNEL_DISCONNECTED for message contents 841 * 842 * @param status to be stored in msg.arg1 843 */ 844 private void replyDisconnected(int status) { 845 Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED); 846 msg.arg1 = status; 847 msg.obj = this; 848 msg.replyTo = mDstMessenger; 849 mSrcHandler.sendMessage(msg); 850 } 851 852 853 /** 854 * ServiceConnection to receive call backs. 855 */ 856 class AsyncChannelConnection implements ServiceConnection { 857 AsyncChannelConnection() { 858 } 859 860 @Override 861 public void onServiceConnected(ComponentName className, IBinder service) { 862 mDstMessenger = new Messenger(service); 863 replyHalfConnected(STATUS_SUCCESSFUL); 864 } 865 866 @Override 867 public void onServiceDisconnected(ComponentName className) { 868 replyDisconnected(STATUS_SUCCESSFUL); 869 } 870 } 871 872 /** 873 * Log the string. 874 * 875 * @param s 876 */ 877 private static void log(String s) { 878 Slog.d(TAG, s); 879 } 880} 881