Home | History | Annotate | Download | only in os
      1 /*
      2  * Copyright (C) 2007 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.Log;
     20 
     21 import java.io.PrintWriter;
     22 import java.util.ArrayList;
     23 import java.util.Set;
     24 import java.util.WeakHashMap;
     25 
     26 /**
     27  * A TokenWatcher watches a collection of {@link IBinder}s. IBinders are added
     28  * to the collection by calling {@link #acquire}, and removed by calling {@link
     29  * #release}. IBinders are also implicitly removed when they become weakly
     30  * reachable. Each IBinder may be added at most once.
     31  *
     32  * The {@link #acquired} method is invoked by posting to the specified handler
     33  * whenever the size of the watched collection becomes nonzero.  The {@link
     34  * #released} method is invoked on the specified handler whenever the size of
     35  * the watched collection becomes zero.
     36  */
     37 public abstract class TokenWatcher
     38 {
     39     /**
     40      * Construct the TokenWatcher
     41      *
     42      * @param h A handler to call {@link #acquired} and {@link #released}
     43      * on.  If you don't care, just call it like this, although your thread
     44      * will have to be a Looper thread.
     45      * <code>new TokenWatcher(new Handler())</code>
     46      * @param tag A debugging tag for this TokenWatcher
     47      */
     48     public TokenWatcher(Handler h, String tag)
     49     {
     50         mHandler = h;
     51         mTag = tag != null ? tag : "TokenWatcher";
     52     }
     53 
     54     /**
     55      * Called when the number of active tokens goes from 0 to 1.
     56      */
     57     public abstract void acquired();
     58 
     59     /**
     60      * Called when the number of active tokens goes from 1 to 0.
     61      */
     62     public abstract void released();
     63 
     64     /**
     65      * Record that this token has been acquired.  When acquire is called, and
     66      * the current count is 0, the acquired method is called on the given
     67      * handler.
     68      *
     69      * Note that the same {@code token} can only be acquired once. If this
     70      * {@code token} has already been acquired, no action is taken. The first
     71      * subsequent call to {@link #release} will release this {@code token}
     72      * immediately.
     73      *
     74      * @param token An IBinder object.
     75      * @param tag   A string used by the {@link #dump} method for debugging,
     76      *              to see who has references.
     77      */
     78     public void acquire(IBinder token, String tag)
     79     {
     80         synchronized (mTokens) {
     81             if (mTokens.containsKey(token)) {
     82                 return;
     83             }
     84 
     85             // explicitly checked to avoid bogus sendNotification calls because
     86             // of the WeakHashMap and the GC
     87             int oldSize = mTokens.size();
     88 
     89             Death d = new Death(token, tag);
     90             try {
     91                 token.linkToDeath(d, 0);
     92             } catch (RemoteException e) {
     93                 return;
     94             }
     95             mTokens.put(token, d);
     96 
     97             if (oldSize == 0 && !mAcquired) {
     98                 sendNotificationLocked(true);
     99                 mAcquired = true;
    100             }
    101         }
    102     }
    103 
    104     public void cleanup(IBinder token, boolean unlink)
    105     {
    106         synchronized (mTokens) {
    107             Death d = mTokens.remove(token);
    108             if (unlink && d != null) {
    109                 d.token.unlinkToDeath(d, 0);
    110                 d.token = null;
    111             }
    112 
    113             if (mTokens.size() == 0 && mAcquired) {
    114                 sendNotificationLocked(false);
    115                 mAcquired = false;
    116             }
    117         }
    118     }
    119 
    120     public void release(IBinder token)
    121     {
    122         cleanup(token, true);
    123     }
    124 
    125     public boolean isAcquired()
    126     {
    127         synchronized (mTokens) {
    128             return mAcquired;
    129         }
    130     }
    131 
    132     public void dump()
    133     {
    134         ArrayList<String> a = dumpInternal();
    135         for (String s : a) {
    136             Log.i(mTag, s);
    137         }
    138     }
    139 
    140     public void dump(PrintWriter pw) {
    141         ArrayList<String> a = dumpInternal();
    142         for (String s : a) {
    143             pw.println(s);
    144         }
    145     }
    146 
    147     private ArrayList<String> dumpInternal() {
    148         ArrayList<String> a = new ArrayList<String>();
    149         synchronized (mTokens) {
    150             Set<IBinder> keys = mTokens.keySet();
    151             a.add("Token count: " + mTokens.size());
    152             int i = 0;
    153             for (IBinder b: keys) {
    154                 a.add("[" + i + "] " + mTokens.get(b).tag + " - " + b);
    155                 i++;
    156             }
    157         }
    158         return a;
    159     }
    160 
    161     private Runnable mNotificationTask = new Runnable() {
    162         public void run()
    163         {
    164             int value;
    165             synchronized (mTokens) {
    166                 value = mNotificationQueue;
    167                 mNotificationQueue = -1;
    168             }
    169             if (value == 1) {
    170                 acquired();
    171             }
    172             else if (value == 0) {
    173                 released();
    174             }
    175         }
    176     };
    177 
    178     private void sendNotificationLocked(boolean on)
    179     {
    180         int value = on ? 1 : 0;
    181         if (mNotificationQueue == -1) {
    182             // empty
    183             mNotificationQueue = value;
    184             mHandler.post(mNotificationTask);
    185         }
    186         else if (mNotificationQueue != value) {
    187             // it's a pair, so cancel it
    188             mNotificationQueue = -1;
    189             mHandler.removeCallbacks(mNotificationTask);
    190         }
    191         // else, same so do nothing -- maybe we should warn?
    192     }
    193 
    194     private class Death implements IBinder.DeathRecipient
    195     {
    196         IBinder token;
    197         String tag;
    198 
    199         Death(IBinder token, String tag)
    200         {
    201             this.token = token;
    202             this.tag = tag;
    203         }
    204 
    205         public void binderDied()
    206         {
    207             cleanup(token, false);
    208         }
    209 
    210         protected void finalize() throws Throwable
    211         {
    212             try {
    213                 if (token != null) {
    214                     Log.w(mTag, "cleaning up leaked reference: " + tag);
    215                     release(token);
    216                 }
    217             }
    218             finally {
    219                 super.finalize();
    220             }
    221         }
    222     }
    223 
    224     private WeakHashMap<IBinder,Death> mTokens = new WeakHashMap<IBinder,Death>();
    225     private Handler mHandler;
    226     private String mTag;
    227     private int mNotificationQueue = -1;
    228     private volatile boolean mAcquired = false;
    229 }
    230