AsyncChannel.java revision d0d420587a462024cdf47eee1d22cf6d52a4a6a0
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        replyDisconnected(STATUS_SUCCESSFUL);
466        mSrcHandler = null;
467        // Unlink only when bindService isn't used
468        if (mConnection == null && mDstMessenger != null && mDeathMonitor!= null) {
469            mDstMessenger.getBinder().unlinkToDeath(mDeathMonitor, 0);
470            mDeathMonitor = null;
471        }
472    }
473
474    /**
475     * Send a message to the destination handler.
476     *
477     * @param msg
478     */
479    public void sendMessage(Message msg) {
480        msg.replyTo = mSrcMessenger;
481        try {
482            mDstMessenger.send(msg);
483        } catch (RemoteException e) {
484            replyDisconnected(STATUS_SEND_UNSUCCESSFUL);
485        }
486    }
487
488    /**
489     * Send a message to the destination handler
490     *
491     * @param what
492     */
493    public void sendMessage(int what) {
494        Message msg = Message.obtain();
495        msg.what = what;
496        sendMessage(msg);
497    }
498
499    /**
500     * Send a message to the destination handler
501     *
502     * @param what
503     * @param arg1
504     */
505    public void sendMessage(int what, int arg1) {
506        Message msg = Message.obtain();
507        msg.what = what;
508        msg.arg1 = arg1;
509        sendMessage(msg);
510    }
511
512    /**
513     * Send a message to the destination handler
514     *
515     * @param what
516     * @param arg1
517     * @param arg2
518     */
519    public void sendMessage(int what, int arg1, int arg2) {
520        Message msg = Message.obtain();
521        msg.what = what;
522        msg.arg1 = arg1;
523        msg.arg2 = arg2;
524        sendMessage(msg);
525    }
526
527    /**
528     * Send a message to the destination handler
529     *
530     * @param what
531     * @param arg1
532     * @param arg2
533     * @param obj
534     */
535    public void sendMessage(int what, int arg1, int arg2, Object obj) {
536        Message msg = Message.obtain();
537        msg.what = what;
538        msg.arg1 = arg1;
539        msg.arg2 = arg2;
540        msg.obj = obj;
541        sendMessage(msg);
542    }
543
544    /**
545     * Send a message to the destination handler
546     *
547     * @param what
548     * @param obj
549     */
550    public void sendMessage(int what, Object obj) {
551        Message msg = Message.obtain();
552        msg.what = what;
553        msg.obj = obj;
554        sendMessage(msg);
555    }
556
557    /**
558     * Reply to srcMsg sending dstMsg
559     *
560     * @param srcMsg
561     * @param dstMsg
562     */
563    public void replyToMessage(Message srcMsg, Message dstMsg) {
564        try {
565            dstMsg.replyTo = mSrcMessenger;
566            srcMsg.replyTo.send(dstMsg);
567        } catch (RemoteException e) {
568            log("TODO: handle replyToMessage RemoteException" + e);
569            e.printStackTrace();
570        }
571    }
572
573    /**
574     * Reply to srcMsg
575     *
576     * @param srcMsg
577     * @param what
578     */
579    public void replyToMessage(Message srcMsg, int what) {
580        Message msg = Message.obtain();
581        msg.what = what;
582        replyToMessage(srcMsg, msg);
583    }
584
585    /**
586     * Reply to srcMsg
587     *
588     * @param srcMsg
589     * @param what
590     * @param arg1
591     */
592    public void replyToMessage(Message srcMsg, int what, int arg1) {
593        Message msg = Message.obtain();
594        msg.what = what;
595        msg.arg1 = arg1;
596        replyToMessage(srcMsg, msg);
597    }
598
599    /**
600     * Reply to srcMsg
601     *
602     * @param srcMsg
603     * @param what
604     * @param arg1
605     * @param arg2
606     */
607    public void replyToMessage(Message srcMsg, int what, int arg1, int arg2) {
608        Message msg = Message.obtain();
609        msg.what = what;
610        msg.arg1 = arg1;
611        msg.arg2 = arg2;
612        replyToMessage(srcMsg, msg);
613    }
614
615    /**
616     * Reply to srcMsg
617     *
618     * @param srcMsg
619     * @param what
620     * @param arg1
621     * @param arg2
622     * @param obj
623     */
624    public void replyToMessage(Message srcMsg, int what, int arg1, int arg2, Object obj) {
625        Message msg = Message.obtain();
626        msg.what = what;
627        msg.arg1 = arg1;
628        msg.arg2 = arg2;
629        msg.obj = obj;
630        replyToMessage(srcMsg, msg);
631    }
632
633    /**
634     * Reply to srcMsg
635     *
636     * @param srcMsg
637     * @param what
638     * @param obj
639     */
640    public void replyToMessage(Message srcMsg, int what, Object obj) {
641        Message msg = Message.obtain();
642        msg.what = what;
643        msg.obj = obj;
644        replyToMessage(srcMsg, msg);
645    }
646
647    /**
648     * Send the Message synchronously.
649     *
650     * @param msg to send
651     * @return reply message or null if an error.
652     */
653    public Message sendMessageSynchronously(Message msg) {
654        Message resultMsg = SyncMessenger.sendMessageSynchronously(mDstMessenger, msg);
655        return resultMsg;
656    }
657
658    /**
659     * Send the Message synchronously.
660     *
661     * @param what
662     * @return reply message or null if an error.
663     */
664    public Message sendMessageSynchronously(int what) {
665        Message msg = Message.obtain();
666        msg.what = what;
667        Message resultMsg = sendMessageSynchronously(msg);
668        return resultMsg;
669    }
670
671    /**
672     * Send the Message synchronously.
673     *
674     * @param what
675     * @param arg1
676     * @return reply message or null if an error.
677     */
678    public Message sendMessageSynchronously(int what, int arg1) {
679        Message msg = Message.obtain();
680        msg.what = what;
681        msg.arg1 = arg1;
682        Message resultMsg = sendMessageSynchronously(msg);
683        return resultMsg;
684    }
685
686    /**
687     * Send the Message synchronously.
688     *
689     * @param what
690     * @param arg1
691     * @param arg2
692     * @return reply message or null if an error.
693     */
694    public Message sendMessageSynchronously(int what, int arg1, int arg2) {
695        Message msg = Message.obtain();
696        msg.what = what;
697        msg.arg1 = arg1;
698        msg.arg2 = arg2;
699        Message resultMsg = sendMessageSynchronously(msg);
700        return resultMsg;
701    }
702
703    /**
704     * Send the Message synchronously.
705     *
706     * @param what
707     * @param arg1
708     * @param arg2
709     * @param obj
710     * @return reply message or null if an error.
711     */
712    public Message sendMessageSynchronously(int what, int arg1, int arg2, Object obj) {
713        Message msg = Message.obtain();
714        msg.what = what;
715        msg.arg1 = arg1;
716        msg.arg2 = arg2;
717        msg.obj = obj;
718        Message resultMsg = sendMessageSynchronously(msg);
719        return resultMsg;
720    }
721
722    /**
723     * Send the Message synchronously.
724     *
725     * @param what
726     * @param obj
727     * @return reply message or null if an error.
728     */
729    public Message sendMessageSynchronously(int what, Object obj) {
730        Message msg = Message.obtain();
731        msg.what = what;
732        msg.obj = obj;
733        Message resultMsg = sendMessageSynchronously(msg);
734        return resultMsg;
735    }
736
737    /**
738     * Helper class to send messages synchronously
739     */
740    private static class SyncMessenger {
741        /** A stack of SyncMessengers */
742        private static Stack<SyncMessenger> sStack = new Stack<SyncMessenger>();
743        /** A number of SyncMessengers created */
744        private static int sCount = 0;
745        /** The handler thread */
746        private HandlerThread mHandlerThread;
747        /** The handler that will receive the result */
748        private SyncHandler mHandler;
749        /** The messenger used to send the message */
750        private Messenger mMessenger;
751
752        /** private constructor */
753        private SyncMessenger() {
754        }
755
756        /** Synchronous Handler class */
757        private class SyncHandler extends Handler {
758            /** The object used to wait/notify */
759            private Object mLockObject = new Object();
760            /** The resulting message */
761            private Message mResultMsg;
762
763            /** Constructor */
764            private SyncHandler(Looper looper) {
765                super(looper);
766            }
767
768            /** Handle of the reply message */
769            @Override
770            public void handleMessage(Message msg) {
771                mResultMsg = Message.obtain();
772                mResultMsg.copyFrom(msg);
773                synchronized(mLockObject) {
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            try {
816                if (dstMessenger != null && msg != null) {
817                    msg.replyTo = sm.mMessenger;
818                    synchronized (sm.mHandler.mLockObject) {
819                        dstMessenger.send(msg);
820                        sm.mHandler.mLockObject.wait();
821                    }
822                } else {
823                    sm.mHandler.mResultMsg = null;
824                }
825            } catch (InterruptedException e) {
826                sm.mHandler.mResultMsg = null;
827            } catch (RemoteException e) {
828                sm.mHandler.mResultMsg = null;
829            }
830            Message resultMsg = sm.mHandler.mResultMsg;
831            sm.recycle();
832            return resultMsg;
833        }
834    }
835
836    /**
837     * Reply to the src handler that we're half connected.
838     * see: CMD_CHANNEL_HALF_CONNECTED for message contents
839     *
840     * @param status to be stored in msg.arg1
841     */
842    private void replyHalfConnected(int status) {
843        Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED);
844        msg.arg1 = status;
845        msg.obj = this;
846        msg.replyTo = mDstMessenger;
847
848        /*
849         * Link to death only when bindService isn't used.
850         */
851        if (mConnection == null) {
852            mDeathMonitor = new DeathMonitor();
853            try {
854                mDstMessenger.getBinder().linkToDeath(mDeathMonitor, 0);
855            } catch (RemoteException e) {
856                mDeathMonitor = null;
857                // Override status to indicate failure
858                msg.arg1 = STATUS_BINDING_UNSUCCESSFUL;
859            }
860        }
861
862        mSrcHandler.sendMessage(msg);
863    }
864
865    /**
866     * Reply to the src handler that we are disconnected
867     * see: CMD_CHANNEL_DISCONNECTED for message contents
868     *
869     * @param status to be stored in msg.arg1
870     */
871    private void replyDisconnected(int status) {
872        // Can't reply if already disconnected. Avoid NullPointerException.
873        if (mSrcHandler == null) return;
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