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