198 lines
5.4 KiB
Java
198 lines
5.4 KiB
Java
|
/*
|
||
|
* 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;
|
||
|
}
|