• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.internal.util;
18 
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.ServiceConnection;
23 import android.os.Handler;
24 import android.os.HandlerThread;
25 import android.os.IBinder;
26 import android.os.Looper;
27 import android.os.Message;
28 import android.os.Messenger;
29 import android.os.RemoteException;
30 import android.util.Slog;
31 
32 import 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  */
86 public 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      */
AsyncChannel()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      */
connectSrcHandlerToPackageSync( Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName)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      */
connectSync(Context srcContext, Handler srcHandler, Messenger dstMessenger)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      */
connectSync(Context srcContext, Handler srcHandler, Handler dstHandler)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      */
fullyConnectSync(Context srcContext, Handler srcHandler, Handler dstHandler)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      */
connect(Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName)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      */
connect(Context srcContext, Handler srcHandler, Class<?> klass)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      */
connect(Context srcContext, Handler srcHandler, Messenger dstMessenger)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      */
connected(Context srcContext, Handler srcHandler, Messenger dstMessenger)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      */
connect(Context srcContext, Handler srcHandler, Handler dstHandler)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      */
connect(AsyncService srcAsyncService, Messenger dstMessenger)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      */
disconnected()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      */
disconnect()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      */
sendMessage(Message msg)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      */
sendMessage(int what)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      */
sendMessage(int what, int arg1)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      */
sendMessage(int what, int arg1, int arg2)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      */
sendMessage(int what, int arg1, int arg2, Object obj)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      */
sendMessage(int what, Object obj)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      */
replyToMessage(Message srcMsg, Message dstMsg)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      */
replyToMessage(Message srcMsg, int what)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      */
replyToMessage(Message srcMsg, int what, int arg1)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      */
replyToMessage(Message srcMsg, int what, int arg1, int arg2)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      */
replyToMessage(Message srcMsg, int what, int arg1, int arg2, Object obj)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      */
replyToMessage(Message srcMsg, int what, Object obj)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      */
sendMessageSynchronously(Message msg)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      */
sendMessageSynchronously(int what)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      */
sendMessageSynchronously(int what, int arg1)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      */
sendMessageSynchronously(int what, int arg1, int arg2)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      */
sendMessageSynchronously(int what, int arg1, int arg2, Object obj)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      */
sendMessageSynchronously(int what, Object obj)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 */
SyncMessenger()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 */
SyncHandler(Looper looper)724             private SyncHandler(Looper looper) {
725                 super(looper);
726             }
727 
728             /** Handle of the reply message */
729             @Override
handleMessage(Message msg)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          */
obtain()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          */
recycle()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          */
sendMessageSynchronously(Messenger dstMessenger, Message msg)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      */
replyHalfConnected(int status)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      */
replyDisconnected(int status)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 {
AsyncChannelConnection()829         AsyncChannelConnection() {
830         }
831 
832         @Override
onServiceConnected(ComponentName className, IBinder service)833         public void onServiceConnected(ComponentName className, IBinder service) {
834             mDstMessenger = new Messenger(service);
835             replyHalfConnected(STATUS_SUCCESSFUL);
836         }
837 
838         @Override
onServiceDisconnected(ComponentName className)839         public void onServiceDisconnected(ComponentName className) {
840             replyDisconnected(STATUS_SUCCESSFUL);
841         }
842     }
843 
844     /**
845      * Log the string.
846      *
847      * @param s
848      */
log(String s)849     private static void log(String s) {
850         Slog.d(TAG, s);
851     }
852 }
853