19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.os;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.WeakHashMap;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Set;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Helper class that helps you use IBinder objects as reference counted
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * tokens.  IBinders make good tokens because we find out when they are
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * removed
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic abstract class TokenWatcher
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project{
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Construct the TokenWatcher
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param h A handler to call {@link #acquired} and {@link #released}
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * on.  If you don't care, just call it like this, although your thread
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * will have to be a Looper thread.
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <code>new TokenWatcher(new Handler())</code>
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param tag A debugging tag for this TokenWatcher
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public TokenWatcher(Handler h, String tag)
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mHandler = h;
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mTag = tag != null ? tag : "TokenWatcher";
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Called when the number of active tokens goes from 0 to 1.
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract void acquired();
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Called when the number of active tokens goes from 1 to 0.
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract void released();
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Record that this token has been acquired.  When acquire is called, and
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the current count is 0, the acquired method is called on the given
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * handler.
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param token An IBinder object.  If this token has already been acquired,
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *              no action is taken.
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param tag   A string used by the {@link #dump} method for debugging,
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *              to see who has references.
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void acquire(IBinder token, String tag)
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        synchronized (mTokens) {
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // explicitly checked to avoid bogus sendNotification calls because
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // of the WeakHashMap and the GC
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int oldSize = mTokens.size();
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Death d = new Death(token, tag);
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                token.linkToDeath(d, 0);
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (RemoteException e) {
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return;
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mTokens.put(token, d);
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (oldSize == 0 && !mAcquired) {
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sendNotificationLocked(true);
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mAcquired = true;
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void cleanup(IBinder token, boolean unlink)
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        synchronized (mTokens) {
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Death d = mTokens.remove(token);
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (unlink && d != null) {
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                d.token.unlinkToDeath(d, 0);
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                d.token = null;
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mTokens.size() == 0 && mAcquired) {
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sendNotificationLocked(false);
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mAcquired = false;
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void release(IBinder token)
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        cleanup(token, true);
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean isAcquired()
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        synchronized (mTokens) {
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mAcquired;
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void dump()
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        synchronized (mTokens) {
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Set<IBinder> keys = mTokens.keySet();
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.i(mTag, "Token count: " + mTokens.size());
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int i = 0;
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (IBinder b: keys) {
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.i(mTag, "[" + i + "] " + mTokens.get(b).tag + " - " + b);
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Runnable mNotificationTask = new Runnable() {
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void run()
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        {
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int value;
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            synchronized (mTokens) {
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                value = mNotificationQueue;
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mNotificationQueue = -1;
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (value == 1) {
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                acquired();
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else if (value == 0) {
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                released();
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void sendNotificationLocked(boolean on)
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int value = on ? 1 : 0;
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mNotificationQueue == -1) {
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // empty
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mNotificationQueue = value;
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mHandler.post(mNotificationTask);
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        else if (mNotificationQueue != value) {
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // it's a pair, so cancel it
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mNotificationQueue = -1;
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mHandler.removeCallbacks(mNotificationTask);
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // else, same so do nothing -- maybe we should warn?
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private class Death implements IBinder.DeathRecipient
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        IBinder token;
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String tag;
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Death(IBinder token, String tag)
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        {
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.token = token;
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.tag = tag;
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void binderDied()
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        {
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            cleanup(token, false);
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        protected void finalize() throws Throwable
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        {
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (token != null) {
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Log.w(mTag, "cleaning up leaked reference: " + tag);
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    release(token);
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            finally {
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                super.finalize();
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private WeakHashMap<IBinder,Death> mTokens = new WeakHashMap<IBinder,Death>();
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Handler mHandler;
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String mTag;
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mNotificationQueue = -1;
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private volatile boolean mAcquired = false;
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
198