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