summaryrefslogtreecommitdiffstats
path: root/core/java/android/os/TokenWatcher.java
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:31:44 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:31:44 -0800
commit9066cfe9886ac131c34d59ed0e2d287b0e3c0087 (patch)
treed88beb88001f2482911e3d28e43833b50e4b4e97 /core/java/android/os/TokenWatcher.java
parentd83a98f4ce9cfa908f5c54bbd70f03eec07e7553 (diff)
downloadframeworks_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-xcore/java/android/os/TokenWatcher.java197
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;
+}