blob: c1d4ae9be79ef8512b38a4cb96c188b9e572257d [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
Dianne Hackborn390517b2013-05-30 15:03:32 -070019import android.util.ArrayMap;
20
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import java.util.HashMap;
22
23/**
24 * Takes care of the grunt work of maintaining a list of remote interfaces,
25 * typically for the use of performing callbacks from a
26 * {@link android.app.Service} to its clients. In particular, this:
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070027 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028 * <ul>
29 * <li> Keeps track of a set of registered {@link IInterface} callbacks,
30 * taking care to identify them through their underlying unique {@link IBinder}
31 * (by calling {@link IInterface#asBinder IInterface.asBinder()}.
32 * <li> Attaches a {@link IBinder.DeathRecipient IBinder.DeathRecipient} to
33 * each registered interface, so that it can be cleaned out of the list if its
34 * process goes away.
35 * <li> Performs locking of the underlying list of interfaces to deal with
36 * multithreaded incoming calls, and a thread-safe way to iterate over a
37 * snapshot of the list without holding its lock.
38 * </ul>
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070039 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040 * <p>To use this class, simply create a single instance along with your
41 * service, and call its {@link #register} and {@link #unregister} methods
42 * as client register and unregister with your service. To call back on to
43 * the registered clients, use {@link #beginBroadcast},
44 * {@link #getBroadcastItem}, and {@link #finishBroadcast}.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070045 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046 * <p>If a registered callback's process goes away, this class will take
47 * care of automatically removing it from the list. If you want to do
48 * additional work in this situation, you can create a subclass that
49 * implements the {@link #onCallbackDied} method.
50 */
51public class RemoteCallbackList<E extends IInterface> {
Dianne Hackborn390517b2013-05-30 15:03:32 -070052 /*package*/ ArrayMap<IBinder, Callback> mCallbacks
53 = new ArrayMap<IBinder, Callback>();
Dianne Hackborn231cc602009-04-27 17:10:36 -070054 private Object[] mActiveBroadcast;
Dianne Hackbornb06ea702009-07-13 13:07:51 -070055 private int mBroadcastCount = -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056 private boolean mKilled = false;
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070057
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058 private final class Callback implements IBinder.DeathRecipient {
59 final E mCallback;
Dianne Hackborn231cc602009-04-27 17:10:36 -070060 final Object mCookie;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061
Dianne Hackborn231cc602009-04-27 17:10:36 -070062 Callback(E callback, Object cookie) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063 mCallback = callback;
Dianne Hackborn231cc602009-04-27 17:10:36 -070064 mCookie = cookie;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070066
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067 public void binderDied() {
68 synchronized (mCallbacks) {
69 mCallbacks.remove(mCallback.asBinder());
70 }
Dianne Hackborn231cc602009-04-27 17:10:36 -070071 onCallbackDied(mCallback, mCookie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 }
73 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070074
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 /**
Dianne Hackborn231cc602009-04-27 17:10:36 -070076 * Simple version of {@link RemoteCallbackList#register(E, Object)}
77 * that does not take a cookie object.
78 */
79 public boolean register(E callback) {
80 return register(callback, null);
81 }
82
83 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 * Add a new callback to the list. This callback will remain in the list
85 * until a corresponding call to {@link #unregister} or its hosting process
86 * goes away. If the callback was already registered (determined by
87 * checking to see if the {@link IInterface#asBinder callback.asBinder()}
88 * object is already in the list), then it will be left as-is.
89 * Registrations are not counted; a single call to {@link #unregister}
90 * will remove a callback after any number calls to register it.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070091 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092 * @param callback The callback interface to be added to the list. Must
93 * not be null -- passing null here will cause a NullPointerException.
94 * Most services will want to check for null before calling this with
95 * an object given from a client, so that clients can't crash the
96 * service with bad data.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070097 *
Dianne Hackborn231cc602009-04-27 17:10:36 -070098 * @param cookie Optional additional data to be associated with this
99 * callback.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100 *
101 * @return Returns true if the callback was successfully added to the list.
102 * Returns false if it was not added, either because {@link #kill} had
103 * previously been called or the callback's process has gone away.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700104 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 * @see #unregister
106 * @see #kill
107 * @see #onCallbackDied
108 */
Dianne Hackborn231cc602009-04-27 17:10:36 -0700109 public boolean register(E callback, Object cookie) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 synchronized (mCallbacks) {
111 if (mKilled) {
112 return false;
113 }
114 IBinder binder = callback.asBinder();
115 try {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700116 Callback cb = new Callback(callback, cookie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 binder.linkToDeath(cb, 0);
118 mCallbacks.put(binder, cb);
119 return true;
120 } catch (RemoteException e) {
121 return false;
122 }
123 }
124 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700125
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 /**
127 * Remove from the list a callback that was previously added with
128 * {@link #register}. This uses the
129 * {@link IInterface#asBinder callback.asBinder()} object to correctly
130 * find the previous registration.
131 * Registrations are not counted; a single unregister call will remove
132 * a callback after any number calls to {@link #register} for it.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700133 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 * @param callback The callback to be removed from the list. Passing
135 * null here will cause a NullPointerException, so you will generally want
136 * to check for null before calling.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700137 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 * @return Returns true if the callback was found and unregistered. Returns
139 * false if the given callback was not found on the list.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700140 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 * @see #register
142 */
143 public boolean unregister(E callback) {
144 synchronized (mCallbacks) {
145 Callback cb = mCallbacks.remove(callback.asBinder());
146 if (cb != null) {
147 cb.mCallback.asBinder().unlinkToDeath(cb, 0);
148 return true;
149 }
150 return false;
151 }
152 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700153
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154 /**
155 * Disable this callback list. All registered callbacks are unregistered,
156 * and the list is disabled so that future calls to {@link #register} will
157 * fail. This should be used when a Service is stopping, to prevent clients
158 * from registering callbacks after it is stopped.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700159 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160 * @see #register
161 */
162 public void kill() {
163 synchronized (mCallbacks) {
Dianne Hackborn390517b2013-05-30 15:03:32 -0700164 for (int cbi=mCallbacks.size()-1; cbi>=0; cbi--) {
165 Callback cb = mCallbacks.valueAt(cbi);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 cb.mCallback.asBinder().unlinkToDeath(cb, 0);
167 }
168 mCallbacks.clear();
169 mKilled = true;
170 }
171 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700172
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 /**
Dianne Hackborn231cc602009-04-27 17:10:36 -0700174 * Old version of {@link #onCallbackDied(E, Object)} that
175 * does not provide a cookie.
176 */
177 public void onCallbackDied(E callback) {
178 }
179
180 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 * Called when the process hosting a callback in the list has gone away.
Dianne Hackborn231cc602009-04-27 17:10:36 -0700182 * The default implementation calls {@link #onCallbackDied(E)}
183 * for backwards compatibility.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184 *
185 * @param callback The callback whose process has died. Note that, since
186 * its process has died, you can not make any calls on to this interface.
187 * You can, however, retrieve its IBinder and compare it with another
188 * IBinder to see if it is the same object.
Dianne Hackborn231cc602009-04-27 17:10:36 -0700189 * @param cookie The cookie object original provided to
190 * {@link #register(E, Object)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 *
192 * @see #register
193 */
Dianne Hackborn231cc602009-04-27 17:10:36 -0700194 public void onCallbackDied(E callback, Object cookie) {
195 onCallbackDied(callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700197
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 /**
199 * Prepare to start making calls to the currently registered callbacks.
200 * This creates a copy of the callback list, which you can retrieve items
201 * from using {@link #getBroadcastItem}. Note that only one broadcast can
202 * be active at a time, so you must be sure to always call this from the
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700203 * same thread (usually by scheduling with {@link Handler}) or
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 * do your own synchronization. You must call {@link #finishBroadcast}
205 * when done.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700206 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 * <p>A typical loop delivering a broadcast looks like this:
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700208 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 * <pre>
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700210 * int i = callbacks.beginBroadcast();
Dianne Hackborndace2302009-07-14 12:51:00 -0700211 * while (i &gt; 0) {
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700212 * i--;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213 * try {
214 * callbacks.getBroadcastItem(i).somethingHappened();
215 * } catch (RemoteException e) {
216 * // The RemoteCallbackList will take care of removing
217 * // the dead object for us.
218 * }
219 * }
220 * callbacks.finishBroadcast();</pre>
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700221 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 * @return Returns the number of callbacks in the broadcast, to be used
223 * with {@link #getBroadcastItem} to determine the range of indices you
224 * can supply.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700225 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226 * @see #getBroadcastItem
227 * @see #finishBroadcast
228 */
229 public int beginBroadcast() {
230 synchronized (mCallbacks) {
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700231 if (mBroadcastCount > 0) {
232 throw new IllegalStateException(
233 "beginBroadcast() called while already in a broadcast");
234 }
235
236 final int N = mBroadcastCount = mCallbacks.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 if (N <= 0) {
238 return 0;
239 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700240 Object[] active = mActiveBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 if (active == null || active.length < N) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700242 mActiveBroadcast = active = new Object[N];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 }
Dianne Hackborn390517b2013-05-30 15:03:32 -0700244 for (int i=0; i<N; i++) {
245 active[i] = mCallbacks.valueAt(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800246 }
Dianne Hackborn390517b2013-05-30 15:03:32 -0700247 return N;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248 }
249 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700250
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251 /**
252 * Retrieve an item in the active broadcast that was previously started
253 * with {@link #beginBroadcast}. This can <em>only</em> be called after
254 * the broadcast is started, and its data is no longer valid after
255 * calling {@link #finishBroadcast}.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700256 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800257 * <p>Note that it is possible for the process of one of the returned
258 * callbacks to go away before you call it, so you will need to catch
259 * {@link RemoteException} when calling on to the returned object.
260 * The callback list itself, however, will take care of unregistering
261 * these objects once it detects that it is no longer valid, so you can
262 * handle such an exception by simply ignoring it.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700263 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264 * @param index Which of the registered callbacks you would like to
265 * retrieve. Ranges from 0 to 1-{@link #beginBroadcast}.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700266 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267 * @return Returns the callback interface that you can call. This will
268 * always be non-null.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700269 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270 * @see #beginBroadcast
271 */
272 public E getBroadcastItem(int index) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700273 return ((Callback)mActiveBroadcast[index]).mCallback;
274 }
275
276 /**
277 * Retrieve the cookie associated with the item
278 * returned by {@link #getBroadcastItem(int)}.
279 *
280 * @see #getBroadcastItem
281 */
282 public Object getBroadcastCookie(int index) {
283 return ((Callback)mActiveBroadcast[index]).mCookie;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700285
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 /**
287 * Clean up the state of a broadcast previously initiated by calling
288 * {@link #beginBroadcast}. This must always be called when you are done
289 * with a broadcast.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700290 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291 * @see #beginBroadcast
292 */
293 public void finishBroadcast() {
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700294 if (mBroadcastCount < 0) {
295 throw new IllegalStateException(
296 "finishBroadcast() called outside of a broadcast");
297 }
298
Dianne Hackborn231cc602009-04-27 17:10:36 -0700299 Object[] active = mActiveBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 if (active != null) {
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700301 final int N = mBroadcastCount;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 for (int i=0; i<N; i++) {
303 active[i] = null;
304 }
305 }
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700306
307 mBroadcastCount = -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308 }
Svetoslav Ganov1cf70bb2012-08-06 10:53:34 -0700309
310 /**
311 * Returns the number of registered callbacks. Note that the number of registered
312 * callbacks may differ from the value returned by {@link #beginBroadcast()} since
313 * the former returns the number of callbacks registered at the time of the call
314 * and the second the number of callback to which the broadcast will be delivered.
315 * <p>
316 * This function is useful to decide whether to schedule a broadcast if this
317 * requires doing some work which otherwise would not be performed.
318 * </p>
319 *
320 * @return The size.
321 */
322 public int getRegisteredCallbackCount() {
323 synchronized (mCallbacks) {
324 if (mKilled) {
325 return 0;
326 }
327 return mCallbacks.size();
328 }
329 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330}