RemoteCallbackList.java revision 105925376f8d0f6b318c9938c7b83ef7fef094da
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 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 } 214 return i; 215 } 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} 260