AsyncChannel.java revision d20a5d6b5a821e28d73eba6502a2135134014a84
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 * An asynchronous channel between two handlers.
36 *
37 * 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.
41 *
42 * 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:
46 *
47 *   1) Client calls AsyncChannel#connect
48 *   2) Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel
49
50 *   3) Client calls AsyncChannel#sendMessage(msgX)
51 *   4) Server receives and processes msgX
52 *   5) Server optionally calls AsyncChannel#replyToMessage(msgY)
53 *      and if sent Client receives and processes msgY
54 *   6) Loop to step 3 until done
55 *
56 *   7) When done Client calls {@link AsyncChannel#disconnect(int)}
57 *   8) Client receives CMD_CHANNEL_DISCONNECTED from AsyncChannel
58 *
59 * 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:
64 *
65 *   1)  Client calls AsyncChannel#connect
66 *   2)  Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel
67 *   3)  Client calls AsyncChannel#sendMessage(CMD_CHANNEL_FULL_CONNECTION)
68 *   4)  Server receives CMD_CHANNEL_FULL_CONNECTION
69 *   5)  Server calls AsyncChannel#connect
70 *   6)  Server receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel
71 *   7)  Server sends AsyncChannel#sendMessage(CMD_CHANNEL_FULLY_CONNECTED)
72 *   8)  Client receives CMD_CHANNEL_FULLY_CONNECTED
73 *
74 *   9)  Client/Server uses AsyncChannel#sendMessage/replyToMessage
75 *       to communicate and perform work
76 *   10) Loop to step 9 until done
77 *
78 *   11) When done Client/Server calls {@link AsyncChannel#disconnect(int)}
79 *   12) Client/Server receives CMD_CHANNEL_DISCONNECTED from AsyncChannel
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            @Override
250            public void run() {
251                connectSrcHandlerToPackage(mSrcCtx, mSrcHdlr, mDstPackageName, mDstClassName,
252                        mToken);
253            }
254        }
255
256        ConnectAsync ca = new ConnectAsync(srcContext, srcHandler, dstPackageName, dstClassName,
257                token);
258        new Thread(ca).start();
259
260        if (DBG) log("connect srcHandler to dst Package & class X");
261    }
262
263    /**
264     * Connect handler to a class
265     *
266     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
267     *      msg.arg1 = status
268     *      msg.arg2 = token
269     *
270     * @param srcContext
271     * @param srcHandler
272     * @param klass is the class to send messages to.
273     * @param token returned in msg.arg2
274     */
275    public void connect(Context srcContext, Handler srcHandler, Class<?> klass, int token) {
276        connect(srcContext, srcHandler, klass.getPackage().getName(), klass.getName(), token);
277    }
278
279    /**
280     * Connect handler and messenger.
281     *
282     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
283     *      msg.arg1 = status
284     *      msg.arg2 = token
285     *
286     * @param srcContext
287     * @param srcHandler
288     * @param dstMessenger
289     * @param token returned in msg.arg2
290     */
291    public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger, int token) {
292        if (DBG) log("connect srcHandler to the dstMessenger  E");
293
294        // Initialize source fields
295        mSrcContext = srcContext;
296        mSrcHandler = srcHandler;
297        mSrcMessenger = new Messenger(mSrcHandler);
298
299        // Initialize destination fields
300        mDstMessenger = dstMessenger;
301
302        if (DBG) log("tell source we are half connected");
303
304        // Tell source we are half connected
305        replyHalfConnected(STATUS_SUCCESSFUL, token);
306
307        if (DBG) log("connect srcHandler to the dstMessenger X");
308    }
309
310    /**
311     * Connect two local Handlers.
312     *
313     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
314     *      msg.arg1 = status
315     *      msg.arg2 = token
316     *
317     * @param srcContext is the context of the source
318     * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
319     *            messages
320     * @param dstHandler is the hander to send messages to.
321     * @param token returned in msg.arg2
322     */
323    public void connect(Context srcContext, Handler srcHandler, Handler dstHandler, int token) {
324        connect(srcContext, srcHandler, new Messenger(dstHandler), token);
325    }
326
327    /**
328     * Connect service and messenger.
329     *
330     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcAsyncService when complete.
331     *      msg.arg1 = status
332     *      msg.arg2 = token
333     *
334     * @param srcAsyncService
335     * @param dstMessenger
336     * @param token returned in msg.arg2
337     */
338    public void connect(AsyncService srcAsyncService, Messenger dstMessenger, int token) {
339        connect(srcAsyncService, srcAsyncService.getHandler(), dstMessenger, token);
340    }
341
342    /**
343     * To close the connection call when handler receives CMD_CHANNEL_DISCONNECTED
344     */
345    public void disconnected() {
346        mSrcHandler = null;
347        mSrcMessenger = null;
348        mDstMessenger = null;
349        mConnection = null;
350    }
351
352    /**
353     * Disconnect
354     */
355    public void disconnect(int token) {
356        if (mConnection != null) {
357            mConnection.setToken(token);
358            mSrcContext.unbindService(mConnection);
359        }
360        if (mSrcHandler != null) {
361            Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
362            msg.arg1 = STATUS_SUCCESSFUL;
363            msg.arg2 = token;
364            msg.replyTo = mDstMessenger;
365            mSrcHandler.sendMessage(msg);
366        }
367    }
368
369    /**
370     * Send a message to the destination handler.
371     *
372     * @param msg
373     */
374    public void sendMessage(Message msg) {
375        msg.replyTo = mSrcMessenger;
376        try {
377            mDstMessenger.send(msg);
378        } catch (RemoteException e) {
379            log("TODO: handle sendMessage RemoteException" + e);
380        }
381    }
382
383    /**
384     * Send a message to the destination handler
385     *
386     * @param what
387     */
388    public void sendMessage(int what) {
389        Message msg = Message.obtain();
390        msg.what = what;
391        sendMessage(msg);
392    }
393
394    /**
395     * Send a message to the destination handler
396     *
397     * @param what
398     * @param arg1
399     */
400    public void sendMessage(int what, int arg1) {
401        Message msg = Message.obtain();
402        msg.what = what;
403        msg.arg1 = arg1;
404        sendMessage(msg);
405    }
406
407    /**
408     * Send a message to the destination handler
409     *
410     * @param what
411     * @param arg1
412     * @param arg2
413     */
414    public void sendMessage(int what, int arg1, int arg2) {
415        Message msg = Message.obtain();
416        msg.what = what;
417        msg.arg1 = arg1;
418        msg.arg2 = arg2;
419        sendMessage(msg);
420    }
421
422    /**
423     * Send a message to the destination handler
424     *
425     * @param what
426     * @param arg1
427     * @param arg2
428     * @param obj
429     */
430    public void sendMessage(int what, int arg1, int arg2, Object obj) {
431        Message msg = Message.obtain();
432        msg.what = what;
433        msg.arg1 = arg1;
434        msg.arg2 = arg2;
435        msg.obj = obj;
436        sendMessage(msg);
437    }
438
439    /**
440     * Send a message to the destination handler
441     *
442     * @param what
443     * @param obj
444     */
445    public void sendMessage(int what, Object obj) {
446        Message msg = Message.obtain();
447        msg.what = what;
448        msg.obj = obj;
449        sendMessage(msg);
450    }
451
452    /**
453     * Reply to srcMsg sending dstMsg
454     *
455     * @param srcMsg
456     * @param dstMsg
457     */
458    public void replyToMessage(Message srcMsg, Message dstMsg) {
459        try {
460            srcMsg.replyTo.send(dstMsg);
461        } catch (RemoteException e) {
462            log("TODO: handle replyToMessage RemoteException" + e);
463            e.printStackTrace();
464        }
465    }
466
467    /**
468     * Reply to srcMsg
469     *
470     * @param srcMsg
471     * @param what
472     */
473    public void replyToMessage(Message srcMsg, int what) {
474        Message msg = Message.obtain();
475        msg.what = what;
476        replyToMessage(srcMsg, msg);
477    }
478
479    /**
480     * Reply to srcMsg
481     *
482     * @param srcMsg
483     * @param what
484     * @param arg1
485     */
486    public void replyToMessage(Message srcMsg, int what, int arg1) {
487        Message msg = Message.obtain();
488        msg.what = what;
489        msg.arg1 = arg1;
490        replyToMessage(srcMsg, msg);
491    }
492
493    /**
494     * Reply to srcMsg
495     *
496     * @param srcMsg
497     * @param what
498     * @param arg1
499     * @param arg2
500     */
501    public void replyToMessage(Message srcMsg, int what, int arg1, int arg2) {
502        Message msg = Message.obtain();
503        msg.what = what;
504        msg.arg1 = arg1;
505        msg.arg2 = arg2;
506        replyToMessage(srcMsg, msg);
507    }
508
509    /**
510     * Reply to srcMsg
511     *
512     * @param srcMsg
513     * @param what
514     * @param arg1
515     * @param arg2
516     * @param obj
517     */
518    public void replyToMessage(Message srcMsg, int what, int arg1, int arg2, Object obj) {
519        Message msg = Message.obtain();
520        msg.what = what;
521        msg.arg1 = arg1;
522        msg.arg2 = arg2;
523        msg.obj = obj;
524        replyToMessage(srcMsg, msg);
525    }
526
527    /**
528     * Reply to srcMsg
529     *
530     * @param srcMsg
531     * @param what
532     * @param obj
533     */
534    public void replyToMessage(Message srcMsg, int what, Object obj) {
535        Message msg = Message.obtain();
536        msg.what = what;
537        msg.obj = obj;
538        replyToMessage(srcMsg, msg);
539    }
540
541    /**
542     * Send the Message synchronously.
543     *
544     * @param msg to send
545     * @return reply message or null if an error.
546     */
547    public Message sendMessageSynchronously(Message msg) {
548        Message resultMsg = SyncMessenger.sendMessageSynchronously(mDstMessenger, msg);
549        return resultMsg;
550    }
551
552    /**
553     * Send the Message synchronously.
554     *
555     * @param what
556     * @return reply message or null if an error.
557     */
558    public Message sendMessageSynchronously(int what) {
559        Message msg = Message.obtain();
560        msg.what = what;
561        Message resultMsg = sendMessageSynchronously(msg);
562        return resultMsg;
563    }
564
565    /**
566     * Send the Message synchronously.
567     *
568     * @param what
569     * @param arg1
570     * @return reply message or null if an error.
571     */
572    public Message sendMessageSynchronously(int what, int arg1) {
573        Message msg = Message.obtain();
574        msg.what = what;
575        msg.arg1 = arg1;
576        Message resultMsg = sendMessageSynchronously(msg);
577        return resultMsg;
578    }
579
580    /**
581     * Send the Message synchronously.
582     *
583     * @param what
584     * @param arg1
585     * @param arg2
586     * @return reply message or null if an error.
587     */
588    public Message sendMessageSynchronously(int what, int arg1, int arg2) {
589        Message msg = Message.obtain();
590        msg.what = what;
591        msg.arg1 = arg1;
592        msg.arg2 = arg2;
593        Message resultMsg = sendMessageSynchronously(msg);
594        return resultMsg;
595    }
596
597    /**
598     * Send the Message synchronously.
599     *
600     * @param what
601     * @param arg1
602     * @param arg2
603     * @param obj
604     * @return reply message or null if an error.
605     */
606    public Message sendMessageSynchronously(int what, int arg1, int arg2, Object obj) {
607        Message msg = Message.obtain();
608        msg.what = what;
609        msg.arg1 = arg1;
610        msg.arg2 = arg2;
611        msg.obj = obj;
612        Message resultMsg = sendMessageSynchronously(msg);
613        return resultMsg;
614    }
615
616    /**
617     * Send the Message synchronously.
618     *
619     * @param what
620     * @param obj
621     * @return reply message or null if an error.
622     */
623    public Message sendMessageSynchronously(int what, Object obj) {
624        Message msg = Message.obtain();
625        msg.what = what;
626        msg.obj = obj;
627        Message resultMsg = sendMessageSynchronously(msg);
628        return resultMsg;
629    }
630
631    /**
632     * Helper class to send messages synchronously
633     */
634    private static class SyncMessenger {
635        /** A stack of SyncMessengers */
636        private static Stack<SyncMessenger> sStack = new Stack<SyncMessenger>();
637        /** A number of SyncMessengers created */
638        private static int sCount = 0;
639        /** The handler thread */
640        private HandlerThread mHandlerThread;
641        /** The handler that will receive the result */
642        private SyncHandler mHandler;
643        /** The messenger used to send the message */
644        private Messenger mMessenger;
645
646        /** private constructor */
647        private SyncMessenger() {
648        }
649
650        /** Synchronous Handler class */
651        private class SyncHandler extends Handler {
652            /** The object used to wait/notify */
653            private Object mLockObject = new Object();
654            /** The resulting message */
655            private Message mResultMsg;
656
657            /** Constructor */
658            private SyncHandler(Looper looper) {
659                super(looper);
660            }
661
662            /** Handle of the reply message */
663            @Override
664            public void handleMessage(Message msg) {
665                mResultMsg = Message.obtain();
666                mResultMsg.copyFrom(msg);
667                synchronized(mLockObject) {
668                    mLockObject.notify();
669                }
670            }
671        }
672
673        /**
674         * @return the SyncMessenger
675         */
676        private static SyncMessenger obtain() {
677            SyncMessenger sm;
678            synchronized (sStack) {
679                if (sStack.isEmpty()) {
680                    sm = new SyncMessenger();
681                    sm.mHandlerThread = new HandlerThread("SyncHandler-" + sCount++);
682                    sm.mHandlerThread.start();
683                    sm.mHandler = sm.new SyncHandler(sm.mHandlerThread.getLooper());
684                    sm.mMessenger = new Messenger(sm.mHandler);
685                } else {
686                    sm = sStack.pop();
687                }
688            }
689            return sm;
690        }
691
692        /**
693         * Recycle this object
694         */
695        private void recycle() {
696            synchronized (sStack) {
697                sStack.push(this);
698            }
699        }
700
701        /**
702         * Send a message synchronously.
703         *
704         * @param msg to send
705         * @return result message or null if an error occurs
706         */
707        private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) {
708            SyncMessenger sm = SyncMessenger.obtain();
709            try {
710                msg.replyTo = sm.mMessenger;
711                dstMessenger.send(msg);
712                synchronized (sm.mHandler.mLockObject) {
713                    sm.mHandler.mLockObject.wait();
714                }
715            } catch (InterruptedException e) {
716                sm.mHandler.mResultMsg = null;
717            } catch (RemoteException e) {
718                sm.mHandler.mResultMsg = null;
719            }
720            Message resultMsg = sm.mHandler.mResultMsg;
721            sm.recycle();
722            return resultMsg;
723        }
724    }
725
726    /**
727     * Reply to the src handler that we're half connected.
728     *
729     * @param status to be stored in msg.arg1
730     */
731    private void replyHalfConnected(int status, int token) {
732        Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED);
733        msg.arg1 = status;
734        msg.arg2 = token;
735        msg.replyTo = mDstMessenger;
736        mSrcHandler.sendMessage(msg);
737    }
738
739    /**
740     * ServiceConnection to receive call backs.
741     */
742    class AsyncChannelConnection implements ServiceConnection {
743        private int mToken;
744
745        AsyncChannelConnection(int token) {
746            mToken = token;
747        }
748
749        /**
750         * @param token
751         */
752        public void setToken(int token) {
753            mToken = token;
754        }
755
756        public void onServiceConnected(ComponentName className, IBinder service) {
757            mDstMessenger = new Messenger(service);
758            replyHalfConnected(STATUS_SUCCESSFUL, mToken);
759        }
760
761        public void onServiceDisconnected(ComponentName className) {
762            Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
763            msg.arg1 = STATUS_SUCCESSFUL;
764            msg.arg2 = mToken;
765            msg.replyTo = mDstMessenger;
766            mSrcHandler.sendMessage(msg);
767        }
768    }
769
770    /**
771     * Log the string.
772     *
773     * @param s
774     */
775    private static void log(String s) {
776        Slog.d(TAG, s);
777    }
778}
779