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.compat.annotation.UnsupportedAppUsage;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.ServiceConnection;
24 import android.os.Build;
25 import android.os.Handler;
26 import android.os.HandlerThread;
27 import android.os.IBinder;
28 import android.os.Looper;
29 import android.os.Message;
30 import android.os.Messenger;
31 import android.os.RemoteException;
32 import android.util.Log;
33 
34 import java.util.Stack;
35 
36 /**
37  * <p>An asynchronous channel between two handlers.</p>
38  *
39  * <p>The handlers maybe in the same process or in another process. There
40  * are two protocol styles that can be used with an AysncChannel. The
41  * first is a simple request/reply protocol where the server does
42  * not need to know which client is issuing the request.</p>
43  *
44  * <p>In a simple request/reply protocol the client/source sends requests to the
45  * server/destination. And the server uses the replyToMessage methods.
46  * In this usage model there is no need for the destination to
47  * use the connect methods. The typical sequence of operations is:</p>
48  *<ol>
49  *   <li>Client calls AsyncChannel#connectSync or Asynchronously:</li>
50  *      <ol>For an asynchronous half connection client calls AsyncChannel#connect.</ol>
51  *          <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li>
52  *      </ol>
53  *   <li><code>comm-loop:</code></li>
54  *   <li>Client calls AsyncChannel#sendMessage</li>
55  *   <li>Server processes messages and optionally replies using AsyncChannel#replyToMessage
56  *   <li>Loop to <code>comm-loop</code> until done</li>
57  *   <li>When done Client calls {@link AsyncChannel#disconnect}</li>
58  *   <li>Client/Server receives CMD_CHANNEL_DISCONNECTED from AsyncChannel</li>
59  *</ol>
60  *<br/>
61  * <p>A second usage model is where the server/destination needs to know
62  * which client it's connected too. For example the server needs to
63  * send unsolicited messages back to the client. Or the server keeps
64  * different state for each client. In this model the server will also
65  * use the connect methods. The typical sequence of operation is:</p>
66  *<ol>
67  *   <li>Client calls AsyncChannel#fullyConnectSync or Asynchronously:<li>
68  *      <ol>For an asynchronous full connection it calls AsyncChannel#connect</li>
69  *          <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li>
70  *          <li>Client calls AsyncChannel#sendMessage(CMD_CHANNEL_FULL_CONNECTION)</li>
71  *      </ol>
72  *   <li>Server receives CMD_CHANNEL_FULL_CONNECTION</li>
73  *   <li>Server calls AsyncChannel#connected</li>
74  *   <li>Server sends AsyncChannel#sendMessage(CMD_CHANNEL_FULLY_CONNECTED)</li>
75  *   <li>Client receives CMD_CHANNEL_FULLY_CONNECTED</li>
76  *   <li><code>comm-loop:</code></li>
77  *   <li>Client/Server uses AsyncChannel#sendMessage/replyToMessage
78  *       to communicate and perform work</li>
79  *   <li>Loop to <code>comm-loop</code> until done</li>
80  *   <li>When done Client/Server calls {@link AsyncChannel#disconnect}</li>
81  *   <li>Client/Server receives CMD_CHANNEL_DISCONNECTED from AsyncChannel</li>
82  *</ol>
83  *
84  * TODO: Consider simplifying where we have connect and fullyConnect with only one response
85  * message RSP_CHANNEL_CONNECT instead of two, CMD_CHANNEL_HALF_CONNECTED and
86  * CMD_CHANNEL_FULLY_CONNECTED. We'd also change CMD_CHANNEL_FULL_CONNECTION to REQ_CHANNEL_CONNECT.
87  */
88 public class AsyncChannel {
89     /** Log tag */
90     private static final String TAG = "AsyncChannel";
91 
92     /** Enable to turn on debugging */
93     private static final boolean DBG = false;
94 
95     private static final int BASE = Protocol.BASE_SYSTEM_ASYNC_CHANNEL;
96 
97     /**
98      * Command sent when the channel is half connected. Half connected
99      * means that the channel can be used to send commends to the destination
100      * but the destination is unaware that the channel exists. The first
101      * command sent to the destination is typically CMD_CHANNEL_FULL_CONNECTION if
102      * it is desired to establish a long term connection, but any command maybe
103      * sent.
104      *
105      * msg.arg1 == 0 : STATUS_SUCCESSFUL
106      *             1 : STATUS_BINDING_UNSUCCESSFUL
107      * msg.obj  == the AsyncChannel
108      * msg.replyTo == dstMessenger if successful
109      */
110     @UnsupportedAppUsage
111     public static final int CMD_CHANNEL_HALF_CONNECTED = BASE + 0;
112 
113     /**
114      * Command typically sent when after receiving the CMD_CHANNEL_HALF_CONNECTED.
115      * This is used to initiate a long term connection with the destination and
116      * typically the destination will reply with CMD_CHANNEL_FULLY_CONNECTED.
117      *
118      * msg.replyTo = srcMessenger.
119      */
120     @UnsupportedAppUsage
121     public static final int CMD_CHANNEL_FULL_CONNECTION = BASE + 1;
122 
123     /**
124      * Command typically sent after the destination receives a CMD_CHANNEL_FULL_CONNECTION.
125      * This signifies the acceptance or rejection of the channel by the sender.
126      *
127      * msg.arg1 == 0 : Accept connection
128      *               : All other values signify the destination rejected the connection
129      *                 and {@link AsyncChannel#disconnect} would typically be called.
130      */
131     public static final int CMD_CHANNEL_FULLY_CONNECTED = BASE + 2;
132 
133     /**
134      * Command sent when one side or the other wishes to disconnect. The sender
135      * may or may not be able to receive a reply depending upon the protocol and
136      * the state of the connection. The receiver should call {@link AsyncChannel#disconnect}
137      * to close its side of the channel and it will receive a CMD_CHANNEL_DISCONNECTED
138      * when the channel is closed.
139      *
140      * msg.replyTo = messenger that is disconnecting
141      */
142     public static final int CMD_CHANNEL_DISCONNECT = BASE + 3;
143 
144     /**
145      * Command sent when the channel becomes disconnected. This is sent when the
146      * channel is forcibly disconnected by the system or as a reply to CMD_CHANNEL_DISCONNECT.
147      *
148      * msg.arg1 == 0 : STATUS_SUCCESSFUL
149      *             1 : STATUS_BINDING_UNSUCCESSFUL
150      *             2 : STATUS_SEND_UNSUCCESSFUL
151      *               : All other values signify failure and the channel state is indeterminate
152      * msg.obj  == the AsyncChannel
153      * msg.replyTo = messenger disconnecting or null if it was never connected.
154      */
155     public static final int CMD_CHANNEL_DISCONNECTED = BASE + 4;
156 
157     private static final int CMD_TO_STRING_COUNT = CMD_CHANNEL_DISCONNECTED - BASE + 1;
158     private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
159     static {
160         sCmdToString[CMD_CHANNEL_HALF_CONNECTED - BASE] = "CMD_CHANNEL_HALF_CONNECTED";
161         sCmdToString[CMD_CHANNEL_FULL_CONNECTION - BASE] = "CMD_CHANNEL_FULL_CONNECTION";
162         sCmdToString[CMD_CHANNEL_FULLY_CONNECTED - BASE] = "CMD_CHANNEL_FULLY_CONNECTED";
163         sCmdToString[CMD_CHANNEL_DISCONNECT - BASE] = "CMD_CHANNEL_DISCONNECT";
164         sCmdToString[CMD_CHANNEL_DISCONNECTED - BASE] = "CMD_CHANNEL_DISCONNECTED";
165     }
166     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
cmdToString(int cmd)167     protected static String cmdToString(int cmd) {
168         cmd -= BASE;
169         if ((cmd >= 0) && (cmd < sCmdToString.length)) {
170             return sCmdToString[cmd];
171         } else {
172             return null;
173         }
174     }
175 
176     /** Successful status always 0, !0 is an unsuccessful status */
177     @UnsupportedAppUsage
178     public static final int STATUS_SUCCESSFUL = 0;
179 
180     /** Error attempting to bind on a connect */
181     public static final int STATUS_BINDING_UNSUCCESSFUL = 1;
182 
183     /** Error attempting to send a message */
184     public static final int STATUS_SEND_UNSUCCESSFUL = 2;
185 
186     /** CMD_FULLY_CONNECTED refused because a connection already exists*/
187     public static final int STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED = 3;
188 
189     /** Error indicating abnormal termination of destination messenger */
190     public static final int STATUS_REMOTE_DISCONNECTION = 4;
191 
192     /** Service connection */
193     private AsyncChannelConnection mConnection;
194 
195     /** Context for source */
196     private Context mSrcContext;
197 
198     /** Handler for source */
199     private Handler mSrcHandler;
200 
201     /** Messenger for source */
202     private Messenger mSrcMessenger;
203 
204     /** Messenger for destination */
205     private Messenger mDstMessenger;
206 
207     /** Death Monitor for destination messenger */
208     private DeathMonitor mDeathMonitor;
209 
210     /**
211      * AsyncChannel constructor
212      */
213     @UnsupportedAppUsage
AsyncChannel()214     public AsyncChannel() {
215     }
216 
217     /**
218      * Connect handler to named package/class synchronously.
219      *
220      * @param srcContext is the context of the source
221      * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
222      *            messages
223      * @param dstPackageName is the destination package name
224      * @param dstClassName is the fully qualified class name (i.e. contains
225      *            package name)
226      *
227      * @return STATUS_SUCCESSFUL on success any other value is an error.
228      */
connectSrcHandlerToPackageSync( Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName)229     public int connectSrcHandlerToPackageSync(
230             Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName) {
231         if (DBG) log("connect srcHandler to dst Package & class E");
232 
233         mConnection = new AsyncChannelConnection();
234 
235         /* Initialize the source information */
236         mSrcContext = srcContext;
237         mSrcHandler = srcHandler;
238         mSrcMessenger = new Messenger(srcHandler);
239 
240         /*
241          * Initialize destination information to null they will
242          * be initialized when the AsyncChannelConnection#onServiceConnected
243          * is called
244          */
245         mDstMessenger = null;
246 
247         /* Send intent to create the connection */
248         Intent intent = new Intent(Intent.ACTION_MAIN);
249         intent.setClassName(dstPackageName, dstClassName);
250         boolean result = srcContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
251         if (DBG) log("connect srcHandler to dst Package & class X result=" + result);
252         return result ? STATUS_SUCCESSFUL : STATUS_BINDING_UNSUCCESSFUL;
253     }
254 
255     /**
256      * Connect a handler to Messenger synchronously.
257      *
258      * @param srcContext is the context of the source
259      * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
260      *            messages
261      * @param dstMessenger is the hander to send messages to.
262      *
263      * @return STATUS_SUCCESSFUL on success any other value is an error.
264      */
265     @UnsupportedAppUsage
connectSync(Context srcContext, Handler srcHandler, Messenger dstMessenger)266     public int connectSync(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
267         if (DBG) log("halfConnectSync srcHandler to the dstMessenger  E");
268 
269         // We are connected
270         connected(srcContext, srcHandler, dstMessenger);
271 
272         if (DBG) log("halfConnectSync srcHandler to the dstMessenger X");
273         return STATUS_SUCCESSFUL;
274     }
275 
276     /**
277      * connect two local Handlers synchronously.
278      *
279      * @param srcContext is the context of the source
280      * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
281      *            messages
282      * @param dstHandler is the hander to send messages to.
283      *
284      * @return STATUS_SUCCESSFUL on success any other value is an error.
285      */
connectSync(Context srcContext, Handler srcHandler, Handler dstHandler)286     public int connectSync(Context srcContext, Handler srcHandler, Handler dstHandler) {
287         return connectSync(srcContext, srcHandler, new Messenger(dstHandler));
288     }
289 
290     /**
291      * Fully connect two local Handlers synchronously.
292      *
293      * @param srcContext is the context of the source
294      * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
295      *            messages
296      * @param dstHandler is the hander to send messages to.
297      *
298      * @return STATUS_SUCCESSFUL on success any other value is an error.
299      */
fullyConnectSync(Context srcContext, Handler srcHandler, Handler dstHandler)300     public int fullyConnectSync(Context srcContext, Handler srcHandler, Handler dstHandler) {
301         int status = connectSync(srcContext, srcHandler, dstHandler);
302         if (status == STATUS_SUCCESSFUL) {
303             Message response = sendMessageSynchronously(CMD_CHANNEL_FULL_CONNECTION);
304             status = response.arg1;
305         }
306         return status;
307     }
308 
309     /**
310      * Connect handler to named package/class.
311      *
312      * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
313      *      msg.arg1 = status
314      *      msg.obj = the AsyncChannel
315      *
316      * @param srcContext is the context of the source
317      * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
318      *            messages
319      * @param dstPackageName is the destination package name
320      * @param dstClassName is the fully qualified class name (i.e. contains
321      *            package name)
322      */
connect(Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName)323     public void connect(Context srcContext, Handler srcHandler, String dstPackageName,
324             String dstClassName) {
325         if (DBG) log("connect srcHandler to dst Package & class E");
326 
327         final class ConnectAsync implements Runnable {
328             Context mSrcCtx;
329             Handler mSrcHdlr;
330             String mDstPackageName;
331             String mDstClassName;
332 
333             ConnectAsync(Context srcContext, Handler srcHandler, String dstPackageName,
334                     String dstClassName) {
335                 mSrcCtx = srcContext;
336                 mSrcHdlr = srcHandler;
337                 mDstPackageName = dstPackageName;
338                 mDstClassName = dstClassName;
339             }
340 
341             @Override
342             public void run() {
343                 int result = connectSrcHandlerToPackageSync(mSrcCtx, mSrcHdlr, mDstPackageName,
344                         mDstClassName);
345                 replyHalfConnected(result);
346             }
347         }
348 
349         ConnectAsync ca = new ConnectAsync(srcContext, srcHandler, dstPackageName, dstClassName);
350         new Thread(ca).start();
351 
352         if (DBG) log("connect srcHandler to dst Package & class X");
353     }
354 
355     /**
356      * Connect handler to a class
357      *
358      * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
359      *      msg.arg1 = status
360      *      msg.obj = the AsyncChannel
361      *
362      * @param srcContext
363      * @param srcHandler
364      * @param klass is the class to send messages to.
365      */
connect(Context srcContext, Handler srcHandler, Class<?> klass)366     public void connect(Context srcContext, Handler srcHandler, Class<?> klass) {
367         connect(srcContext, srcHandler, klass.getPackage().getName(), klass.getName());
368     }
369 
370     /**
371      * Connect handler and messenger.
372      *
373      * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
374      *      msg.arg1 = status
375      *      msg.obj = the AsyncChannel
376      *
377      * @param srcContext
378      * @param srcHandler
379      * @param dstMessenger
380      */
381     @UnsupportedAppUsage
connect(Context srcContext, Handler srcHandler, Messenger dstMessenger)382     public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
383         if (DBG) log("connect srcHandler to the dstMessenger  E");
384 
385         // We are connected
386         connected(srcContext, srcHandler, dstMessenger);
387 
388         // Tell source we are half connected
389         replyHalfConnected(STATUS_SUCCESSFUL);
390 
391         if (DBG) log("connect srcHandler to the dstMessenger X");
392     }
393 
394     /**
395      * Connect handler to messenger. This method is typically called
396      * when a server receives a CMD_CHANNEL_FULL_CONNECTION request
397      * and initializes the internal instance variables to allow communication
398      * with the dstMessenger.
399      *
400      * @param srcContext
401      * @param srcHandler
402      * @param dstMessenger
403      */
404     @UnsupportedAppUsage
connected(Context srcContext, Handler srcHandler, Messenger dstMessenger)405     public void connected(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
406         if (DBG) log("connected srcHandler to the dstMessenger  E");
407 
408         // Initialize source fields
409         mSrcContext = srcContext;
410         mSrcHandler = srcHandler;
411         mSrcMessenger = new Messenger(mSrcHandler);
412 
413         // Initialize destination fields
414         mDstMessenger = dstMessenger;
415         if (DBG) log("connected srcHandler to the dstMessenger X");
416     }
417 
418     /**
419      * Connect two local Handlers.
420      *
421      * @param srcContext is the context of the source
422      * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
423      *            messages
424      * @param dstHandler is the hander to send messages to.
425      */
connect(Context srcContext, Handler srcHandler, Handler dstHandler)426     public void connect(Context srcContext, Handler srcHandler, Handler dstHandler) {
427         connect(srcContext, srcHandler, new Messenger(dstHandler));
428     }
429 
430     /**
431      * Connect service and messenger.
432      *
433      * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcAsyncService when complete.
434      *      msg.arg1 = status
435      *      msg.obj = the AsyncChannel
436      *
437      * @param srcAsyncService
438      * @param dstMessenger
439      */
connect(AsyncService srcAsyncService, Messenger dstMessenger)440     public void connect(AsyncService srcAsyncService, Messenger dstMessenger) {
441         connect(srcAsyncService, srcAsyncService.getHandler(), dstMessenger);
442     }
443 
444     /**
445      * To close the connection call when handler receives CMD_CHANNEL_DISCONNECTED
446      */
disconnected()447     public void disconnected() {
448         mSrcContext = null;
449         mSrcHandler = null;
450         mSrcMessenger = null;
451         mDstMessenger = null;
452         mDeathMonitor = null;
453         mConnection = null;
454     }
455 
456     /**
457      * Disconnect
458      */
459     @UnsupportedAppUsage
disconnect()460     public void disconnect() {
461         if ((mConnection != null) && (mSrcContext != null)) {
462             mSrcContext.unbindService(mConnection);
463             mConnection = null;
464         }
465         try {
466             // Send the DISCONNECTED, although it may not be received
467             // but its the best we can do.
468             Message msg = Message.obtain();
469             msg.what = CMD_CHANNEL_DISCONNECTED;
470             msg.replyTo = mSrcMessenger;
471             mDstMessenger.send(msg);
472         } catch(Exception e) {
473         }
474         // Tell source we're disconnected.
475         replyDisconnected(STATUS_SUCCESSFUL);
476         mSrcHandler = null;
477         // Unlink only when bindService isn't used
478         if (mConnection == null && mDstMessenger != null && mDeathMonitor!= null) {
479             mDstMessenger.getBinder().unlinkToDeath(mDeathMonitor, 0);
480             mDeathMonitor = null;
481         }
482     }
483 
484     /**
485      * Send a message to the destination handler.
486      *
487      * @param msg
488      */
489     @UnsupportedAppUsage
sendMessage(Message msg)490     public void sendMessage(Message msg) {
491         msg.replyTo = mSrcMessenger;
492         try {
493             mDstMessenger.send(msg);
494         } catch (RemoteException e) {
495             replyDisconnected(STATUS_SEND_UNSUCCESSFUL);
496         }
497     }
498 
499     /**
500      * Send a message to the destination handler
501      *
502      * @param what
503      */
504     @UnsupportedAppUsage
sendMessage(int what)505     public void sendMessage(int what) {
506         Message msg = Message.obtain();
507         msg.what = what;
508         sendMessage(msg);
509     }
510 
511     /**
512      * Send a message to the destination handler
513      *
514      * @param what
515      * @param arg1
516      */
517     @UnsupportedAppUsage
sendMessage(int what, int arg1)518     public void sendMessage(int what, int arg1) {
519         Message msg = Message.obtain();
520         msg.what = what;
521         msg.arg1 = arg1;
522         sendMessage(msg);
523     }
524 
525     /**
526      * Send a message to the destination handler
527      *
528      * @param what
529      * @param arg1
530      * @param arg2
531      */
532     @UnsupportedAppUsage
sendMessage(int what, int arg1, int arg2)533     public void sendMessage(int what, int arg1, int arg2) {
534         Message msg = Message.obtain();
535         msg.what = what;
536         msg.arg1 = arg1;
537         msg.arg2 = arg2;
538         sendMessage(msg);
539     }
540 
541     /**
542      * Send a message to the destination handler
543      *
544      * @param what
545      * @param arg1
546      * @param arg2
547      * @param obj
548      */
549     @UnsupportedAppUsage
sendMessage(int what, int arg1, int arg2, Object obj)550     public void sendMessage(int what, int arg1, int arg2, Object obj) {
551         Message msg = Message.obtain();
552         msg.what = what;
553         msg.arg1 = arg1;
554         msg.arg2 = arg2;
555         msg.obj = obj;
556         sendMessage(msg);
557     }
558 
559     /**
560      * Send a message to the destination handler
561      *
562      * @param what
563      * @param obj
564      */
sendMessage(int what, Object obj)565     public void sendMessage(int what, Object obj) {
566         Message msg = Message.obtain();
567         msg.what = what;
568         msg.obj = obj;
569         sendMessage(msg);
570     }
571 
572     /**
573      * Reply to srcMsg sending dstMsg
574      *
575      * @param srcMsg
576      * @param dstMsg
577      */
578     @UnsupportedAppUsage
replyToMessage(Message srcMsg, Message dstMsg)579     public void replyToMessage(Message srcMsg, Message dstMsg) {
580         try {
581             dstMsg.replyTo = mSrcMessenger;
582             srcMsg.replyTo.send(dstMsg);
583         } catch (RemoteException e) {
584             log("TODO: handle replyToMessage RemoteException" + e);
585             e.printStackTrace();
586         }
587     }
588 
589     /**
590      * Reply to srcMsg
591      *
592      * @param srcMsg
593      * @param what
594      */
595     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
replyToMessage(Message srcMsg, int what)596     public void replyToMessage(Message srcMsg, int what) {
597         Message msg = Message.obtain();
598         msg.what = what;
599         replyToMessage(srcMsg, msg);
600     }
601 
602     /**
603      * Reply to srcMsg
604      *
605      * @param srcMsg
606      * @param what
607      * @param arg1
608      */
609     @UnsupportedAppUsage
replyToMessage(Message srcMsg, int what, int arg1)610     public void replyToMessage(Message srcMsg, int what, int arg1) {
611         Message msg = Message.obtain();
612         msg.what = what;
613         msg.arg1 = arg1;
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      */
replyToMessage(Message srcMsg, int what, int arg1, int arg2)625     public void replyToMessage(Message srcMsg, int what, int arg1, int arg2) {
626         Message msg = Message.obtain();
627         msg.what = what;
628         msg.arg1 = arg1;
629         msg.arg2 = arg2;
630         replyToMessage(srcMsg, msg);
631     }
632 
633     /**
634      * Reply to srcMsg
635      *
636      * @param srcMsg
637      * @param what
638      * @param arg1
639      * @param arg2
640      * @param obj
641      */
642     @UnsupportedAppUsage
replyToMessage(Message srcMsg, int what, int arg1, int arg2, Object obj)643     public void replyToMessage(Message srcMsg, int what, int arg1, int arg2, Object obj) {
644         Message msg = Message.obtain();
645         msg.what = what;
646         msg.arg1 = arg1;
647         msg.arg2 = arg2;
648         msg.obj = obj;
649         replyToMessage(srcMsg, msg);
650     }
651 
652     /**
653      * Reply to srcMsg
654      *
655      * @param srcMsg
656      * @param what
657      * @param obj
658      */
659     @UnsupportedAppUsage
replyToMessage(Message srcMsg, int what, Object obj)660     public void replyToMessage(Message srcMsg, int what, Object obj) {
661         Message msg = Message.obtain();
662         msg.what = what;
663         msg.obj = obj;
664         replyToMessage(srcMsg, msg);
665     }
666 
667     /**
668      * Send the Message synchronously.
669      *
670      * @param msg to send
671      * @return reply message or null if an error.
672      */
673     @UnsupportedAppUsage
sendMessageSynchronously(Message msg)674     public Message sendMessageSynchronously(Message msg) {
675         Message resultMsg = SyncMessenger.sendMessageSynchronously(mDstMessenger, msg);
676         return resultMsg;
677     }
678 
679     /**
680      * Send the Message synchronously.
681      *
682      * @param what
683      * @return reply message or null if an error.
684      */
sendMessageSynchronously(int what)685     public Message sendMessageSynchronously(int what) {
686         Message msg = Message.obtain();
687         msg.what = what;
688         Message resultMsg = sendMessageSynchronously(msg);
689         return resultMsg;
690     }
691 
692     /**
693      * Send the Message synchronously.
694      *
695      * @param what
696      * @param arg1
697      * @return reply message or null if an error.
698      */
sendMessageSynchronously(int what, int arg1)699     public Message sendMessageSynchronously(int what, int arg1) {
700         Message msg = Message.obtain();
701         msg.what = what;
702         msg.arg1 = arg1;
703         Message resultMsg = sendMessageSynchronously(msg);
704         return resultMsg;
705     }
706 
707     /**
708      * Send the Message synchronously.
709      *
710      * @param what
711      * @param arg1
712      * @param arg2
713      * @return reply message or null if an error.
714      */
715     @UnsupportedAppUsage
sendMessageSynchronously(int what, int arg1, int arg2)716     public Message sendMessageSynchronously(int what, int arg1, int arg2) {
717         Message msg = Message.obtain();
718         msg.what = what;
719         msg.arg1 = arg1;
720         msg.arg2 = arg2;
721         Message resultMsg = sendMessageSynchronously(msg);
722         return resultMsg;
723     }
724 
725     /**
726      * Send the Message synchronously.
727      *
728      * @param what
729      * @param arg1
730      * @param arg2
731      * @param obj
732      * @return reply message or null if an error.
733      */
sendMessageSynchronously(int what, int arg1, int arg2, Object obj)734     public Message sendMessageSynchronously(int what, int arg1, int arg2, Object obj) {
735         Message msg = Message.obtain();
736         msg.what = what;
737         msg.arg1 = arg1;
738         msg.arg2 = arg2;
739         msg.obj = obj;
740         Message resultMsg = sendMessageSynchronously(msg);
741         return resultMsg;
742     }
743 
744     /**
745      * Send the Message synchronously.
746      *
747      * @param what
748      * @param obj
749      * @return reply message or null if an error.
750      */
sendMessageSynchronously(int what, Object obj)751     public Message sendMessageSynchronously(int what, Object obj) {
752         Message msg = Message.obtain();
753         msg.what = what;
754         msg.obj = obj;
755         Message resultMsg = sendMessageSynchronously(msg);
756         return resultMsg;
757     }
758 
759     /**
760      * Helper class to send messages synchronously
761      */
762     private static class SyncMessenger {
763         /** A stack of SyncMessengers */
764         private static Stack<SyncMessenger> sStack = new Stack<SyncMessenger>();
765         /** A number of SyncMessengers created */
766         private static int sCount = 0;
767         /** The handler thread */
768         private HandlerThread mHandlerThread;
769         /** The handler that will receive the result */
770         private SyncHandler mHandler;
771         /** The messenger used to send the message */
772         private Messenger mMessenger;
773 
774         /** private constructor */
SyncMessenger()775         private SyncMessenger() {
776         }
777 
778         /** Synchronous Handler class */
779         private class SyncHandler extends Handler {
780             /** The object used to wait/notify */
781             private Object mLockObject = new Object();
782             /** The resulting message */
783             private Message mResultMsg;
784 
785             /** Constructor */
SyncHandler(Looper looper)786             private SyncHandler(Looper looper) {
787                 super(looper);
788             }
789 
790             /** Handle of the reply message */
791             @Override
handleMessage(Message msg)792             public void handleMessage(Message msg) {
793                 Message msgCopy = Message.obtain();
794                 msgCopy.copyFrom(msg);
795                 synchronized(mLockObject) {
796                     mResultMsg = msgCopy;
797                     mLockObject.notify();
798                 }
799             }
800         }
801 
802         /**
803          * @return the SyncMessenger
804          */
obtain()805         private static SyncMessenger obtain() {
806             SyncMessenger sm;
807             synchronized (sStack) {
808                 if (sStack.isEmpty()) {
809                     sm = new SyncMessenger();
810                     sm.mHandlerThread = new HandlerThread("SyncHandler-" + sCount++);
811                     sm.mHandlerThread.start();
812                     sm.mHandler = sm.new SyncHandler(sm.mHandlerThread.getLooper());
813                     sm.mMessenger = new Messenger(sm.mHandler);
814                 } else {
815                     sm = sStack.pop();
816                 }
817             }
818             return sm;
819         }
820 
821         /**
822          * Recycle this object
823          */
recycle()824         private void recycle() {
825             synchronized (sStack) {
826                 sStack.push(this);
827             }
828         }
829 
830         /**
831          * Send a message synchronously.
832          *
833          * @param msg to send
834          * @return result message or null if an error occurs
835          */
sendMessageSynchronously(Messenger dstMessenger, Message msg)836         private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) {
837             SyncMessenger sm = SyncMessenger.obtain();
838             Message resultMsg = null;
839             try {
840                 if (dstMessenger != null && msg != null) {
841                     msg.replyTo = sm.mMessenger;
842                     synchronized (sm.mHandler.mLockObject) {
843                         if (sm.mHandler.mResultMsg != null) {
844                             Log.wtf(TAG, "mResultMsg should be null here");
845                             sm.mHandler.mResultMsg = null;
846                         }
847                         dstMessenger.send(msg);
848                         sm.mHandler.mLockObject.wait();
849                         resultMsg = sm.mHandler.mResultMsg;
850                         sm.mHandler.mResultMsg = null;
851                     }
852                 }
853             } catch (InterruptedException e) {
854                 Log.e(TAG, "error in sendMessageSynchronously", e);
855             } catch (RemoteException e) {
856                 Log.e(TAG, "error in sendMessageSynchronously", e);
857             }
858             sm.recycle();
859             return resultMsg;
860         }
861     }
862 
863     /**
864      * Reply to the src handler that we're half connected.
865      * see: CMD_CHANNEL_HALF_CONNECTED for message contents
866      *
867      * @param status to be stored in msg.arg1
868      */
replyHalfConnected(int status)869     private void replyHalfConnected(int status) {
870         Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED);
871         msg.arg1 = status;
872         msg.obj = this;
873         msg.replyTo = mDstMessenger;
874         if (!linkToDeathMonitor()) {
875             // Override status to indicate failure
876             msg.arg1 = STATUS_BINDING_UNSUCCESSFUL;
877         }
878 
879         mSrcHandler.sendMessage(msg);
880     }
881 
882     /**
883      * Link to death monitor for destination messenger. Returns true if successfully binded to
884      * destination messenger; false otherwise.
885      */
linkToDeathMonitor()886     private boolean linkToDeathMonitor() {
887         // Link to death only when bindService isn't used and not already linked.
888         if (mConnection == null && mDeathMonitor == null) {
889             mDeathMonitor = new DeathMonitor();
890             try {
891                 mDstMessenger.getBinder().linkToDeath(mDeathMonitor, 0);
892             } catch (RemoteException e) {
893                 mDeathMonitor = null;
894                 return false;
895             }
896         }
897         return true;
898     }
899 
900     /**
901      * Reply to the src handler that we are disconnected
902      * see: CMD_CHANNEL_DISCONNECTED for message contents
903      *
904      * @param status to be stored in msg.arg1
905      */
replyDisconnected(int status)906     private void replyDisconnected(int status) {
907         // Can't reply if already disconnected. Avoid NullPointerException.
908         if (mSrcHandler == null) return;
909         Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
910         msg.arg1 = status;
911         msg.obj = this;
912         msg.replyTo = mDstMessenger;
913         mSrcHandler.sendMessage(msg);
914     }
915 
916 
917     /**
918      * ServiceConnection to receive call backs.
919      */
920     class AsyncChannelConnection implements ServiceConnection {
AsyncChannelConnection()921         AsyncChannelConnection() {
922         }
923 
924         @Override
onServiceConnected(ComponentName className, IBinder service)925         public void onServiceConnected(ComponentName className, IBinder service) {
926             mDstMessenger = new Messenger(service);
927             replyHalfConnected(STATUS_SUCCESSFUL);
928         }
929 
930         @Override
onServiceDisconnected(ComponentName className)931         public void onServiceDisconnected(ComponentName className) {
932             replyDisconnected(STATUS_SUCCESSFUL);
933         }
934     }
935 
936     /**
937      * Log the string.
938      *
939      * @param s
940      */
log(String s)941     private static void log(String s) {
942         Log.d(TAG, s);
943     }
944 
945     private final class DeathMonitor implements IBinder.DeathRecipient {
946 
DeathMonitor()947         DeathMonitor() {
948         }
949 
binderDied()950         public void binderDied() {
951             replyDisconnected(STATUS_REMOTE_DISCONNECTION);
952         }
953 
954     }
955 }
956