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