Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6b8f486b authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Extract SparseArrayMap from QuotaController."

parents 595205d9 a26ae90b
Loading
Loading
Loading
Loading
+19 −106
Original line number Diff line number Diff line
@@ -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);
+1 −0
Original line number Diff line number Diff line
@@ -48769,6 +48769,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);
+19 −0
Original line number Diff line number Diff line
@@ -4245,6 +4245,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);
  }
+8 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
/*
 * 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));
            }
        }
    }
}