RemoteCallbackList.java revision 7b0b1ed979aa665175bf3952c8902ce13c763ab8
1/*
2 * Copyright (C) 2008 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 android.os;
18
19import java.util.HashMap;
20
21/**
22 * Takes care of the grunt work of maintaining a list of remote interfaces,
23 * typically for the use of performing callbacks from a
24 * {@link android.app.Service} to its clients.  In particular, this:
25 *
26 * <ul>
27 * <li> Keeps track of a set of registered {@link IInterface} callbacks,
28 * taking care to identify them through their underlying unique {@link IBinder}
29 * (by calling {@link IInterface#asBinder IInterface.asBinder()}.
30 * <li> Attaches a {@link IBinder.DeathRecipient IBinder.DeathRecipient} to
31 * each registered interface, so that it can be cleaned out of the list if its
32 * process goes away.
33 * <li> Performs locking of the underlying list of interfaces to deal with
34 * multithreaded incoming calls, and a thread-safe way to iterate over a
35 * snapshot of the list without holding its lock.
36 * </ul>
37 *
38 * <p>To use this class, simply create a single instance along with your
39 * service, and call its {@link #register} and {@link #unregister} methods
40 * as client register and unregister with your service.  To call back on to
41 * the registered clients, use {@link #beginBroadcast},
42 * {@link #getBroadcastItem}, and {@link #finishBroadcast}.
43 *
44 * <p>If a registered callback's process goes away, this class will take
45 * care of automatically removing it from the list.  If you want to do
46 * additional work in this situation, you can create a subclass that
47 * implements the {@link #onCallbackDied} method.
48 */
49public class RemoteCallbackList<E extends IInterface> {
50    /*package*/ HashMap<IBinder, Callback> mCallbacks
51            = new HashMap<IBinder, Callback>();
52    private IInterface[] mActiveBroadcast;
53    private boolean mKilled = false;
54
55    private final class Callback implements IBinder.DeathRecipient {
56        final E mCallback;
57
58        Callback(E callback) {
59            mCallback = callback;
60        }
61
62        public void binderDied() {
63            synchronized (mCallbacks) {
64                mCallbacks.remove(mCallback.asBinder());
65            }
66            onCallbackDied(mCallback);
67        }
68    }
69
70    /**
71     * Add a new callback to the list.  This callback will remain in the list
72     * until a corresponding call to {@link #unregister} or its hosting process
73     * goes away.  If the callback was already registered (determined by
74     * checking to see if the {@link IInterface#asBinder callback.asBinder()}
75     * object is already in the list), then it will be left as-is.
76     * Registrations are not counted; a single call to {@link #unregister}
77     * will remove a callback after any number calls to register it.
78     *
79     * @param callback The callback interface to be added to the list.  Must
80     * not be null -- passing null here will cause a NullPointerException.
81     * Most services will want to check for null before calling this with
82     * an object given from a client, so that clients can't crash the
83     * service with bad data.
84     *
85     * @return Returns true if the callback was successfully added to the list.
86     * Returns false if it was not added, either because {@link #kill} had
87     * previously been called or the callback's process has gone away.
88     *
89     * @see #unregister
90     * @see #kill
91     * @see #onCallbackDied
92     */
93    public boolean register(E callback) {
94        synchronized (mCallbacks) {
95            if (mKilled) {
96                return false;
97            }
98            IBinder binder = callback.asBinder();
99            try {
100                Callback cb = new Callback(callback);
101                binder.linkToDeath(cb, 0);
102                mCallbacks.put(binder, cb);
103                return true;
104            } catch (RemoteException e) {
105                return false;
106            }
107        }
108    }
109
110    /**
111     * Remove from the list a callback that was previously added with
112     * {@link #register}.  This uses the
113     * {@link IInterface#asBinder callback.asBinder()} object to correctly
114     * find the previous registration.
115     * Registrations are not counted; a single unregister call will remove
116     * a callback after any number calls to {@link #register} for it.
117     *
118     * @param callback The callback to be removed from the list.  Passing
119     * null here will cause a NullPointerException, so you will generally want
120     * to check for null before calling.
121     *
122     * @return Returns true if the callback was found and unregistered.  Returns
123     * false if the given callback was not found on the list.
124     *
125     * @see #register
126     */
127    public boolean unregister(E callback) {
128        synchronized (mCallbacks) {
129            Callback cb = mCallbacks.remove(callback.asBinder());
130            if (cb != null) {
131                cb.mCallback.asBinder().unlinkToDeath(cb, 0);
132                return true;
133            }
134            return false;
135        }
136    }
137
138    /**
139     * Disable this callback list.  All registered callbacks are unregistered,
140     * and the list is disabled so that future calls to {@link #register} will
141     * fail.  This should be used when a Service is stopping, to prevent clients
142     * from registering callbacks after it is stopped.
143     *
144     * @see #register
145     */
146    public void kill() {
147        synchronized (mCallbacks) {
148            for (Callback cb : mCallbacks.values()) {
149                cb.mCallback.asBinder().unlinkToDeath(cb, 0);
150            }
151            mCallbacks.clear();
152            mKilled = true;
153        }
154    }
155
156    /**
157     * Called when the process hosting a callback in the list has gone away.
158     * The default implementation does nothing.
159     *
160     * @param callback The callback whose process has died.  Note that, since
161     * its process has died, you can not make any calls on to this interface.
162     * You can, however, retrieve its IBinder and compare it with another
163     * IBinder to see if it is the same object.
164     *
165     * @see #register
166     */
167    public void onCallbackDied(E callback) {
168    }
169
170    /**
171     * Prepare to start making calls to the currently registered callbacks.
172     * This creates a copy of the callback list, which you can retrieve items
173     * from using {@link #getBroadcastItem}.  Note that only one broadcast can
174     * be active at a time, so you must be sure to always call this from the
175     * same thread (usually by scheduling with {@link Handler} or
176     * do your own synchronization.  You must call {@link #finishBroadcast}
177     * when done.
178     *
179     * <p>A typical loop delivering a broadcast looks like this:
180     *
181     * <pre>
182     * final int N = callbacks.beginBroadcast();
183     * for (int i=0; i&lt;N; i++) {
184     *     try {
185     *         callbacks.getBroadcastItem(i).somethingHappened();
186     *     } catch (RemoteException e) {
187     *         // The RemoteCallbackList will take care of removing
188     *         // the dead object for us.
189     *     }
190     * }
191     * callbacks.finishBroadcast();</pre>
192     *
193     * @return Returns the number of callbacks in the broadcast, to be used
194     * with {@link #getBroadcastItem} to determine the range of indices you
195     * can supply.
196     *
197     * @see #getBroadcastItem
198     * @see #finishBroadcast
199     */
200    public int beginBroadcast() {
201        synchronized (mCallbacks) {
202            final int N = mCallbacks.size();
203            if (N <= 0) {
204                return 0;
205            }
206            IInterface[] active = mActiveBroadcast;
207            if (active == null || active.length < N) {
208                mActiveBroadcast = active = new IInterface[N];
209            }
210            int i=0;
211            for (Callback cb : mCallbacks.values()) {
212                active[i++] = cb.mCallback;
213            }
214            return i;
215        }
216    }
217
218    /**
219     * Retrieve an item in the active broadcast that was previously started
220     * with {@link #beginBroadcast}.  This can <em>only</em> be called after
221     * the broadcast is started, and its data is no longer valid after
222     * calling {@link #finishBroadcast}.
223     *
224     * <p>Note that it is possible for the process of one of the returned
225     * callbacks to go away before you call it, so you will need to catch
226     * {@link RemoteException} when calling on to the returned object.
227     * The callback list itself, however, will take care of unregistering
228     * these objects once it detects that it is no longer valid, so you can
229     * handle such an exception by simply ignoring it.
230     *
231     * @param index Which of the registered callbacks you would like to
232     * retrieve.  Ranges from 0 to 1-{@link #beginBroadcast}.
233     *
234     * @return Returns the callback interface that you can call.  This will
235     * always be non-null.
236     *
237     * @see #beginBroadcast
238     */
239    public E getBroadcastItem(int index) {
240        return (E)mActiveBroadcast[index];
241    }
242
243    /**
244     * Clean up the state of a broadcast previously initiated by calling
245     * {@link #beginBroadcast}.  This must always be called when you are done
246     * with a broadcast.
247     *
248     * @see #beginBroadcast
249     */
250    public void finishBroadcast() {
251        IInterface[] active = mActiveBroadcast;
252        if (active != null) {
253            final int N = active.length;
254            for (int i=0; i<N; i++) {
255                active[i] = null;
256            }
257        }
258    }
259}
260