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