Home | History | Annotate | Download | only in os
      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 
     17 package android.os;
     18 
     19 import android.util.ArrayMap;
     20 import android.util.Slog;
     21 
     22 import java.io.PrintWriter;
     23 import java.util.function.Consumer;
     24 
     25 /**
     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:
     29  *
     30  * <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>
     41  *
     42  * <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}.
     47  *
     48  * <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  */
     53 public class RemoteCallbackList<E extends IInterface> {
     54     private static final String TAG = "RemoteCallbackList";
     55 
     56     /*package*/ ArrayMap<IBinder, Callback> mCallbacks
     57             = new ArrayMap<IBinder, Callback>();
     58     private Object[] mActiveBroadcast;
     59     private int mBroadcastCount = -1;
     60     private boolean mKilled = false;
     61     private StringBuilder mRecentCallers;
     62 
     63     private final class Callback implements IBinder.DeathRecipient {
     64         final E mCallback;
     65         final Object mCookie;
     66 
     67         Callback(E callback, Object cookie) {
     68             mCallback = callback;
     69             mCookie = cookie;
     70         }
     71 
     72         public void binderDied() {
     73             synchronized (mCallbacks) {
     74                 mCallbacks.remove(mCallback.asBinder());
     75             }
     76             onCallbackDied(mCallback, mCookie);
     77         }
     78     }
     79 
     80     /**
     81      * 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     }
     87 
     88     /**
     89      * 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.
     96      *
     97      * @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.
    102      *
    103      * @param cookie Optional additional data to be associated with this
    104      * callback.
    105      *
    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.
    109      *
    110      * @see #unregister
    111      * @see #kill
    112      * @see #onCallbackDied
    113      */
    114     public boolean register(E callback, Object cookie) {
    115         synchronized (mCallbacks) {
    116             if (mKilled) {
    117                 return false;
    118             }
    119             // Flag unusual case that could be caused by a leak. b/36778087
    120             logExcessiveCallbacks();
    121             IBinder binder = callback.asBinder();
    122             try {
    123                 Callback cb = new Callback(callback, cookie);
    124                 binder.linkToDeath(cb, 0);
    125                 mCallbacks.put(binder, cb);
    126                 return true;
    127             } catch (RemoteException e) {
    128                 return false;
    129             }
    130         }
    131     }
    132 
    133     /**
    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.
    140      *
    141      * @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.
    144      *
    145      * @return Returns true if the callback was found and unregistered.  Returns
    146      * false if the given callback was not found on the list.
    147      *
    148      * @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     }
    160 
    161     /**
    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.
    166      *
    167      * @see #register
    168      */
    169     public void kill() {
    170         synchronized (mCallbacks) {
    171             for (int cbi=mCallbacks.size()-1; cbi>=0; cbi--) {
    172                 Callback cb = mCallbacks.valueAt(cbi);
    173                 cb.mCallback.asBinder().unlinkToDeath(cb, 0);
    174             }
    175             mCallbacks.clear();
    176             mKilled = true;
    177         }
    178     }
    179 
    180     /**
    181      * Old version of {@link #onCallbackDied(E, Object)} that
    182      * does not provide a cookie.
    183      */
    184     public void onCallbackDied(E callback) {
    185     }
    186 
    187     /**
    188      * Called when the process hosting a callback in the list has gone away.
    189      * The default implementation calls {@link #onCallbackDied(E)}
    190      * for backwards compatibility.
    191      *
    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.
    196      * @param cookie The cookie object original provided to
    197      * {@link #register(E, Object)}.
    198      *
    199      * @see #register
    200      */
    201     public void onCallbackDied(E callback, Object cookie) {
    202         onCallbackDied(callback);
    203     }
    204 
    205     /**
    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
    210      * same thread (usually by scheduling with {@link Handler}) or
    211      * do your own synchronization.  You must call {@link #finishBroadcast}
    212      * when done.
    213      *
    214      * <p>A typical loop delivering a broadcast looks like this:
    215      *
    216      * <pre>
    217      * int i = callbacks.beginBroadcast();
    218      * while (i &gt; 0) {
    219      *     i--;
    220      *     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>
    228      *
    229      * @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.
    232      *
    233      * @see #getBroadcastItem
    234      * @see #finishBroadcast
    235      */
    236     public int beginBroadcast() {
    237         synchronized (mCallbacks) {
    238             if (mBroadcastCount > 0) {
    239                 throw new IllegalStateException(
    240                         "beginBroadcast() called while already in a broadcast");
    241             }
    242 
    243             final int N = mBroadcastCount = mCallbacks.size();
    244             if (N <= 0) {
    245                 return 0;
    246             }
    247             Object[] active = mActiveBroadcast;
    248             if (active == null || active.length < N) {
    249                 mActiveBroadcast = active = new Object[N];
    250             }
    251             for (int i=0; i<N; i++) {
    252                 active[i] = mCallbacks.valueAt(i);
    253             }
    254             return N;
    255         }
    256     }
    257 
    258     /**
    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}.
    263      *
    264      * <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.
    270      *
    271      * @param index Which of the registered callbacks you would like to
    272      * retrieve.  Ranges from 0 to 1-{@link #beginBroadcast}.
    273      *
    274      * @return Returns the callback interface that you can call.  This will
    275      * always be non-null.
    276      *
    277      * @see #beginBroadcast
    278      */
    279     public E getBroadcastItem(int index) {
    280         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;
    291     }
    292 
    293     /**
    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.
    297      *
    298      * @see #beginBroadcast
    299      */
    300     public void finishBroadcast() {
    301         synchronized (mCallbacks) {
    302             if (mBroadcastCount < 0) {
    303                 throw new IllegalStateException(
    304                         "finishBroadcast() called outside of a broadcast");
    305             }
    306 
    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;
    316         }
    317     }
    318 
    319     /**
    320      * 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     /**
    337      * 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     /**
    354      * 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     }
    373 
    374     /**
    375      * 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
    378      * 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      *
    382      * @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.
    407      *
    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     }
    419 
    420     /** @hide */
    421     public void dump(PrintWriter pw, String prefix) {
    422         pw.print(prefix); pw.print("callbacks: "); pw.println(mCallbacks.size());
    423         pw.print(prefix); pw.print("killed: "); pw.println(mKilled);
    424         pw.print(prefix); pw.print("broadcasts count: "); pw.println(mBroadcastCount);
    425     }
    426 
    427     private void logExcessiveCallbacks() {
    428         final long size = mCallbacks.size();
    429         final long TOO_MANY = 3000;
    430         final long MAX_CHARS = 1000;
    431         if (size >= TOO_MANY) {
    432             if (size == TOO_MANY && mRecentCallers == null) {
    433                 mRecentCallers = new StringBuilder();
    434             }
    435             if (mRecentCallers != null && mRecentCallers.length() < MAX_CHARS) {
    436                 mRecentCallers.append(Debug.getCallers(5));
    437                 mRecentCallers.append('\n');
    438                 if (mRecentCallers.length() >= MAX_CHARS) {
    439                     Slog.wtf(TAG, "More than "
    440                             + TOO_MANY + " remote callbacks registered. Recent callers:\n"
    441                             + mRecentCallers.toString());
    442                     mRecentCallers = null;
    443                 }
    444             }
    445         }
    446     }
    447 }
    448