blob: be8f7847f6a89bbb4014b28c9ad32685a19a07b1 [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;
Amith Yamasani18a23f22017-04-06 10:29:33 -070020import android.util.Slog;
Dianne Hackborn390517b2013-05-30 15:03:32 -070021
Koji Fukuiccec6a62017-10-18 17:48:40 +090022import java.io.PrintWriter;
Eugene Suslaef4351c2017-01-18 11:07:06 -080023import java.util.function.Consumer;
24
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025/**
26 * Takes care of the grunt work of maintaining a list of remote interfaces,
27 * typically for the use of performing callbacks from a
28 * {@link android.app.Service} to its clients. In particular, this:
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070029 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030 * <ul>
31 * <li> Keeps track of a set of registered {@link IInterface} callbacks,
32 * taking care to identify them through their underlying unique {@link IBinder}
33 * (by calling {@link IInterface#asBinder IInterface.asBinder()}.
34 * <li> Attaches a {@link IBinder.DeathRecipient IBinder.DeathRecipient} to
35 * each registered interface, so that it can be cleaned out of the list if its
36 * process goes away.
37 * <li> Performs locking of the underlying list of interfaces to deal with
38 * multithreaded incoming calls, and a thread-safe way to iterate over a
39 * snapshot of the list without holding its lock.
40 * </ul>
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070041 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042 * <p>To use this class, simply create a single instance along with your
43 * service, and call its {@link #register} and {@link #unregister} methods
44 * as client register and unregister with your service. To call back on to
45 * the registered clients, use {@link #beginBroadcast},
46 * {@link #getBroadcastItem}, and {@link #finishBroadcast}.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070047 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048 * <p>If a registered callback's process goes away, this class will take
49 * care of automatically removing it from the list. If you want to do
50 * additional work in this situation, you can create a subclass that
51 * implements the {@link #onCallbackDied} method.
52 */
53public class RemoteCallbackList<E extends IInterface> {
Amith Yamasani18a23f22017-04-06 10:29:33 -070054 private static final String TAG = "RemoteCallbackList";
55
Dianne Hackborn390517b2013-05-30 15:03:32 -070056 /*package*/ ArrayMap<IBinder, Callback> mCallbacks
57 = new ArrayMap<IBinder, Callback>();
Dianne Hackborn231cc602009-04-27 17:10:36 -070058 private Object[] mActiveBroadcast;
Dianne Hackbornb06ea702009-07-13 13:07:51 -070059 private int mBroadcastCount = -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060 private boolean mKilled = false;
Amith Yamasani18a23f22017-04-06 10:29:33 -070061 private StringBuilder mRecentCallers;
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070062
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063 private final class Callback implements IBinder.DeathRecipient {
64 final E mCallback;
Dianne Hackborn231cc602009-04-27 17:10:36 -070065 final Object mCookie;
Amith Yamasani18a23f22017-04-06 10:29:33 -070066
Dianne Hackborn231cc602009-04-27 17:10:36 -070067 Callback(E callback, Object cookie) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068 mCallback = callback;
Dianne Hackborn231cc602009-04-27 17:10:36 -070069 mCookie = cookie;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070071
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 public void binderDied() {
73 synchronized (mCallbacks) {
74 mCallbacks.remove(mCallback.asBinder());
75 }
Dianne Hackborn231cc602009-04-27 17:10:36 -070076 onCallbackDied(mCallback, mCookie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077 }
78 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070079
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080 /**
Dianne Hackborn231cc602009-04-27 17:10:36 -070081 * Simple version of {@link RemoteCallbackList#register(E, Object)}
82 * that does not take a cookie object.
83 */
84 public boolean register(E callback) {
85 return register(callback, null);
86 }
Dianne Hackborn5614bf52016-11-07 17:26:41 -080087
Dianne Hackborn231cc602009-04-27 17:10:36 -070088 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089 * Add a new callback to the list. This callback will remain in the list
90 * until a corresponding call to {@link #unregister} or its hosting process
91 * goes away. If the callback was already registered (determined by
92 * checking to see if the {@link IInterface#asBinder callback.asBinder()}
93 * object is already in the list), then it will be left as-is.
94 * Registrations are not counted; a single call to {@link #unregister}
95 * will remove a callback after any number calls to register it.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -070096 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 * @param callback The callback interface to be added to the list. Must
98 * not be null -- passing null here will cause a NullPointerException.
99 * Most services will want to check for null before calling this with
100 * an object given from a client, so that clients can't crash the
101 * service with bad data.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700102 *
Dianne Hackborn231cc602009-04-27 17:10:36 -0700103 * @param cookie Optional additional data to be associated with this
104 * callback.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 *
106 * @return Returns true if the callback was successfully added to the list.
107 * Returns false if it was not added, either because {@link #kill} had
108 * previously been called or the callback's process has gone away.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700109 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 * @see #unregister
111 * @see #kill
112 * @see #onCallbackDied
113 */
Dianne Hackborn231cc602009-04-27 17:10:36 -0700114 public boolean register(E callback, Object cookie) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 synchronized (mCallbacks) {
116 if (mKilled) {
117 return false;
118 }
Amith Yamasani18a23f22017-04-06 10:29:33 -0700119 // Flag unusual case that could be caused by a leak. b/36778087
120 logExcessiveCallbacks();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 IBinder binder = callback.asBinder();
122 try {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700123 Callback cb = new Callback(callback, cookie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 binder.linkToDeath(cb, 0);
125 mCallbacks.put(binder, cb);
126 return true;
127 } catch (RemoteException e) {
128 return false;
129 }
130 }
131 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700132
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 /**
134 * Remove from the list a callback that was previously added with
135 * {@link #register}. This uses the
136 * {@link IInterface#asBinder callback.asBinder()} object to correctly
137 * find the previous registration.
138 * Registrations are not counted; a single unregister call will remove
139 * a callback after any number calls to {@link #register} for it.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700140 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 * @param callback The callback to be removed from the list. Passing
142 * null here will cause a NullPointerException, so you will generally want
143 * to check for null before calling.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700144 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145 * @return Returns true if the callback was found and unregistered. Returns
146 * false if the given callback was not found on the list.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700147 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 * @see #register
149 */
150 public boolean unregister(E callback) {
151 synchronized (mCallbacks) {
152 Callback cb = mCallbacks.remove(callback.asBinder());
153 if (cb != null) {
154 cb.mCallback.asBinder().unlinkToDeath(cb, 0);
155 return true;
156 }
157 return false;
158 }
159 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700160
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161 /**
162 * Disable this callback list. All registered callbacks are unregistered,
163 * and the list is disabled so that future calls to {@link #register} will
164 * fail. This should be used when a Service is stopping, to prevent clients
165 * from registering callbacks after it is stopped.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700166 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 * @see #register
168 */
169 public void kill() {
170 synchronized (mCallbacks) {
Dianne Hackborn390517b2013-05-30 15:03:32 -0700171 for (int cbi=mCallbacks.size()-1; cbi>=0; cbi--) {
172 Callback cb = mCallbacks.valueAt(cbi);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 cb.mCallback.asBinder().unlinkToDeath(cb, 0);
174 }
175 mCallbacks.clear();
176 mKilled = true;
177 }
178 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700179
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180 /**
Dianne Hackborn231cc602009-04-27 17:10:36 -0700181 * Old version of {@link #onCallbackDied(E, Object)} that
182 * does not provide a cookie.
183 */
184 public void onCallbackDied(E callback) {
185 }
186
187 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 * Called when the process hosting a callback in the list has gone away.
Dianne Hackborn231cc602009-04-27 17:10:36 -0700189 * The default implementation calls {@link #onCallbackDied(E)}
190 * for backwards compatibility.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 *
192 * @param callback The callback whose process has died. Note that, since
193 * its process has died, you can not make any calls on to this interface.
194 * You can, however, retrieve its IBinder and compare it with another
195 * IBinder to see if it is the same object.
Dianne Hackborn231cc602009-04-27 17:10:36 -0700196 * @param cookie The cookie object original provided to
197 * {@link #register(E, Object)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 *
199 * @see #register
200 */
Dianne Hackborn231cc602009-04-27 17:10:36 -0700201 public void onCallbackDied(E callback, Object cookie) {
202 onCallbackDied(callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700204
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 /**
206 * Prepare to start making calls to the currently registered callbacks.
207 * This creates a copy of the callback list, which you can retrieve items
208 * from using {@link #getBroadcastItem}. Note that only one broadcast can
209 * be active at a time, so you must be sure to always call this from the
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700210 * same thread (usually by scheduling with {@link Handler}) or
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 * do your own synchronization. You must call {@link #finishBroadcast}
212 * when done.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700213 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 * <p>A typical loop delivering a broadcast looks like this:
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700215 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216 * <pre>
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700217 * int i = callbacks.beginBroadcast();
Dianne Hackborndace2302009-07-14 12:51:00 -0700218 * while (i &gt; 0) {
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700219 * i--;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 * try {
221 * callbacks.getBroadcastItem(i).somethingHappened();
222 * } catch (RemoteException e) {
223 * // The RemoteCallbackList will take care of removing
224 * // the dead object for us.
225 * }
226 * }
227 * callbacks.finishBroadcast();</pre>
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700228 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 * @return Returns the number of callbacks in the broadcast, to be used
230 * with {@link #getBroadcastItem} to determine the range of indices you
231 * can supply.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700232 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 * @see #getBroadcastItem
234 * @see #finishBroadcast
235 */
236 public int beginBroadcast() {
237 synchronized (mCallbacks) {
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700238 if (mBroadcastCount > 0) {
239 throw new IllegalStateException(
240 "beginBroadcast() called while already in a broadcast");
241 }
242
243 final int N = mBroadcastCount = mCallbacks.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244 if (N <= 0) {
245 return 0;
246 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700247 Object[] active = mActiveBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248 if (active == null || active.length < N) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700249 mActiveBroadcast = active = new Object[N];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800250 }
Dianne Hackborn390517b2013-05-30 15:03:32 -0700251 for (int i=0; i<N; i++) {
252 active[i] = mCallbacks.valueAt(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253 }
Dianne Hackborn390517b2013-05-30 15:03:32 -0700254 return N;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 }
256 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700257
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800258 /**
259 * Retrieve an item in the active broadcast that was previously started
260 * with {@link #beginBroadcast}. This can <em>only</em> be called after
261 * the broadcast is started, and its data is no longer valid after
262 * calling {@link #finishBroadcast}.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700263 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264 * <p>Note that it is possible for the process of one of the returned
265 * callbacks to go away before you call it, so you will need to catch
266 * {@link RemoteException} when calling on to the returned object.
267 * The callback list itself, however, will take care of unregistering
268 * these objects once it detects that it is no longer valid, so you can
269 * handle such an exception by simply ignoring it.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700270 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800271 * @param index Which of the registered callbacks you would like to
272 * retrieve. Ranges from 0 to 1-{@link #beginBroadcast}.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700273 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800274 * @return Returns the callback interface that you can call. This will
275 * always be non-null.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700276 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 * @see #beginBroadcast
278 */
279 public E getBroadcastItem(int index) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700280 return ((Callback)mActiveBroadcast[index]).mCallback;
281 }
282
283 /**
284 * Retrieve the cookie associated with the item
285 * returned by {@link #getBroadcastItem(int)}.
286 *
287 * @see #getBroadcastItem
288 */
289 public Object getBroadcastCookie(int index) {
290 return ((Callback)mActiveBroadcast[index]).mCookie;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700292
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 /**
294 * Clean up the state of a broadcast previously initiated by calling
295 * {@link #beginBroadcast}. This must always be called when you are done
296 * with a broadcast.
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700297 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 * @see #beginBroadcast
299 */
300 public void finishBroadcast() {
Makoto Onuki7bdb9ce2016-09-15 12:52:57 -0700301 synchronized (mCallbacks) {
302 if (mBroadcastCount < 0) {
303 throw new IllegalStateException(
304 "finishBroadcast() called outside of a broadcast");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305 }
Makoto Onuki7bdb9ce2016-09-15 12:52:57 -0700306
307 Object[] active = mActiveBroadcast;
308 if (active != null) {
309 final int N = mBroadcastCount;
310 for (int i=0; i<N; i++) {
311 active[i] = null;
312 }
313 }
314
315 mBroadcastCount = -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317 }
Svetoslav Ganov1cf70bb2012-08-06 10:53:34 -0700318
319 /**
Eugene Suslaef4351c2017-01-18 11:07:06 -0800320 * Performs {@code action} on each callback, calling
321 * {@link #beginBroadcast()}/{@link #finishBroadcast()} before/after looping
322 *
323 * @hide
324 */
325 public void broadcast(Consumer<E> action) {
326 int itemCount = beginBroadcast();
327 try {
328 for (int i = 0; i < itemCount; i++) {
329 action.accept(getBroadcastItem(i));
330 }
331 } finally {
332 finishBroadcast();
333 }
334 }
335
336 /**
Eugene Susla4b7c919e2017-12-07 11:23:50 -0800337 * Performs {@code action} for each cookie associated with a callback, calling
338 * {@link #beginBroadcast()}/{@link #finishBroadcast()} before/after looping
339 *
340 * @hide
341 */
342 public <C> void broadcastForEachCookie(Consumer<C> action) {
343 int itemCount = beginBroadcast();
344 try {
345 for (int i = 0; i < itemCount; i++) {
346 action.accept((C) getBroadcastCookie(i));
347 }
348 } finally {
349 finishBroadcast();
350 }
351 }
352
353 /**
Svetoslav Ganov1cf70bb2012-08-06 10:53:34 -0700354 * Returns the number of registered callbacks. Note that the number of registered
355 * callbacks may differ from the value returned by {@link #beginBroadcast()} since
356 * the former returns the number of callbacks registered at the time of the call
357 * and the second the number of callback to which the broadcast will be delivered.
358 * <p>
359 * This function is useful to decide whether to schedule a broadcast if this
360 * requires doing some work which otherwise would not be performed.
361 * </p>
362 *
363 * @return The size.
364 */
365 public int getRegisteredCallbackCount() {
366 synchronized (mCallbacks) {
367 if (mKilled) {
368 return 0;
369 }
370 return mCallbacks.size();
371 }
372 }
Dianne Hackborn5614bf52016-11-07 17:26:41 -0800373
374 /**
Dianne Hackborn59359d02017-03-03 16:03:01 -0800375 * Return a currently registered callback. Note that this is
376 * <em>not</em> the same as {@link #getBroadcastItem} and should not be used
377 * interchangeably with it. This method returns the registered callback at the given
Dianne Hackborn5614bf52016-11-07 17:26:41 -0800378 * index, not the current broadcast state. This means that it is not itself thread-safe:
379 * any call to {@link #register} or {@link #unregister} will change these indices, so you
380 * must do your own thread safety between these to protect from such changes.
381 *
Dianne Hackborn59359d02017-03-03 16:03:01 -0800382 * @param index Index of which callback registration to return, from 0 to
383 * {@link #getRegisteredCallbackCount()} - 1.
384 *
385 * @return Returns whatever callback is associated with this index, or null if
386 * {@link #kill()} has been called.
387 */
388 public E getRegisteredCallbackItem(int index) {
389 synchronized (mCallbacks) {
390 if (mKilled) {
391 return null;
392 }
393 return mCallbacks.valueAt(index).mCallback;
394 }
395 }
396
397 /**
398 * Return any cookie associated with a currently registered callback. Note that this is
399 * <em>not</em> the same as {@link #getBroadcastCookie} and should not be used
400 * interchangeably with it. This method returns the current cookie registered at the given
401 * index, not the current broadcast state. This means that it is not itself thread-safe:
402 * any call to {@link #register} or {@link #unregister} will change these indices, so you
403 * must do your own thread safety between these to protect from such changes.
404 *
405 * @param index Index of which registration cookie to return, from 0 to
406 * {@link #getRegisteredCallbackCount()} - 1.
Dianne Hackborn5614bf52016-11-07 17:26:41 -0800407 *
408 * @return Returns whatever cookie object is associated with this index, or null if
409 * {@link #kill()} has been called.
410 */
411 public Object getRegisteredCallbackCookie(int index) {
412 synchronized (mCallbacks) {
413 if (mKilled) {
414 return null;
415 }
416 return mCallbacks.valueAt(index).mCookie;
417 }
418 }
Amith Yamasani18a23f22017-04-06 10:29:33 -0700419
Koji Fukuiccec6a62017-10-18 17:48:40 +0900420 /** @hide */
421 public void dump(PrintWriter pw, String prefix) {
Wei Wang356a75a2018-11-01 16:07:38 -0700422 synchronized (mCallbacks) {
423 pw.print(prefix); pw.print("callbacks: "); pw.println(mCallbacks.size());
424 pw.print(prefix); pw.print("killed: "); pw.println(mKilled);
425 pw.print(prefix); pw.print("broadcasts count: "); pw.println(mBroadcastCount);
426 }
Koji Fukuiccec6a62017-10-18 17:48:40 +0900427 }
428
Amith Yamasani18a23f22017-04-06 10:29:33 -0700429 private void logExcessiveCallbacks() {
430 final long size = mCallbacks.size();
431 final long TOO_MANY = 3000;
432 final long MAX_CHARS = 1000;
433 if (size >= TOO_MANY) {
434 if (size == TOO_MANY && mRecentCallers == null) {
435 mRecentCallers = new StringBuilder();
436 }
437 if (mRecentCallers != null && mRecentCallers.length() < MAX_CHARS) {
438 mRecentCallers.append(Debug.getCallers(5));
439 mRecentCallers.append('\n');
440 if (mRecentCallers.length() >= MAX_CHARS) {
441 Slog.wtf(TAG, "More than "
442 + TOO_MANY + " remote callbacks registered. Recent callers:\n"
443 + mRecentCallers.toString());
444 mRecentCallers = null;
445 }
446 }
447 }
448 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800449}