diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:31:44 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:31:44 -0800 |
commit | 9066cfe9886ac131c34d59ed0e2d287b0e3c0087 (patch) | |
tree | d88beb88001f2482911e3d28e43833b50e4b4e97 /core/java/android/os/TokenWatcher.java | |
parent | d83a98f4ce9cfa908f5c54bbd70f03eec07e7553 (diff) | |
download | frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.zip frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.gz frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'core/java/android/os/TokenWatcher.java')
-rwxr-xr-x | core/java/android/os/TokenWatcher.java | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/core/java/android/os/TokenWatcher.java b/core/java/android/os/TokenWatcher.java new file mode 100755 index 0000000..ac3cc92 --- /dev/null +++ b/core/java/android/os/TokenWatcher.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import java.util.WeakHashMap; +import java.util.Set; +import android.util.Log; + +/** + * Helper class that helps you use IBinder objects as reference counted + * tokens. IBinders make good tokens because we find out when they are + * removed + * + */ +public abstract class TokenWatcher +{ + /** + * Construct the TokenWatcher + * + * @param h A handler to call {@link #acquired} and {@link #released} + * on. If you don't care, just call it like this, although your thread + * will have to be a Looper thread. + * <code>new TokenWatcher(new Handler())</code> + * @param tag A debugging tag for this TokenWatcher + */ + public TokenWatcher(Handler h, String tag) + { + mHandler = h; + mTag = tag != null ? tag : "TokenWatcher"; + } + + /** + * Called when the number of active tokens goes from 0 to 1. + */ + public abstract void acquired(); + + /** + * Called when the number of active tokens goes from 1 to 0. + */ + public abstract void released(); + + /** + * Record that this token has been acquired. When acquire is called, and + * the current count is 0, the acquired method is called on the given + * handler. + * + * @param token An IBinder object. If this token has already been acquired, + * no action is taken. + * @param tag A string used by the {@link #dump} method for debugging, + * to see who has references. + */ + public void acquire(IBinder token, String tag) + { + synchronized (mTokens) { + // explicitly checked to avoid bogus sendNotification calls because + // of the WeakHashMap and the GC + int oldSize = mTokens.size(); + + Death d = new Death(token, tag); + try { + token.linkToDeath(d, 0); + } catch (RemoteException e) { + return; + } + mTokens.put(token, d); + + if (oldSize == 0 && !mAcquired) { + sendNotificationLocked(true); + mAcquired = true; + } + } + } + + public void cleanup(IBinder token, boolean unlink) + { + synchronized (mTokens) { + Death d = mTokens.remove(token); + if (unlink && d != null) { + d.token.unlinkToDeath(d, 0); + d.token = null; + } + + if (mTokens.size() == 0 && mAcquired) { + sendNotificationLocked(false); + mAcquired = false; + } + } + } + + public void release(IBinder token) + { + cleanup(token, true); + } + + public boolean isAcquired() + { + synchronized (mTokens) { + return mAcquired; + } + } + + public void dump() + { + synchronized (mTokens) { + Set<IBinder> keys = mTokens.keySet(); + Log.i(mTag, "Token count: " + mTokens.size()); + int i = 0; + for (IBinder b: keys) { + Log.i(mTag, "[" + i + "] " + mTokens.get(b).tag + " - " + b); + i++; + } + } + } + + private Runnable mNotificationTask = new Runnable() { + public void run() + { + int value; + synchronized (mTokens) { + value = mNotificationQueue; + mNotificationQueue = -1; + } + if (value == 1) { + acquired(); + } + else if (value == 0) { + released(); + } + } + }; + + private void sendNotificationLocked(boolean on) + { + int value = on ? 1 : 0; + if (mNotificationQueue == -1) { + // empty + mNotificationQueue = value; + mHandler.post(mNotificationTask); + } + else if (mNotificationQueue != value) { + // it's a pair, so cancel it + mNotificationQueue = -1; + mHandler.removeCallbacks(mNotificationTask); + } + // else, same so do nothing -- maybe we should warn? + } + + private class Death implements IBinder.DeathRecipient + { + IBinder token; + String tag; + + Death(IBinder token, String tag) + { + this.token = token; + this.tag = tag; + } + + public void binderDied() + { + cleanup(token, false); + } + + protected void finalize() throws Throwable + { + try { + if (token != null) { + Log.w(mTag, "cleaning up leaked reference: " + tag); + release(token); + } + } + finally { + super.finalize(); + } + } + } + + private WeakHashMap<IBinder,Death> mTokens = new WeakHashMap<IBinder,Death>(); + private Handler mHandler; + private String mTag; + private int mNotificationQueue = -1; + private volatile boolean mAcquired = false; +} |