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