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

Commit 6f4f8294 authored by Lee Shombert's avatar Lee Shombert Committed by Automerger Merge Worker
Browse files

Merge "A space-efficient 2D matrix" into sc-dev am: 1b1bbfc9 am: 0bc2e224

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14671775

Change-Id: I49a14d6f9b75e405513c7624f8397ba2d71a1b9e
parents 321e2e85 0bc2e224
Loading
Loading
Loading
Loading
+42 −46
Original line number Diff line number Diff line
@@ -58,10 +58,12 @@ import com.android.server.compat.CompatChange;
import com.android.server.om.OverlayReferenceMapper;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.utils.Snappable;
import com.android.server.utils.SnapshotCache;
import com.android.server.utils.Snapshots;
import com.android.server.utils.Watchable;
import com.android.server.utils.WatchableImpl;
import com.android.server.utils.WatchedArrayMap;
import com.android.server.utils.WatchedSparseBooleanMatrix;
import com.android.server.utils.Watcher;

import java.io.PrintWriter;
@@ -158,12 +160,21 @@ public class AppsFilter implements Watchable, Snappable {
     * initial scam and is null until {@link #onSystemReady()} is called.
     */
    @GuardedBy("mCacheLock")
    private volatile SparseArray<SparseBooleanArray> mShouldFilterCache;
    private volatile WatchedSparseBooleanMatrix mShouldFilterCache;

    /**
     * A cached snapshot.
     */
    private volatile AppsFilter mSnapshot = null;
    private final SnapshotCache<AppsFilter> mSnapshot;

    private SnapshotCache<AppsFilter> makeCache() {
        return new SnapshotCache<AppsFilter>(this, this) {
            @Override
            public AppsFilter createSnapshot() {
                AppsFilter s = new AppsFilter(mSource);
                return s;
            }};
    }

    /**
     * Watchable machinery
@@ -211,7 +222,6 @@ public class AppsFilter implements Watchable, Snappable {
     */
    @Override
    public void dispatchChange(@Nullable Watchable what) {
        mSnapshot = null;
        mWatchable.dispatchChange(what);
    }

@@ -236,6 +246,7 @@ public class AppsFilter implements Watchable, Snappable {
                overlayProvider);
        mStateProvider = stateProvider;
        mBackgroundExecutor = backgroundExecutor;
        mSnapshot = makeCache();
    }

    /**
@@ -258,8 +269,14 @@ public class AppsFilter implements Watchable, Snappable {
        mSystemSigningDetails = orig.mSystemSigningDetails;
        mProtectedBroadcasts = orig.mProtectedBroadcasts;
        mShouldFilterCache = orig.mShouldFilterCache;
        if (mShouldFilterCache != null) {
            synchronized (orig.mCacheLock) {
                mShouldFilterCache = mShouldFilterCache.snapshot();
            }
        }

        mBackgroundExecutor = null;
        mSnapshot = new SnapshotCache.Sealed<>();
    }

    /**
@@ -268,13 +285,7 @@ public class AppsFilter implements Watchable, Snappable {
     * condition causes the cached snapshot to be cleared asynchronously to this method.
     */
    public AppsFilter snapshot() {
        AppsFilter s = mSnapshot;
        if (s == null) {
            s = new AppsFilter(this);
            s.mWatchable.seal();
            mSnapshot = s;
        }
        return s;
        return mSnapshot.snapshot();
    }

    /**
@@ -636,12 +647,7 @@ public class AppsFilter implements Watchable, Snappable {
            if (mShouldFilterCache != null) {
                // update the cache in a one-off manner since we've got all the information we
                // need.
                SparseBooleanArray visibleUids = mShouldFilterCache.get(recipientUid);
                if (visibleUids == null) {
                    visibleUids = new SparseBooleanArray();
                    mShouldFilterCache.put(recipientUid, visibleUids);
                }
                visibleUids.put(visibleUid, false);
                mShouldFilterCache.put(recipientUid, visibleUid, false);
            }
        }
        if (changed) {
@@ -813,23 +819,21 @@ public class AppsFilter implements Watchable, Snappable {
        if (mShouldFilterCache == null) {
            return;
        }
        for (int i = mShouldFilterCache.size() - 1; i >= 0; i--) {
        for (int i = 0; i < mShouldFilterCache.size(); i++) {
            if (UserHandle.getAppId(mShouldFilterCache.keyAt(i)) == appId) {
                mShouldFilterCache.removeAt(i);
                continue;
            }
            SparseBooleanArray targetSparseArray = mShouldFilterCache.valueAt(i);
            for (int j = targetSparseArray.size() - 1; j >= 0; j--) {
                if (UserHandle.getAppId(targetSparseArray.keyAt(j)) == appId) {
                    targetSparseArray.removeAt(j);
                }
                // The key was deleted so the list of keys has shifted left.  That means i
                // is now pointing at the next key to be examined.  The decrement here and
                // the loop increment together mean that i will be unchanged in the need
                // iteration and will correctly point to the next key to be examined.
                i--;
            }
        }
    }

    private void updateEntireShouldFilterCache() {
        mStateProvider.runWithState((settings, users) -> {
            SparseArray<SparseBooleanArray> cache =
            WatchedSparseBooleanMatrix cache =
                    updateEntireShouldFilterCacheInner(settings, users);
            synchronized (mCacheLock) {
                mShouldFilterCache = cache;
@@ -837,10 +841,10 @@ public class AppsFilter implements Watchable, Snappable {
        });
    }

    private SparseArray<SparseBooleanArray> updateEntireShouldFilterCacheInner(
    private WatchedSparseBooleanMatrix updateEntireShouldFilterCacheInner(
            ArrayMap<String, PackageSetting> settings, UserInfo[] users) {
        SparseArray<SparseBooleanArray> cache =
                new SparseArray<>(users.length * settings.size());
        WatchedSparseBooleanMatrix cache =
                new WatchedSparseBooleanMatrix(users.length * settings.size());
        for (int i = settings.size() - 1; i >= 0; i--) {
            updateShouldFilterCacheForPackage(cache,
                    null /*skipPackage*/, settings.valueAt(i), settings, users, i);
