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