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        }
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        if (mSrcHandler != null) {
465            replyDisconnected(STATUS_SUCCESSFUL);
466        }
467        // Unlink only when bindService isn't used
468        if (mConnection == null && mDstMessenger != null && mDeathMonitor!= null) {
469            mDstMessenger.getBinder().unlinkToDeath(mDeathMonitor, 0);
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                mResultMsg = Message.obtain();
771                mResultMsg.copyFrom(msg);
772                synchronized(mLockObject) {
773                    mLockObject.notify();
774                }
775            }
776        }
777
778        /**
779         * @return the SyncMessenger
780         */
781        private static SyncMessenger obtain() {
782            SyncMessenger sm;
783            synchronized (sStack) {
784                if (sStack.isEmpty()) {
785                    sm = new SyncMessenger();
786                    sm.mHandlerThread = new HandlerThread("SyncHandler-" + sCount++);
787                    sm.mHandlerThread.start();
788                    sm.mHandler = sm.new SyncHandler(sm.mHandlerThread.getLooper());
789                    sm.mMessenger = new Messenger(sm.mHandler);
790                } else {
791                    sm = sStack.pop();
792                }
793            }
794            return sm;
795        }
796
797        /**
798         * Recycle this object
799         */
800        private void recycle() {
801            synchronized (sStack) {
802                sStack.push(this);
803            }
804        }
805
806        /**
807         * Send a message synchronously.
808         *
809         * @param msg to send
810         * @return result message or null if an error occurs
811         */
812        private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) {
813            SyncMessenger sm = SyncMessenger.obtain();
814            try {
815                if (dstMessenger != null && msg != null) {
816                    msg.replyTo = sm.mMessenger;
817                    synchronized (sm.mHandler.mLockObject) {
818                        dstMessenger.send(msg);
819                        sm.mHandler.mLockObject.wait();
820                    }
821                } else {
822                    sm.mHandler.mResultMsg = null;
823                }
824            } catch (InterruptedException e) {
825                sm.mHandler.mResultMsg = null;
826            } catch (RemoteException e) {
827                sm.mHandler.mResultMsg = null;
828            }
829            Message resultMsg = sm.mHandler.mResultMsg;
830            sm.recycle();
831            return resultMsg;
832        }
833    }
834
835    /**
836     * Reply to the src handler that we're half connected.
837     * see: CMD_CHANNEL_HALF_CONNECTED for message contents
838     *
839     * @param status to be stored in msg.arg1
840     */
841    private void replyHalfConnected(int status) {
842        Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED);
843        msg.arg1 = status;
844        msg.obj = this;
845        msg.replyTo = mDstMessenger;
846
847        /*
848         * Link to death only when bindService isn't used.
849         */
850        if (mConnection == null) {
851            mDeathMonitor = new DeathMonitor();
852            try {
853                mDstMessenger.getBinder().linkToDeath(mDeathMonitor, 0);
854            } catch (RemoteException e) {
855                mDeathMonitor = null;
856                // Override status to indicate failure
857                msg.arg1 = STATUS_BINDING_UNSUCCESSFUL;
858            }
859        }
860
861        mSrcHandler.sendMessage(msg);
862    }
863
864    /**
865     * Reply to the src handler that we are disconnected
866     * see: CMD_CHANNEL_DISCONNECTED for message contents
867     *
868     * @param status to be stored in msg.arg1
869     */
870    private void replyDisconnected(int status) {
871        Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
872        msg.arg1 = status;
873        msg.obj = this;
874        msg.replyTo = mDstMessenger;
875        mSrcHandler.sendMessage(msg);
876    }
877
878
879    /**
880     * ServiceConnection to receive call backs.
881     */
882    class AsyncChannelConnection implements ServiceConnection {
883        AsyncChannelConnection() {
884        }
885
886        @Override
887        public void onServiceConnected(ComponentName className, IBinder service) {
888            mDstMessenger = new Messenger(service);
889            replyHalfConnected(STATUS_SUCCESSFUL);
890        }
891
892        @Override
893        public void onServiceDisconnected(ComponentName className) {
894            replyDisconnected(STATUS_SUCCESSFUL);
895        }
896    }
897
898    /**
899     * Log the string.
900     *
901     * @param s
902     */
903    private static void log(String s) {
904        Slog.d(TAG, s);
905    }
906
907    private final class DeathMonitor implements IBinder.DeathRecipient {
908
909        DeathMonitor() {
910        }
911
912        public void binderDied() {
913            replyDisconnected(STATUS_REMOTE_DISCONNECTION);
914        }
915
916    }
917}
918