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        if (DBG) log("connected srcHandler to the dstMessenger X");
406    }
407
408    /**
409     * Connect two local Handlers.
410     *
411     * @param srcContext is the context of the source
412     * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
413     *            messages
414     * @param dstHandler is the hander to send messages to.
415     */
416    public void connect(Context srcContext, Handler srcHandler, Handler dstHandler) {
417        connect(srcContext, srcHandler, new Messenger(dstHandler));
418    }
419
420    /**
421     * Connect service and messenger.
422     *
423     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcAsyncService when complete.
424     *      msg.arg1 = status
425     *      msg.obj = the AsyncChannel
426     *
427     * @param srcAsyncService
428     * @param dstMessenger
429     */
430    public void connect(AsyncService srcAsyncService, Messenger dstMessenger) {
431        connect(srcAsyncService, srcAsyncService.getHandler(), dstMessenger);
432    }
433
434    /**
435     * To close the connection call when handler receives CMD_CHANNEL_DISCONNECTED
436     */
437    public void disconnected() {
438        mSrcContext = null;
439        mSrcHandler = null;
440        mSrcMessenger = null;
441        mDstMessenger = null;
442        mDeathMonitor = null;
443        mConnection = null;
444    }
445
446    /**
447     * Disconnect
448     */
449    public void disconnect() {
450        if ((mConnection != null) && (mSrcContext != null)) {
451            mSrcContext.unbindService(mConnection);
452            mConnection = null;
453        }
454        try {
455            // Send the DISCONNECTED, although it may not be received
456            // but its the best we can do.
457            Message msg = Message.obtain();
458            msg.what = CMD_CHANNEL_DISCONNECTED;
459            msg.replyTo = mSrcMessenger;
460            mDstMessenger.send(msg);
461        } catch(Exception e) {
462        }
463        // Tell source we're disconnected.
464        replyDisconnected(STATUS_SUCCESSFUL);
465        mSrcHandler = null;
466        // Unlink only when bindService isn't used
467        if (mConnection == null && mDstMessenger != null && mDeathMonitor!= null) {
468            mDstMessenger.getBinder().unlinkToDeath(mDeathMonitor, 0);
469            mDeathMonitor = null;
470        }
471    }
472
473    /**
474     * Send a message to the destination handler.
475     *
476     * @param msg
477     */
478    public void sendMessage(Message msg) {
479        msg.replyTo = mSrcMessenger;
480        try {
481            mDstMessenger.send(msg);
482        } catch (RemoteException e) {
483            replyDisconnected(STATUS_SEND_UNSUCCESSFUL);
484        }
485    }
486
487    /**
488     * Send a message to the destination handler
489     *
490     * @param what
491     */
492    public void sendMessage(int what) {
493        Message msg = Message.obtain();
494        msg.what = what;
495        sendMessage(msg);
496    }
497
498    /**
499     * Send a message to the destination handler
500     *
501     * @param what
502     * @param arg1
503     */
504    public void sendMessage(int what, int arg1) {
505        Message msg = Message.obtain();
506        msg.what = what;
507        msg.arg1 = arg1;
508        sendMessage(msg);
509    }
510
511    /**
512     * Send a message to the destination handler
513     *
514     * @param what
515     * @param arg1
516     * @param arg2
517     */
518    public void sendMessage(int what, int arg1, int arg2) {
519        Message msg = Message.obtain();
520        msg.what = what;
521        msg.arg1 = arg1;
522        msg.arg2 = arg2;
523        sendMessage(msg);
524    }
525
526    /**
527     * Send a message to the destination handler
528     *
529     * @param what
530     * @param arg1
531     * @param arg2
532     * @param obj
533     */
534    public void sendMessage(int what, int arg1, int arg2, Object obj) {
535        Message msg = Message.obtain();
536        msg.what = what;
537        msg.arg1 = arg1;
538        msg.arg2 = arg2;
539        msg.obj = obj;
540        sendMessage(msg);
541    }
542
543    /**
544     * Send a message to the destination handler
545     *
546     * @param what
547     * @param obj
548     */
549    public void sendMessage(int what, Object obj) {
550        Message msg = Message.obtain();
551        msg.what = what;
552        msg.obj = obj;
553        sendMessage(msg);
554    }
555
556    /**
557     * Reply to srcMsg sending dstMsg
558     *
559     * @param srcMsg
560     * @param dstMsg
561     */
562    public void replyToMessage(Message srcMsg, Message dstMsg) {
563        try {
564            dstMsg.replyTo = mSrcMessenger;
565            srcMsg.replyTo.send(dstMsg);
566        } catch (RemoteException e) {
567            log("TODO: handle replyToMessage RemoteException" + e);
568            e.printStackTrace();
569        }
570    }
571
572    /**
573     * Reply to srcMsg
574     *
575     * @param srcMsg
576     * @param what
577     */
578    public void replyToMessage(Message srcMsg, int what) {
579        Message msg = Message.obtain();
580        msg.what = what;
581        replyToMessage(srcMsg, msg);
582    }
583
584    /**
585     * Reply to srcMsg
586     *
587     * @param srcMsg
588     * @param what
589     * @param arg1
590     */
591    public void replyToMessage(Message srcMsg, int what, int arg1) {
592        Message msg = Message.obtain();
593        msg.what = what;
594        msg.arg1 = arg1;
595        replyToMessage(srcMsg, msg);
596    }
597
598    /**
599     * Reply to srcMsg
600     *
601     * @param srcMsg
602     * @param what
603     * @param arg1
604     * @param arg2
605     */
606    public void replyToMessage(Message srcMsg, int what, int arg1, int arg2) {
607        Message msg = Message.obtain();
608        msg.what = what;
609        msg.arg1 = arg1;
610        msg.arg2 = arg2;
611        replyToMessage(srcMsg, msg);
612    }
613
614    /**
615     * Reply to srcMsg
616     *
617     * @param srcMsg
618     * @param what
619     * @param arg1
620     * @param arg2
621     * @param obj
622     */
623    public void replyToMessage(Message srcMsg, int what, int arg1, int arg2, Object obj) {
624        Message msg = Message.obtain();
625        msg.what = what;
626        msg.arg1 = arg1;
627        msg.arg2 = arg2;
628        msg.obj = obj;
629        replyToMessage(srcMsg, msg);
630    }
631
632    /**
633     * Reply to srcMsg
634     *
635     * @param srcMsg
636     * @param what
637     * @param obj
638     */
639    public void replyToMessage(Message srcMsg, int what, Object obj) {
640        Message msg = Message.obtain();
641        msg.what = what;
642        msg.obj = obj;
643        replyToMessage(srcMsg, msg);
644    }
645
646    /**
647     * Send the Message synchronously.
648     *
649     * @param msg to send
650     * @return reply message or null if an error.
651     */
652    public Message sendMessageSynchronously(Message msg) {
653        Message resultMsg = SyncMessenger.sendMessageSynchronously(mDstMessenger, msg);
654        return resultMsg;
655    }
656
657    /**
658     * Send the Message synchronously.
659     *
660     * @param what
661     * @return reply message or null if an error.
662     */
663    public Message sendMessageSynchronously(int what) {
664        Message msg = Message.obtain();
665        msg.what = what;
666        Message resultMsg = sendMessageSynchronously(msg);
667        return resultMsg;
668    }
669
670    /**
671     * Send the Message synchronously.
672     *
673     * @param what
674     * @param arg1
675     * @return reply message or null if an error.
676     */
677    public Message sendMessageSynchronously(int what, int arg1) {
678        Message msg = Message.obtain();
679        msg.what = what;
680        msg.arg1 = arg1;
681        Message resultMsg = sendMessageSynchronously(msg);
682        return resultMsg;
683    }
684
685    /**
686     * Send the Message synchronously.
687     *
688     * @param what
689     * @param arg1
690     * @param arg2
691     * @return reply message or null if an error.
692     */
693    public Message sendMessageSynchronously(int what, int arg1, int arg2) {
694        Message msg = Message.obtain();
695        msg.what = what;
696        msg.arg1 = arg1;
697        msg.arg2 = arg2;
698        Message resultMsg = sendMessageSynchronously(msg);
699        return resultMsg;
700    }
701
702    /**
703     * Send the Message synchronously.
704     *
705     * @param what
706     * @param arg1
707     * @param arg2
708     * @param obj
709     * @return reply message or null if an error.
710     */
711    public Message sendMessageSynchronously(int what, int arg1, int arg2, Object obj) {
712        Message msg = Message.obtain();
713        msg.what = what;
714        msg.arg1 = arg1;
715        msg.arg2 = arg2;
716        msg.obj = obj;
717        Message resultMsg = sendMessageSynchronously(msg);
718        return resultMsg;
719    }
720
721    /**
722     * Send the Message synchronously.
723     *
724     * @param what
725     * @param obj
726     * @return reply message or null if an error.
727     */
728    public Message sendMessageSynchronously(int what, Object obj) {
729        Message msg = Message.obtain();
730        msg.what = what;
731        msg.obj = obj;
732        Message resultMsg = sendMessageSynchronously(msg);
733        return resultMsg;
734    }
735
736    /**
737     * Helper class to send messages synchronously
738     */
739    private static class SyncMessenger {
740        /** A stack of SyncMessengers */
741        private static Stack<SyncMessenger> sStack = new Stack<SyncMessenger>();
742        /** A number of SyncMessengers created */
743        private static int sCount = 0;
744        /** The handler thread */
745        private HandlerThread mHandlerThread;
746        /** The handler that will receive the result */
747        private SyncHandler mHandler;
748        /** The messenger used to send the message */
749        private Messenger mMessenger;
750
751        /** private constructor */
752        private SyncMessenger() {
753        }
754
755        /** Synchronous Handler class */
756        private class SyncHandler extends Handler {
757            /** The object used to wait/notify */
758            private Object mLockObject = new Object();
759            /** The resulting message */
760            private Message mResultMsg;
761
762            /** Constructor */
763            private SyncHandler(Looper looper) {
764                super(looper);
765            }
766
767            /** Handle of the reply message */
768            @Override
769            public void handleMessage(Message msg) {
770                Message msgCopy = Message.obtain();
771                msgCopy.copyFrom(msg);
772                synchronized(mLockObject) {
773                    mResultMsg = msgCopy;
774                    mLockObject.notify();
775                }
776            }
777        }
778
779        /**
780         * @return the SyncMessenger
781         */
782        private static SyncMessenger obtain() {
783            SyncMessenger sm;
784            synchronized (sStack) {
785                if (sStack.isEmpty()) {
786                    sm = new SyncMessenger();
787                    sm.mHandlerThread = new HandlerThread("SyncHandler-" + sCount++);
788                    sm.mHandlerThread.start();
789                    sm.mHandler = sm.new SyncHandler(sm.mHandlerThread.getLooper());
790                    sm.mMessenger = new Messenger(sm.mHandler);
791                } else {
792                    sm = sStack.pop();
793                }
794            }
795            return sm;
796        }
797
798        /**
799         * Recycle this object
800         */
801        private void recycle() {
802            synchronized (sStack) {
803                sStack.push(this);
804            }
805        }
806
807        /**
808         * Send a message synchronously.
809         *
810         * @param msg to send
811         * @return result message or null if an error occurs
812         */
813        private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) {
814            SyncMessenger sm = SyncMessenger.obtain();
815            Message resultMsg = null;
816            try {
817                if (dstMessenger != null && msg != null) {
818                    msg.replyTo = sm.mMessenger;
819                    synchronized (sm.mHandler.mLockObject) {
820                        if (sm.mHandler.mResultMsg != null) {
821                            Slog.wtf(TAG, "mResultMsg should be null here");
822                            sm.mHandler.mResultMsg = null;
823                        }
824                        dstMessenger.send(msg);
825                        sm.mHandler.mLockObject.wait();
826                        resultMsg = sm.mHandler.mResultMsg;
827                        sm.mHandler.mResultMsg = null;
828                    }
829                }
830            } catch (InterruptedException e) {
831                Slog.e(TAG, "error in sendMessageSynchronously", e);
832            } catch (RemoteException e) {
833                Slog.e(TAG, "error in sendMessageSynchronously", e);
834            }
835            sm.recycle();
836            return resultMsg;
837        }
838    }
839
840    /**
841     * Reply to the src handler that we're half connected.
842     * see: CMD_CHANNEL_HALF_CONNECTED for message contents
843     *
844     * @param status to be stored in msg.arg1
845     */
846    private void replyHalfConnected(int status) {
847        Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED);
848        msg.arg1 = status;
849        msg.obj = this;
850        msg.replyTo = mDstMessenger;
851        if (!linkToDeathMonitor()) {
852            // Override status to indicate failure
853            msg.arg1 = STATUS_BINDING_UNSUCCESSFUL;
854        }
855
856        mSrcHandler.sendMessage(msg);
857    }
858
859    /**
860     * Link to death monitor for destination messenger. Returns true if successfully binded to
861     * destination messenger; false otherwise.
862     */
863    private boolean linkToDeathMonitor() {
864        // Link to death only when bindService isn't used and not already linked.
865        if (mConnection == null && mDeathMonitor == null) {
866            mDeathMonitor = new DeathMonitor();
867            try {
868                mDstMessenger.getBinder().linkToDeath(mDeathMonitor, 0);
869            } catch (RemoteException e) {
870                mDeathMonitor = null;
871                return false;
872            }
873        }
874        return true;
875    }
876
877    /**
878     * Reply to the src handler that we are disconnected
879     * see: CMD_CHANNEL_DISCONNECTED for message contents
880     *
881     * @param status to be stored in msg.arg1
882     */
883    private void replyDisconnected(int status) {
884        // Can't reply if already disconnected. Avoid NullPointerException.
885        if (mSrcHandler == null) return;
886        Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
887        msg.arg1 = status;
888        msg.obj = this;
889        msg.replyTo = mDstMessenger;
890        mSrcHandler.sendMessage(msg);
891    }
892
893
894    /**
895     * ServiceConnection to receive call backs.
896     */
897    class AsyncChannelConnection implements ServiceConnection {
898        AsyncChannelConnection() {
899        }
900
901        @Override
902        public void onServiceConnected(ComponentName className, IBinder service) {
903            mDstMessenger = new Messenger(service);
904            replyHalfConnected(STATUS_SUCCESSFUL);
905        }
906
907        @Override
908        public void onServiceDisconnected(ComponentName className) {
909            replyDisconnected(STATUS_SUCCESSFUL);
910        }
911    }
912
913    /**
914     * Log the string.
915     *
916     * @param s
917     */
918    private static void log(String s) {
919        Slog.d(TAG, s);
920    }
921
922    private final class DeathMonitor implements IBinder.DeathRecipient {
923
924        DeathMonitor() {
925        }
926
927        public void binderDied() {
928            replyDisconnected(STATUS_REMOTE_DISCONNECTION);
929        }
930
931    }
932}
933