blob: 50a9a8359ec3c2624da05247af7395c51f117f08 [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>();
52 private IInterface[] mActiveBroadcast;
53 private boolean mKilled = false;
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070054
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055 private final class Callback implements IBinder.DeathRecipient {
56 final E mCallback;
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070057
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058 Callback(E callback) {
59 mCallback = callback;
60 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070061
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062 public void binderDied() {
63 synchronized (mCallbacks) {
64 mCallbacks.remove(mCallback.asBinder());
65 }
66 onCallbackDied(mCallback);
67 }
68 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070069
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 /**
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.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070078 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079 * @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.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070084 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 * @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.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070088 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089 * @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 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700109
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 /**
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.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700117 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118 * @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.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700121 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 * @return Returns true if the callback was found and unregistered. Returns
123 * false if the given callback was not found on the list.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700124 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 * @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 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700137
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 /**
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.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700143 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144 * @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 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700155
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156 /**
157 * Called when the process hosting a callback in the list has gone away.
158 * The default implementation does nothing.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700159 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160 * @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.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700164 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 * @see #register
166 */
167 public void onCallbackDied(E callback) {
168 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700169
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 /**
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.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700178 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 * <p>A typical loop delivering a broadcast looks like this:
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700180 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 * <pre>
182 * final int N = callbacks.beginBroadcast();
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700183 * for (int i=0; i&lt;N; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184 * 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>
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700192 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 * @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.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700196 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197 * @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 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700217
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 /**
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}.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700223 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 * <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.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700230 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 * @param index Which of the registered callbacks you would like to
232 * retrieve. Ranges from 0 to 1-{@link #beginBroadcast}.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700233 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234 * @return Returns the callback interface that you can call. This will
235 * always be non-null.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700236 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 * @see #beginBroadcast
238 */
239 public E getBroadcastItem(int index) {
240 return (E)mActiveBroadcast[index];
241 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700242
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 /**
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.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700247 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248 * @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}