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 
     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  */
     49 public class RemoteCallbackList<E extends IInterface> {
     50     /*package*/ ArrayMap<IBinder, Callback> mCallbacks
     51             = new ArrayMap<IBinder, Callback>();
     52     private Object[] mActiveBroadcast;
     53     private int mBroadcastCount = -1;
     54     private boolean mKilled = false;
     55 
     56     private final class Callback implements IBinder.DeathRecipient {
     57         final E mCallback;
     58         final Object mCookie;
     59 
     60         Callback(E callback, Object cookie) {
     61             mCallback = callback;
     62             mCookie = cookie;
     63         }
     64 
     65         public void binderDied() {
     66             synchronized (mCallbacks) {
     67                 mCallbacks.remove(mCallback.asBinder());
     68             }
     69             onCallbackDied(mCallback, mCookie);
     70         }
     71     }
     72 
     73     /**
     74      * Simple version of {@link RemoteCallbackList#register(E, Object)}
     75      * that does not take a cookie object.
     76      */
     77     public boolean register(E callback) {
     78         return register(callback, null);
     79     }
     80     /**
     81      * Add a new callback to the list.  This callback will remain in the list
     82      * until a corresponding call to {@link #unregister} or its hosting process
     83      * goes away.  If the callback was already registered (determined by
     84      * checking to see if the {@link IInterface#asBinder callback.asBinder()}
     85      * object is already in the list), then it will be left as-is.
     86      * Registrations are not counted; a single call to {@link #unregister}
     87      * will remove a callback after any number calls to register it.
     88      *
     89      * @param callback The callback interface to be added to the list.  Must
     90      * not be null -- passing null here will cause a NullPointerException.
     91      * Most services will want to check for null before calling this with
     92      * an object given from a client, so that clients can't crash the
     93      * service with bad data.
     94      *
     95      * @param cookie Optional additional data to be associated with this
     96      * callback.
     97      *
     98      * @return Returns true if the callback was successfully added to the list.
     99      * Returns false if it was not added, either because {@link #kill} had
    100      * previously been called or the callback's process has gone away.
    101      *
    102      * @see #unregister
    103      * @see #kill
    104      * @see #onCallbackDied
    105      */
    106     public boolean register(E callback, Object cookie) {
    107         synchronized (mCallbacks) {
    108             if (mKilled) {
    109                 return false;
    110             }
    111             IBinder binder = callback.asBinder();
    112             try {
    113                 Callback cb = new Callback(callback, cookie);
    114                 binder.linkToDeath(cb, 0);
    115                 mCallbacks.put(binder, cb);
    116                 return true;
    117             } catch (RemoteException e) {
    118                 return false;
    119             }
    120         }
    121     }
    122 
    123     /**
    124      * Remove from the list a callback that was previously added with
    125      * {@link #register}.  This uses the
    126      * {@link IInterface#asBinder callback.asBinder()} object to correctly
    127      * find the previous registration.
    128      * Registrations are not counted; a single unregister call will remove
    129      * a callback after any number calls to {@link #register} for it.
    130      *
    131      * @param callback The callback to be removed from the list.  Passing
    132      * null here will cause a NullPointerException, so you will generally want
    133      * to check for null before calling.
    134      *
    135      * @return Returns true if the callback was found and unregistered.  Returns
    136      * false if the given callback was not found on the list.
    137      *
    138      * @see #register
    139      */
    140     public boolean unregister(E callback) {
    141         synchronized (mCallbacks) {
    142             Callback cb = mCallbacks.remove(callback.asBinder());
    143             if (cb != null) {
    144                 cb.mCallback.asBinder().unlinkToDeath(cb, 0);
    145                 return true;
    146             }
    147             return false;
    148         }
    149     }
    150 
    151     /**
    152      * Disable this callback list.  All registered callbacks are unregistered,
    153      * and the list is disabled so that future calls to {@link #register} will
    154      * fail.  This should be used when a Service is stopping, to prevent clients
    155      * from registering callbacks after it is stopped.
    156      *
    157      * @see #register
    158      */
    159     public void kill() {
    160         synchronized (mCallbacks) {
    161             for (int cbi=mCallbacks.size()-1; cbi>=0; cbi--) {
    162                 Callback cb = mCallbacks.valueAt(cbi);
    163                 cb.mCallback.asBinder().unlinkToDeath(cb, 0);
    164             }
    165             mCallbacks.clear();
    166             mKilled = true;
    167         }
    168     }
    169 
    170     /**
    171      * Old version of {@link #onCallbackDied(E, Object)} that
    172      * does not provide a cookie.
    173      */
    174     public void onCallbackDied(E callback) {
    175     }
    176 
    177     /**
    178      * Called when the process hosting a callback in the list has gone away.
    179      * The default implementation calls {@link #onCallbackDied(E)}
    180      * for backwards compatibility.
    181      *
    182      * @param callback The callback whose process has died.  Note that, since
    183      * its process has died, you can not make any calls on to this interface.
    184      * You can, however, retrieve its IBinder and compare it with another
    185      * IBinder to see if it is the same object.
    186      * @param cookie The cookie object original provided to
    187      * {@link #register(E, Object)}.
    188      *
    189      * @see #register
    190      */
    191     public void onCallbackDied(E callback, Object cookie) {
    192         onCallbackDied(callback);
    193     }
    194 
    195     /**
    196      * Prepare to start making calls to the currently registered callbacks.
    197      * This creates a copy of the callback list, which you can retrieve items
    198      * from using {@link #getBroadcastItem}.  Note that only one broadcast can
    199      * be active at a time, so you must be sure to always call this from the
    200      * same thread (usually by scheduling with {@link Handler}) or
    201      * do your own synchronization.  You must call {@link #finishBroadcast}
    202      * when done.
    203      *
    204      * <p>A typical loop delivering a broadcast looks like this:
    205      *
    206      * <pre>
    207      * int i = callbacks.beginBroadcast();
    208      * while (i &gt; 0) {
    209      *     i--;
    210      *     try {
    211      *         callbacks.getBroadcastItem(i).somethingHappened();
    212      *     } catch (RemoteException e) {
    213      *         // The RemoteCallbackList will take care of removing
    214      *         // the dead object for us.
    215      *     }
    216      * }
    217      * callbacks.finishBroadcast();</pre>
    218      *
    219      * @return Returns the number of callbacks in the broadcast, to be used
    220      * with {@link #getBroadcastItem} to determine the range of indices you
    221      * can supply.
    222      *
    223      * @see #getBroadcastItem
    224      * @see #finishBroadcast
    225      */
    226     public int beginBroadcast() {
    227         synchronized (mCallbacks) {
    228             if (mBroadcastCount > 0) {
    229                 throw new IllegalStateException(
    230                         "beginBroadcast() called while already in a broadcast");
    231             }
    232 
    233             final int N = mBroadcastCount = mCallbacks.size();
    234             if (N <= 0) {
    235                 return 0;
    236             }
    237             Object[] active = mActiveBroadcast;
    238             if (active == null || active.length < N) {
    239                 mActiveBroadcast = active = new Object[N];
    240             }
    241             for (int i=0; i<N; i++) {
    242                 active[i] = mCallbacks.valueAt(i);
    243             }
    244             return N;
    245         }
    246     }
    247 
    248     /**
    249      * Retrieve an item in the active broadcast that was previously started
    250      * with {@link #beginBroadcast}.  This can <em>only</em> be called after
    251      * the broadcast is started, and its data is no longer valid after
    252      * calling {@link #finishBroadcast}.
    253      *
    254      * <p>Note that it is possible for the process of one of the returned
    255      * callbacks to go away before you call it, so you will need to catch
    256      * {@link RemoteException} when calling on to the returned object.
    257      * The callback list itself, however, will take care of unregistering
    258      * these objects once it detects that it is no longer valid, so you can
    259      * handle such an exception by simply ignoring it.
    260      *
    261      * @param index Which of the registered callbacks you would like to
    262      * retrieve.  Ranges from 0 to 1-{@link #beginBroadcast}.
    263      *
    264      * @return Returns the callback interface that you can call.  This will
    265      * always be non-null.
    266      *
    267      * @see #beginBroadcast
    268      */
    269     public E getBroadcastItem(int index) {
    270         return ((Callback)mActiveBroadcast[index]).mCallback;
    271     }
    272 
    273     /**
    274      * Retrieve the cookie associated with the item
    275      * returned by {@link #getBroadcastItem(int)}.
    276      *
    277      * @see #getBroadcastItem
    278      */
    279     public Object getBroadcastCookie(int index) {
    280         return ((Callback)mActiveBroadcast[index]).mCookie;
    281     }
    282 
    283     /**
    284      * Clean up the state of a broadcast previously initiated by calling
    285      * {@link #beginBroadcast}.  This must always be called when you are done
    286      * with a broadcast.
    287      *
    288      * @see #beginBroadcast
    289      */
    290     public void finishBroadcast() {
    291         synchronized (mCallbacks) {
    292             if (mBroadcastCount < 0) {
    293                 throw new IllegalStateException(
    294                         "finishBroadcast() called outside of a broadcast");
    295             }
    296 
    297             Object[] active = mActiveBroadcast;
    298             if (active != null) {
    299                 final int N = mBroadcastCount;
    300                 for (int i=0; i<N; i++) {
    301                     active[i] = null;
    302                 }
    303             }
    304 
    305             mBroadcastCount = -1;
    306         }
    307     }
    308 
    309     /**
    310      * Returns the number of registered callbacks. Note that the number of registered
    311      * callbacks may differ from the value returned by {@link #beginBroadcast()} since
    312      * the former returns the number of callbacks registered at the time of the call
    313      * and the second the number of callback to which the broadcast will be delivered.
    314      * <p>
    315      * This function is useful to decide whether to schedule a broadcast if this
    316      * requires doing some work which otherwise would not be performed.
    317      * </p>
    318      *
    319      * @return The size.
    320      */
    321     public int getRegisteredCallbackCount() {
    322         synchronized (mCallbacks) {
    323             if (mKilled) {
    324                 return 0;
    325             }
    326             return mCallbacks.size();
    327         }
    328     }
    329 }
    330