AsyncChannel.java revision 33c54e3365d621fcc5b9f7564f18b33dc1e300df
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.arg2 == token parameter 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.arg2 == token parameter 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 * @param token unique id for this connection 183 */ 184 private void connectSrcHandlerToPackage( 185 Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName, 186 int token) { 187 if (DBG) log("connect srcHandler to dst Package & class E"); 188 189 mConnection = new AsyncChannelConnection(token); 190 191 /* Initialize the source information */ 192 mSrcContext = srcContext; 193 mSrcHandler = srcHandler; 194 mSrcMessenger = new Messenger(srcHandler); 195 196 /* 197 * Initialize destination information to null they will 198 * be initialized when the AsyncChannelConnection#onServiceConnected 199 * is called 200 */ 201 mDstMessenger = null; 202 203 /* Send intent to create the connection */ 204 Intent intent = new Intent(Intent.ACTION_MAIN); 205 intent.setClassName(dstPackageName, dstClassName); 206 boolean result = srcContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 207 if (!result) { 208 replyHalfConnected(STATUS_BINDING_UNSUCCESSFUL, token); 209 } 210 211 if (DBG) log("connect srcHandler to dst Package & class X result=" + result); 212 } 213 214 /** 215 * Connect handler to named package/class. 216 * 217 * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete. 218 * msg.arg1 = status 219 * msg.arg2 = token 220 * 221 * @param srcContext is the context of the source 222 * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED 223 * messages 224 * @param dstPackageName is the destination package name 225 * @param dstClassName is the fully qualified class name (i.e. contains 226 * package name) 227 * @param token returned in msg.arg2 228 */ 229 public void connect(Context srcContext, Handler srcHandler, String dstPackageName, 230 String dstClassName, int token) { 231 if (DBG) log("connect srcHandler to dst Package & class E"); 232 233 final class ConnectAsync implements Runnable { 234 Context mSrcCtx; 235 Handler mSrcHdlr; 236 String mDstPackageName; 237 String mDstClassName; 238 int mToken; 239 240 ConnectAsync(Context srcContext, Handler srcHandler, String dstPackageName, 241 String dstClassName, int token) { 242 mSrcCtx = srcContext; 243 mSrcHdlr = srcHandler; 244 mDstPackageName = dstPackageName; 245 mDstClassName = dstClassName; 246 mToken = token; 247 } 248 249 public void run() { 250 connectSrcHandlerToPackage(mSrcCtx, mSrcHdlr, mDstPackageName, mDstClassName, 251 mToken); 252 } 253 } 254 255 ConnectAsync ca = new ConnectAsync(srcContext, srcHandler, dstPackageName, dstClassName, 256 token); 257 new Thread(ca).start(); 258 259 if (DBG) log("connect srcHandler to dst Package & class X"); 260 } 261 262 /** 263 * Connect handler to a class 264 * 265 * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete. 266 * msg.arg1 = status 267 * msg.arg2 = token 268 * 269 * @param srcContext 270 * @param srcHandler 271 * @param klass is the class to send messages to. 272 * @param token returned in msg.arg2 273 */ 274 public void connect(Context srcContext, Handler srcHandler, Class<?> klass, int token) { 275 connect(srcContext, srcHandler, klass.getPackage().getName(), klass.getName(), token); 276 } 277 278 /** 279 * Connect handler and messenger. 280 * 281 * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete. 282 * msg.arg1 = status 283 * msg.arg2 = token 284 * 285 * @param srcContext 286 * @param srcHandler 287 * @param dstMessenger 288 * @param token returned in msg.arg2 289 */ 290 public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger, int token) { 291 if (DBG) log("connect srcHandler to the dstMessenger E"); 292 293 // Initialize source fields 294 mSrcContext = srcContext; 295 mSrcHandler = srcHandler; 296 mSrcMessenger = new Messenger(mSrcHandler); 297 298 // Initialize destination fields 299 mDstMessenger = dstMessenger; 300 301 if (DBG) log("tell source we are half connected"); 302 303 // Tell source we are half connected 304 replyHalfConnected(STATUS_SUCCESSFUL, token); 305 306 if (DBG) log("connect srcHandler to the dstMessenger X"); 307 } 308 309 /** 310 * Connect two local Handlers. 311 * 312 * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete. 313 * msg.arg1 = status 314 * msg.arg2 = token 315 * 316 * @param srcContext is the context of the source 317 * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED 318 * messages 319 * @param dstHandler is the hander to send messages to. 320 * @param token returned in msg.arg2 321 */ 322 public void connect(Context srcContext, Handler srcHandler, Handler dstHandler, int token) { 323 connect(srcContext, srcHandler, new Messenger(dstHandler), token); 324 } 325 326 /** 327 * Connect service and messenger. 328 * 329 * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcAsyncService when complete. 330 * msg.arg1 = status 331 * msg.arg2 = token 332 * 333 * @param srcAsyncService 334 * @param dstMessenger 335 * @param token returned in msg.arg2 336 */ 337 public void connect(AsyncService srcAsyncService, Messenger dstMessenger, int token) { 338 connect(srcAsyncService, srcAsyncService.getHandler(), dstMessenger, token); 339 } 340 341 /** 342 * To close the connection call when handler receives CMD_CHANNEL_DISCONNECTED 343 */ 344 public void disconnected() { 345 mSrcHandler = null; 346 mSrcMessenger = null; 347 mDstMessenger = null; 348 mConnection = null; 349 } 350 351 /** 352 * Disconnect 353 */ 354 public void disconnect(int token) { 355 if (mConnection != null) { 356 mConnection.setToken(token); 357 mSrcContext.unbindService(mConnection); 358 } 359 if (mSrcHandler != null) { 360 Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED); 361 msg.arg1 = STATUS_SUCCESSFUL; 362 msg.arg2 = token; 363 msg.replyTo = mDstMessenger; 364 mSrcHandler.sendMessage(msg); 365 } 366 } 367 368 /** 369 * Send a message to the destination handler. 370 * 371 * @param msg 372 */ 373 public void sendMessage(Message msg) { 374 msg.replyTo = mSrcMessenger; 375 try { 376 mDstMessenger.send(msg); 377 } catch (RemoteException e) { 378 log("TODO: handle sendMessage RemoteException" + e); 379 } 380 } 381 382 /** 383 * Send a message to the destination handler 384 * 385 * @param what 386 */ 387 public void sendMessage(int what) { 388 Message msg = Message.obtain(); 389 msg.what = what; 390 sendMessage(msg); 391 } 392 393 /** 394 * Send a message to the destination handler 395 * 396 * @param what 397 * @param arg1 398 */ 399 public void sendMessage(int what, int arg1) { 400 Message msg = Message.obtain(); 401 msg.what = what; 402 msg.arg1 = arg1; 403 sendMessage(msg); 404 } 405 406 /** 407 * Send a message to the destination handler 408 * 409 * @param what 410 * @param arg1 411 * @param arg2 412 */ 413 public void sendMessage(int what, int arg1, int arg2) { 414 Message msg = Message.obtain(); 415 msg.what = what; 416 msg.arg1 = arg1; 417 msg.arg2 = arg2; 418 sendMessage(msg); 419 } 420 421 /** 422 * Send a message to the destination handler 423 * 424 * @param what 425 * @param arg1 426 * @param arg2 427 * @param obj 428 */ 429 public void sendMessage(int what, int arg1, int arg2, Object obj) { 430 Message msg = Message.obtain(); 431 msg.what = what; 432 msg.arg1 = arg1; 433 msg.arg2 = arg2; 434 msg.obj = obj; 435 sendMessage(msg); 436 } 437 438 /** 439 * Send a message to the destination handler 440 * 441 * @param what 442 * @param obj 443 */ 444 public void sendMessage(int what, Object obj) { 445 Message msg = Message.obtain(); 446 msg.what = what; 447 msg.obj = obj; 448 sendMessage(msg); 449 } 450 451 /** 452 * Reply to srcMsg sending dstMsg 453 * 454 * @param srcMsg 455 * @param dstMsg 456 */ 457 public void replyToMessage(Message srcMsg, Message dstMsg) { 458 try { 459 srcMsg.replyTo.send(dstMsg); 460 } catch (RemoteException e) { 461 log("TODO: handle replyToMessage RemoteException" + e); 462 e.printStackTrace(); 463 } 464 } 465 466 /** 467 * Reply to srcMsg 468 * 469 * @param srcMsg 470 * @param what 471 */ 472 public void replyToMessage(Message srcMsg, int what) { 473 Message msg = Message.obtain(); 474 msg.what = what; 475 replyToMessage(srcMsg, msg); 476 } 477 478 /** 479 * Reply to srcMsg 480 * 481 * @param srcMsg 482 * @param what 483 * @param arg1 484 */ 485 public void replyToMessage(Message srcMsg, int what, int arg1) { 486 Message msg = Message.obtain(); 487 msg.what = what; 488 msg.arg1 = arg1; 489 replyToMessage(srcMsg, msg); 490 } 491 492 /** 493 * Reply to srcMsg 494 * 495 * @param srcMsg 496 * @param what 497 * @param arg1 498 * @param arg2 499 */ 500 public void replyToMessage(Message srcMsg, int what, int arg1, int arg2) { 501 Message msg = Message.obtain(); 502 msg.what = what; 503 msg.arg1 = arg1; 504 msg.arg2 = arg2; 505 replyToMessage(srcMsg, msg); 506 } 507 508 /** 509 * Reply to srcMsg 510 * 511 * @param srcMsg 512 * @param what 513 * @param arg1 514 * @param arg2 515 * @param obj 516 */ 517 public void replyToMessage(Message srcMsg, int what, int arg1, int arg2, Object obj) { 518 Message msg = Message.obtain(); 519 msg.what = what; 520 msg.arg1 = arg1; 521 msg.arg2 = arg2; 522 msg.obj = obj; 523 replyToMessage(srcMsg, msg); 524 } 525 526 /** 527 * Reply to srcMsg 528 * 529 * @param srcMsg 530 * @param what 531 * @param obj 532 */ 533 public void replyToMessage(Message srcMsg, int what, Object obj) { 534 Message msg = Message.obtain(); 535 msg.what = what; 536 msg.obj = obj; 537 replyToMessage(srcMsg, msg); 538 } 539 540 /** 541 * Send the Message synchronously. 542 * 543 * @param msg to send 544 * @return reply message or null if an error. 545 */ 546 public Message sendMessageSynchronously(Message msg) { 547 Message resultMsg = SyncMessenger.sendMessageSynchronously(mDstMessenger, msg); 548 return resultMsg; 549 } 550 551 /** 552 * Send the Message synchronously. 553 * 554 * @param what 555 * @return reply message or null if an error. 556 */ 557 public Message sendMessageSynchronously(int what) { 558 Message msg = Message.obtain(); 559 msg.what = what; 560 Message resultMsg = sendMessageSynchronously(msg); 561 return resultMsg; 562 } 563 564 /** 565 * Send the Message synchronously. 566 * 567 * @param what 568 * @param arg1 569 * @return reply message or null if an error. 570 */ 571 public Message sendMessageSynchronously(int what, int arg1) { 572 Message msg = Message.obtain(); 573 msg.what = what; 574 msg.arg1 = arg1; 575 Message resultMsg = sendMessageSynchronously(msg); 576 return resultMsg; 577 } 578 579 /** 580 * Send the Message synchronously. 581 * 582 * @param what 583 * @param arg1 584 * @param arg2 585 * @return reply message or null if an error. 586 */ 587 public Message sendMessageSynchronously(int what, int arg1, int arg2) { 588 Message msg = Message.obtain(); 589 msg.what = what; 590 msg.arg1 = arg1; 591 msg.arg2 = arg2; 592 Message resultMsg = sendMessageSynchronously(msg); 593 return resultMsg; 594 } 595 596 /** 597 * Send the Message synchronously. 598 * 599 * @param what 600 * @param arg1 601 * @param arg2 602 * @param obj 603 * @return reply message or null if an error. 604 */ 605 public Message sendMessageSynchronously(int what, int arg1, int arg2, Object obj) { 606 Message msg = Message.obtain(); 607 msg.what = what; 608 msg.arg1 = arg1; 609 msg.arg2 = arg2; 610 msg.obj = obj; 611 Message resultMsg = sendMessageSynchronously(msg); 612 return resultMsg; 613 } 614 615 /** 616 * Send the Message synchronously. 617 * 618 * @param what 619 * @param obj 620 * @return reply message or null if an error. 621 */ 622 public Message sendMessageSynchronously(int what, Object obj) { 623 Message msg = Message.obtain(); 624 msg.what = what; 625 msg.obj = obj; 626 Message resultMsg = sendMessageSynchronously(msg); 627 return resultMsg; 628 } 629 630 /** 631 * Helper class to send messages synchronously 632 */ 633 private static class SyncMessenger { 634 /** A stack of SyncMessengers */ 635 private static Stack<SyncMessenger> sStack = new Stack<SyncMessenger>(); 636 /** A number of SyncMessengers created */ 637 private static int sCount = 0; 638 /** The handler thread */ 639 private HandlerThread mHandlerThread; 640 /** The handler that will receive the result */ 641 private SyncHandler mHandler; 642 /** The messenger used to send the message */ 643 private Messenger mMessenger; 644 645 /** private constructor */ 646 private SyncMessenger() { 647 } 648 649 /** Synchronous Handler class */ 650 private class SyncHandler extends Handler { 651 /** The object used to wait/notify */ 652 private Object mLockObject = new Object(); 653 /** The resulting message */ 654 private Message mResultMsg; 655 656 /** Constructor */ 657 private SyncHandler(Looper looper) { 658 super(looper); 659 } 660 661 /** Handle of the reply message */ 662 @Override 663 public void handleMessage(Message msg) { 664 mResultMsg = Message.obtain(); 665 mResultMsg.copyFrom(msg); 666 synchronized(mLockObject) { 667 mLockObject.notify(); 668 } 669 } 670 } 671 672 /** 673 * @return the SyncMessenger 674 */ 675 private static SyncMessenger obtain() { 676 SyncMessenger sm; 677 synchronized (sStack) { 678 if (sStack.isEmpty()) { 679 sm = new SyncMessenger(); 680 sm.mHandlerThread = new HandlerThread("SyncHandler-" + sCount++); 681 sm.mHandlerThread.start(); 682 sm.mHandler = sm.new SyncHandler(sm.mHandlerThread.getLooper()); 683 sm.mMessenger = new Messenger(sm.mHandler); 684 } else { 685 sm = sStack.pop(); 686 } 687 } 688 return sm; 689 } 690 691 /** 692 * Recycle this object 693 */ 694 private void recycle() { 695 synchronized (sStack) { 696 sStack.push(this); 697 } 698 } 699 700 /** 701 * Send a message synchronously. 702 * 703 * @param msg to send 704 * @return result message or null if an error occurs 705 */ 706 private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) { 707 SyncMessenger sm = SyncMessenger.obtain(); 708 try { 709 msg.replyTo = sm.mMessenger; 710 dstMessenger.send(msg); 711 synchronized (sm.mHandler.mLockObject) { 712 sm.mHandler.mLockObject.wait(); 713 } 714 } catch (InterruptedException e) { 715 sm.mHandler.mResultMsg = null; 716 } catch (RemoteException e) { 717 sm.mHandler.mResultMsg = null; 718 } 719 Message resultMsg = sm.mHandler.mResultMsg; 720 sm.recycle(); 721 return resultMsg; 722 } 723 } 724 725 /** 726 * Reply to the src handler that we're half connected. 727 * 728 * @param status to be stored in msg.arg1 729 */ 730 private void replyHalfConnected(int status, int token) { 731 Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED); 732 msg.arg1 = status; 733 msg.arg2 = token; 734 msg.replyTo = mDstMessenger; 735 mSrcHandler.sendMessage(msg); 736 } 737 738 /** 739 * ServiceConnection to receive call backs. 740 */ 741 class AsyncChannelConnection implements ServiceConnection { 742 private int mToken; 743 744 AsyncChannelConnection(int token) { 745 mToken = token; 746 } 747 748 /** 749 * @param token 750 */ 751 public void setToken(int token) { 752 mToken = token; 753 } 754 755 public void onServiceConnected(ComponentName className, IBinder service) { 756 mDstMessenger = new Messenger(service); 757 replyHalfConnected(STATUS_SUCCESSFUL, mToken); 758 } 759 760 public void onServiceDisconnected(ComponentName className) { 761 Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED); 762 msg.arg1 = STATUS_SUCCESSFUL; 763 msg.arg2 = mToken; 764 msg.replyTo = mDstMessenger; 765 mSrcHandler.sendMessage(msg); 766 } 767 } 768 769 /** 770 * Log the string. 771 * 772 * @param s 773 */ 774 private static void log(String s) { 775 Slog.d(TAG, s); 776 } 777} 778