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
198662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tateimport java.io.PrintWriter;
208662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tateimport java.util.ArrayList;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.WeakHashMap;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Set;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Helper class that helps you use IBinder objects as reference counted
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * tokens.  IBinders make good tokens because we find out when they are
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * removed
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic abstract class TokenWatcher
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project{
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Construct the TokenWatcher
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param h A handler to call {@link #acquired} and {@link #released}
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * on.  If you don't care, just call it like this, although your thread
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * will have to be a Looper thread.
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <code>new TokenWatcher(new Handler())</code>
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param tag A debugging tag for this TokenWatcher
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public TokenWatcher(Handler h, String tag)
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mHandler = h;
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mTag = tag != null ? tag : "TokenWatcher";
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Called when the number of active tokens goes from 0 to 1.
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract void acquired();
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Called when the number of active tokens goes from 1 to 0.
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract void released();
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Record that this token has been acquired.  When acquire is called, and
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the current count is 0, the acquired method is called on the given
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * handler.
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param token An IBinder object.  If this token has already been acquired,
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *              no action is taken.
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param tag   A string used by the {@link #dump} method for debugging,
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *              to see who has references.
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void acquire(IBinder token, String tag)
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        synchronized (mTokens) {
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // explicitly checked to avoid bogus sendNotification calls because
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // of the WeakHashMap and the GC
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int oldSize = mTokens.size();
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Death d = new Death(token, tag);
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                token.linkToDeath(d, 0);
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (RemoteException e) {
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return;
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mTokens.put(token, d);
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (oldSize == 0 && !mAcquired) {
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sendNotificationLocked(true);
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mAcquired = true;
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void cleanup(IBinder token, boolean unlink)
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        synchronized (mTokens) {
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Death d = mTokens.remove(token);
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (unlink && d != null) {
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                d.token.unlinkToDeath(d, 0);
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                d.token = null;
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mTokens.size() == 0 && mAcquired) {
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sendNotificationLocked(false);
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mAcquired = false;
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void release(IBinder token)
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        cleanup(token, true);
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean isAcquired()
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        synchronized (mTokens) {
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mAcquired;
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void dump()
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
1208662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        ArrayList<String> a = dumpInternal();
1218662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        for (String s : a) {
1228662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate            Log.i(mTag, s);
1238662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        }
1248662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    }
1258662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate
1268662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    public void dump(PrintWriter pw) {
1278662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        ArrayList<String> a = dumpInternal();
1288662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        for (String s : a) {
1298662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate            pw.println(s);
1308662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        }
1318662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    }
1328662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate
1338662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    private ArrayList<String> dumpInternal() {
1348662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        ArrayList<String> a = new ArrayList<String>();
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        synchronized (mTokens) {
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Set<IBinder> keys = mTokens.keySet();
1378662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate            a.add("Token count: " + mTokens.size());
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int i = 0;
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (IBinder b: keys) {
1408662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate                a.add("[" + i + "] " + mTokens.get(b).tag + " - " + b);
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1448662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        return a;
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Runnable mNotificationTask = new Runnable() {
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void run()
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        {
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int value;
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            synchronized (mTokens) {
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                value = mNotificationQueue;
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mNotificationQueue = -1;
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (value == 1) {
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                acquired();
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else if (value == 0) {
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                released();
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void sendNotificationLocked(boolean on)
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int value = on ? 1 : 0;
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mNotificationQueue == -1) {
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // empty
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mNotificationQueue = value;
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mHandler.post(mNotificationTask);
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        else if (mNotificationQueue != value) {
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // it's a pair, so cancel it
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mNotificationQueue = -1;
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mHandler.removeCallbacks(mNotificationTask);
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // else, same so do nothing -- maybe we should warn?
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private class Death implements IBinder.DeathRecipient
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        IBinder token;
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String tag;
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Death(IBinder token, String tag)
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        {
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.token = token;
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.tag = tag;
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void binderDied()
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        {
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            cleanup(token, false);
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        protected void finalize() throws Throwable
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        {
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (token != null) {
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Log.w(mTag, "cleaning up leaked reference: " + tag);
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    release(token);
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            finally {
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                super.finalize();
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private WeakHashMap<IBinder,Death> mTokens = new WeakHashMap<IBinder,Death>();
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Handler mHandler;
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String mTag;
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mNotificationQueue = -1;
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private volatile boolean mAcquired = false;
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
216