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