AsyncChannel.java revision cfce303cbdd59a3883957e4bc96a0476ceeb86ac
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#connect</li> 48 * <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li> 49 * <li><code>comm-loop:</code></li> 50 * <li>Client calls AsyncChannel#sendMessage(msgX)</li> 51 * <li>Server receives and processes msgX</li> 52 * <li>Server optionally calls AsyncChannel#replyToMessage(msgY) 53 * and if sent Client receives and processes msgY</li> 54 * <li>Loop to <code>comm-loop</code> until done</li> 55 * <li>When done Client calls {@link AsyncChannel#disconnect(int)}</li> 56 * <li>Client 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#connect</li> 66 * <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li> 67 * <li>Client calls AsyncChannel#sendMessage(CMD_CHANNEL_FULL_CONNECTION)</li> 68 * <li>Server receives CMD_CHANNEL_FULL_CONNECTION</li> 69 * <li>Server calls AsyncChannel#connect</li> 70 * <li>Server receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li> 71 * <li>Server sends AsyncChannel#sendMessage(CMD_CHANNEL_FULLY_CONNECTED)</li> 72 * <li>Client receives CMD_CHANNEL_FULLY_CONNECTED</li> 73 * <li><code>comm-loop:</code></li> 74 * <li>Client/Server uses AsyncChannel#sendMessage/replyToMessage 75 * to communicate and perform work</li> 76 * <li>Loop to <code>comm-loop</code> until done</li> 77 * <li>When done Client/Server calls {@link AsyncChannel#disconnect(int)}</li> 78 * <li>Client/Server receives CMD_CHANNEL_DISCONNECTED from AsyncChannel</li> 79 *</ol> 80 */ 81public class AsyncChannel { 82 /** Log tag */ 83 private static final String TAG = "AsyncChannel"; 84 85 /** Enable to turn on debugging */ 86 private static final boolean DBG = false; 87 88 /** 89 * Command sent when the channel is half connected. Half connected 90 * means that the channel can be used to send commends to the destination 91 * but the destination is unaware that the channel exists. The first 92 * command sent to the destination is typically CMD_CHANNEL_FULL_CONNECTION if 93 * it is desired to establish a long term connection, but any command maybe 94 * sent. 95 * 96 * msg.arg1 == 0 : STATUS_SUCCESSFUL 97 * 1 : STATUS_BINDING_UNSUCCESSFUL 98 * msg.obj == the AsyncChannel 99 * msg.replyTo == dstMessenger if successful 100 */ 101 public static final int CMD_CHANNEL_HALF_CONNECTED = -1; 102 103 /** 104 * Command typically sent when after receiving the CMD_CHANNEL_HALF_CONNECTED. 105 * This is used to initiate a long term connection with the destination and 106 * typically the destination will reply with CMD_CHANNEL_FULLY_CONNECTED. 107 * 108 * msg.replyTo = srcMessenger. 109 */ 110 public static final int CMD_CHANNEL_FULL_CONNECTION = -2; 111 112 /** 113 * Command typically sent after the destination receives a CMD_CHANNEL_FULL_CONNECTION. 114 * This signifies the acceptance or rejection of the channel by the sender. 115 * 116 * msg.arg1 == 0 : Accept connection 117 * : All other values signify the destination rejected the connection 118 * and {@link AsyncChannel#disconnect(int)} would typically be called. 119 */ 120 public static final int CMD_CHANNEL_FULLY_CONNECTED = -3; 121 122 /** 123 * Command sent when one side or the other wishes to disconnect. The sender 124 * may or may not be able to receive a reply depending upon the protocol and 125 * the state of the connection. The receiver should call {@link AsyncChannel#disconnect(int)} 126 * to close its side of the channel and it will receive a CMD_CHANNEL_DISCONNECTED 127 * when the channel is closed. 128 * 129 * msg.replyTo = messenger that is disconnecting 130 */ 131 public static final int CMD_CHANNEL_DISCONNECT = -4; 132 133 /** 134 * Command sent when the channel becomes disconnected. This is sent when the 135 * channel is forcibly disconnected by the system or as a reply to CMD_CHANNEL_DISCONNECT. 136 * 137 * msg.arg1 == 0 : STATUS_SUCCESSFUL 138 * : All other values signify failure and the channel state is indeterminate 139 * msg.obj == the AsyncChannel 140 * msg.replyTo = messenger disconnecting or null if it was never connected. 141 */ 142 public static final int CMD_CHANNEL_DISCONNECTED = -5; 143 144 /** Successful status always 0, !0 is an unsuccessful status */ 145 public static final int STATUS_SUCCESSFUL = 0; 146 147 /** Error attempting to bind on a connect */ 148 public static final int STATUS_BINDING_UNSUCCESSFUL = 1; 149 150 /** Service connection */ 151 private AsyncChannelConnection mConnection; 152 153 /** Context for source */ 154 private Context mSrcContext; 155 156 /** Handler for source */ 157 private Handler mSrcHandler; 158 159 /** Messenger for source */ 160 private Messenger mSrcMessenger; 161 162 /** Messenger for destination */ 163 private Messenger mDstMessenger; 164 165 /** 166 * AsyncChannel constructor 167 */ 168 public AsyncChannel() { 169 } 170 171 /** 172 * Connect handler to named package/class. 173 * 174 * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete. 175 * 176 * @param srcContext is the context of the source 177 * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED 178 * messages 179 * @param dstPackageName is the destination package name 180 * @param dstClassName is the fully qualified class name (i.e. contains 181 * package name) 182 */ 183 private void connectSrcHandlerToPackage( 184 Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName) { 185 if (DBG) log("connect srcHandler to dst Package & class E"); 186 187 mConnection = new AsyncChannelConnection(); 188 189 /* Initialize the source information */ 190 mSrcContext = srcContext; 191 mSrcHandler = srcHandler; 192 mSrcMessenger = new Messenger(srcHandler); 193 194 /* 195 * Initialize destination information to null they will 196 * be initialized when the AsyncChannelConnection#onServiceConnected 197 * is called 198 */ 199 mDstMessenger = null; 200 201 /* Send intent to create the connection */ 202 Intent intent = new Intent(Intent.ACTION_MAIN); 203 intent.setClassName(dstPackageName, dstClassName); 204 boolean result = srcContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 205 if (!result) { 206 replyHalfConnected(STATUS_BINDING_UNSUCCESSFUL); 207 } 208 209 if (DBG) log("connect srcHandler to dst Package & class X result=" + result); 210 } 211 212 /** 213 * Connect handler to named package/class. 214 * 215 * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete. 216 * msg.arg1 = status 217 * msg.obj = the AsyncChannel 218 * 219 * @param srcContext is the context of the source 220 * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED 221 * messages 222 * @param dstPackageName is the destination package name 223 * @param dstClassName is the fully qualified class name (i.e. contains 224 * package name) 225 */ 226 public void connect(Context srcContext, Handler srcHandler, String dstPackageName, 227 String dstClassName) { 228 if (DBG) log("connect srcHandler to dst Package & class E"); 229 230 final class ConnectAsync implements Runnable { 231 Context mSrcCtx; 232 Handler mSrcHdlr; 233 String mDstPackageName; 234 String mDstClassName; 235 236 ConnectAsync(Context srcContext, Handler srcHandler, String dstPackageName, 237 String dstClassName) { 238 mSrcCtx = srcContext; 239 mSrcHdlr = srcHandler; 240 mDstPackageName = dstPackageName; 241 mDstClassName = dstClassName; 242 } 243 244 public void run() { 245 connectSrcHandlerToPackage(mSrcCtx, mSrcHdlr, mDstPackageName, mDstClassName); 246 } 247 } 248 249 ConnectAsync ca = new ConnectAsync(srcContext, srcHandler, dstPackageName, dstClassName); 250 new Thread(ca).start(); 251 252 if (DBG) log("connect srcHandler to dst Package & class X"); 253 } 254 255 /** 256 * Connect handler to a class 257 * 258 * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete. 259 * msg.arg1 = status 260 * msg.obj = the AsyncChannel 261 * 262 * @param srcContext 263 * @param srcHandler 264 * @param klass is the class to send messages to. 265 */ 266 public void connect(Context srcContext, Handler srcHandler, Class<?> klass) { 267 connect(srcContext, srcHandler, klass.getPackage().getName(), klass.getName()); 268 } 269 270 /** 271 * Connect handler and messenger. 272 * 273 * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete. 274 * msg.arg1 = status 275 * msg.obj = the AsyncChannel 276 * 277 * @param srcContext 278 * @param srcHandler 279 * @param dstMessenger 280 */ 281 public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger) { 282 if (DBG) log("connect srcHandler to the dstMessenger E"); 283 284 // Initialize source fields 285 mSrcContext = srcContext; 286 mSrcHandler = srcHandler; 287 mSrcMessenger = new Messenger(mSrcHandler); 288 289 // Initialize destination fields 290 mDstMessenger = dstMessenger; 291 292 if (DBG) log("tell source we are half connected"); 293 294 // Tell source we are half connected 295 replyHalfConnected(STATUS_SUCCESSFUL); 296 297 if (DBG) log("connect srcHandler to the dstMessenger X"); 298 } 299 300 /** 301 * Connect two local Handlers. 302 * 303 * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete. 304 * msg.arg1 = status 305 * msg.obj = the AsyncChannel 306 * 307 * @param srcContext is the context of the source 308 * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED 309 * messages 310 * @param dstHandler is the hander to send messages to. 311 */ 312 public void connect(Context srcContext, Handler srcHandler, Handler dstHandler) { 313 connect(srcContext, srcHandler, new Messenger(dstHandler)); 314 } 315 316 /** 317 * Connect service and messenger. 318 * 319 * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcAsyncService when complete. 320 * msg.arg1 = status 321 * msg.obj = the AsyncChannel 322 * 323 * @param srcAsyncService 324 * @param dstMessenger 325 */ 326 public void connect(AsyncService srcAsyncService, Messenger dstMessenger) { 327 connect(srcAsyncService, srcAsyncService.getHandler(), dstMessenger); 328 } 329 330 /** 331 * To close the connection call when handler receives CMD_CHANNEL_DISCONNECTED 332 */ 333 public void disconnected() { 334 mSrcHandler = null; 335 mSrcMessenger = null; 336 mDstMessenger = null; 337 mConnection = null; 338 } 339 340 /** 341 * Disconnect 342 */ 343 public void disconnect() { 344 if (mConnection != null) { 345 mSrcContext.unbindService(mConnection); 346 } 347 if (mSrcHandler != null) { 348 Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED); 349 msg.arg1 = STATUS_SUCCESSFUL; 350 msg.obj = this; 351 msg.replyTo = mDstMessenger; 352 mSrcHandler.sendMessage(msg); 353 } 354 } 355 356 /** 357 * Send a message to the destination handler. 358 * 359 * @param msg 360 */ 361 public void sendMessage(Message msg) { 362 msg.replyTo = mSrcMessenger; 363 try { 364 mDstMessenger.send(msg); 365 } catch (RemoteException e) { 366 log("TODO: handle sendMessage RemoteException" + e); 367 } 368 } 369 370 /** 371 * Send a message to the destination handler 372 * 373 * @param what 374 */ 375 public void sendMessage(int what) { 376 Message msg = Message.obtain(); 377 msg.what = what; 378 sendMessage(msg); 379 } 380 381 /** 382 * Send a message to the destination handler 383 * 384 * @param what 385 * @param arg1 386 */ 387 public void sendMessage(int what, int arg1) { 388 Message msg = Message.obtain(); 389 msg.what = what; 390 msg.arg1 = arg1; 391 sendMessage(msg); 392 } 393 394 /** 395 * Send a message to the destination handler 396 * 397 * @param what 398 * @param arg1 399 * @param arg2 400 */ 401 public void sendMessage(int what, int arg1, int arg2) { 402 Message msg = Message.obtain(); 403 msg.what = what; 404 msg.arg1 = arg1; 405 msg.arg2 = arg2; 406 sendMessage(msg); 407 } 408 409 /** 410 * Send a message to the destination handler 411 * 412 * @param what 413 * @param arg1 414 * @param arg2 415 * @param obj 416 */ 417 public void sendMessage(int what, int arg1, int arg2, Object obj) { 418 Message msg = Message.obtain(); 419 msg.what = what; 420 msg.arg1 = arg1; 421 msg.arg2 = arg2; 422 msg.obj = obj; 423 sendMessage(msg); 424 } 425 426 /** 427 * Send a message to the destination handler 428 * 429 * @param what 430 * @param obj 431 */ 432 public void sendMessage(int what, Object obj) { 433 Message msg = Message.obtain(); 434 msg.what = what; 435 msg.obj = obj; 436 sendMessage(msg); 437 } 438 439 /** 440 * Reply to srcMsg sending dstMsg 441 * 442 * @param srcMsg 443 * @param dstMsg 444 */ 445 public void replyToMessage(Message srcMsg, Message dstMsg) { 446 try { 447 srcMsg.replyTo.send(dstMsg); 448 } catch (RemoteException e) { 449 log("TODO: handle replyToMessage RemoteException" + e); 450 e.printStackTrace(); 451 } 452 } 453 454 /** 455 * Reply to srcMsg 456 * 457 * @param srcMsg 458 * @param what 459 */ 460 public void replyToMessage(Message srcMsg, int what) { 461 Message msg = Message.obtain(); 462 msg.what = what; 463 replyToMessage(srcMsg, msg); 464 } 465 466 /** 467 * Reply to srcMsg 468 * 469 * @param srcMsg 470 * @param what 471 * @param arg1 472 */ 473 public void replyToMessage(Message srcMsg, int what, int arg1) { 474 Message msg = Message.obtain(); 475 msg.what = what; 476 msg.arg1 = arg1; 477 replyToMessage(srcMsg, msg); 478 } 479 480 /** 481 * Reply to srcMsg 482 * 483 * @param srcMsg 484 * @param what 485 * @param arg1 486 * @param arg2 487 */ 488 public void replyToMessage(Message srcMsg, int what, int arg1, int arg2) { 489 Message msg = Message.obtain(); 490 msg.what = what; 491 msg.arg1 = arg1; 492 msg.arg2 = arg2; 493 replyToMessage(srcMsg, msg); 494 } 495 496 /** 497 * Reply to srcMsg 498 * 499 * @param srcMsg 500 * @param what 501 * @param arg1 502 * @param arg2 503 * @param obj 504 */ 505 public void replyToMessage(Message srcMsg, int what, int arg1, int arg2, Object obj) { 506 Message msg = Message.obtain(); 507 msg.what = what; 508 msg.arg1 = arg1; 509 msg.arg2 = arg2; 510 msg.obj = obj; 511 replyToMessage(srcMsg, msg); 512 } 513 514 /** 515 * Reply to srcMsg 516 * 517 * @param srcMsg 518 * @param what 519 * @param obj 520 */ 521 public void replyToMessage(Message srcMsg, int what, Object obj) { 522 Message msg = Message.obtain(); 523 msg.what = what; 524 msg.obj = obj; 525 replyToMessage(srcMsg, msg); 526 } 527 528 /** 529 * Send the Message synchronously. 530 * 531 * @param msg to send 532 * @return reply message or null if an error. 533 */ 534 public Message sendMessageSynchronously(Message msg) { 535 Message resultMsg = SyncMessenger.sendMessageSynchronously(mDstMessenger, msg); 536 return resultMsg; 537 } 538 539 /** 540 * Send the Message synchronously. 541 * 542 * @param what 543 * @return reply message or null if an error. 544 */ 545 public Message sendMessageSynchronously(int what) { 546 Message msg = Message.obtain(); 547 msg.what = what; 548 Message resultMsg = sendMessageSynchronously(msg); 549 return resultMsg; 550 } 551 552 /** 553 * Send the Message synchronously. 554 * 555 * @param what 556 * @param arg1 557 * @return reply message or null if an error. 558 */ 559 public Message sendMessageSynchronously(int what, int arg1) { 560 Message msg = Message.obtain(); 561 msg.what = what; 562 msg.arg1 = arg1; 563 Message resultMsg = sendMessageSynchronously(msg); 564 return resultMsg; 565 } 566 567 /** 568 * Send the Message synchronously. 569 * 570 * @param what 571 * @param arg1 572 * @param arg2 573 * @return reply message or null if an error. 574 */ 575 public Message sendMessageSynchronously(int what, int arg1, int arg2) { 576 Message msg = Message.obtain(); 577 msg.what = what; 578 msg.arg1 = arg1; 579 msg.arg2 = arg2; 580 Message resultMsg = sendMessageSynchronously(msg); 581 return resultMsg; 582 } 583 584 /** 585 * Send the Message synchronously. 586 * 587 * @param what 588 * @param arg1 589 * @param arg2 590 * @param obj 591 * @return reply message or null if an error. 592 */ 593 public Message sendMessageSynchronously(int what, int arg1, int arg2, Object obj) { 594 Message msg = Message.obtain(); 595 msg.what = what; 596 msg.arg1 = arg1; 597 msg.arg2 = arg2; 598 msg.obj = obj; 599 Message resultMsg = sendMessageSynchronously(msg); 600 return resultMsg; 601 } 602 603 /** 604 * Send the Message synchronously. 605 * 606 * @param what 607 * @param obj 608 * @return reply message or null if an error. 609 */ 610 public Message sendMessageSynchronously(int what, Object obj) { 611 Message msg = Message.obtain(); 612 msg.what = what; 613 msg.obj = obj; 614 Message resultMsg = sendMessageSynchronously(msg); 615 return resultMsg; 616 } 617 618 /** 619 * Helper class to send messages synchronously 620 */ 621 private static class SyncMessenger { 622 /** A stack of SyncMessengers */ 623 private static Stack<SyncMessenger> sStack = new Stack<SyncMessenger>(); 624 /** A number of SyncMessengers created */ 625 private static int sCount = 0; 626 /** The handler thread */ 627 private HandlerThread mHandlerThread; 628 /** The handler that will receive the result */ 629 private SyncHandler mHandler; 630 /** The messenger used to send the message */ 631 private Messenger mMessenger; 632 633 /** private constructor */ 634 private SyncMessenger() { 635 } 636 637 /** Synchronous Handler class */ 638 private class SyncHandler extends Handler { 639 /** The object used to wait/notify */ 640 private Object mLockObject = new Object(); 641 /** The resulting message */ 642 private Message mResultMsg; 643 644 /** Constructor */ 645 private SyncHandler(Looper looper) { 646 super(looper); 647 } 648 649 /** Handle of the reply message */ 650 @Override 651 public void handleMessage(Message msg) { 652 mResultMsg = Message.obtain(); 653 mResultMsg.copyFrom(msg); 654 synchronized(mLockObject) { 655 mLockObject.notify(); 656 } 657 } 658 } 659 660 /** 661 * @return the SyncMessenger 662 */ 663 private static SyncMessenger obtain() { 664 SyncMessenger sm; 665 synchronized (sStack) { 666 if (sStack.isEmpty()) { 667 sm = new SyncMessenger(); 668 sm.mHandlerThread = new HandlerThread("SyncHandler-" + sCount++); 669 sm.mHandlerThread.start(); 670 sm.mHandler = sm.new SyncHandler(sm.mHandlerThread.getLooper()); 671 sm.mMessenger = new Messenger(sm.mHandler); 672 } else { 673 sm = sStack.pop(); 674 } 675 } 676 return sm; 677 } 678 679 /** 680 * Recycle this object 681 */ 682 private void recycle() { 683 synchronized (sStack) { 684 sStack.push(this); 685 } 686 } 687 688 /** 689 * Send a message synchronously. 690 * 691 * @param msg to send 692 * @return result message or null if an error occurs 693 */ 694 private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) { 695 SyncMessenger sm = SyncMessenger.obtain(); 696 try { 697 msg.replyTo = sm.mMessenger; 698 dstMessenger.send(msg); 699 synchronized (sm.mHandler.mLockObject) { 700 sm.mHandler.mLockObject.wait(); 701 } 702 } catch (InterruptedException e) { 703 sm.mHandler.mResultMsg = null; 704 } catch (RemoteException e) { 705 sm.mHandler.mResultMsg = null; 706 } 707 Message resultMsg = sm.mHandler.mResultMsg; 708 sm.recycle(); 709 return resultMsg; 710 } 711 } 712 713 /** 714 * Reply to the src handler that we're half connected. 715 * 716 * @param status to be stored in msg.arg1 717 */ 718 private void replyHalfConnected(int status) { 719 Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED); 720 msg.arg1 = status; 721 msg.obj = this; 722 msg.replyTo = mDstMessenger; 723 mSrcHandler.sendMessage(msg); 724 } 725 726 /** 727 * ServiceConnection to receive call backs. 728 */ 729 class AsyncChannelConnection implements ServiceConnection { 730 AsyncChannelConnection() { 731 } 732 733 public void onServiceConnected(ComponentName className, IBinder service) { 734 mDstMessenger = new Messenger(service); 735 replyHalfConnected(STATUS_SUCCESSFUL); 736 } 737 738 public void onServiceDisconnected(ComponentName className) { 739 Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED); 740 msg.arg1 = STATUS_SUCCESSFUL; 741 msg.obj = AsyncChannel.this; 742 msg.replyTo = mDstMessenger; 743 mSrcHandler.sendMessage(msg); 744 } 745 } 746 747 /** 748 * Log the string. 749 * 750 * @param s 751 */ 752 private static void log(String s) { 753 Slog.d(TAG, s); 754 } 755} 756