AsyncChannel.java revision 33c54e3365d621fcc5b9f7564f18b33dc1e300df
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
17package com.android.internal.util;
18
19import android.content.ComponentName;
20import android.content.Context;
21import android.content.Intent;
22import android.content.ServiceConnection;
23import android.os.Handler;
24import android.os.HandlerThread;
25import android.os.IBinder;
26import android.os.Looper;
27import android.os.Message;
28import android.os.Messenger;
29import android.os.RemoteException;
30import android.util.Slog;
31
32import 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#connect</li>
48 *   <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li>
49 *   <li><code>comm-loop:</code></li>
50 *   <li>Client calls AsyncChannel#sendMessage(msgX)</li>
51 *   <li>Server receives and processes msgX</li>
52 *   <li>Server optionally calls AsyncChannel#replyToMessage(msgY)
53 *       and if sent Client receives and processes msgY</li>
54 *   <li>Loop to <code>comm-loop</code> until done</li>
55 *   <li>When done Client calls {@link AsyncChannel#disconnect(int)}</li>
56 *   <li>Client 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#connect</li>
66 *   <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li>
67 *   <li>Client calls AsyncChannel#sendMessage(CMD_CHANNEL_FULL_CONNECTION)</li>
68 *   <li>Server receives CMD_CHANNEL_FULL_CONNECTION</li>
69 *   <li>Server calls AsyncChannel#connect</li>
70 *   <li>Server receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li>
71 *   <li>Server sends AsyncChannel#sendMessage(CMD_CHANNEL_FULLY_CONNECTED)</li>
72 *   <li>Client receives CMD_CHANNEL_FULLY_CONNECTED</li>
73 *   <li><code>comm-loop:</code></li>
74 *   <li>Client/Server uses AsyncChannel#sendMessage/replyToMessage
75 *       to communicate and perform work</li>
76 *   <li>Loop to <code>comm-loop</code> until done</li>
77 *   <li>When done Client/Server calls {@link AsyncChannel#disconnect(int)}</li>
78 *   <li>Client/Server receives CMD_CHANNEL_DISCONNECTED from AsyncChannel</li>
79 *</ol>
80 */
81public class AsyncChannel {
82    /** Log tag */
83    private static final String TAG = "AsyncChannel";
84
85    /** Enable to turn on debugging */
86    private static final boolean DBG = false;
87
88    /**
89     * Command sent when the channel is half connected. Half connected
90     * means that the channel can be used to send commends to the destination
91     * but the destination is unaware that the channel exists. The first
92     * command sent to the destination is typically CMD_CHANNEL_FULL_CONNECTION if
93     * it is desired to establish a long term connection, but any command maybe
94     * sent.
95     *
96     * msg.arg1 == 0 : STATUS_SUCCESSFUL
97     *             1 : STATUS_BINDING_UNSUCCESSFUL
98     * msg.arg2 == token parameter
99     * msg.replyTo == dstMessenger if successful
100     */
101    public static final int CMD_CHANNEL_HALF_CONNECTED = -1;
102
103    /**
104     * Command typically sent when after receiving the CMD_CHANNEL_HALF_CONNECTED.
105     * This is used to initiate a long term connection with the destination and
106     * typically the destination will reply with CMD_CHANNEL_FULLY_CONNECTED.
107     *
108     * msg.replyTo = srcMessenger.
109     */
110    public static final int CMD_CHANNEL_FULL_CONNECTION = -2;
111
112    /**
113     * Command typically sent after the destination receives a CMD_CHANNEL_FULL_CONNECTION.
114     * This signifies the acceptance or rejection of the channel by the sender.
115     *
116     * msg.arg1 == 0 : Accept connection
117     *               : All other values signify the destination rejected the connection
118     *                 and {@link AsyncChannel#disconnect(int)} would typically be called.
119     */
120    public static final int CMD_CHANNEL_FULLY_CONNECTED = -3;
121
122    /**
123     * Command sent when one side or the other wishes to disconnect. The sender
124     * may or may not be able to receive a reply depending upon the protocol and
125     * the state of the connection. The receiver should call {@link AsyncChannel#disconnect(int)}
126     * to close its side of the channel and it will receive a CMD_CHANNEL_DISCONNECTED
127     * when the channel is closed.
128     *
129     * msg.replyTo = messenger that is disconnecting
130     */
131    public static final int CMD_CHANNEL_DISCONNECT = -4;
132
133    /**
134     * Command sent when the channel becomes disconnected. This is sent when the
135     * channel is forcibly disconnected by the system or as a reply to CMD_CHANNEL_DISCONNECT.
136     *
137     * msg.arg1 == 0 : STATUS_SUCCESSFUL
138     *               : All other values signify failure and the channel state is indeterminate
139     * msg.arg2 == token parameter
140     * msg.replyTo = messenger disconnecting or null if it was never connected.
141     */
142    public static final int CMD_CHANNEL_DISCONNECTED = -5;
143
144    /** Successful status always 0, !0 is an unsuccessful status */
145    public static final int STATUS_SUCCESSFUL = 0;
146
147    /** Error attempting to bind on a connect */
148    public static final int STATUS_BINDING_UNSUCCESSFUL = 1;
149
150    /** Service connection */
151    private AsyncChannelConnection mConnection;
152
153    /** Context for source */
154    private Context mSrcContext;
155
156    /** Handler for source */
157    private Handler mSrcHandler;
158
159    /** Messenger for source */
160    private Messenger mSrcMessenger;
161
162    /** Messenger for destination */
163    private Messenger mDstMessenger;
164
165    /**
166     * AsyncChannel constructor
167     */
168    public AsyncChannel() {
169    }
170
171    /**
172     * Connect handler to named package/class.
173     *
174     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
175     *
176     * @param srcContext is the context of the source
177     * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
178     *            messages
179     * @param dstPackageName is the destination package name
180     * @param dstClassName is the fully qualified class name (i.e. contains
181     *            package name)
182     * @param token unique id for this connection
183     */
184    private void connectSrcHandlerToPackage(
185            Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName,
186            int token) {
187        if (DBG) log("connect srcHandler to dst Package & class E");
188
189        mConnection = new AsyncChannelConnection(token);
190
191        /* Initialize the source information */
192        mSrcContext = srcContext;
193        mSrcHandler = srcHandler;
194        mSrcMessenger = new Messenger(srcHandler);
195
196        /*
197         * Initialize destination information to null they will
198         * be initialized when the AsyncChannelConnection#onServiceConnected
199         * is called
200         */
201        mDstMessenger = null;
202
203        /* Send intent to create the connection */
204        Intent intent = new Intent(Intent.ACTION_MAIN);
205        intent.setClassName(dstPackageName, dstClassName);
206        boolean result = srcContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
207        if (!result) {
208            replyHalfConnected(STATUS_BINDING_UNSUCCESSFUL, token);
209        }
210
211        if (DBG) log("connect srcHandler to dst Package & class X result=" + result);
212    }
213
214    /**
215     * Connect handler to named package/class.
216     *
217     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
218     *      msg.arg1 = status
219     *      msg.arg2 = token
220     *
221     * @param srcContext is the context of the source
222     * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
223     *            messages
224     * @param dstPackageName is the destination package name
225     * @param dstClassName is the fully qualified class name (i.e. contains
226     *            package name)
227     * @param token returned in msg.arg2
228     */
229    public void connect(Context srcContext, Handler srcHandler, String dstPackageName,
230            String dstClassName, int token) {
231        if (DBG) log("connect srcHandler to dst Package & class E");
232
233        final class ConnectAsync implements Runnable {
234            Context mSrcCtx;
235            Handler mSrcHdlr;
236            String mDstPackageName;
237            String mDstClassName;
238            int mToken;
239
240            ConnectAsync(Context srcContext, Handler srcHandler, String dstPackageName,
241                    String dstClassName, int token) {
242                mSrcCtx = srcContext;
243                mSrcHdlr = srcHandler;
244                mDstPackageName = dstPackageName;
245                mDstClassName = dstClassName;
246                mToken = token;
247            }
248
249            public void run() {
250                connectSrcHandlerToPackage(mSrcCtx, mSrcHdlr, mDstPackageName, mDstClassName,
251                        mToken);
252            }
253        }
254
255        ConnectAsync ca = new ConnectAsync(srcContext, srcHandler, dstPackageName, dstClassName,
256                token);
257        new Thread(ca).start();
258
259        if (DBG) log("connect srcHandler to dst Package & class X");
260    }
261
262    /**
263     * Connect handler to a class
264     *
265     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
266     *      msg.arg1 = status
267     *      msg.arg2 = token
268     *
269     * @param srcContext
270     * @param srcHandler
271     * @param klass is the class to send messages to.
272     * @param token returned in msg.arg2
273     */
274    public void connect(Context srcContext, Handler srcHandler, Class<?> klass, int token) {
275        connect(srcContext, srcHandler, klass.getPackage().getName(), klass.getName(), token);
276    }
277
278    /**
279     * Connect handler and messenger.
280     *
281     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
282     *      msg.arg1 = status
283     *      msg.arg2 = token
284     *
285     * @param srcContext
286     * @param srcHandler
287     * @param dstMessenger
288     * @param token returned in msg.arg2
289     */
290    public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger, int token) {
291        if (DBG) log("connect srcHandler to the dstMessenger  E");
292
293        // Initialize source fields
294        mSrcContext = srcContext;
295        mSrcHandler = srcHandler;
296        mSrcMessenger = new Messenger(mSrcHandler);
297
298        // Initialize destination fields
299        mDstMessenger = dstMessenger;
300
301        if (DBG) log("tell source we are half connected");
302
303        // Tell source we are half connected
304        replyHalfConnected(STATUS_SUCCESSFUL, token);
305
306        if (DBG) log("connect srcHandler to the dstMessenger X");
307    }
308
309    /**
310     * Connect two local Handlers.
311     *
312     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
313     *      msg.arg1 = status
314     *      msg.arg2 = token
315     *
316     * @param srcContext is the context of the source
317     * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
318     *            messages
319     * @param dstHandler is the hander to send messages to.
320     * @param token returned in msg.arg2
321     */
322    public void connect(Context srcContext, Handler srcHandler, Handler dstHandler, int token) {
323        connect(srcContext, srcHandler, new Messenger(dstHandler), token);
324    }
325
326    /**
327     * Connect service and messenger.
328     *
329     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcAsyncService when complete.
330     *      msg.arg1 = status
331     *      msg.arg2 = token
332     *
333     * @param srcAsyncService
334     * @param dstMessenger
335     * @param token returned in msg.arg2
336     */
337    public void connect(AsyncService srcAsyncService, Messenger dstMessenger, int token) {
338        connect(srcAsyncService, srcAsyncService.getHandler(), dstMessenger, token);
339    }
340
341    /**
342     * To close the connection call when handler receives CMD_CHANNEL_DISCONNECTED
343     */
344    public void disconnected() {
345        mSrcHandler = null;
346        mSrcMessenger = null;
347        mDstMessenger = null;
348        mConnection = null;
349    }
350
351    /**
352     * Disconnect
353     */
354    public void disconnect(int token) {
355        if (mConnection != null) {
356            mConnection.setToken(token);
357            mSrcContext.unbindService(mConnection);
358        }
359        if (mSrcHandler != null) {
360            Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
361            msg.arg1 = STATUS_SUCCESSFUL;
362            msg.arg2 = token;
363            msg.replyTo = mDstMessenger;
364            mSrcHandler.sendMessage(msg);
365        }
366    }
367
368    /**
369     * Send a message to the destination handler.
370     *
371     * @param msg
372     */
373    public void sendMessage(Message msg) {
374        msg.replyTo = mSrcMessenger;
375        try {
376            mDstMessenger.send(msg);
377        } catch (RemoteException e) {
378            log("TODO: handle sendMessage RemoteException" + e);
379        }
380    }
381
382    /**
383     * Send a message to the destination handler
384     *
385     * @param what
386     */
387    public void sendMessage(int what) {
388        Message msg = Message.obtain();
389        msg.what = what;
390        sendMessage(msg);
391    }
392
393    /**
394     * Send a message to the destination handler
395     *
396     * @param what
397     * @param arg1
398     */
399    public void sendMessage(int what, int arg1) {
400        Message msg = Message.obtain();
401        msg.what = what;
402        msg.arg1 = arg1;
403        sendMessage(msg);
404    }
405
406    /**
407     * Send a message to the destination handler
408     *
409     * @param what
410     * @param arg1
411     * @param arg2
412     */
413    public void sendMessage(int what, int arg1, int arg2) {
414        Message msg = Message.obtain();
415        msg.what = what;
416        msg.arg1 = arg1;
417        msg.arg2 = arg2;
418        sendMessage(msg);
419    }
420
421    /**
422     * Send a message to the destination handler
423     *
424     * @param what
425     * @param arg1
426     * @param arg2
427     * @param obj
428     */
429    public void sendMessage(int what, int arg1, int arg2, Object obj) {
430        Message msg = Message.obtain();
431        msg.what = what;
432        msg.arg1 = arg1;
433        msg.arg2 = arg2;
434        msg.obj = obj;
435        sendMessage(msg);
436    }
437
438    /**
439     * Send a message to the destination handler
440     *
441     * @param what
442     * @param obj
443     */
444    public void sendMessage(int what, Object obj) {
445        Message msg = Message.obtain();
446        msg.what = what;
447        msg.obj = obj;
448        sendMessage(msg);
449    }
450
451    /**
452     * Reply to srcMsg sending dstMsg
453     *
454     * @param srcMsg
455     * @param dstMsg
456     */
457    public void replyToMessage(Message srcMsg, Message dstMsg) {
458        try {
459            srcMsg.replyTo.send(dstMsg);
460        } catch (RemoteException e) {
461            log("TODO: handle replyToMessage RemoteException" + e);
462            e.printStackTrace();
463        }
464    }
465
466    /**
467     * Reply to srcMsg
468     *
469     * @param srcMsg
470     * @param what
471     */
472    public void replyToMessage(Message srcMsg, int what) {
473        Message msg = Message.obtain();
474        msg.what = what;
475        replyToMessage(srcMsg, msg);
476    }
477
478    /**
479     * Reply to srcMsg
480     *
481     * @param srcMsg
482     * @param what
483     * @param arg1
484     */
485    public void replyToMessage(Message srcMsg, int what, int arg1) {
486        Message msg = Message.obtain();
487        msg.what = what;
488        msg.arg1 = arg1;
489        replyToMessage(srcMsg, msg);
490    }
491
492    /**
493     * Reply to srcMsg
494     *
495     * @param srcMsg
496     * @param what
497     * @param arg1
498     * @param arg2
499     */
500    public void replyToMessage(Message srcMsg, int what, int arg1, int arg2) {
501        Message msg = Message.obtain();
502        msg.what = what;
503        msg.arg1 = arg1;
504        msg.arg2 = arg2;
505        replyToMessage(srcMsg, msg);
506    }
507
508    /**
509     * Reply to srcMsg
510     *
511     * @param srcMsg
512     * @param what
513     * @param arg1
514     * @param arg2
515     * @param obj
516     */
517    public void replyToMessage(Message srcMsg, int what, int arg1, int arg2, Object obj) {
518        Message msg = Message.obtain();
519        msg.what = what;
520        msg.arg1 = arg1;
521        msg.arg2 = arg2;
522        msg.obj = obj;
523        replyToMessage(srcMsg, msg);
524    }
525
526    /**
527     * Reply to srcMsg
528     *
529     * @param srcMsg
530     * @param what
531     * @param obj
532     */
533    public void replyToMessage(Message srcMsg, int what, Object obj) {
534        Message msg = Message.obtain();
535        msg.what = what;
536        msg.obj = obj;
537        replyToMessage(srcMsg, msg);
538    }
539
540    /**
541     * Send the Message synchronously.
542     *
543     * @param msg to send
544     * @return reply message or null if an error.
545     */
546    public Message sendMessageSynchronously(Message msg) {
547        Message resultMsg = SyncMessenger.sendMessageSynchronously(mDstMessenger, msg);
548        return resultMsg;
549    }
550
551    /**
552     * Send the Message synchronously.
553     *
554     * @param what
555     * @return reply message or null if an error.
556     */
557    public Message sendMessageSynchronously(int what) {
558        Message msg = Message.obtain();
559        msg.what = what;
560        Message resultMsg = sendMessageSynchronously(msg);
561        return resultMsg;
562    }
563
564    /**
565     * Send the Message synchronously.
566     *
567     * @param what
568     * @param arg1
569     * @return reply message or null if an error.
570     */
571    public Message sendMessageSynchronously(int what, int arg1) {
572        Message msg = Message.obtain();
573        msg.what = what;
574        msg.arg1 = arg1;
575        Message resultMsg = sendMessageSynchronously(msg);
576        return resultMsg;
577    }
578
579    /**
580     * Send the Message synchronously.
581     *
582     * @param what
583     * @param arg1
584     * @param arg2
585     * @return reply message or null if an error.
586     */
587    public Message sendMessageSynchronously(int what, int arg1, int arg2) {
588        Message msg = Message.obtain();
589        msg.what = what;
590        msg.arg1 = arg1;
591        msg.arg2 = arg2;
592        Message resultMsg = sendMessageSynchronously(msg);
593        return resultMsg;
594    }
595
596    /**
597     * Send the Message synchronously.
598     *
599     * @param what
600     * @param arg1
601     * @param arg2
602     * @param obj
603     * @return reply message or null if an error.
604     */
605    public Message sendMessageSynchronously(int what, int arg1, int arg2, Object obj) {
606        Message msg = Message.obtain();
607        msg.what = what;
608        msg.arg1 = arg1;
609        msg.arg2 = arg2;
610        msg.obj = obj;
611        Message resultMsg = sendMessageSynchronously(msg);
612        return resultMsg;
613    }
614
615    /**
616     * Send the Message synchronously.
617     *
618     * @param what
619     * @param obj
620     * @return reply message or null if an error.
621     */
622    public Message sendMessageSynchronously(int what, Object obj) {
623        Message msg = Message.obtain();
624        msg.what = what;
625        msg.obj = obj;
626        Message resultMsg = sendMessageSynchronously(msg);
627        return resultMsg;
628    }
629
630    /**
631     * Helper class to send messages synchronously
632     */
633    private static class SyncMessenger {
634        /** A stack of SyncMessengers */
635        private static Stack<SyncMessenger> sStack = new Stack<SyncMessenger>();
636        /** A number of SyncMessengers created */
637        private static int sCount = 0;
638        /** The handler thread */
639        private HandlerThread mHandlerThread;
640        /** The handler that will receive the result */
641        private SyncHandler mHandler;
642        /** The messenger used to send the message */
643        private Messenger mMessenger;
644
645        /** private constructor */
646        private SyncMessenger() {
647        }
648
649        /** Synchronous Handler class */
650        private class SyncHandler extends Handler {
651            /** The object used to wait/notify */
652            private Object mLockObject = new Object();
653            /** The resulting message */
654            private Message mResultMsg;
655
656            /** Constructor */
657            private SyncHandler(Looper looper) {
658                super(looper);
659            }
660
661            /** Handle of the reply message */
662            @Override
663            public void handleMessage(Message msg) {
664                mResultMsg = Message.obtain();
665                mResultMsg.copyFrom(msg);
666                synchronized(mLockObject) {
667                    mLockObject.notify();
668                }
669            }
670        }
671
672        /**
673         * @return the SyncMessenger
674         */
675        private static SyncMessenger obtain() {
676            SyncMessenger sm;
677            synchronized (sStack) {
678                if (sStack.isEmpty()) {
679                    sm = new SyncMessenger();
680                    sm.mHandlerThread = new HandlerThread("SyncHandler-" + sCount++);
681                    sm.mHandlerThread.start();
682                    sm.mHandler = sm.new SyncHandler(sm.mHandlerThread.getLooper());
683                    sm.mMessenger = new Messenger(sm.mHandler);
684                } else {
685                    sm = sStack.pop();
686                }
687            }
688            return sm;
689        }
690
691        /**
692         * Recycle this object
693         */
694        private void recycle() {
695            synchronized (sStack) {
696                sStack.push(this);
697            }
698        }
699
700        /**
701         * Send a message synchronously.
702         *
703         * @param msg to send
704         * @return result message or null if an error occurs
705         */
706        private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) {
707            SyncMessenger sm = SyncMessenger.obtain();
708            try {
709                msg.replyTo = sm.mMessenger;
710                dstMessenger.send(msg);
711                synchronized (sm.mHandler.mLockObject) {
712                    sm.mHandler.mLockObject.wait();
713                }
714            } catch (InterruptedException e) {
715                sm.mHandler.mResultMsg = null;
716            } catch (RemoteException e) {
717                sm.mHandler.mResultMsg = null;
718            }
719            Message resultMsg = sm.mHandler.mResultMsg;
720            sm.recycle();
721            return resultMsg;
722        }
723    }
724
725    /**
726     * Reply to the src handler that we're half connected.
727     *
728     * @param status to be stored in msg.arg1
729     */
730    private void replyHalfConnected(int status, int token) {
731        Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED);
732        msg.arg1 = status;
733        msg.arg2 = token;
734        msg.replyTo = mDstMessenger;
735        mSrcHandler.sendMessage(msg);
736    }
737
738    /**
739     * ServiceConnection to receive call backs.
740     */
741    class AsyncChannelConnection implements ServiceConnection {
742        private int mToken;
743
744        AsyncChannelConnection(int token) {
745            mToken = token;
746        }
747
748        /**
749         * @param token
750         */
751        public void setToken(int token) {
752            mToken = token;
753        }
754
755        public void onServiceConnected(ComponentName className, IBinder service) {
756            mDstMessenger = new Messenger(service);
757            replyHalfConnected(STATUS_SUCCESSFUL, mToken);
758        }
759
760        public void onServiceDisconnected(ComponentName className) {
761            Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
762            msg.arg1 = STATUS_SUCCESSFUL;
763            msg.arg2 = mToken;
764            msg.replyTo = mDstMessenger;
765            mSrcHandler.sendMessage(msg);
766        }
767    }
768
769    /**
770     * Log the string.
771     *
772     * @param s
773     */
774    private static void log(String s) {
775        Slog.d(TAG, s);
776    }
777}
778