blob: 63f6dffba87e5b23617b0c0831552f4460273e86 [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:
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<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 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700214 return i;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 }
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}