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