blob: d02a3203e9ac3121a7389679cf36388efb53c043 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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:
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070025 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026 * <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>
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070037 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038 * <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}.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070043 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044 * <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>();
Dianne Hackborn231cc602009-04-27 17:10:36 -070052 private Object[] mActiveBroadcast;
Dianne Hackbornb06ea702009-07-13 13:07:51 -070053 private int mBroadcastCount = -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054 private boolean mKilled = false;
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070055
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056 private final class Callback implements IBinder.DeathRecipient {
57 final E mCallback;
Dianne Hackborn231cc602009-04-27 17:10:36 -070058 final Object mCookie;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059
Dianne Hackborn231cc602009-04-27 17:10:36 -070060 Callback(E callback, Object cookie) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061 mCallback = callback;
Dianne Hackborn231cc602009-04-27 17:10:36 -070062 mCookie = cookie;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070064
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065 public void binderDied() {
66 synchronized (mCallbacks) {
67 mCallbacks.remove(mCallback.asBinder());
68 }
Dianne Hackborn231cc602009-04-27 17:10:36 -070069 onCallbackDied(mCallback, mCookie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 }
71 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070072
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073 /**
Dianne Hackborn231cc602009-04-27 17:10:36 -070074 * Simple version of {@link RemoteCallbackList#register(E, Object)}
75 * that does not take a cookie object.
76 */
77 public boolean register(E callback) {
78 return register(callback, null);
79 }
80
81 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 * 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.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070089 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090 * @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.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070095 *
Dianne Hackborn231cc602009-04-27 17:10:36 -070096 * @param cookie Optional additional data to be associated with this
97 * callback.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 *
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.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700102 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 * @see #unregister
104 * @see #kill
105 * @see #onCallbackDied
106 */
Dianne Hackborn231cc602009-04-27 17:10:36 -0700107 public boolean register(E callback, Object cookie) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 synchronized (mCallbacks) {
109 if (mKilled) {
110 return false;
111 }
112 IBinder binder = callback.asBinder();
113 try {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700114 Callback cb = new Callback(callback, cookie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 binder.linkToDeath(cb, 0);
116 mCallbacks.put(binder, cb);
117 return true;
118 } catch (RemoteException e) {
119 return false;
120 }
121 }
122 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700123
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 /**
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.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700131 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 * @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.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700135 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 * @return Returns true if the callback was found and unregistered. Returns
137 * false if the given callback was not found on the list.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700138 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 * @see #register
140 */
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 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700151
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 /**
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.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700157 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800158 * @see #register
159 */
160 public void kill() {
161 synchronized (mCallbacks) {
162 for (Callback cb : mCallbacks.values()) {
163 cb.mCallback.asBinder().unlinkToDeath(cb, 0);
164 }
165 mCallbacks.clear();
166 mKilled = true;
167 }
168 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700169
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 /**
Dianne Hackborn231cc602009-04-27 17:10:36 -0700171 * Old version of {@link #onCallbackDied(E, Object)} that
172 * does not provide a cookie.
173 */
174 public void onCallbackDied(E callback) {
175 }
176
177 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 * Called when the process hosting a callback in the list has gone away.
Dianne Hackborn231cc602009-04-27 17:10:36 -0700179 * The default implementation calls {@link #onCallbackDied(E)}
180 * for backwards compatibility.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 *
182 * @param callback The callback whose process has died. Note that, since
183 * its process has died, you can not make any calls on to this interface.
184 * You can, however, retrieve its IBinder and compare it with another
185 * IBinder to see if it is the same object.
Dianne Hackborn231cc602009-04-27 17:10:36 -0700186 * @param cookie The cookie object original provided to
187 * {@link #register(E, Object)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 *
189 * @see #register
190 */
Dianne Hackborn231cc602009-04-27 17:10:36 -0700191 public void onCallbackDied(E callback, Object cookie) {
192 onCallbackDied(callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700194
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 /**
196 * Prepare to start making calls to the currently registered callbacks.
197 * This creates a copy of the callback list, which you can retrieve items
198 * from using {@link #getBroadcastItem}. Note that only one broadcast can
199 * be active at a time, so you must be sure to always call this from the
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700200 * same thread (usually by scheduling with {@link Handler}) or
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 * do your own synchronization. You must call {@link #finishBroadcast}
202 * when done.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700203 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 * <p>A typical loop delivering a broadcast looks like this:
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700205 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 * <pre>
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700207 * int i = callbacks.beginBroadcast();
Dianne Hackborndace2302009-07-14 12:51:00 -0700208 * while (i &gt; 0) {
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700209 * i--;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 * try {
211 * callbacks.getBroadcastItem(i).somethingHappened();
212 * } catch (RemoteException e) {
213 * // The RemoteCallbackList will take care of removing
214 * // the dead object for us.
215 * }
216 * }
217 * callbacks.finishBroadcast();</pre>
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700218 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 * @return Returns the number of callbacks in the broadcast, to be used
220 * with {@link #getBroadcastItem} to determine the range of indices you
221 * can supply.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700222 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 * @see #getBroadcastItem
224 * @see #finishBroadcast
225 */
226 public int beginBroadcast() {
227 synchronized (mCallbacks) {
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700228 if (mBroadcastCount > 0) {
229 throw new IllegalStateException(
230 "beginBroadcast() called while already in a broadcast");
231 }
232
233 final int N = mBroadcastCount = mCallbacks.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234 if (N <= 0) {
235 return 0;
236 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700237 Object[] active = mActiveBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 if (active == null || active.length < N) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700239 mActiveBroadcast = active = new Object[N];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 }
241 int i=0;
242 for (Callback cb : mCallbacks.values()) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700243 active[i++] = cb;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700245 return i;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800246 }
247 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700248
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 /**
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}.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700254 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 * <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.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700261 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262 * @param index Which of the registered callbacks you would like to
263 * retrieve. Ranges from 0 to 1-{@link #beginBroadcast}.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700264 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800265 * @return Returns the callback interface that you can call. This will
266 * always be non-null.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700267 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 * @see #beginBroadcast
269 */
270 public E getBroadcastItem(int index) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700271 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 */
280 public Object getBroadcastCookie(int index) {
281 return ((Callback)mActiveBroadcast[index]).mCookie;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700283
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 /**
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.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700288 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 * @see #beginBroadcast
290 */
291 public void finishBroadcast() {
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700292 if (mBroadcastCount < 0) {
293 throw new IllegalStateException(
294 "finishBroadcast() called outside of a broadcast");
295 }
296
Dianne Hackborn231cc602009-04-27 17:10:36 -0700297 Object[] active = mActiveBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 if (active != null) {
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700299 final int N = mBroadcastCount;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 for (int i=0; i<N; i++) {
301 active[i] = null;
302 }
303 }
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700304
305 mBroadcastCount = -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 }
Svetoslav Ganov1cf70bb2012-08-06 10:53:34 -0700307
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 */
320 public int getRegisteredCallbackCount() {
321 synchronized (mCallbacks) {
322 if (mKilled) {
323 return 0;
324 }
325 return mCallbacks.size();
326 }
327 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328}