Extract SparseArrayMap from QuotaController.
This structure can be useful in other parts of the system, so extracting it and making it more generic makes sense. Bug: 135764360 Bug: 141645789 Test: atest QuotaControllerTest Test: atest SparseArrayMapTest Change-Id: I2aeb3ea53e3dc2ec00667a0a2ccdb112cf562377
This commit is contained in:
@@ -50,12 +50,11 @@ import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.KeyValueListParser;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseArrayMap;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.util.SparseSetArray;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
@@ -113,92 +112,6 @@ public final class QuotaController extends StateController {
|
||||
private static final String ALARM_TAG_CLEANUP = "*job.cleanup*";
|
||||
private static final String ALARM_TAG_QUOTA_CHECK = "*job.quota_check*";
|
||||
|
||||
/**
|
||||
* A sparse array of ArrayMaps, which is suitable for holding (userId, packageName)->object
|
||||
* associations.
|
||||
*/
|
||||
private static class UserPackageMap<T> {
|
||||
private final SparseArray<ArrayMap<String, T>> mData = new SparseArray<>();
|
||||
|
||||
public void add(int userId, @NonNull String packageName, @Nullable T obj) {
|
||||
ArrayMap<String, T> data = mData.get(userId);
|
||||
if (data == null) {
|
||||
data = new ArrayMap<String, T>();
|
||||
mData.put(userId, data);
|
||||
}
|
||||
data.put(packageName, obj);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
for (int i = 0; i < mData.size(); ++i) {
|
||||
mData.valueAt(i).clear();
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes all the data for the user, if there was any. */
|
||||
public void delete(int userId) {
|
||||
mData.delete(userId);
|
||||
}
|
||||
|
||||
/** Removes the data for the user and package, if there was any. */
|
||||
public void delete(int userId, @NonNull String packageName) {
|
||||
ArrayMap<String, T> data = mData.get(userId);
|
||||
if (data != null) {
|
||||
data.remove(packageName);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public T get(int userId, @NonNull String packageName) {
|
||||
ArrayMap<String, T> data = mData.get(userId);
|
||||
if (data != null) {
|
||||
return data.get(packageName);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @see SparseArray#indexOfKey */
|
||||
public int indexOfKey(int userId) {
|
||||
return mData.indexOfKey(userId);
|
||||
}
|
||||
|
||||
/** Returns the userId at the given index. */
|
||||
public int keyAt(int index) {
|
||||
return mData.keyAt(index);
|
||||
}
|
||||
|
||||
/** Returns the package name at the given index. */
|
||||
@NonNull
|
||||
public String keyAt(int userIndex, int packageIndex) {
|
||||
return mData.valueAt(userIndex).keyAt(packageIndex);
|
||||
}
|
||||
|
||||
/** Returns the size of the outer (userId) array. */
|
||||
public int numUsers() {
|
||||
return mData.size();
|
||||
}
|
||||
|
||||
public int numPackagesForUser(int userId) {
|
||||
ArrayMap<String, T> data = mData.get(userId);
|
||||
return data == null ? 0 : data.size();
|
||||
}
|
||||
|
||||
/** Returns the value T at the given user and index. */
|
||||
@Nullable
|
||||
public T valueAt(int userIndex, int packageIndex) {
|
||||
return mData.valueAt(userIndex).valueAt(packageIndex);
|
||||
}
|
||||
|
||||
public void forEach(Consumer<T> consumer) {
|
||||
for (int i = numUsers() - 1; i >= 0; --i) {
|
||||
ArrayMap<String, T> data = mData.valueAt(i);
|
||||
for (int j = data.size() - 1; j >= 0; --j) {
|
||||
consumer.accept(data.valueAt(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Standardize the output of userId-packageName combo.
|
||||
*/
|
||||
@@ -378,22 +291,22 @@ public final class QuotaController extends StateController {
|
||||
}
|
||||
|
||||
/** List of all tracked jobs keyed by source package-userId combo. */
|
||||
private final UserPackageMap<ArraySet<JobStatus>> mTrackedJobs = new UserPackageMap<>();
|
||||
private final SparseArrayMap<ArraySet<JobStatus>> mTrackedJobs = new SparseArrayMap<>();
|
||||
|
||||
/** Timer for each package-userId combo. */
|
||||
private final UserPackageMap<Timer> mPkgTimers = new UserPackageMap<>();
|
||||
private final SparseArrayMap<Timer> mPkgTimers = new SparseArrayMap<>();
|
||||
|
||||
/** List of all timing sessions for a package-userId combo, in chronological order. */
|
||||
private final UserPackageMap<List<TimingSession>> mTimingSessions = new UserPackageMap<>();
|
||||
private final SparseArrayMap<List<TimingSession>> mTimingSessions = new SparseArrayMap<>();
|
||||
|
||||
/**
|
||||
* List of alarm listeners for each package that listen for when each package comes back within
|
||||
* quota.
|
||||
*/
|
||||
private final UserPackageMap<QcAlarmListener> mInQuotaAlarmListeners = new UserPackageMap<>();
|
||||
private final SparseArrayMap<QcAlarmListener> mInQuotaAlarmListeners = new SparseArrayMap<>();
|
||||
|
||||
/** Cached calculation results for each app, with the standby buckets as the array indices. */
|
||||
private final UserPackageMap<ExecutionStats[]> mExecutionStatsCache = new UserPackageMap<>();
|
||||
private final SparseArrayMap<ExecutionStats[]> mExecutionStatsCache = new SparseArrayMap<>();
|
||||
|
||||
/** List of UIDs currently in the foreground. */
|
||||
private final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
|
||||
@@ -1206,9 +1119,9 @@ public final class QuotaController extends StateController {
|
||||
|
||||
private void maybeUpdateAllConstraintsLocked() {
|
||||
boolean changed = false;
|
||||
for (int u = 0; u < mTrackedJobs.numUsers(); ++u) {
|
||||
for (int u = 0; u < mTrackedJobs.numMaps(); ++u) {
|
||||
final int userId = mTrackedJobs.keyAt(u);
|
||||
for (int p = 0; p < mTrackedJobs.numPackagesForUser(userId); ++p) {
|
||||
for (int p = 0; p < mTrackedJobs.numElementsForKey(userId); ++p) {
|
||||
final String packageName = mTrackedJobs.keyAt(u, p);
|
||||
changed |= maybeUpdateConstraintForPkgLocked(userId, packageName);
|
||||
}
|
||||
@@ -1268,7 +1181,7 @@ public final class QuotaController extends StateController {
|
||||
}
|
||||
|
||||
private class UidConstraintUpdater implements Consumer<JobStatus> {
|
||||
private final UserPackageMap<Integer> mToScheduleStartAlarms = new UserPackageMap<>();
|
||||
private final SparseArrayMap<Integer> mToScheduleStartAlarms = new SparseArrayMap<>();
|
||||
public boolean wasJobChanged;
|
||||
|
||||
@Override
|
||||
@@ -1290,9 +1203,9 @@ public final class QuotaController extends StateController {
|
||||
}
|
||||
|
||||
void postProcess() {
|
||||
for (int u = 0; u < mToScheduleStartAlarms.numUsers(); ++u) {
|
||||
for (int u = 0; u < mToScheduleStartAlarms.numMaps(); ++u) {
|
||||
final int userId = mToScheduleStartAlarms.keyAt(u);
|
||||
for (int p = 0; p < mToScheduleStartAlarms.numPackagesForUser(userId); ++p) {
|
||||
for (int p = 0; p < mToScheduleStartAlarms.numElementsForKey(userId); ++p) {
|
||||
final String packageName = mToScheduleStartAlarms.keyAt(u, p);
|
||||
final int standbyBucket = mToScheduleStartAlarms.get(userId, packageName);
|
||||
maybeScheduleStartAlarmLocked(userId, packageName, standbyBucket);
|
||||
@@ -2547,9 +2460,9 @@ public final class QuotaController extends StateController {
|
||||
});
|
||||
|
||||
pw.println();
|
||||
for (int u = 0; u < mPkgTimers.numUsers(); ++u) {
|
||||
for (int u = 0; u < mPkgTimers.numMaps(); ++u) {
|
||||
final int userId = mPkgTimers.keyAt(u);
|
||||
for (int p = 0; p < mPkgTimers.numPackagesForUser(userId); ++p) {
|
||||
for (int p = 0; p < mPkgTimers.numElementsForKey(userId); ++p) {
|
||||
final String pkgName = mPkgTimers.keyAt(u, p);
|
||||
mPkgTimers.valueAt(u, p).dump(pw, predicate);
|
||||
pw.println();
|
||||
@@ -2571,9 +2484,9 @@ public final class QuotaController extends StateController {
|
||||
|
||||
pw.println("Cached execution stats:");
|
||||
pw.increaseIndent();
|
||||
for (int u = 0; u < mExecutionStatsCache.numUsers(); ++u) {
|
||||
for (int u = 0; u < mExecutionStatsCache.numMaps(); ++u) {
|
||||
final int userId = mExecutionStatsCache.keyAt(u);
|
||||
for (int p = 0; p < mExecutionStatsCache.numPackagesForUser(userId); ++p) {
|
||||
for (int p = 0; p < mExecutionStatsCache.numElementsForKey(userId); ++p) {
|
||||
final String pkgName = mExecutionStatsCache.keyAt(u, p);
|
||||
ExecutionStats[] stats = mExecutionStatsCache.valueAt(u, p);
|
||||
|
||||
@@ -2595,9 +2508,9 @@ public final class QuotaController extends StateController {
|
||||
pw.println();
|
||||
pw.println("In quota alarms:");
|
||||
pw.increaseIndent();
|
||||
for (int u = 0; u < mInQuotaAlarmListeners.numUsers(); ++u) {
|
||||
for (int u = 0; u < mInQuotaAlarmListeners.numMaps(); ++u) {
|
||||
final int userId = mInQuotaAlarmListeners.keyAt(u);
|
||||
for (int p = 0; p < mInQuotaAlarmListeners.numPackagesForUser(userId); ++p) {
|
||||
for (int p = 0; p < mInQuotaAlarmListeners.numElementsForKey(userId); ++p) {
|
||||
final String pkgName = mInQuotaAlarmListeners.keyAt(u, p);
|
||||
QcAlarmListener alarmListener = mInQuotaAlarmListeners.valueAt(u, p);
|
||||
|
||||
@@ -2667,9 +2580,9 @@ public final class QuotaController extends StateController {
|
||||
}
|
||||
});
|
||||
|
||||
for (int u = 0; u < mPkgTimers.numUsers(); ++u) {
|
||||
for (int u = 0; u < mPkgTimers.numMaps(); ++u) {
|
||||
final int userId = mPkgTimers.keyAt(u);
|
||||
for (int p = 0; p < mPkgTimers.numPackagesForUser(userId); ++p) {
|
||||
for (int p = 0; p < mPkgTimers.numElementsForKey(userId); ++p) {
|
||||
final String pkgName = mPkgTimers.keyAt(u, p);
|
||||
final long psToken = proto.start(
|
||||
StateControllerProto.QuotaController.PACKAGE_STATS);
|
||||
|
||||
@@ -48689,6 +48689,7 @@ package android.util {
|
||||
method public void append(int, E);
|
||||
method public void clear();
|
||||
method public android.util.SparseArray<E> clone();
|
||||
method public boolean contains(int);
|
||||
method public void delete(int);
|
||||
method public E get(int);
|
||||
method public E get(int, E);
|
||||
|
||||
@@ -4242,6 +4242,25 @@ package android.util {
|
||||
field public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2";
|
||||
}
|
||||
|
||||
public class SparseArrayMap<T> {
|
||||
ctor public SparseArrayMap();
|
||||
method public void add(int, @NonNull String, @Nullable T);
|
||||
method public void clear();
|
||||
method public boolean contains(int, @NonNull String);
|
||||
method public void delete(int);
|
||||
method @Nullable public T delete(int, @NonNull String);
|
||||
method public void forEach(@NonNull java.util.function.Consumer<T>);
|
||||
method @Nullable public T get(int, @NonNull String);
|
||||
method @Nullable public T getOrDefault(int, @NonNull String, T);
|
||||
method public int indexOfKey(int);
|
||||
method public int indexOfKey(int, @NonNull String);
|
||||
method public int keyAt(int);
|
||||
method @NonNull public String keyAt(int, int);
|
||||
method public int numElementsForKey(int);
|
||||
method public int numMaps();
|
||||
method @Nullable public T valueAt(int, int);
|
||||
}
|
||||
|
||||
public class TimeUtils {
|
||||
method public static String formatDuration(long);
|
||||
}
|
||||
|
||||
@@ -103,6 +103,14 @@ public class SparseArray<E> implements Cloneable {
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the key exists in the array. This is equivalent to
|
||||
* {@link #indexOfKey(int)} >= 0.
|
||||
*/
|
||||
public boolean contains(int key) {
|
||||
return indexOfKey(key) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Object mapped from the specified key, or <code>null</code>
|
||||
* if no such mapping has been made.
|
||||
|
||||
159
core/java/android/util/SparseArrayMap.java
Normal file
159
core/java/android/util/SparseArrayMap.java
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.util;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.TestApi;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* A sparse array of ArrayMaps, which is suitable for holding (userId, packageName)->object
|
||||
* associations.
|
||||
*
|
||||
* @param <T> Any class
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
public class SparseArrayMap<T> {
|
||||
private final SparseArray<ArrayMap<String, T>> mData = new SparseArray<>();
|
||||
|
||||
/** Add an entry associating obj with the int-String pair. */
|
||||
public void add(int key, @NonNull String mapKey, @Nullable T obj) {
|
||||
ArrayMap<String, T> data = mData.get(key);
|
||||
if (data == null) {
|
||||
data = new ArrayMap<>();
|
||||
mData.put(key, data);
|
||||
}
|
||||
data.put(mapKey, obj);
|
||||
}
|
||||
|
||||
/** Remove all entries from the map. */
|
||||
public void clear() {
|
||||
for (int i = 0; i < mData.size(); ++i) {
|
||||
mData.valueAt(i).clear();
|
||||
}
|
||||
}
|
||||
|
||||
/** Return true if the structure contains an explicit entry for the int-String pair. */
|
||||
public boolean contains(int key, @NonNull String mapKey) {
|
||||
return mData.contains(key) && mData.get(key).containsKey(mapKey);
|
||||
}
|
||||
|
||||
/** Removes all the data for the key, if there was any. */
|
||||
public void delete(int key) {
|
||||
mData.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the data for the key and mapKey, if there was any.
|
||||
*
|
||||
* @return Returns the value that was stored under the keys, or null if there was none.
|
||||
*/
|
||||
@Nullable
|
||||
public T delete(int key, @NonNull String mapKey) {
|
||||
ArrayMap<String, T> data = mData.get(key);
|
||||
if (data != null) {
|
||||
return data.remove(mapKey);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value associated with the int-String pair.
|
||||
*/
|
||||
@Nullable
|
||||
public T get(int key, @NonNull String mapKey) {
|
||||
ArrayMap<String, T> data = mData.get(key);
|
||||
if (data != null) {
|
||||
return data.get(mapKey);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value to which the specified key and mapKey are mapped, or defaultValue if this
|
||||
* map contains no mapping for them.
|
||||
*/
|
||||
@Nullable
|
||||
public T getOrDefault(int key, @NonNull String mapKey, T defaultValue) {
|
||||
if (mData.contains(key)) {
|
||||
ArrayMap<String, T> data = mData.get(key);
|
||||
if (data != null && data.containsKey(mapKey)) {
|
||||
return data.get(mapKey);
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/** @see SparseArray#indexOfKey */
|
||||
public int indexOfKey(int key) {
|
||||
return mData.indexOfKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the mapKey.
|
||||
*
|
||||
* @see SparseArray#indexOfKey
|
||||
*/
|
||||
public int indexOfKey(int key, @NonNull String mapKey) {
|
||||
ArrayMap<String, T> data = mData.get(key);
|
||||
if (data != null) {
|
||||
return data.indexOfKey(mapKey);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Returns the key at the given index. */
|
||||
public int keyAt(int index) {
|
||||
return mData.keyAt(index);
|
||||
}
|
||||
|
||||
/** Returns the map's key at the given mapIndex for the given keyIndex. */
|
||||
@NonNull
|
||||
public String keyAt(int keyIndex, int mapIndex) {
|
||||
return mData.valueAt(keyIndex).keyAt(mapIndex);
|
||||
}
|
||||
|
||||
/** Returns the size of the outer array. */
|
||||
public int numMaps() {
|
||||
return mData.size();
|
||||
}
|
||||
|
||||
/** Returns the number of elements in the map of the given key. */
|
||||
public int numElementsForKey(int key) {
|
||||
ArrayMap<String, T> data = mData.get(key);
|
||||
return data == null ? 0 : data.size();
|
||||
}
|
||||
|
||||
/** Returns the value T at the given key and map index. */
|
||||
@Nullable
|
||||
public T valueAt(int keyIndex, int mapIndex) {
|
||||
return mData.valueAt(keyIndex).valueAt(mapIndex);
|
||||
}
|
||||
|
||||
/** Iterate through all int-String pairs and operate on all of the values. */
|
||||
public void forEach(@NonNull Consumer<T> consumer) {
|
||||
for (int i = numMaps() - 1; i >= 0; --i) {
|
||||
ArrayMap<String, T> data = mData.valueAt(i);
|
||||
for (int j = data.size() - 1; j >= 0; --j) {
|
||||
consumer.accept(data.valueAt(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user