Loading services/core/java/com/android/server/pm/AppsFilter.java +42 −46 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -211,7 +222,6 @@ public class AppsFilter implements Watchable, Snappable { */ @Override public void dispatchChange(@Nullable Watchable what) { mSnapshot = null; mWatchable.dispatchChange(what); } Loading @@ -236,6 +246,7 @@ public class AppsFilter implements Watchable, Snappable { overlayProvider); mStateProvider = stateProvider; mBackgroundExecutor = backgroundExecutor; mSnapshot = makeCache(); } /** Loading @@ -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<>(); } /** Loading @@ -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(); } /** Loading Loading @@ -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) { Loading Loading @@ -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; Loading @@ -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); Loading @@ -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. Loading Loading @@ -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--) { Loading @@ -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)); } Loading Loading @@ -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)) { Loading services/core/java/com/android/server/utils/WatchedSparseBooleanMatrix.java 0 → 100644 +562 −0 File added.Preview size limit exceeded, changes collapsed. Show changes services/tests/servicestests/src/com/android/server/utils/WatcherTest.java +92 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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}. * Loading Loading @@ -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"; Loading Loading
services/core/java/com/android/server/pm/AppsFilter.java +42 −46 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -211,7 +222,6 @@ public class AppsFilter implements Watchable, Snappable { */ @Override public void dispatchChange(@Nullable Watchable what) { mSnapshot = null; mWatchable.dispatchChange(what); } Loading @@ -236,6 +246,7 @@ public class AppsFilter implements Watchable, Snappable { overlayProvider); mStateProvider = stateProvider; mBackgroundExecutor = backgroundExecutor; mSnapshot = makeCache(); } /** Loading @@ -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<>(); } /** Loading @@ -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(); } /** Loading Loading @@ -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) { Loading Loading @@ -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; Loading @@ -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); Loading @@ -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. Loading Loading @@ -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--) { Loading @@ -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)); } Loading Loading @@ -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)) { Loading
services/core/java/com/android/server/utils/WatchedSparseBooleanMatrix.java 0 → 100644 +562 −0 File added.Preview size limit exceeded, changes collapsed. Show changes
services/tests/servicestests/src/com/android/server/utils/WatcherTest.java +92 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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}. * Loading Loading @@ -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"; Loading