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        try {
448            // Send the DISCONNECTED, although it may not be received
449            // but its the best we can do.
450            Message msg = Message.obtain();
451            msg.what = CMD_CHANNEL_DISCONNECTED;
452            msg.replyTo = mSrcMessenger;
453            mDstMessenger.send(msg);
454        } catch(Exception e) {
455        }
456        // Tell source we're disconnected.
457        if (mSrcHandler != null) {
458            replyDisconnected(STATUS_SUCCESSFUL);
459        }
460    }
461
462    /**
463     * Send a message to the destination handler.
464     *
465     * @param msg
466     */
467    public void sendMessage(Message msg) {
468        msg.replyTo = mSrcMessenger;
469        try {
470            mDstMessenger.send(msg);
471        } catch (RemoteException e) {
472            replyDisconnected(STATUS_SEND_UNSUCCESSFUL);
473        }
474    }
475
476    /**
477     * Send a message to the destination handler
478     *
479     * @param what
480     */
481    public void sendMessage(int what) {
482        Message msg = Message.obtain();
483        msg.what = what;
484        sendMessage(msg);
485    }
486
487    /**
488     * Send a message to the destination handler
489     *
490     * @param what
491     * @param arg1
492     */
493    public void sendMessage(int what, int arg1) {
494        Message msg = Message.obtain();
495        msg.what = what;
496        msg.arg1 = arg1;
497        sendMessage(msg);
498    }
499
500    /**
501     * Send a message to the destination handler
502     *
503     * @param what
504     * @param arg1
505     * @param arg2
506     */
507    public void sendMessage(int what, int arg1, int arg2) {
508        Message msg = Message.obtain();
509        msg.what = what;
510        msg.arg1 = arg1;
511        msg.arg2 = arg2;
512        sendMessage(msg);
513    }
514
515    /**
516     * Send a message to the destination handler
517     *
518     * @param what
519     * @param arg1
520     * @param arg2
521     * @param obj
522     */
523    public void sendMessage(int what, int arg1, int arg2, Object obj) {
524        Message msg = Message.obtain();
525        msg.what = what;
526        msg.arg1 = arg1;
527        msg.arg2 = arg2;
528        msg.obj = obj;
529        sendMessage(msg);
530    }
531
532    /**
533     * Send a message to the destination handler
534     *
535     * @param what
536     * @param obj
537     */
538    public void sendMessage(int what, Object obj) {
539        Message msg = Message.obtain();
540        msg.what = what;
541        msg.obj = obj;
542        sendMessage(msg);
543    }
544
545    /**
546     * Reply to srcMsg sending dstMsg
547     *
548     * @param srcMsg
549     * @param dstMsg
550     */
551    public void replyToMessage(Message srcMsg, Message dstMsg) {
552        try {
553            dstMsg.replyTo = mSrcMessenger;
554            srcMsg.replyTo.send(dstMsg);
555        } catch (RemoteException e) {
556            log("TODO: handle replyToMessage RemoteException" + e);
557            e.printStackTrace();
558        }
559    }
560
561    /**
562     * Reply to srcMsg
563     *
564     * @param srcMsg
565     * @param what
566     */
567    public void replyToMessage(Message srcMsg, int what) {
568        Message msg = Message.obtain();
569        msg.what = what;
570        replyToMessage(srcMsg, msg);
571    }
572
573    /**
574     * Reply to srcMsg
575     *
576     * @param srcMsg
577     * @param what
578     * @param arg1
579     */
580    public void replyToMessage(Message srcMsg, int what, int arg1) {
581        Message msg = Message.obtain();
582        msg.what = what;
583        msg.arg1 = arg1;
584        replyToMessage(srcMsg, msg);
585    }
586
587    /**
588     * Reply to srcMsg
589     *
590     * @param srcMsg
591     * @param what
592     * @param arg1
593     * @param arg2
594     */
595    public void replyToMessage(Message srcMsg, int what, int arg1, int arg2) {
596        Message msg = Message.obtain();
597        msg.what = what;
598        msg.arg1 = arg1;
599        msg.arg2 = arg2;
600        replyToMessage(srcMsg, msg);
601    }
602
603    /**
604     * Reply to srcMsg
605     *
606     * @param srcMsg
607     * @param what
608     * @param arg1
609     * @param arg2
610     * @param obj
611     */
612    public void replyToMessage(Message srcMsg, int what, int arg1, int arg2, Object obj) {
613        Message msg = Message.obtain();
614        msg.what = what;
615        msg.arg1 = arg1;
616        msg.arg2 = arg2;
617        msg.obj = obj;
618        replyToMessage(srcMsg, msg);
619    }
620
621    /**
622     * Reply to srcMsg
623     *
624     * @param srcMsg
625     * @param what
626     * @param obj
627     */
628    public void replyToMessage(Message srcMsg, int what, Object obj) {
629        Message msg = Message.obtain();
630        msg.what = what;
631        msg.obj = obj;
632        replyToMessage(srcMsg, msg);
633    }
634
635    /**
636     * Send the Message synchronously.
637     *
638     * @param msg to send
639     * @return reply message or null if an error.
640     */
641    public Message sendMessageSynchronously(Message msg) {
642        Message resultMsg = SyncMessenger.sendMessageSynchronously(mDstMessenger, msg);
643        return resultMsg;
644    }
645
646    /**
647     * Send the Message synchronously.
648     *
649     * @param what
650     * @return reply message or null if an error.
651     */
652    public Message sendMessageSynchronously(int what) {
653        Message msg = Message.obtain();
654        msg.what = what;
655        Message resultMsg = sendMessageSynchronously(msg);
656        return resultMsg;
657    }
658
659    /**
660     * Send the Message synchronously.
661     *
662     * @param what
663     * @param arg1
664     * @return reply message or null if an error.
665     */
666    public Message sendMessageSynchronously(int what, int arg1) {
667        Message msg = Message.obtain();
668        msg.what = what;
669        msg.arg1 = arg1;
670        Message resultMsg = sendMessageSynchronously(msg);
671        return resultMsg;
672    }
673
674    /**
675     * Send the Message synchronously.
676     *
677     * @param what
678     * @param arg1
679     * @param arg2
680     * @return reply message or null if an error.
681     */
682    public Message sendMessageSynchronously(int what, int arg1, int arg2) {
683        Message msg = Message.obtain();
684        msg.what = what;
685        msg.arg1 = arg1;
686        msg.arg2 = arg2;
687        Message resultMsg = sendMessageSynchronously(msg);
688        return resultMsg;
689    }
690
691    /**
692     * Send the Message synchronously.
693     *
694     * @param what
695     * @param arg1
696     * @param arg2
697     * @param obj
698     * @return reply message or null if an error.
699     */
700    public Message sendMessageSynchronously(int what, int arg1, int arg2, Object obj) {
701        Message msg = Message.obtain();
702        msg.what = what;
703        msg.arg1 = arg1;
704        msg.arg2 = arg2;
705        msg.obj = obj;
706        Message resultMsg = sendMessageSynchronously(msg);
707        return resultMsg;
708    }
709
710    /**
711     * Send the Message synchronously.
712     *
713     * @param what
714     * @param obj
715     * @return reply message or null if an error.
716     */
717    public Message sendMessageSynchronously(int what, Object obj) {
718        Message msg = Message.obtain();
719        msg.what = what;
720        msg.obj = obj;
721        Message resultMsg = sendMessageSynchronously(msg);
722        return resultMsg;
723    }
724
725    /**
726     * Helper class to send messages synchronously
727     */
728    private static class SyncMessenger {
729        /** A stack of SyncMessengers */
730        private static Stack<SyncMessenger> sStack = new Stack<SyncMessenger>();
731        /** A number of SyncMessengers created */
732        private static int sCount = 0;
733        /** The handler thread */
734        private HandlerThread mHandlerThread;
735        /** The handler that will receive the result */
736        private SyncHandler mHandler;
737        /** The messenger used to send the message */
738        private Messenger mMessenger;
739
740        /** private constructor */
741        private SyncMessenger() {
742        }
743
744        /** Synchronous Handler class */
745        private class SyncHandler extends Handler {
746            /** The object used to wait/notify */
747            private Object mLockObject = new Object();
748            /** The resulting message */
749            private Message mResultMsg;
750
751            /** Constructor */
752            private SyncHandler(Looper looper) {
753                super(looper);
754            }
755
756            /** Handle of the reply message */
757            @Override
758            public void handleMessage(Message msg) {
759                mResultMsg = Message.obtain();
760                mResultMsg.copyFrom(msg);
761                synchronized(mLockObject) {
762                    mLockObject.notify();
763                }
764            }
765        }
766
767        /**
768         * @return the SyncMessenger
769         */
770        private static SyncMessenger obtain() {
771            SyncMessenger sm;
772            synchronized (sStack) {
773                if (sStack.isEmpty()) {
774                    sm = new SyncMessenger();
775                    sm.mHandlerThread = new HandlerThread("SyncHandler-" + sCount++);
776                    sm.mHandlerThread.start();
777                    sm.mHandler = sm.new SyncHandler(sm.mHandlerThread.getLooper());
778                    sm.mMessenger = new Messenger(sm.mHandler);
779                } else {
780                    sm = sStack.pop();
781                }
782            }
783            return sm;
784        }
785
786        /**
787         * Recycle this object
788         */
789        private void recycle() {
790            synchronized (sStack) {
791                sStack.push(this);
792            }
793        }
794
795        /**
796         * Send a message synchronously.
797         *
798         * @param msg to send
799         * @return result message or null if an error occurs
800         */
801        private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) {
802            SyncMessenger sm = SyncMessenger.obtain();
803            try {
804                if (dstMessenger != null && msg != null) {
805                    msg.replyTo = sm.mMessenger;
806                    synchronized (sm.mHandler.mLockObject) {
807                        dstMessenger.send(msg);
808                        sm.mHandler.mLockObject.wait();
809                    }
810                } else {
811                    sm.mHandler.mResultMsg = null;
812                }
813            } catch (InterruptedException e) {
814                sm.mHandler.mResultMsg = null;
815            } catch (RemoteException e) {
816                sm.mHandler.mResultMsg = null;
817            }
818            Message resultMsg = sm.mHandler.mResultMsg;
819            sm.recycle();
820            return resultMsg;
821        }
822    }
823
824    /**
825     * Reply to the src handler that we're half connected.
826     * see: CMD_CHANNEL_HALF_CONNECTED for message contents
827     *
828     * @param status to be stored in msg.arg1
829     */
830    private void replyHalfConnected(int status) {
831        Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED);
832        msg.arg1 = status;
833        msg.obj = this;
834        msg.replyTo = mDstMessenger;
835        mSrcHandler.sendMessage(msg);
836    }
837
838    /**
839     * Reply to the src handler that we are disconnected
840     * see: CMD_CHANNEL_DISCONNECTED for message contents
841     *
842     * @param status to be stored in msg.arg1
843     */
844    private void replyDisconnected(int status) {
845        Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
846        msg.arg1 = status;
847        msg.obj = this;
848        msg.replyTo = mDstMessenger;
849        mSrcHandler.sendMessage(msg);
850    }
851
852
853    /**
854     * ServiceConnection to receive call backs.
855     */
856    class AsyncChannelConnection implements ServiceConnection {
857        AsyncChannelConnection() {
858        }
859
860        @Override
861        public void onServiceConnected(ComponentName className, IBinder service) {
862            mDstMessenger = new Messenger(service);
863            replyHalfConnected(STATUS_SUCCESSFUL);
864        }
865
866        @Override
867        public void onServiceDisconnected(ComponentName className) {
868            replyDisconnected(STATUS_SUCCESSFUL);
869        }
870    }
871
872    /**
873     * Log the string.
874     *
875     * @param s
876     */
877    private static void log(String s) {
878        Slog.d(TAG, s);
879    }
880}
881