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