@@ -864,7 +868,7 @@ public class AppsFilter implements Watchable, Snappable {
                    packagesCache.put(settings.keyAt(i), pkg);
                }
            });
            SparseArray<SparseBooleanArray> cache =
            WatchedSparseBooleanMatrix cache =
                    updateEntireShouldFilterCacheInner(settingsCopy, usersRef[0]);
            boolean[] changed = new boolean[1];
            // We have a cache, let's make sure the world hasn't changed out from under us.
@@ -916,7 +920,7 @@ public class AppsFilter implements Watchable, Snappable {
        }
    }

    private void updateShouldFilterCacheForPackage(SparseArray<SparseBooleanArray> cache,
    private void updateShouldFilterCacheForPackage(WatchedSparseBooleanMatrix cache,
            @Nullable String skipPackageName, PackageSetting subjectSetting, ArrayMap<String,
            PackageSetting> allSettings, UserInfo[] allUsers, int maxIndex) {
        for (int i = Math.min(maxIndex, allSettings.size() - 1); i >= 0; i--) {
@@ -935,17 +939,11 @@ public class AppsFilter implements Watchable, Snappable {
                for (int ou = 0; ou < userCount; ou++) {
                    int otherUser = allUsers[ou].id;
                    int subjectUid = UserHandle.getUid(subjectUser, subjectSetting.appId);
                    if (!cache.contains(subjectUid)) {
                        cache.put(subjectUid, new SparseBooleanArray(appxUidCount));
                    }
                    int otherUid = UserHandle.getUid(otherUser, otherSetting.appId);
                    if (!cache.contains(otherUid)) {
                        cache.put(otherUid, new SparseBooleanArray(appxUidCount));
                    }
                    cache.get(subjectUid).put(otherUid,
                    cache.put(subjectUid, otherUid,
                            shouldFilterApplicationInternal(
                                    subjectUid, subjectSetting, otherSetting, otherUser));
                    cache.get(otherUid).put(subjectUid,
                    cache.put(otherUid, subjectUid,
                            shouldFilterApplicationInternal(
                                    otherUid, otherSetting, subjectSetting, subjectUser));
                }
@@ -1198,22 +1196,20 @@ public class AppsFilter implements Watchable, Snappable {
            }
            synchronized (mCacheLock) {
                if (mShouldFilterCache != null) { // use cache
                    SparseBooleanArray shouldFilterTargets = mShouldFilterCache.get(callingUid);
                    final int targetUid = UserHandle.getUid(userId, targetPkgSetting.appId);
                    if (shouldFilterTargets == null) {
                    final int callingIndex = mShouldFilterCache.indexOfKey(callingUid);
                    if (callingIndex < 0) {
                        Slog.wtf(TAG, "Encountered calling uid with no cached rules: "
                                + callingUid);
                        return true;
                    }
                    int indexOfTargetUid = shouldFilterTargets.indexOfKey(targetUid);
                    if (indexOfTargetUid < 0) {
                    final int targetUid = UserHandle.getUid(userId, targetPkgSetting.appId);
                    final int targetIndex = mShouldFilterCache.indexOfKey(targetUid);
                    if (targetIndex < 0) {
                        Slog.w(TAG, "Encountered calling -> target with no cached rules: "
                                + callingUid + " -> " + targetUid);
                        return true;
                    }
                    if (!shouldFilterTargets.valueAt(indexOfTargetUid)) {
                        return false;
                    }
                    return mShouldFilterCache.valueAt(callingIndex, targetIndex);
                } else {
                    if (!shouldFilterApplicationInternal(
                            callingUid, callingSetting, targetPkgSetting, userId)) {
+562 −0

File added.

Preview size limit exceeded, changes collapsed.

+92 −1
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static org.junit.Assert.fail;

import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -34,9 +35,12 @@ import org.junit.Before;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;

/**
 * Test class for {@link Watcher}, {@link Watchable}, {@link WatchableImpl},
 * Test class for various utility classes that support the Watchable or Snappable
 * features.  This covers {@link Watcher}, {@link Watchable}, {@link WatchableImpl},
 * {@link WatchedArrayMap}, {@link WatchedSparseArray}, and
 * {@link WatchedSparseBooleanArray}.
 *
@@ -858,6 +862,93 @@ public class WatcherTest {
        }
    }

    private static class IndexGenerator {
        private final int mSeed;
        private final Random mRandom;
        public IndexGenerator(int seed) {
            mSeed = seed;
            mRandom = new Random(mSeed);
        }
        public int index() {
            return mRandom.nextInt(50000);
        }
        public void reset() {
            mRandom.setSeed(mSeed);
        }
    }

    // Return a value based on the row and column.  The algorithm tries to avoid simple
    // patterns like checkerboard.
    private final boolean cellValue(int row, int col) {
        return (((row * 4 + col) % 3)& 1) == 1;
    }

    // This is an inefficient way to know if a value appears in an array.
    private final boolean contains(int[] s, int length, int k) {
        for (int i = 0; i < length; i++) {
            if (s[i] == k) {
                return true;
            }
        }
        return false;
    }

    private void matrixTest(WatchedSparseBooleanMatrix matrix, int size, IndexGenerator indexer) {
        indexer.reset();
        int[] indexes = new int[size];
        for (int i = 0; i < size; i++) {
            int key = indexer.index();
            // Ensure the list of indices are unique.
            while (contains(indexes, i, key)) {
                key = indexer.index();
            }
            indexes[i] = key;
        }
        // Set values in the matrix.
        for (int i = 0; i < size; i++) {
            int row = indexes[i];
            for (int j = 0; j < size; j++) {
                int col = indexes[j];
                boolean want = cellValue(i, j);
                matrix.put(row, col, want);
            }
        }

        assertEquals(matrix.size(), size);

        // Read back and verify
        for (int i = 0; i < matrix.size(); i++) {
            int row = indexes[i];
            for (int j = 0; j < matrix.size(); j++) {
                int col = indexes[j];
                boolean want = cellValue(i, j);
                boolean actual = matrix.get(row, col);
                String msg = String.format("matrix(%d:%d, %d:%d) == %s, expected %s",
                                           i, row, j, col, actual, want);
                assertEquals(msg, actual, want);
            }
        }

        // Test the keyAt/indexOfKey methods
        for (int i = 0; i < matrix.size(); i++) {
            int key = indexes[i];
            assertEquals(matrix.keyAt(matrix.indexOfKey(key)), key);
        }
    }

    @Test
    public void testWatchedSparseBooleanMatrix() {
        final String name = "WatchedSparseBooleanMatrix";

        // The first part of this method tests the core matrix functionality.  The second
        // part tests the watchable behavior.  The third part tests the snappable
        // behavior.
        IndexGenerator indexer = new IndexGenerator(3);
        matrixTest(new WatchedSparseBooleanMatrix(), 10, indexer);
        matrixTest(new WatchedSparseBooleanMatrix(1000), 500, indexer);
        matrixTest(new WatchedSparseBooleanMatrix(1000), 2000, indexer);
    }

    @Test
    public void testNestedArrays() {
        final String name = "NestedArrays";