• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.os;
18 
19 import android.util.ArrayMap;
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  */
49 public class RemoteCallbackList<E extends IInterface> {
50     /*package*/ ArrayMap<IBinder, Callback> mCallbacks
51             = new ArrayMap<IBinder, Callback>();
52     private Object[] mActiveBroadcast;
53     private int mBroadcastCount = -1;
54     private boolean mKilled = false;
55 
56     private final class Callback implements IBinder.DeathRecipient {
57         final E mCallback;
58         final Object mCookie;
59 
Callback(E callback, Object cookie)60         Callback(E callback, Object cookie) {
61             mCallback = callback;
62             mCookie = cookie;
63         }
64 
binderDied()65         public void binderDied() {
66             synchronized (mCallbacks) {
67                 mCallbacks.remove(mCallback.asBinder());
68             }
69             onCallbackDied(mCallback, mCookie);
70         }
71     }
72 
73     /**
74      * Simple version of {@link RemoteCallbackList#register(E, Object)}
75      * that does not take a cookie object.
76      */
register(E callback)77     public boolean register(E callback) {
78         return register(callback, null);
79     }
80 
81     /**
82      * Add a new callback to the list.  This callback will remain in the list
83      * until a corresponding call to {@link #unregister} or its hosting process
84      * goes away.  If the callback was already registered (determined by
85      * checking to see if the {@link IInterface#asBinder callback.asBinder()}
86      * object is already in the list), then it will be left as-is.
87      * Registrations are not counted; a single call to {@link #unregister}
88      * will remove a callback after any number calls to register it.
89      *
90      * @param callback The callback interface to be added to the list.  Must
91      * not be null -- passing null here will cause a NullPointerException.
92      * Most services will want to check for null before calling this with
93      * an object given from a client, so that clients can't crash the
94      * service with bad data.
95      *
96      * @param cookie Optional additional data to be associated with this
97      * callback.
98      *
99      * @return Returns true if the callback was successfully added to the list.
100      * Returns false if it was not added, either because {@link #kill} had
101      * previously been called or the callback's process has gone away.
102      *
103      * @see #unregister
104      * @see #kill
105      * @see #onCallbackDied
106      */
register(E callback, Object cookie)107     public boolean register(E callback, Object cookie) {
108         synchronized (mCallbacks) {
109             if (mKilled) {
110                 return false;
111             }
112             IBinder binder = callback.asBinder();
113             try {
114                 Callback cb = new Callback(callback, cookie);
115                 binder.linkToDeath(cb, 0);
116                 mCallbacks.put(binder, cb);
117                 return true;
118             } catch (RemoteException e) {
119                 return false;
120             }
121         }
122     }
123 
124     /**
125      * Remove from the list a callback that was previously added with
126      * {@link #register}.  This uses the
127      * {@link IInterface#asBinder callback.asBinder()} object to correctly
128      * find the previous registration.
129      * Registrations are not counted; a single unregister call will remove
130      * a callback after any number calls to {@link #register} for it.
131      *
132      * @param callback The callback to be removed from the list.  Passing
133      * null here will cause a NullPointerException, so you will generally want
134      * to check for null before calling.
135      *
136      * @return Returns true if the callback was found and unregistered.  Returns
137      * false if the given callback was not found on the list.
138      *
139      * @see #register
140      */
unregister(E callback)141     public boolean unregister(E callback) {
142         synchronized (mCallbacks) {
143             Callback cb = mCallbacks.remove(callback.asBinder());
144             if (cb != null) {
145                 cb.mCallback.asBinder().unlinkToDeath(cb, 0);
146                 return true;
147             }
148             return false;
149         }
150     }
151 
152     /**
153      * Disable this callback list.  All registered callbacks are unregistered,
154      * and the list is disabled so that future calls to {@link #register} will
155      * fail.  This should be used when a Service is stopping, to prevent clients
156      * from registering callbacks after it is stopped.
157      *
158      * @see #register
159      */
kill()160     public void kill() {
161         synchronized (mCallbacks) {
162             for (int cbi=mCallbacks.size()-1; cbi>=0; cbi--) {
163                 Callback cb = mCallbacks.valueAt(cbi);
164                 cb.mCallback.asBinder().unlinkToDeath(cb, 0);
165             }
166             mCallbacks.clear();
167             mKilled = true;
168         }
169     }
170 
171     /**
172      * Old version of {@link #onCallbackDied(E, Object)} that
173      * does not provide a cookie.
174      */
onCallbackDied(E callback)175     public void onCallbackDied(E callback) {
176     }
177 
178     /**
179      * Called when the process hosting a callback in the list has gone away.
180      * The default implementation calls {@link #onCallbackDied(E)}
181      * for backwards compatibility.
182      *
183      * @param callback The callback whose process has died.  Note that, since
184      * its process has died, you can not make any calls on to this interface.
185      * You can, however, retrieve its IBinder and compare it with another
186      * IBinder to see if it is the same object.
187      * @param cookie The cookie object original provided to
188      * {@link #register(E, Object)}.
189      *
190      * @see #register
191      */
onCallbackDied(E callback, Object cookie)192     public void onCallbackDied(E callback, Object cookie) {
193         onCallbackDied(callback);
194     }
195 
196     /**
197      * Prepare to start making calls to the currently registered callbacks.
198      * This creates a copy of the callback list, which you can retrieve items
199      * from using {@link #getBroadcastItem}.  Note that only one broadcast can
200      * be active at a time, so you must be sure to always call this from the
201      * same thread (usually by scheduling with {@link Handler}) or
202      * do your own synchronization.  You must call {@link #finishBroadcast}
203      * when done.
204      *
205      * <p>A typical loop delivering a broadcast looks like this:
206      *
207      * <pre>
208      * int i = callbacks.beginBroadcast();
209      * while (i &gt; 0) {
210      *     i--;
211      *     try {
212      *         callbacks.getBroadcastItem(i).somethingHappened();
213      *     } catch (RemoteException e) {
214      *         // The RemoteCallbackList will take care of removing
215      *         // the dead object for us.
216      *     }
217      * }
218      * callbacks.finishBroadcast();</pre>
219      *
220      * @return Returns the number of callbacks in the broadcast, to be used
221      * with {@link #getBroadcastItem} to determine the range of indices you
222      * can supply.
223      *
224      * @see #getBroadcastItem
225      * @see #finishBroadcast
226      */
beginBroadcast()227     public int beginBroadcast() {
228         synchronized (mCallbacks) {
229             if (mBroadcastCount > 0) {
230                 throw new IllegalStateException(
231                         "beginBroadcast() called while already in a broadcast");
232             }
233 
234             final int N = mBroadcastCount = mCallbacks.size();
235             if (N <= 0) {
236                 return 0;
237             }
238             Object[] active = mActiveBroadcast;
239             if (active == null || active.length < N) {
240                 mActiveBroadcast = active = new Object[N];
241             }
242             for (int i=0; i<N; i++) {
243                 active[i] = mCallbacks.valueAt(i);
244             }
245             return N;
246         }
247     }
248 
249     /**
250      * Retrieve an item in the active broadcast that was previously started
251      * with {@link #beginBroadcast}.  This can <em>only</em> be called after
252      * the broadcast is started, and its data is no longer valid after
253      * calling {@link #finishBroadcast}.
254      *
255      * <p>Note that it is possible for the process of one of the returned
256      * callbacks to go away before you call it, so you will need to catch
257      * {@link RemoteException} when calling on to the returned object.
258      * The callback list itself, however, will take care of unregistering
259      * these objects once it detects that it is no longer valid, so you can
260      * handle such an exception by simply ignoring it.
261      *
262      * @param index Which of the registered callbacks you would like to
263      * retrieve.  Ranges from 0 to 1-{@link #beginBroadcast}.
264      *
265      * @return Returns the callback interface that you can call.  This will
266      * always be non-null.
267      *
268      * @see #beginBroadcast
269      */
getBroadcastItem(int index)270     public E getBroadcastItem(int index) {
271         return ((Callback)mActiveBroadcast[index]).mCallback;
272     }
273 
274     /**
275      * Retrieve the cookie associated with the item
276      * returned by {@link #getBroadcastItem(int)}.
277      *
278      * @see #getBroadcastItem
279      */
getBroadcastCookie(int index)280     public Object getBroadcastCookie(int index) {
281         return ((Callback)mActiveBroadcast[index]).mCookie;
282     }
283 
284     /**
285      * Clean up the state of a broadcast previously initiated by calling
286      * {@link #beginBroadcast}.  This must always be called when you are done
287      * with a broadcast.
288      *
289      * @see #beginBroadcast
290      */
finishBroadcast()291     public void finishBroadcast() {
292         if (mBroadcastCount < 0) {
293             throw new IllegalStateException(
294                     "finishBroadcast() called outside of a broadcast");
295         }
296 
297         Object[] active = mActiveBroadcast;
298         if (active != null) {
299             final int N = mBroadcastCount;
300             for (int i=0; i<N; i++) {
301                 active[i] = null;
302             }
303         }
304 
305         mBroadcastCount = -1;
306     }
307 
308     /**
309      * Returns the number of registered callbacks. Note that the number of registered
310      * callbacks may differ from the value returned by {@link #beginBroadcast()} since
311      * the former returns the number of callbacks registered at the time of the call
312      * and the second the number of callback to which the broadcast will be delivered.
313      * <p>
314      * This function is useful to decide whether to schedule a broadcast if this
315      * requires doing some work which otherwise would not be performed.
316      * </p>
317      *
318      * @return The size.
319      */
getRegisteredCallbackCount()320     public int getRegisteredCallbackCount() {
321         synchronized (mCallbacks) {
322             if (mKilled) {
323                 return 0;
324             }
325             return mCallbacks.size();
326         }
327     }
328 }
329