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