19189445c0a66d074dcded77b9c7322ef45422727George Mount/*
29189445c0a66d074dcded77b9c7322ef45422727George Mount * Copyright (C) 2015 The Android Open Source Project
39189445c0a66d074dcded77b9c7322ef45422727George Mount *
49189445c0a66d074dcded77b9c7322ef45422727George Mount * Licensed under the Apache License, Version 2.0 (the "License");
59189445c0a66d074dcded77b9c7322ef45422727George Mount * you may not use this file except in compliance with the License.
69189445c0a66d074dcded77b9c7322ef45422727George Mount * You may obtain a copy of the License at
79189445c0a66d074dcded77b9c7322ef45422727George Mount *
89189445c0a66d074dcded77b9c7322ef45422727George Mount *      http://www.apache.org/licenses/LICENSE-2.0
99189445c0a66d074dcded77b9c7322ef45422727George Mount *
109189445c0a66d074dcded77b9c7322ef45422727George Mount * Unless required by applicable law or agreed to in writing, software
119189445c0a66d074dcded77b9c7322ef45422727George Mount * distributed under the License is distributed on an "AS IS" BASIS,
129189445c0a66d074dcded77b9c7322ef45422727George Mount * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139189445c0a66d074dcded77b9c7322ef45422727George Mount * See the License for the specific language governing permissions and
149189445c0a66d074dcded77b9c7322ef45422727George Mount * limitations under the License.
159189445c0a66d074dcded77b9c7322ef45422727George Mount */
169189445c0a66d074dcded77b9c7322ef45422727George Mountpackage com.android.internal.util;
179189445c0a66d074dcded77b9c7322ef45422727George Mount
189189445c0a66d074dcded77b9c7322ef45422727George Mountimport java.util.ArrayList;
199189445c0a66d074dcded77b9c7322ef45422727George Mountimport java.util.List;
209189445c0a66d074dcded77b9c7322ef45422727George Mount
219189445c0a66d074dcded77b9c7322ef45422727George Mount/**
229189445c0a66d074dcded77b9c7322ef45422727George Mount * Tracks callbacks for the event. This class supports reentrant modification
239189445c0a66d074dcded77b9c7322ef45422727George Mount * of the callbacks during notification without adversely disrupting notifications.
249189445c0a66d074dcded77b9c7322ef45422727George Mount * A common pattern for callbacks is to receive a notification and then remove
259189445c0a66d074dcded77b9c7322ef45422727George Mount * themselves. This class handles this behavior with constant memory under
269189445c0a66d074dcded77b9c7322ef45422727George Mount * most circumstances.
279189445c0a66d074dcded77b9c7322ef45422727George Mount *
289189445c0a66d074dcded77b9c7322ef45422727George Mount * <p>A subclass of {@link CallbackRegistry.NotifierCallback} must be passed to
299189445c0a66d074dcded77b9c7322ef45422727George Mount * the constructor to define how notifications should be called. That implementation
309189445c0a66d074dcded77b9c7322ef45422727George Mount * does the actual notification on the listener.</p>
319189445c0a66d074dcded77b9c7322ef45422727George Mount *
329189445c0a66d074dcded77b9c7322ef45422727George Mount * <p>This class supports only callbacks with at most two parameters.
339189445c0a66d074dcded77b9c7322ef45422727George Mount * Typically, these are the notification originator and a parameter, but these may
349189445c0a66d074dcded77b9c7322ef45422727George Mount * be used as required. If more than two parameters are required or primitive types
359189445c0a66d074dcded77b9c7322ef45422727George Mount * must be used, <code>A</code> should be some kind of containing structure that
369189445c0a66d074dcded77b9c7322ef45422727George Mount * the subclass may reuse between notifications.</p>
379189445c0a66d074dcded77b9c7322ef45422727George Mount *
389189445c0a66d074dcded77b9c7322ef45422727George Mount * @param <C> The callback type.
399189445c0a66d074dcded77b9c7322ef45422727George Mount * @param <T> The notification sender type. Typically this is the containing class.
409189445c0a66d074dcded77b9c7322ef45422727George Mount * @param <A> Opaque argument used to pass additional data beyond an int.
419189445c0a66d074dcded77b9c7322ef45422727George Mount */
429189445c0a66d074dcded77b9c7322ef45422727George Mountpublic class CallbackRegistry<C, T, A> implements Cloneable {
439189445c0a66d074dcded77b9c7322ef45422727George Mount    private static final String TAG = "CallbackRegistry";
449189445c0a66d074dcded77b9c7322ef45422727George Mount
459189445c0a66d074dcded77b9c7322ef45422727George Mount    /** An ordered collection of listeners waiting to be notified. */
469189445c0a66d074dcded77b9c7322ef45422727George Mount    private List<C> mCallbacks = new ArrayList<C>();
479189445c0a66d074dcded77b9c7322ef45422727George Mount
489189445c0a66d074dcded77b9c7322ef45422727George Mount    /**
499189445c0a66d074dcded77b9c7322ef45422727George Mount     * A bit flag for the first 64 listeners that are removed during notification.
509189445c0a66d074dcded77b9c7322ef45422727George Mount     * The lowest significant bit corresponds to the 0th index into mCallbacks.
519189445c0a66d074dcded77b9c7322ef45422727George Mount     * For a small number of callbacks, no additional array of objects needs to
529189445c0a66d074dcded77b9c7322ef45422727George Mount     * be allocated.
539189445c0a66d074dcded77b9c7322ef45422727George Mount     */
549189445c0a66d074dcded77b9c7322ef45422727George Mount    private long mFirst64Removed = 0x0;
559189445c0a66d074dcded77b9c7322ef45422727George Mount
569189445c0a66d074dcded77b9c7322ef45422727George Mount    /**
579189445c0a66d074dcded77b9c7322ef45422727George Mount     * Bit flags for the remaining callbacks that are removed during notification.
589189445c0a66d074dcded77b9c7322ef45422727George Mount     * When there are more than 64 callbacks and one is marked for removal, a dynamic
599189445c0a66d074dcded77b9c7322ef45422727George Mount     * array of bits are allocated for the callbacks.
609189445c0a66d074dcded77b9c7322ef45422727George Mount     */
619189445c0a66d074dcded77b9c7322ef45422727George Mount    private long[] mRemainderRemoved;
629189445c0a66d074dcded77b9c7322ef45422727George Mount
639189445c0a66d074dcded77b9c7322ef45422727George Mount    /**
649189445c0a66d074dcded77b9c7322ef45422727George Mount     * The reentrancy level of the notification. When we notify a callback, it may cause
659189445c0a66d074dcded77b9c7322ef45422727George Mount     * further notifications. The reentrancy level must be tracked to let us clean up
669189445c0a66d074dcded77b9c7322ef45422727George Mount     * the callback state when all notifications have been processed.
679189445c0a66d074dcded77b9c7322ef45422727George Mount     */
689189445c0a66d074dcded77b9c7322ef45422727George Mount    private int mNotificationLevel;
699189445c0a66d074dcded77b9c7322ef45422727George Mount
709189445c0a66d074dcded77b9c7322ef45422727George Mount    /** The notification mechanism for notifying an event. */
719189445c0a66d074dcded77b9c7322ef45422727George Mount    private final NotifierCallback<C, T, A> mNotifier;
729189445c0a66d074dcded77b9c7322ef45422727George Mount
739189445c0a66d074dcded77b9c7322ef45422727George Mount    /**
749189445c0a66d074dcded77b9c7322ef45422727George Mount     * Creates an EventRegistry that notifies the event with notifier.
759189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param notifier The class to use to notify events.
769189445c0a66d074dcded77b9c7322ef45422727George Mount     */
779189445c0a66d074dcded77b9c7322ef45422727George Mount    public CallbackRegistry(NotifierCallback<C, T, A> notifier) {
789189445c0a66d074dcded77b9c7322ef45422727George Mount        mNotifier = notifier;
799189445c0a66d074dcded77b9c7322ef45422727George Mount    }
809189445c0a66d074dcded77b9c7322ef45422727George Mount
819189445c0a66d074dcded77b9c7322ef45422727George Mount    /**
829189445c0a66d074dcded77b9c7322ef45422727George Mount     * Notify all callbacks.
839189445c0a66d074dcded77b9c7322ef45422727George Mount     *
849189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param sender The originator. This is an opaque parameter passed to
859189445c0a66d074dcded77b9c7322ef45422727George Mount     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
869189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param arg An opaque parameter passed to
879189445c0a66d074dcded77b9c7322ef45422727George Mount     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
889189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param arg2 An opaque parameter passed to
899189445c0a66d074dcded77b9c7322ef45422727George Mount     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
909189445c0a66d074dcded77b9c7322ef45422727George Mount     */
919189445c0a66d074dcded77b9c7322ef45422727George Mount    public synchronized void notifyCallbacks(T sender, int arg, A arg2) {
929189445c0a66d074dcded77b9c7322ef45422727George Mount        mNotificationLevel++;
939189445c0a66d074dcded77b9c7322ef45422727George Mount        notifyRecurseLocked(sender, arg, arg2);
949189445c0a66d074dcded77b9c7322ef45422727George Mount        mNotificationLevel--;
959189445c0a66d074dcded77b9c7322ef45422727George Mount        if (mNotificationLevel == 0) {
969189445c0a66d074dcded77b9c7322ef45422727George Mount            if (mRemainderRemoved != null) {
979189445c0a66d074dcded77b9c7322ef45422727George Mount                for (int i = mRemainderRemoved.length - 1; i >= 0; i--) {
989189445c0a66d074dcded77b9c7322ef45422727George Mount                    final long removedBits = mRemainderRemoved[i];
999189445c0a66d074dcded77b9c7322ef45422727George Mount                    if (removedBits != 0) {
1009189445c0a66d074dcded77b9c7322ef45422727George Mount                        removeRemovedCallbacks((i + 1) * Long.SIZE, removedBits);
1019189445c0a66d074dcded77b9c7322ef45422727George Mount                        mRemainderRemoved[i] = 0;
1029189445c0a66d074dcded77b9c7322ef45422727George Mount                    }
1039189445c0a66d074dcded77b9c7322ef45422727George Mount                }
1049189445c0a66d074dcded77b9c7322ef45422727George Mount            }
1059189445c0a66d074dcded77b9c7322ef45422727George Mount            if (mFirst64Removed != 0) {
1069189445c0a66d074dcded77b9c7322ef45422727George Mount                removeRemovedCallbacks(0, mFirst64Removed);
1079189445c0a66d074dcded77b9c7322ef45422727George Mount                mFirst64Removed = 0;
1089189445c0a66d074dcded77b9c7322ef45422727George Mount            }
1099189445c0a66d074dcded77b9c7322ef45422727George Mount        }
1109189445c0a66d074dcded77b9c7322ef45422727George Mount    }
1119189445c0a66d074dcded77b9c7322ef45422727George Mount
1129189445c0a66d074dcded77b9c7322ef45422727George Mount    /**
1139189445c0a66d074dcded77b9c7322ef45422727George Mount     * Notify up to the first Long.SIZE callbacks that don't have a bit set in <code>removed</code>.
1149189445c0a66d074dcded77b9c7322ef45422727George Mount     *
1159189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param sender The originator. This is an opaque parameter passed to
1169189445c0a66d074dcded77b9c7322ef45422727George Mount     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
1179189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param arg An opaque parameter passed to
1189189445c0a66d074dcded77b9c7322ef45422727George Mount     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
1199189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param arg2 An opaque parameter passed to
1209189445c0a66d074dcded77b9c7322ef45422727George Mount     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
1219189445c0a66d074dcded77b9c7322ef45422727George Mount     */
1229189445c0a66d074dcded77b9c7322ef45422727George Mount    private void notifyFirst64Locked(T sender, int arg, A arg2) {
1239189445c0a66d074dcded77b9c7322ef45422727George Mount        final int maxNotified = Math.min(Long.SIZE, mCallbacks.size());
1249189445c0a66d074dcded77b9c7322ef45422727George Mount        notifyCallbacksLocked(sender, arg, arg2, 0, maxNotified, mFirst64Removed);
1259189445c0a66d074dcded77b9c7322ef45422727George Mount    }
1269189445c0a66d074dcded77b9c7322ef45422727George Mount
1279189445c0a66d074dcded77b9c7322ef45422727George Mount    /**
1289189445c0a66d074dcded77b9c7322ef45422727George Mount     * Notify all callbacks using a recursive algorithm to avoid allocating on the heap.
1299189445c0a66d074dcded77b9c7322ef45422727George Mount     * This part captures the callbacks beyond Long.SIZE that have no bits allocated for
1309189445c0a66d074dcded77b9c7322ef45422727George Mount     * removal before it recurses into {@link #notifyRemainderLocked(Object, int, A, int)}.
1319189445c0a66d074dcded77b9c7322ef45422727George Mount     * <p>
1329189445c0a66d074dcded77b9c7322ef45422727George Mount     * Recursion is used to avoid allocating temporary state on the heap. Each stack has one
1339189445c0a66d074dcded77b9c7322ef45422727George Mount     * long (64 callbacks) worth of information of which has been removed.
1349189445c0a66d074dcded77b9c7322ef45422727George Mount     *
1359189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param sender The originator. This is an opaque parameter passed to
1369189445c0a66d074dcded77b9c7322ef45422727George Mount     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
1379189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param arg An opaque parameter passed to
1389189445c0a66d074dcded77b9c7322ef45422727George Mount     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
1399189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param arg2 An opaque parameter passed to
1409189445c0a66d074dcded77b9c7322ef45422727George Mount     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
1419189445c0a66d074dcded77b9c7322ef45422727George Mount     */
1429189445c0a66d074dcded77b9c7322ef45422727George Mount    private void notifyRecurseLocked(T sender, int arg, A arg2) {
1439189445c0a66d074dcded77b9c7322ef45422727George Mount        final int callbackCount = mCallbacks.size();
1449189445c0a66d074dcded77b9c7322ef45422727George Mount        final int remainderIndex = mRemainderRemoved == null ? -1 : mRemainderRemoved.length - 1;
1459189445c0a66d074dcded77b9c7322ef45422727George Mount
1469189445c0a66d074dcded77b9c7322ef45422727George Mount        // Now we've got all callbacks that have no mRemainderRemoved value, so notify the
1479189445c0a66d074dcded77b9c7322ef45422727George Mount        // others.
1489189445c0a66d074dcded77b9c7322ef45422727George Mount        notifyRemainderLocked(sender, arg, arg2, remainderIndex);
1499189445c0a66d074dcded77b9c7322ef45422727George Mount
1509189445c0a66d074dcded77b9c7322ef45422727George Mount        // notifyRemainderLocked notifies all at maxIndex, so we'd normally start at maxIndex + 1
1519189445c0a66d074dcded77b9c7322ef45422727George Mount        // However, we must also keep track of those in mFirst64Removed, so we add 2 instead:
1529189445c0a66d074dcded77b9c7322ef45422727George Mount        final int startCallbackIndex = (remainderIndex + 2) * Long.SIZE;
1539189445c0a66d074dcded77b9c7322ef45422727George Mount
1549189445c0a66d074dcded77b9c7322ef45422727George Mount        // The remaining have no bit set
1559189445c0a66d074dcded77b9c7322ef45422727George Mount        notifyCallbacksLocked(sender, arg, arg2, startCallbackIndex, callbackCount, 0);
1569189445c0a66d074dcded77b9c7322ef45422727George Mount    }
1579189445c0a66d074dcded77b9c7322ef45422727George Mount
1589189445c0a66d074dcded77b9c7322ef45422727George Mount    /**
1599189445c0a66d074dcded77b9c7322ef45422727George Mount     * Notify callbacks that have mRemainderRemoved bits set for remainderIndex. If
1609189445c0a66d074dcded77b9c7322ef45422727George Mount     * remainderIndex is -1, the first 64 will be notified instead.
1619189445c0a66d074dcded77b9c7322ef45422727George Mount     *
1629189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param sender The originator. This is an opaque parameter passed to
1639189445c0a66d074dcded77b9c7322ef45422727George Mount     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
1649189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param arg An opaque parameter passed to
1659189445c0a66d074dcded77b9c7322ef45422727George Mount     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
1669189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param arg2 An opaque parameter passed to
1679189445c0a66d074dcded77b9c7322ef45422727George Mount     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
1689189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param remainderIndex The index into mRemainderRemoved that should be notified.
1699189445c0a66d074dcded77b9c7322ef45422727George Mount     */
1709189445c0a66d074dcded77b9c7322ef45422727George Mount    private void notifyRemainderLocked(T sender, int arg, A arg2, int remainderIndex) {
1719189445c0a66d074dcded77b9c7322ef45422727George Mount        if (remainderIndex < 0) {
1729189445c0a66d074dcded77b9c7322ef45422727George Mount            notifyFirst64Locked(sender, arg, arg2);
1739189445c0a66d074dcded77b9c7322ef45422727George Mount        } else {
1749189445c0a66d074dcded77b9c7322ef45422727George Mount            final long bits = mRemainderRemoved[remainderIndex];
1759189445c0a66d074dcded77b9c7322ef45422727George Mount            final int startIndex = (remainderIndex + 1) * Long.SIZE;
1769189445c0a66d074dcded77b9c7322ef45422727George Mount            final int endIndex = Math.min(mCallbacks.size(), startIndex + Long.SIZE);
1779189445c0a66d074dcded77b9c7322ef45422727George Mount            notifyRemainderLocked(sender, arg, arg2, remainderIndex - 1);
1789189445c0a66d074dcded77b9c7322ef45422727George Mount            notifyCallbacksLocked(sender, arg, arg2, startIndex, endIndex, bits);
1799189445c0a66d074dcded77b9c7322ef45422727George Mount        }
1809189445c0a66d074dcded77b9c7322ef45422727George Mount    }
1819189445c0a66d074dcded77b9c7322ef45422727George Mount
1829189445c0a66d074dcded77b9c7322ef45422727George Mount    /**
1839189445c0a66d074dcded77b9c7322ef45422727George Mount     * Notify callbacks from startIndex to endIndex, using bits as the bit status
1849189445c0a66d074dcded77b9c7322ef45422727George Mount     * for whether they have been removed or not. bits should be from mRemainderRemoved or
1859189445c0a66d074dcded77b9c7322ef45422727George Mount     * mFirst64Removed. bits set to 0 indicates that all callbacks from startIndex to
1869189445c0a66d074dcded77b9c7322ef45422727George Mount     * endIndex should be notified.
1879189445c0a66d074dcded77b9c7322ef45422727George Mount     *
1889189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param sender The originator. This is an opaque parameter passed to
1899189445c0a66d074dcded77b9c7322ef45422727George Mount     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
1909189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param arg An opaque parameter passed to
1919189445c0a66d074dcded77b9c7322ef45422727George Mount     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
1929189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param arg2 An opaque parameter passed to
1939189445c0a66d074dcded77b9c7322ef45422727George Mount     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
1949189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param startIndex The index into the mCallbacks to start notifying.
1959189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param endIndex One past the last index into mCallbacks to notify.
1969189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param bits A bit field indicating which callbacks have been removed and shouldn't
1979189445c0a66d074dcded77b9c7322ef45422727George Mount     *             be notified.
1989189445c0a66d074dcded77b9c7322ef45422727George Mount     */
1999189445c0a66d074dcded77b9c7322ef45422727George Mount    private void notifyCallbacksLocked(T sender, int arg, A arg2, final int startIndex,
2009189445c0a66d074dcded77b9c7322ef45422727George Mount            final int endIndex, final long bits) {
2019189445c0a66d074dcded77b9c7322ef45422727George Mount        long bitMask = 1;
2029189445c0a66d074dcded77b9c7322ef45422727George Mount        for (int i = startIndex; i < endIndex; i++) {
2039189445c0a66d074dcded77b9c7322ef45422727George Mount            if ((bits & bitMask) == 0) {
2049189445c0a66d074dcded77b9c7322ef45422727George Mount                mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2);
2059189445c0a66d074dcded77b9c7322ef45422727George Mount            }
2069189445c0a66d074dcded77b9c7322ef45422727George Mount            bitMask <<= 1;
2079189445c0a66d074dcded77b9c7322ef45422727George Mount        }
2089189445c0a66d074dcded77b9c7322ef45422727George Mount    }
2099189445c0a66d074dcded77b9c7322ef45422727George Mount
2109189445c0a66d074dcded77b9c7322ef45422727George Mount    /**
2119189445c0a66d074dcded77b9c7322ef45422727George Mount     * Add a callback to be notified. If the callback is already in the list, another won't
2129189445c0a66d074dcded77b9c7322ef45422727George Mount     * be added. This does not affect current notifications.
2139189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param callback The callback to add.
2149189445c0a66d074dcded77b9c7322ef45422727George Mount     */
2159189445c0a66d074dcded77b9c7322ef45422727George Mount    public synchronized void add(C callback) {
2169189445c0a66d074dcded77b9c7322ef45422727George Mount        int index = mCallbacks.lastIndexOf(callback);
2179189445c0a66d074dcded77b9c7322ef45422727George Mount        if (index < 0 || isRemovedLocked(index)) {
2189189445c0a66d074dcded77b9c7322ef45422727George Mount            mCallbacks.add(callback);
2199189445c0a66d074dcded77b9c7322ef45422727George Mount        }
2209189445c0a66d074dcded77b9c7322ef45422727George Mount    }
2219189445c0a66d074dcded77b9c7322ef45422727George Mount
2229189445c0a66d074dcded77b9c7322ef45422727George Mount    /**
2239189445c0a66d074dcded77b9c7322ef45422727George Mount     * Returns true if the callback at index has been marked for removal.
2249189445c0a66d074dcded77b9c7322ef45422727George Mount     *
2259189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param index The index into mCallbacks to check.
2269189445c0a66d074dcded77b9c7322ef45422727George Mount     * @return true if the callback at index has been marked for removal.
2279189445c0a66d074dcded77b9c7322ef45422727George Mount     */
2289189445c0a66d074dcded77b9c7322ef45422727George Mount    private boolean isRemovedLocked(int index) {
2299189445c0a66d074dcded77b9c7322ef45422727George Mount        if (index < Long.SIZE) {
2309189445c0a66d074dcded77b9c7322ef45422727George Mount            // It is in the first 64 callbacks, just check the bit.
2319189445c0a66d074dcded77b9c7322ef45422727George Mount            final long bitMask = 1L << index;
2329189445c0a66d074dcded77b9c7322ef45422727George Mount            return (mFirst64Removed & bitMask) != 0;
2339189445c0a66d074dcded77b9c7322ef45422727George Mount        } else if (mRemainderRemoved == null) {
2349189445c0a66d074dcded77b9c7322ef45422727George Mount            // It is after the first 64 callbacks, but nothing else was marked for removal.
2359189445c0a66d074dcded77b9c7322ef45422727George Mount            return false;
2369189445c0a66d074dcded77b9c7322ef45422727George Mount        } else {
2379189445c0a66d074dcded77b9c7322ef45422727George Mount            final int maskIndex = (index / Long.SIZE) - 1;
2389189445c0a66d074dcded77b9c7322ef45422727George Mount            if (maskIndex >= mRemainderRemoved.length) {
2399189445c0a66d074dcded77b9c7322ef45422727George Mount                // There are some items in mRemainderRemoved, but nothing at the given index.
2409189445c0a66d074dcded77b9c7322ef45422727George Mount                return false;
2419189445c0a66d074dcded77b9c7322ef45422727George Mount            } else {
2429189445c0a66d074dcded77b9c7322ef45422727George Mount                // There is something marked for removal, so we have to check the bit.
2439189445c0a66d074dcded77b9c7322ef45422727George Mount                final long bits = mRemainderRemoved[maskIndex];
2449189445c0a66d074dcded77b9c7322ef45422727George Mount                final long bitMask = 1L << (index % Long.SIZE);
2459189445c0a66d074dcded77b9c7322ef45422727George Mount                return (bits & bitMask) != 0;
2469189445c0a66d074dcded77b9c7322ef45422727George Mount            }
2479189445c0a66d074dcded77b9c7322ef45422727George Mount        }
2489189445c0a66d074dcded77b9c7322ef45422727George Mount    }
2499189445c0a66d074dcded77b9c7322ef45422727George Mount
2509189445c0a66d074dcded77b9c7322ef45422727George Mount    /**
2519189445c0a66d074dcded77b9c7322ef45422727George Mount     * Removes callbacks from startIndex to startIndex + Long.SIZE, based
2529189445c0a66d074dcded77b9c7322ef45422727George Mount     * on the bits set in removed.
2539189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param startIndex The index into the mCallbacks to start removing callbacks.
2549189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param removed The bits indicating removal, where each bit is set for one callback
2559189445c0a66d074dcded77b9c7322ef45422727George Mount     *                to be removed.
2569189445c0a66d074dcded77b9c7322ef45422727George Mount     */
2579189445c0a66d074dcded77b9c7322ef45422727George Mount    private void removeRemovedCallbacks(int startIndex, long removed) {
2589189445c0a66d074dcded77b9c7322ef45422727George Mount        // The naive approach should be fine. There may be a better bit-twiddling approach.
2599189445c0a66d074dcded77b9c7322ef45422727George Mount        final int endIndex = startIndex + Long.SIZE;
2609189445c0a66d074dcded77b9c7322ef45422727George Mount
2619189445c0a66d074dcded77b9c7322ef45422727George Mount        long bitMask = 1L << (Long.SIZE - 1);
2629189445c0a66d074dcded77b9c7322ef45422727George Mount        for (int i = endIndex - 1; i >= startIndex; i--) {
2639189445c0a66d074dcded77b9c7322ef45422727George Mount            if ((removed & bitMask) != 0) {
2649189445c0a66d074dcded77b9c7322ef45422727George Mount                mCallbacks.remove(i);
2659189445c0a66d074dcded77b9c7322ef45422727George Mount            }
2669189445c0a66d074dcded77b9c7322ef45422727George Mount            bitMask >>>= 1;
2679189445c0a66d074dcded77b9c7322ef45422727George Mount        }
2689189445c0a66d074dcded77b9c7322ef45422727George Mount    }
2699189445c0a66d074dcded77b9c7322ef45422727George Mount
2709189445c0a66d074dcded77b9c7322ef45422727George Mount    /**
2719189445c0a66d074dcded77b9c7322ef45422727George Mount     * Remove a callback. This callback won't be notified after this call completes.
2729189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param callback The callback to remove.
2739189445c0a66d074dcded77b9c7322ef45422727George Mount     */
2749189445c0a66d074dcded77b9c7322ef45422727George Mount    public synchronized void remove(C callback) {
2759189445c0a66d074dcded77b9c7322ef45422727George Mount        if (mNotificationLevel == 0) {
2769189445c0a66d074dcded77b9c7322ef45422727George Mount            mCallbacks.remove(callback);
2779189445c0a66d074dcded77b9c7322ef45422727George Mount        } else {
2789189445c0a66d074dcded77b9c7322ef45422727George Mount            int index = mCallbacks.lastIndexOf(callback);
2799189445c0a66d074dcded77b9c7322ef45422727George Mount            if (index >= 0) {
2809189445c0a66d074dcded77b9c7322ef45422727George Mount                setRemovalBitLocked(index);
2819189445c0a66d074dcded77b9c7322ef45422727George Mount            }
2829189445c0a66d074dcded77b9c7322ef45422727George Mount        }
2839189445c0a66d074dcded77b9c7322ef45422727George Mount    }
2849189445c0a66d074dcded77b9c7322ef45422727George Mount
2859189445c0a66d074dcded77b9c7322ef45422727George Mount    private void setRemovalBitLocked(int index) {
2869189445c0a66d074dcded77b9c7322ef45422727George Mount        if (index < Long.SIZE) {
2879189445c0a66d074dcded77b9c7322ef45422727George Mount            // It is in the first 64 callbacks, just check the bit.
2889189445c0a66d074dcded77b9c7322ef45422727George Mount            final long bitMask = 1L << index;
2899189445c0a66d074dcded77b9c7322ef45422727George Mount            mFirst64Removed |= bitMask;
2909189445c0a66d074dcded77b9c7322ef45422727George Mount        } else {
2919189445c0a66d074dcded77b9c7322ef45422727George Mount            final int remainderIndex = (index / Long.SIZE) - 1;
2929189445c0a66d074dcded77b9c7322ef45422727George Mount            if (mRemainderRemoved == null) {
2939189445c0a66d074dcded77b9c7322ef45422727George Mount                mRemainderRemoved = new long[mCallbacks.size() / Long.SIZE];
2949189445c0a66d074dcded77b9c7322ef45422727George Mount            } else if (mRemainderRemoved.length < remainderIndex) {
2959189445c0a66d074dcded77b9c7322ef45422727George Mount                // need to make it bigger
2969189445c0a66d074dcded77b9c7322ef45422727George Mount                long[] newRemainders = new long[mCallbacks.size() / Long.SIZE];
2979189445c0a66d074dcded77b9c7322ef45422727George Mount                System.arraycopy(mRemainderRemoved, 0, newRemainders, 0, mRemainderRemoved.length);
2989189445c0a66d074dcded77b9c7322ef45422727George Mount                mRemainderRemoved = newRemainders;
2999189445c0a66d074dcded77b9c7322ef45422727George Mount            }
3009189445c0a66d074dcded77b9c7322ef45422727George Mount            final long bitMask = 1L << (index % Long.SIZE);
3019189445c0a66d074dcded77b9c7322ef45422727George Mount            mRemainderRemoved[remainderIndex] |= bitMask;
3029189445c0a66d074dcded77b9c7322ef45422727George Mount        }
3039189445c0a66d074dcded77b9c7322ef45422727George Mount    }
3049189445c0a66d074dcded77b9c7322ef45422727George Mount
3059189445c0a66d074dcded77b9c7322ef45422727George Mount    /**
3069189445c0a66d074dcded77b9c7322ef45422727George Mount     * Makes a copy of the registered callbacks and returns it.
3079189445c0a66d074dcded77b9c7322ef45422727George Mount     *
3089189445c0a66d074dcded77b9c7322ef45422727George Mount     * @return a copy of the registered callbacks.
3099189445c0a66d074dcded77b9c7322ef45422727George Mount     */
3109189445c0a66d074dcded77b9c7322ef45422727George Mount    public synchronized ArrayList<C> copyListeners() {
3119189445c0a66d074dcded77b9c7322ef45422727George Mount        ArrayList<C> callbacks = new ArrayList<C>(mCallbacks.size());
3129189445c0a66d074dcded77b9c7322ef45422727George Mount        int numListeners = mCallbacks.size();
3139189445c0a66d074dcded77b9c7322ef45422727George Mount        for (int i = 0; i < numListeners; i++) {
3149189445c0a66d074dcded77b9c7322ef45422727George Mount            if (!isRemovedLocked(i)) {
3159189445c0a66d074dcded77b9c7322ef45422727George Mount                callbacks.add(mCallbacks.get(i));
3169189445c0a66d074dcded77b9c7322ef45422727George Mount            }
3179189445c0a66d074dcded77b9c7322ef45422727George Mount        }
3189189445c0a66d074dcded77b9c7322ef45422727George Mount        return callbacks;
3199189445c0a66d074dcded77b9c7322ef45422727George Mount    }
3209189445c0a66d074dcded77b9c7322ef45422727George Mount
3219189445c0a66d074dcded77b9c7322ef45422727George Mount    /**
3229189445c0a66d074dcded77b9c7322ef45422727George Mount     * Returns true if there are no registered callbacks or false otherwise.
3239189445c0a66d074dcded77b9c7322ef45422727George Mount     *
3249189445c0a66d074dcded77b9c7322ef45422727George Mount     * @return true if there are no registered callbacks or false otherwise.
3259189445c0a66d074dcded77b9c7322ef45422727George Mount     */
3269189445c0a66d074dcded77b9c7322ef45422727George Mount    public synchronized boolean isEmpty() {
3279189445c0a66d074dcded77b9c7322ef45422727George Mount        if (mCallbacks.isEmpty()) {
3289189445c0a66d074dcded77b9c7322ef45422727George Mount            return true;
3299189445c0a66d074dcded77b9c7322ef45422727George Mount        } else if (mNotificationLevel == 0) {
3309189445c0a66d074dcded77b9c7322ef45422727George Mount            return false;
3319189445c0a66d074dcded77b9c7322ef45422727George Mount        } else {
3329189445c0a66d074dcded77b9c7322ef45422727George Mount            int numListeners = mCallbacks.size();
3339189445c0a66d074dcded77b9c7322ef45422727George Mount            for (int i = 0; i < numListeners; i++) {
3349189445c0a66d074dcded77b9c7322ef45422727George Mount                if (!isRemovedLocked(i)) {
3359189445c0a66d074dcded77b9c7322ef45422727George Mount                    return false;
3369189445c0a66d074dcded77b9c7322ef45422727George Mount                }
3379189445c0a66d074dcded77b9c7322ef45422727George Mount            }
3389189445c0a66d074dcded77b9c7322ef45422727George Mount            return true;
3399189445c0a66d074dcded77b9c7322ef45422727George Mount        }
3409189445c0a66d074dcded77b9c7322ef45422727George Mount    }
3419189445c0a66d074dcded77b9c7322ef45422727George Mount
3429189445c0a66d074dcded77b9c7322ef45422727George Mount    /**
3439189445c0a66d074dcded77b9c7322ef45422727George Mount     * Removes all callbacks from the list.
3449189445c0a66d074dcded77b9c7322ef45422727George Mount     */
3459189445c0a66d074dcded77b9c7322ef45422727George Mount    public synchronized void clear() {
3469189445c0a66d074dcded77b9c7322ef45422727George Mount        if (mNotificationLevel == 0) {
3479189445c0a66d074dcded77b9c7322ef45422727George Mount            mCallbacks.clear();
3489189445c0a66d074dcded77b9c7322ef45422727George Mount        } else if (!mCallbacks.isEmpty()) {
3499189445c0a66d074dcded77b9c7322ef45422727George Mount            for (int i = mCallbacks.size() - 1; i >= 0; i--) {
3509189445c0a66d074dcded77b9c7322ef45422727George Mount                setRemovalBitLocked(i);
3519189445c0a66d074dcded77b9c7322ef45422727George Mount            }
3529189445c0a66d074dcded77b9c7322ef45422727George Mount        }
3539189445c0a66d074dcded77b9c7322ef45422727George Mount    }
3549189445c0a66d074dcded77b9c7322ef45422727George Mount
3559189445c0a66d074dcded77b9c7322ef45422727George Mount    public synchronized CallbackRegistry<C, T, A> clone() {
3569189445c0a66d074dcded77b9c7322ef45422727George Mount        CallbackRegistry<C, T, A> clone = null;
3579189445c0a66d074dcded77b9c7322ef45422727George Mount        try {
3589189445c0a66d074dcded77b9c7322ef45422727George Mount            clone = (CallbackRegistry<C, T, A>) super.clone();
3599189445c0a66d074dcded77b9c7322ef45422727George Mount            clone.mFirst64Removed = 0;
3609189445c0a66d074dcded77b9c7322ef45422727George Mount            clone.mRemainderRemoved = null;
3619189445c0a66d074dcded77b9c7322ef45422727George Mount            clone.mNotificationLevel = 0;
3629189445c0a66d074dcded77b9c7322ef45422727George Mount            clone.mCallbacks = new ArrayList<C>();
3639189445c0a66d074dcded77b9c7322ef45422727George Mount            final int numListeners = mCallbacks.size();
3649189445c0a66d074dcded77b9c7322ef45422727George Mount            for (int i = 0; i < numListeners; i++) {
3659189445c0a66d074dcded77b9c7322ef45422727George Mount                if (!isRemovedLocked(i)) {
3669189445c0a66d074dcded77b9c7322ef45422727George Mount                    clone.mCallbacks.add(mCallbacks.get(i));
3679189445c0a66d074dcded77b9c7322ef45422727George Mount                }
3689189445c0a66d074dcded77b9c7322ef45422727George Mount            }
3699189445c0a66d074dcded77b9c7322ef45422727George Mount        } catch (CloneNotSupportedException e) {
3709189445c0a66d074dcded77b9c7322ef45422727George Mount            e.printStackTrace();
3719189445c0a66d074dcded77b9c7322ef45422727George Mount        }
3729189445c0a66d074dcded77b9c7322ef45422727George Mount        return clone;
3739189445c0a66d074dcded77b9c7322ef45422727George Mount    }
3749189445c0a66d074dcded77b9c7322ef45422727George Mount
3759189445c0a66d074dcded77b9c7322ef45422727George Mount    /**
3769189445c0a66d074dcded77b9c7322ef45422727George Mount     * Class used to notify events from CallbackRegistry.
3779189445c0a66d074dcded77b9c7322ef45422727George Mount     *
3789189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param <C> The callback type.
3799189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param <T> The notification sender type. Typically this is the containing class.
3809189445c0a66d074dcded77b9c7322ef45422727George Mount     * @param <A> An opaque argument to pass to the notifier
3819189445c0a66d074dcded77b9c7322ef45422727George Mount     */
3829189445c0a66d074dcded77b9c7322ef45422727George Mount    public abstract static class NotifierCallback<C, T, A> {
3839189445c0a66d074dcded77b9c7322ef45422727George Mount        /**
3849189445c0a66d074dcded77b9c7322ef45422727George Mount         * Used to notify the callback.
3859189445c0a66d074dcded77b9c7322ef45422727George Mount         *
3869189445c0a66d074dcded77b9c7322ef45422727George Mount         * @param callback The callback to notify.
3879189445c0a66d074dcded77b9c7322ef45422727George Mount         * @param sender The opaque sender object.
3889189445c0a66d074dcded77b9c7322ef45422727George Mount         * @param arg The opaque notification parameter.
3899189445c0a66d074dcded77b9c7322ef45422727George Mount         * @param arg2 An opaque argument passed in
3909189445c0a66d074dcded77b9c7322ef45422727George Mount         *        {@link CallbackRegistry#notifyCallbacks}
3919189445c0a66d074dcded77b9c7322ef45422727George Mount         * @see CallbackRegistry#CallbackRegistry(CallbackRegistry.NotifierCallback)
3929189445c0a66d074dcded77b9c7322ef45422727George Mount         */
3939189445c0a66d074dcded77b9c7322ef45422727George Mount        public abstract void onNotifyCallback(C callback, T sender, int arg, A arg2);
3949189445c0a66d074dcded77b9c7322ef45422727George Mount    }
3959189445c0a66d074dcded77b9c7322ef45422727George Mount}
396