• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.util;
18 
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.ServiceConnection;
23 import android.os.Handler;
24 import android.os.HandlerThread;
25 import android.os.IBinder;
26 import android.os.Looper;
27 import android.os.Message;
28 import android.os.Messenger;
29 import android.os.RemoteException;
30 import android.util.Slog;
31 
32 import java.util.Stack;
33 
34 /**
35  * <p>An asynchronous channel between two handlers.</p>
36  *
37  * <p>The handlers maybe in the same process or in another process. There
38  * are two protocol styles that can be used with an AysncChannel. The
39  * first is a simple request/reply protocol where the server does
40  * not need to know which client is issuing the request.</p>
41  *
42  * <p>In a simple request/reply protocol the client/source sends requests to the
43  * server/destination. And the server uses the replyToMessage methods.
44  * In this usage model there is no need for the destination to
45  * use the connect methods. The typical sequence of operations is:</p>
46  *<ol>
47  *   <li>Client calls AsyncChannel#connectSync or Asynchronously:</li>
48  *      <ol>For an asynchronous half connection client calls AsyncChannel#connect.</ol>
49  *          <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li>
50  *      </ol>
51  *   <li><code>comm-loop:</code></li>
52  *   <li>Client calls AsyncChannel#sendMessage</li>
53  *   <li>Server processes messages and optionally replies using AsyncChannel#replyToMessage
54  *   <li>Loop to <code>comm-loop</code> until done</li>
55  *   <li>When done Client calls {@link AsyncChannel#disconnect}</li>
56  *   <li>Client/Server receives CMD_CHANNEL_DISCONNECTED from AsyncChannel</li>
57  *</ol>
58  *<br/>
59  * <p>A second usage model is where the server/destination needs to know
60  * which client it's connected too. For example the server needs to
61  * send unsolicited messages back to the client. Or the server keeps
62  * different state for each client. In this model the server will also
63  * use the connect methods. The typical sequence of operation is:</p>
64  *<ol>
65  *   <li>Client calls AsyncChannel#fullyConnectSync or Asynchronously:<li>
66  *      <ol>For an asynchronous full connection it calls AsyncChannel#connect</li>
67  *          <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li>
68  *          <li>Client calls AsyncChannel#sendMessage(CMD_CHANNEL_FULL_CONNECTION)</li>
69  *      </ol>
70  *   <li>Server receives CMD_CHANNEL_FULL_CONNECTION</li>
71  *   <li>Server calls AsyncChannel#connected</li>
72  *   <li>Server sends AsyncChannel#sendMessage(CMD_CHANNEL_FULLY_CONNECTED)</li>
73  *   <li>Client receives CMD_CHANNEL_FULLY_CONNECTED</li>
74  *   <li><code>comm-loop:</code></li>
75  *   <li>Client/Server uses AsyncChannel#sendMessage/replyToMessage
76  *       to communicate and perform work</li>
77  *   <li>Loop to <code>comm-loop</code> until done</li>
78  *   <li>When done Client/Server calls {@link AsyncChannel#disconnect}</li>
79  *   <li>Client/Server receives CMD_CHANNEL_DISCONNECTED from AsyncChannel</li>
80  *</ol>
81  *
82  * TODO: Consider simplifying where we have connect and fullyConnect with only one response
83  * message RSP_CHANNEL_CONNECT instead of two, CMD_CHANNEL_HALF_CONNECTED and
84  * CMD_CHANNEL_FULLY_CONNECTED. We'd also change CMD_CHANNEL_FULL_CONNECTION to REQ_CHANNEL_CONNECT.
85  */
86 public class AsyncChannel {
87     /** Log tag */
88     private static final String TAG = "AsyncChannel";
89 
90     /** Enable to turn on debugging */
91     private static final boolean DBG = false;
92 
93     private static final int BASE = Protocol.BASE_SYSTEM_ASYNC_CHANNEL;
94 
95     /**
96      * Command sent when the channel is half connected. Half connected
97      * means that the channel can be used to send commends to the destination
98      * but the destination is unaware that the channel exists. The first
99      * command sent to the destination is typically CMD_CHANNEL_FULL_CONNECTION if
100      * it is desired to establish a long term connection, but any command maybe
101      * sent.
102      *
103      * msg.arg1 == 0 : STATUS_SUCCESSFUL
104      *             1 : STATUS_BINDING_UNSUCCESSFUL
105      * msg.obj  == the AsyncChannel
106      * msg.replyTo == dstMessenger if successful
107      */
108     public static final int CMD_CHANNEL_HALF_CONNECTED = BASE + 0;
109 
110     /**
111      * Command typically sent when after receiving the CMD_CHANNEL_HALF_CONNECTED.
112      * This is used to initiate a long term connection with the destination and
113      * typically the destination will reply with CMD_CHANNEL_FULLY_CONNECTED.
114      *
115      * msg.replyTo = srcMessenger.
116      */
117     public static final int CMD_CHANNEL_FULL_CONNECTION = BASE + 1;
118 
119     /**
120      * Command typically sent after the destination receives a CMD_CHANNEL_FULL_CONNECTION.
121      * This signifies the acceptance or rejection of the channel by the sender.
122      *
123      * msg.arg1 == 0 : Accept connection
124      *               : All other values signify the destination rejected the connection
125      *                 and {@link AsyncChannel#disconnect} would typically be called.
126      */
127     public static final int CMD_CHANNEL_FULLY_CONNECTED = BASE + 2;
128 
129     /**
130      * Command sent when one side or the other wishes to disconnect. The sender
131      * may or may not be able to receive a reply depending upon the protocol and
132      * the state of the connection. The receiver should call {@link AsyncChannel#disconnect}
133      * to close its side of the channel and it will receive a CMD_CHANNEL_DISCONNECTED
134      * when the channel is closed.
135      *
136      * msg.replyTo = messenger that is disconnecting
137      */
138     public static final int CMD_CHANNEL_DISCONNECT = BASE + 3;
139 
140     /**
141      * Command sent when the channel becomes disconnected. This is sent when the
142      * channel is forcibly disconnected by the system or as a reply to CMD_CHANNEL_DISCONNECT.
143      *
144      * msg.arg1 == 0 : STATUS_SUCCESSFUL
145      *             1 : STATUS_BINDING_UNSUCCESSFUL
146      *             2 : STATUS_SEND_UNSUCCESSFUL
147      *               : All other values signify failure and the channel state is indeterminate
148      * msg.obj  == the AsyncChannel
149      * msg.replyTo = messenger disconnecting or null if it was never connected.
150      */
151     public static final int CMD_CHANNEL_DISCONNECTED = BASE + 4;
152 
153     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         if (DBG) log("connected srcHandler to the dstMessenger X");
406     }
407 
408     /**
409      * Connect two local Handlers.
410      *
411      * @param srcContext is the context of the source
412      * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
413      *            messages
414      * @param dstHandler is the hander to send messages to.
415      */
connect(Context srcContext, Handler srcHandler, Handler dstHandler)416     public void connect(Context srcContext, Handler srcHandler, Handler dstHandler) {
417         connect(srcContext, srcHandler, new Messenger(dstHandler));
418     }
419 
420     /**
421      * Connect service and messenger.
422      *
423      * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcAsyncService when complete.
424      *      msg.arg1 = status
425      *      msg.obj = the AsyncChannel
426      *
427      * @param srcAsyncService
428      * @param dstMessenger
429      */
connect(AsyncService srcAsyncService, Messenger dstMessenger)430     public void connect(AsyncService srcAsyncService, Messenger dstMessenger) {
431         connect(srcAsyncService, srcAsyncService.getHandler(), dstMessenger);
432     }
433 
434     /**
435      * To close the connection call when handler receives CMD_CHANNEL_DISCONNECTED
436      */
disconnected()437     public void disconnected() {
438         mSrcContext = null;
439         mSrcHandler = null;
440         mSrcMessenger = null;
441         mDstMessenger = null;
442         mDeathMonitor = null;
443         mConnection = null;
444     }
445 
446     /**
447      * Disconnect
448      */
disconnect()449     public void disconnect() {
450         if ((mConnection != null) && (mSrcContext != null)) {
451             mSrcContext.unbindService(mConnection);
452             mConnection = null;
453         }
454         try {
455             // Send the DISCONNECTED, although it may not be received
456             // but its the best we can do.
457             Message msg = Message.obtain();
458             msg.what = CMD_CHANNEL_DISCONNECTED;
459             msg.replyTo = mSrcMessenger;
460             mDstMessenger.send(msg);
461         } catch(Exception e) {
462         }
463         // Tell source we're disconnected.
464         replyDisconnected(STATUS_SUCCESSFUL);
465         mSrcHandler = null;
466         // Unlink only when bindService isn't used
467         if (mConnection == null && mDstMessenger != null && mDeathMonitor!= null) {
468             mDstMessenger.getBinder().unlinkToDeath(mDeathMonitor, 0);
469             mDeathMonitor = null;
470         }
471     }
472 
473     /**
474      * Send a message to the destination handler.
475      *
476      * @param msg
477      */
sendMessage(Message msg)478     public void sendMessage(Message msg) {
479         msg.replyTo = mSrcMessenger;
480         try {
481             mDstMessenger.send(msg);
482         } catch (RemoteException e) {
483             replyDisconnected(STATUS_SEND_UNSUCCESSFUL);
484         }
485     }
486 
487     /**
488      * Send a message to the destination handler
489      *
490      * @param what
491      */
sendMessage(int what)492     public void sendMessage(int what) {
493         Message msg = Message.obtain();
494         msg.what = what;
495         sendMessage(msg);
496     }
497 
498     /**
499      * Send a message to the destination handler
500      *
501      * @param what
502      * @param arg1
503      */
sendMessage(int what, int arg1)504     public void sendMessage(int what, int arg1) {
505         Message msg = Message.obtain();
506         msg.what = what;
507         msg.arg1 = arg1;
508         sendMessage(msg);
509     }
510 
511     /**
512      * Send a message to the destination handler
513      *
514      * @param what
515      * @param arg1
516      * @param arg2
517      */
sendMessage(int what, int arg1, int arg2)518     public void sendMessage(int what, int arg1, int arg2) {
519         Message msg = Message.obtain();
520         msg.what = what;
521         msg.arg1 = arg1;
522         msg.arg2 = arg2;
523         sendMessage(msg);
524     }
525 
526     /**
527      * Send a message to the destination handler
528      *
529      * @param what
530      * @param arg1
531      * @param arg2
532      * @param obj
533      */
sendMessage(int what, int arg1, int arg2, Object obj)534     public void sendMessage(int what, int arg1, int arg2, Object obj) {
535         Message msg = Message.obtain();
536         msg.what = what;
537         msg.arg1 = arg1;
538         msg.arg2 = arg2;
539         msg.obj = obj;
540         sendMessage(msg);
541     }
542 
543     /**
544      * Send a message to the destination handler
545      *
546      * @param what
547      * @param obj
548      */
sendMessage(int what, Object obj)549     public void sendMessage(int what, Object obj) {
550         Message msg = Message.obtain();
551         msg.what = what;
552         msg.obj = obj;
553         sendMessage(msg);
554     }
555 
556     /**
557      * Reply to srcMsg sending dstMsg
558      *
559      * @param srcMsg
560      * @param dstMsg
561      */
replyToMessage(Message srcMsg, Message dstMsg)562     public void replyToMessage(Message srcMsg, Message dstMsg) {
563         try {
564             dstMsg.replyTo = mSrcMessenger;
565             srcMsg.replyTo.send(dstMsg);
566         } catch (RemoteException e) {
567             log("TODO: handle replyToMessage RemoteException" + e);
568             e.printStackTrace();
569         }
570     }
571 
572     /**
573      * Reply to srcMsg
574      *
575      * @param srcMsg
576      * @param what
577      */
replyToMessage(Message srcMsg, int what)578     public void replyToMessage(Message srcMsg, int what) {
579         Message msg = Message.obtain();
580         msg.what = what;
581         replyToMessage(srcMsg, msg);
582     }
583 
584     /**
585      * Reply to srcMsg
586      *
587      * @param srcMsg
588      * @param what
589      * @param arg1
590      */
replyToMessage(Message srcMsg, int what, int arg1)591     public void replyToMessage(Message srcMsg, int what, int arg1) {
592         Message msg = Message.obtain();
593         msg.what = what;
594         msg.arg1 = arg1;
595         replyToMessage(srcMsg, msg);
596     }
597 
598     /**
599      * Reply to srcMsg
600      *
601      * @param srcMsg
602      * @param what
603      * @param arg1
604      * @param arg2
605      */
replyToMessage(Message srcMsg, int what, int arg1, int arg2)606     public void replyToMessage(Message srcMsg, int what, int arg1, int arg2) {
607         Message msg = Message.obtain();
608         msg.what = what;
609         msg.arg1 = arg1;
610         msg.arg2 = arg2;
611         replyToMessage(srcMsg, msg);
612     }
613 
614     /**
615      * Reply to srcMsg
616      *
617      * @param srcMsg
618      * @param what
619      * @param arg1
620      * @param arg2
621      * @param obj
622      */
replyToMessage(Message srcMsg, int what, int arg1, int arg2, Object obj)623     public void replyToMessage(Message srcMsg, int what, int arg1, int arg2, Object obj) {
624         Message msg = Message.obtain();
625         msg.what = what;
626         msg.arg1 = arg1;
627         msg.arg2 = arg2;
628         msg.obj = obj;
629         replyToMessage(srcMsg, msg);
630     }
631 
632     /**
633      * Reply to srcMsg
634      *
635      * @param srcMsg
636      * @param what
637      * @param obj
638      */
replyToMessage(Message srcMsg, int what, Object obj)639     public void replyToMessage(Message srcMsg, int what, Object obj) {
640         Message msg = Message.obtain();
641         msg.what = what;
642         msg.obj = obj;
643         replyToMessage(srcMsg, msg);
644     }
645 
646     /**
647      * Send the Message synchronously.
648      *
649      * @param msg to send
650      * @return reply message or null if an error.
651      */
sendMessageSynchronously(Message msg)652     public Message sendMessageSynchronously(Message msg) {
653         Message resultMsg = SyncMessenger.sendMessageSynchronously(mDstMessenger, msg);
654         return resultMsg;
655     }
656 
657     /**
658      * Send the Message synchronously.
659      *
660      * @param what
661      * @return reply message or null if an error.
662      */
sendMessageSynchronously(int what)663     public Message sendMessageSynchronously(int what) {
664         Message msg = Message.obtain();
665         msg.what = what;
666         Message resultMsg = sendMessageSynchronously(msg);
667         return resultMsg;
668     }
669 
670     /**
671      * Send the Message synchronously.
672      *
673      * @param what
674      * @param arg1
675      * @return reply message or null if an error.
676      */
sendMessageSynchronously(int what, int arg1)677     public Message sendMessageSynchronously(int what, int arg1) {
678         Message msg = Message.obtain();
679         msg.what = what;
680         msg.arg1 = arg1;
681         Message resultMsg = sendMessageSynchronously(msg);
682         return resultMsg;
683     }
684 
685     /**
686      * Send the Message synchronously.
687      *
688      * @param what
689      * @param arg1
690      * @param arg2
691      * @return reply message or null if an error.
692      */
sendMessageSynchronously(int what, int arg1, int arg2)693     public Message sendMessageSynchronously(int what, int arg1, int arg2) {
694         Message msg = Message.obtain();
695         msg.what = what;
696         msg.arg1 = arg1;
697         msg.arg2 = arg2;
698         Message resultMsg = sendMessageSynchronously(msg);
699         return resultMsg;
700     }
701 
702     /**
703      * Send the Message synchronously.
704      *
705      * @param what
706      * @param arg1
707      * @param arg2
708      * @param obj
709      * @return reply message or null if an error.
710      */
sendMessageSynchronously(int what, int arg1, int arg2, Object obj)711     public Message sendMessageSynchronously(int what, int arg1, int arg2, Object obj) {
712         Message msg = Message.obtain();
713         msg.what = what;
714         msg.arg1 = arg1;
715         msg.arg2 = arg2;
716         msg.obj = obj;
717         Message resultMsg = sendMessageSynchronously(msg);
718         return resultMsg;
719     }
720 
721     /**
722      * Send the Message synchronously.
723      *
724      * @param what
725      * @param obj
726      * @return reply message or null if an error.
727      */
sendMessageSynchronously(int what, Object obj)728     public Message sendMessageSynchronously(int what, Object obj) {
729         Message msg = Message.obtain();
730         msg.what = what;
731         msg.obj = obj;
732         Message resultMsg = sendMessageSynchronously(msg);
733         return resultMsg;
734     }
735 
736     /**
737      * Helper class to send messages synchronously
738      */
739     private static class SyncMessenger {
740         /** A stack of SyncMessengers */
741         private static Stack<SyncMessenger> sStack = new Stack<SyncMessenger>();
742         /** A number of SyncMessengers created */
743         private static int sCount = 0;
744         /** The handler thread */
745         private HandlerThread mHandlerThread;
746         /** The handler that will receive the result */
747         private SyncHandler mHandler;
748         /** The messenger used to send the message */
749         private Messenger mMessenger;
750 
751         /** private constructor */
SyncMessenger()752         private SyncMessenger() {
753         }
754 
755         /** Synchronous Handler class */
756         private class SyncHandler extends Handler {
757             /** The object used to wait/notify */
758             private Object mLockObject = new Object();
759             /** The resulting message */
760             private Message mResultMsg;
761 
762             /** Constructor */
SyncHandler(Looper looper)763             private SyncHandler(Looper looper) {
764                 super(looper);
765             }
766 
767             /** Handle of the reply message */
768             @Override
handleMessage(Message msg)769             public void handleMessage(Message msg) {
770                 Message msgCopy = Message.obtain();
771                 msgCopy.copyFrom(msg);
772                 synchronized(mLockObject) {
773                     mResultMsg = msgCopy;
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             Message resultMsg = null;
816             try {
817                 if (dstMessenger != null && msg != null) {
818                     msg.replyTo = sm.mMessenger;
819                     synchronized (sm.mHandler.mLockObject) {
820                         if (sm.mHandler.mResultMsg != null) {
821                             Slog.wtf(TAG, "mResultMsg should be null here");
822                             sm.mHandler.mResultMsg = null;
823                         }
824                         dstMessenger.send(msg);
825                         sm.mHandler.mLockObject.wait();
826                         resultMsg = sm.mHandler.mResultMsg;
827                         sm.mHandler.mResultMsg = null;
828                     }
829                 }
830             } catch (InterruptedException e) {
831                 Slog.e(TAG, "error in sendMessageSynchronously", e);
832             } catch (RemoteException e) {
833                 Slog.e(TAG, "error in sendMessageSynchronously", e);
834             }
835             sm.recycle();
836             return resultMsg;
837         }
838     }
839 
840     /**
841      * Reply to the src handler that we're half connected.
842      * see: CMD_CHANNEL_HALF_CONNECTED for message contents
843      *
844      * @param status to be stored in msg.arg1
845      */
replyHalfConnected(int status)846     private void replyHalfConnected(int status) {
847         Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED);
848         msg.arg1 = status;
849         msg.obj = this;
850         msg.replyTo = mDstMessenger;
851         if (!linkToDeathMonitor()) {
852             // Override status to indicate failure
853             msg.arg1 = STATUS_BINDING_UNSUCCESSFUL;
854         }
855 
856         mSrcHandler.sendMessage(msg);
857     }
858 
859     /**
860      * Link to death monitor for destination messenger. Returns true if successfully binded to
861      * destination messenger; false otherwise.
862      */
linkToDeathMonitor()863     private boolean linkToDeathMonitor() {
864         // Link to death only when bindService isn't used and not already linked.
865         if (mConnection == null && mDeathMonitor == null) {
866             mDeathMonitor = new DeathMonitor();
867             try {
868                 mDstMessenger.getBinder().linkToDeath(mDeathMonitor, 0);
869             } catch (RemoteException e) {
870                 mDeathMonitor = null;
871                 return false;
872             }
873         }
874         return true;
875     }
876 
877     /**
878      * Reply to the src handler that we are disconnected
879      * see: CMD_CHANNEL_DISCONNECTED for message contents
880      *
881      * @param status to be stored in msg.arg1
882      */
replyDisconnected(int status)883     private void replyDisconnected(int status) {
884         // Can't reply if already disconnected. Avoid NullPointerException.
885         if (mSrcHandler == null) return;
886         Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
887         msg.arg1 = status;
888         msg.obj = this;
889         msg.replyTo = mDstMessenger;
890         mSrcHandler.sendMessage(msg);
891     }
892 
893 
894     /**
895      * ServiceConnection to receive call backs.
896      */
897     class AsyncChannelConnection implements ServiceConnection {
AsyncChannelConnection()898         AsyncChannelConnection() {
899         }
900 
901         @Override
onServiceConnected(ComponentName className, IBinder service)902         public void onServiceConnected(ComponentName className, IBinder service) {
903             mDstMessenger = new Messenger(service);
904             replyHalfConnected(STATUS_SUCCESSFUL);
905         }
906 
907         @Override
onServiceDisconnected(ComponentName className)908         public void onServiceDisconnected(ComponentName className) {
909             replyDisconnected(STATUS_SUCCESSFUL);
910         }
911     }
912 
913     /**
914      * Log the string.
915      *
916      * @param s
917      */
log(String s)918     private static void log(String s) {
919         Slog.d(TAG, s);
920     }
921 
922     private final class DeathMonitor implements IBinder.DeathRecipient {
923 
DeathMonitor()924         DeathMonitor() {
925         }
926 
binderDied()927         public void binderDied() {
928             replyDisconnected(STATUS_REMOTE_DISCONNECTION);
929         }
930 
931     }
932 }
933