diff options
| author | 2022-03-31 14:35:02 +0000 | |
|---|---|---|
| committer | 2022-03-31 14:35:02 +0000 | |
| commit | 2e9d7c9822bb5a6a767069fdc861c5f108c11120 (patch) | |
| tree | 2970e416fb5ece9fc9436029157e1dcad5e82f70 | |
| parent | 336b6a15773fd76f91395d06d65cb3f52ab83b88 (diff) | |
| parent | 8cb3e6aac93147287bcb9fa41a284b1745d47c75 (diff) | |
Merge "[AppsFilter] read-only interface for snapshots" into tm-dev
13 files changed, 1065 insertions, 556 deletions
diff --git a/core/java/android/util/SparseSetArray.java b/core/java/android/util/SparseSetArray.java index f5025f7a9e99..eb53b425a228 100644 --- a/core/java/android/util/SparseSetArray.java +++ b/core/java/android/util/SparseSetArray.java @@ -21,9 +21,26 @@ package android.util; * @hide */ public class SparseSetArray<T> { - private final SparseArray<ArraySet<T>> mData = new SparseArray<>(); + private final SparseArray<ArraySet<T>> mData; public SparseSetArray() { + mData = new SparseArray<>(); + } + + /** + * Copy constructor + */ + public SparseSetArray(SparseSetArray<T> src) { + final int arraySize = src.size(); + mData = new SparseArray<>(arraySize); + for (int i = 0; i < arraySize; i++) { + final int key = src.keyAt(i); + final ArraySet<T> set = src.get(key); + final int setSize = set.size(); + for (int j = 0; j < setSize; j++) { + add(key, set.valueAt(j)); + } + } } /** diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java index 152c74553d32..17dc403cf2c5 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilterImpl.java @@ -44,7 +44,6 @@ import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; -import android.util.SparseSetArray; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -52,7 +51,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.function.QuadFunction; import com.android.server.FgThread; -import com.android.server.LocalServices; import com.android.server.compat.CompatChange; import com.android.server.om.OverlayReferenceMapper; import com.android.server.pm.parsing.pkg.AndroidPackage; @@ -65,14 +63,18 @@ import com.android.server.pm.pkg.component.ParsedMainComponent; import com.android.server.pm.pkg.component.ParsedProvider; 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.Watched; +import com.android.server.utils.WatchedArrayList; import com.android.server.utils.WatchedArrayMap; +import com.android.server.utils.WatchedArraySet; import com.android.server.utils.WatchedSparseBooleanMatrix; +import com.android.server.utils.WatchedSparseSetArray; import com.android.server.utils.Watcher; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -85,7 +87,7 @@ import java.util.concurrent.Executor; * manifests. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) -public class AppsFilter implements Watchable, Snappable { +public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable { private static final String TAG = "AppsFilter"; @@ -100,32 +102,48 @@ public class AppsFilter implements Watchable, Snappable { * application B is implicitly allowed to query for application A; regardless of any manifest * entries. */ - private final SparseSetArray<Integer> mImplicitlyQueryable = new SparseSetArray<>(); + @GuardedBy("mLock") + @Watched + private final WatchedSparseSetArray<Integer> mImplicitlyQueryable; + private final SnapshotCache<WatchedSparseSetArray<Integer>> mImplicitQueryableSnapshot; /** * This contains a list of app UIDs that are implicitly queryable because another app explicitly * interacted with it, but could keep across package updates. For example, if application A * grants persistable uri permission to application B; regardless of any manifest entries. */ - private final SparseSetArray<Integer> mRetainedImplicitlyQueryable = new SparseSetArray<>(); + @GuardedBy("mLock") + @Watched + private final WatchedSparseSetArray<Integer> mRetainedImplicitlyQueryable; + private final SnapshotCache<WatchedSparseSetArray<Integer>> + mRetainedImplicitlyQueryableSnapshot; /** * A mapping from the set of App IDs that query other App IDs via package name to the * list of packages that they can see. */ - private final SparseSetArray<Integer> mQueriesViaPackage = new SparseSetArray<>(); + @GuardedBy("mLock") + @Watched + private final WatchedSparseSetArray<Integer> mQueriesViaPackage; + private final SnapshotCache<WatchedSparseSetArray<Integer>> mQueriesViaPackageSnapshot; /** * A mapping from the set of App IDs that query others via component match to the list * of packages that the they resolve to. */ - private final SparseSetArray<Integer> mQueriesViaComponent = new SparseSetArray<>(); + @GuardedBy("mLock") + @Watched + private final WatchedSparseSetArray<Integer> mQueriesViaComponent; + private final SnapshotCache<WatchedSparseSetArray<Integer>> mQueriesViaComponentSnapshot; /** * A mapping from the set of App IDs that query other App IDs via library name to the * list of packages that they can see. */ - private final SparseSetArray<Integer> mQueryableViaUsesLibrary = new SparseSetArray<>(); + @GuardedBy("mLock") + @Watched + private final WatchedSparseSetArray<Integer> mQueryableViaUsesLibrary; + private final SnapshotCache<WatchedSparseSetArray<Integer>> mQueryableViaUsesLibrarySnapshot; /** * Executor for running reasonably short background tasks such as building the initial @@ -145,7 +163,10 @@ public class AppsFilter implements Watchable, Snappable { * A set of App IDs that are always queryable by any package, regardless of their manifest * content. */ - private final ArraySet<Integer> mForceQueryable = new ArraySet<>(); + @Watched + @GuardedBy("mLock") + private final WatchedArraySet<Integer> mForceQueryable; + private final SnapshotCache<WatchedArraySet<Integer>> mForceQueryableSnapshot; /** * The set of package names provided by the device that should be force queryable regardless of @@ -155,14 +176,16 @@ public class AppsFilter implements Watchable, Snappable { /** True if all system apps should be made queryable by default. */ private final boolean mSystemAppsQueryable; - private final FeatureConfig mFeatureConfig; private final OverlayReferenceMapper mOverlayReferenceMapper; private final StateProvider mStateProvider; private final PackageManagerInternal mPmInternal; - private SigningDetails mSystemSigningDetails; - private Set<String> mProtectedBroadcasts = new ArraySet<>(); + + @GuardedBy("mLock") + @Watched + private final WatchedArrayList<String> mProtectedBroadcasts; + private final SnapshotCache<WatchedArrayList<String>> mProtectedBroadcastsSnapshot; private final Object mCacheLock = new Object(); @@ -171,29 +194,47 @@ public class AppsFilter implements Watchable, Snappable { * filtered to the second. It's essentially a cache of the * {@link #shouldFilterApplicationInternal(int, Object, PackageStateInternal, int)} call. * NOTE: It can only be relied upon after the system is ready to avoid unnecessary update on - * initial scam and is null until {@link #onSystemReady()} is called. + * initial scam and is empty until {@link #onSystemReady()} is called. */ @GuardedBy("mCacheLock") - private volatile WatchedSparseBooleanMatrix mShouldFilterCache; + @NonNull + private final WatchedSparseBooleanMatrix mShouldFilterCache; + private final SnapshotCache<WatchedSparseBooleanMatrix> mShouldFilterCacheSnapshot; + + /** + * Guards the accesses for the list/set fields except for {@link #mShouldFilterCache} + */ + private final Object mLock = new Object(); /** * A cached snapshot. */ - private final SnapshotCache<AppsFilter> mSnapshot; + private final SnapshotCache<AppsFilterImpl> mSnapshot; - private SnapshotCache<AppsFilter> makeCache() { - return new SnapshotCache<AppsFilter>(this, this) { + private SnapshotCache<AppsFilterImpl> makeCache() { + return new SnapshotCache<AppsFilterImpl>(this, this) { @Override - public AppsFilter createSnapshot() { - AppsFilter s = new AppsFilter(mSource); + public AppsFilterImpl createSnapshot() { + AppsFilterImpl s = new AppsFilterImpl(mSource); + s.mWatchable.seal(); return s; - }}; + } + }; } /** * Watchable machinery */ private final WatchableImpl mWatchable = new WatchableImpl(); + /** + * The observer that watches for changes from array members + */ + private final Watcher mObserver = new Watcher() { + @Override + public void onChange(@Nullable Watchable what) { + AppsFilterImpl.this.dispatchChange(what); + } + }; /** * Ensures an observer is in the list, exactly once. The observer cannot be null. The @@ -219,6 +260,7 @@ public class AppsFilter implements Watchable, Snappable { /** * Return true if the {@link Watcher) is a registered observer. + * * @param observer A {@link Watcher} that might be registered * @return true if the observer is registered with this {@link Watchable}. */ @@ -247,7 +289,7 @@ public class AppsFilter implements Watchable, Snappable { } @VisibleForTesting(visibility = PRIVATE) - AppsFilter(StateProvider stateProvider, + AppsFilterImpl(StateProvider stateProvider, FeatureConfig featureConfig, String[] forceQueryableList, boolean systemAppsQueryable, @@ -262,34 +304,70 @@ public class AppsFilter implements Watchable, Snappable { mStateProvider = stateProvider; mPmInternal = pmInternal; mBackgroundExecutor = backgroundExecutor; + mImplicitlyQueryable = new WatchedSparseSetArray<>(); + mImplicitQueryableSnapshot = new SnapshotCache.Auto<>( + mImplicitlyQueryable, mImplicitlyQueryable, "AppsFilter.mImplicitlyQueryable"); + mRetainedImplicitlyQueryable = new WatchedSparseSetArray<>(); + mRetainedImplicitlyQueryableSnapshot = new SnapshotCache.Auto<>( + mRetainedImplicitlyQueryable, mRetainedImplicitlyQueryable, + "AppsFilter.mRetainedImplicitlyQueryable"); + mQueriesViaPackage = new WatchedSparseSetArray<>(); + mQueriesViaPackageSnapshot = new SnapshotCache.Auto<>( + mQueriesViaPackage, mQueriesViaPackage, "AppsFilter.mQueriesViaPackage"); + mQueriesViaComponent = new WatchedSparseSetArray<>(); + mQueriesViaComponentSnapshot = new SnapshotCache.Auto<>( + mQueriesViaComponent, mQueriesViaComponent, "AppsFilter.mQueriesViaComponent"); + mQueryableViaUsesLibrary = new WatchedSparseSetArray<>(); + mQueryableViaUsesLibrarySnapshot = new SnapshotCache.Auto<>( + mQueryableViaUsesLibrary, mQueryableViaUsesLibrary, + "AppsFilter.mQueryableViaUsesLibrary"); + mForceQueryable = new WatchedArraySet<>(); + mForceQueryableSnapshot = new SnapshotCache.Auto<>( + mForceQueryable, mForceQueryable, "AppsFilter.mForceQueryable"); + mProtectedBroadcasts = new WatchedArrayList<>(); + mProtectedBroadcastsSnapshot = new SnapshotCache.Auto<>( + mProtectedBroadcasts, mProtectedBroadcasts, "AppsFilter.mProtectedBroadcasts"); + mShouldFilterCache = new WatchedSparseBooleanMatrix(); + mShouldFilterCacheSnapshot = new SnapshotCache.Auto<>( + mShouldFilterCache, mShouldFilterCache, "AppsFilter.mShouldFilterCache"); + + registerObservers(); + Watchable.verifyWatchedAttributes(this, mObserver); mSnapshot = makeCache(); } /** * The copy constructor is used by PackageManagerService to construct a snapshot. - * Attributes are not deep-copied since these are supposed to be immutable. - * TODO: deep-copy the attributes, if necessary. */ - private AppsFilter(AppsFilter orig) { - Snapshots.copy(mImplicitlyQueryable, orig.mImplicitlyQueryable); - Snapshots.copy(mRetainedImplicitlyQueryable, orig.mRetainedImplicitlyQueryable); - Snapshots.copy(mQueriesViaPackage, orig.mQueriesViaPackage); - Snapshots.copy(mQueriesViaComponent, orig.mQueriesViaComponent); - Snapshots.copy(mQueryableViaUsesLibrary, orig.mQueryableViaUsesLibrary); + private AppsFilterImpl(AppsFilterImpl orig) { + synchronized (orig.mLock) { + mImplicitlyQueryable = orig.mImplicitQueryableSnapshot.snapshot(); + mImplicitQueryableSnapshot = new SnapshotCache.Sealed<>(); + mRetainedImplicitlyQueryable = orig.mRetainedImplicitlyQueryableSnapshot.snapshot(); + mRetainedImplicitlyQueryableSnapshot = new SnapshotCache.Sealed<>(); + mQueriesViaPackage = orig.mQueriesViaPackageSnapshot.snapshot(); + mQueriesViaPackageSnapshot = new SnapshotCache.Sealed<>(); + mQueriesViaComponent = orig.mQueriesViaComponentSnapshot.snapshot(); + mQueriesViaComponentSnapshot = new SnapshotCache.Sealed<>(); + mQueryableViaUsesLibrary = orig.mQueryableViaUsesLibrarySnapshot.snapshot(); + mQueryableViaUsesLibrarySnapshot = new SnapshotCache.Sealed<>(); + mForceQueryable = orig.mForceQueryableSnapshot.snapshot(); + mForceQueryableSnapshot = new SnapshotCache.Sealed<>(); + mProtectedBroadcasts = orig.mProtectedBroadcastsSnapshot.snapshot(); + mProtectedBroadcastsSnapshot = new SnapshotCache.Sealed<>(); + } mQueriesViaComponentRequireRecompute = orig.mQueriesViaComponentRequireRecompute; - mForceQueryable.addAll(orig.mForceQueryable); - mForceQueryableByDevicePackageNames = orig.mForceQueryableByDevicePackageNames; + mForceQueryableByDevicePackageNames = + Arrays.copyOf(orig.mForceQueryableByDevicePackageNames, + orig.mForceQueryableByDevicePackageNames.length); mSystemAppsQueryable = orig.mSystemAppsQueryable; mFeatureConfig = orig.mFeatureConfig; mOverlayReferenceMapper = orig.mOverlayReferenceMapper; mStateProvider = orig.mStateProvider; mSystemSigningDetails = orig.mSystemSigningDetails; - mProtectedBroadcasts = orig.mProtectedBroadcasts; - mShouldFilterCache = orig.mShouldFilterCache; - if (mShouldFilterCache != null) { - synchronized (orig.mCacheLock) { - mShouldFilterCache = mShouldFilterCache.snapshot(); - } + synchronized (orig.mCacheLock) { + mShouldFilterCache = orig.mShouldFilterCacheSnapshot.snapshot(); + mShouldFilterCacheSnapshot = new SnapshotCache.Sealed<>(); } mBackgroundExecutor = null; @@ -297,12 +375,24 @@ public class AppsFilter implements Watchable, Snappable { mSnapshot = new SnapshotCache.Sealed<>(); } + @SuppressWarnings("GuardedBy") + private void registerObservers() { + mImplicitlyQueryable.registerObserver(mObserver); + mRetainedImplicitlyQueryable.registerObserver(mObserver); + mQueriesViaPackage.registerObserver(mObserver); + mQueriesViaComponent.registerObserver(mObserver); + mQueryableViaUsesLibrary.registerObserver(mObserver); + mForceQueryable.registerObserver(mObserver); + mProtectedBroadcasts.registerObserver(mObserver); + mShouldFilterCache.registerObserver(mObserver); + } + /** * Return a snapshot. If the cached snapshot is null, build a new one. The logic in * the function ensures that this function returns a valid snapshot even if a race * condition causes the cached snapshot to be cleared asynchronously to this method. */ - public AppsFilter snapshot() { + public AppsFilterSnapshot snapshot() { return mSnapshot.snapshot(); } @@ -363,7 +453,7 @@ public class AppsFilter implements Watchable, Snappable { @Nullable private SparseBooleanArray mLoggingEnabled = null; - private AppsFilter mAppsFilter; + private AppsFilterImpl mAppsFilter; private FeatureConfigImpl( PackageManagerInternal pmInternal, PackageManagerServiceInjector injector) { @@ -371,7 +461,7 @@ public class AppsFilter implements Watchable, Snappable { mInjector = injector; } - public void setAppsFilter(AppsFilter filter) { + public void setAppsFilter(AppsFilterImpl filter) { mAppsFilter = filter; } @@ -475,8 +565,9 @@ public class AppsFilter implements Watchable, Snappable { @Override public void updatePackageState(PackageStateInternal setting, boolean removed) { - final boolean enableLogging = setting.getPkg() != null && - !removed && (setting.getPkg().isTestOnly() || setting.getPkg().isDebuggable()); + final boolean enableLogging = setting.getPkg() != null + && !removed && (setting.getPkg().isTestOnly() + || setting.getPkg().isDebuggable()); enableLogging(setting.getAppId(), enableLogging); if (removed) { mDisabledPackages.remove(setting.getPackageName()); @@ -487,7 +578,7 @@ public class AppsFilter implements Watchable, Snappable { } /** Builder method for an AppsFilter */ - public static AppsFilter create(@NonNull PackageManagerServiceInjector injector, + public static AppsFilterImpl create(@NonNull PackageManagerServiceInjector injector, @NonNull PackageManagerInternal pmInt) { final boolean forceSystemAppsQueryable = injector.getContext().getResources() @@ -511,7 +602,7 @@ public class AppsFilter implements Watchable, Snappable { injector.getUserManagerInternal().getUserInfos()); } }; - AppsFilter appsFilter = new AppsFilter(stateProvider, featureConfig, + AppsFilterImpl appsFilter = new AppsFilterImpl(stateProvider, featureConfig, forcedQueryablePackageNames, forceSystemAppsQueryable, null, injector.getBackgroundExecutor(), pmInt); featureConfig.setAppsFilter(appsFilter); @@ -524,7 +615,7 @@ public class AppsFilter implements Watchable, Snappable { /** Returns true if the querying package may query for the potential target package */ private static boolean canQueryViaComponents(AndroidPackage querying, - AndroidPackage potentialTarget, Set<String> protectedBroadcasts) { + AndroidPackage potentialTarget, WatchedArrayList<String> protectedBroadcasts) { if (!querying.getQueriesIntents().isEmpty()) { for (Intent intent : querying.getQueriesIntents()) { if (matchesPackage(intent, potentialTarget, protectedBroadcasts)) { @@ -596,7 +687,7 @@ public class AppsFilter implements Watchable, Snappable { } private static boolean matchesPackage(Intent intent, AndroidPackage potentialTarget, - Set<String> protectedBroadcasts) { + WatchedArrayList<String> protectedBroadcasts) { if (matchesAnyComponents( intent, potentialTarget.getServices(), null /*protectedBroadcasts*/)) { return true; @@ -617,7 +708,7 @@ public class AppsFilter implements Watchable, Snappable { private static boolean matchesAnyComponents(Intent intent, List<? extends ParsedMainComponent> components, - Set<String> protectedBroadcasts) { + WatchedArrayList<String> protectedBroadcasts) { for (int i = ArrayUtils.size(components) - 1; i >= 0; i--) { ParsedMainComponent component = components.get(i); if (!component.isExported()) { @@ -631,7 +722,7 @@ public class AppsFilter implements Watchable, Snappable { } private static boolean matchesAnyFilter(Intent intent, ParsedComponent component, - Set<String> protectedBroadcasts) { + WatchedArrayList<String> protectedBroadcasts) { List<ParsedIntentInfo> intents = component.getIntents(); for (int i = ArrayUtils.size(intents) - 1; i >= 0; i--) { IntentFilter intentFilter = intents.get(i).getIntentFilter(); @@ -643,38 +734,38 @@ public class AppsFilter implements Watchable, Snappable { } private static boolean matchesIntentFilter(Intent intent, IntentFilter intentFilter, - @Nullable Set<String> protectedBroadcasts) { + @Nullable WatchedArrayList<String> protectedBroadcasts) { return intentFilter.match(intent.getAction(), intent.getType(), intent.getScheme(), - intent.getData(), intent.getCategories(), "AppsFilter", true, protectedBroadcasts) - > 0; + intent.getData(), intent.getCategories(), "AppsFilter", true, + protectedBroadcasts != null ? protectedBroadcasts.untrackedStorage() : null) > 0; } /** * Grants access based on an interaction between a calling and target package, granting * visibility of the caller from the target. * - * @param recipientUid the uid gaining visibility of the {@code visibleUid}. - * @param visibleUid the uid becoming visible to the {@recipientUid} - * @param retainOnUpdate if the implicit access retained across package updates. + * @param recipientUid the uid gaining visibility of the {@code visibleUid}. + * @param visibleUid the uid becoming visible to the {@recipientUid} + * @param retainOnUpdate if the implicit access retained across package updates. * @return {@code true} if implicit access was not already granted. */ public boolean grantImplicitAccess(int recipientUid, int visibleUid, boolean retainOnUpdate) { if (recipientUid == visibleUid) { return false; } - final boolean changed = retainOnUpdate - ? mRetainedImplicitlyQueryable.add(recipientUid, visibleUid) - : mImplicitlyQueryable.add(recipientUid, visibleUid); - if (changed && DEBUG_LOGGING) { - Slog.i(TAG, (retainOnUpdate ? "retained " : "") + "implicit access granted: " - + recipientUid + " -> " + visibleUid); + final boolean changed; + synchronized (mLock) { + changed = retainOnUpdate + ? mRetainedImplicitlyQueryable.add(recipientUid, visibleUid) + : mImplicitlyQueryable.add(recipientUid, visibleUid); + if (changed && DEBUG_LOGGING) { + Slog.i(TAG, (retainOnUpdate ? "retained " : "") + "implicit access granted: " + + recipientUid + " -> " + visibleUid); + } } synchronized (mCacheLock) { - if (mShouldFilterCache != null) { - // update the cache in a one-off manner since we've got all the information we - // need. - mShouldFilterCache.put(recipientUid, visibleUid, false); - } + // update the cache in a one-off manner since we've got all the information we need. + mShouldFilterCache.put(recipientUid, visibleUid, false); } if (changed) { onChanged(); @@ -694,7 +785,7 @@ public class AppsFilter implements Watchable, Snappable { * Adds a package that should be considered when filtering visibility between apps. * * @param newPkgSetting the new setting being added - * @param isReplace if the package is being replaced and may need extra cleanup. + * @param isReplace if the package is being replaced and may need extra cleanup. */ public void addPackage(PackageStateInternal newPkgSetting, boolean isReplace) { if (DEBUG_TRACING) { @@ -709,27 +800,25 @@ public class AppsFilter implements Watchable, Snappable { ArraySet<String> additionalChangedPackages = addPackageInternal(newPkgSetting, settings); synchronized (mCacheLock) { - if (mShouldFilterCache != null) { - updateShouldFilterCacheForPackage(mShouldFilterCache, null, newPkgSetting, - settings, users, USER_ALL, settings.size()); - if (additionalChangedPackages != null) { - for (int index = 0; index < additionalChangedPackages.size(); index++) { - String changedPackage = additionalChangedPackages.valueAt(index); - PackageStateInternal changedPkgSetting = - settings.get(changedPackage); - if (changedPkgSetting == null) { - // It's possible for the overlay mapper to know that an actor - // package changed via an explicit reference, even if the actor - // isn't installed, so skip if that's the case. - continue; - } - - updateShouldFilterCacheForPackage(mShouldFilterCache, null, - changedPkgSetting, settings, users, USER_ALL, - settings.size()); + updateShouldFilterCacheForPackage(mShouldFilterCache, null, newPkgSetting, + settings, users, USER_ALL, settings.size()); + if (additionalChangedPackages != null) { + for (int index = 0; index < additionalChangedPackages.size(); index++) { + String changedPackage = additionalChangedPackages.valueAt(index); + PackageStateInternal changedPkgSetting = + settings.get(changedPackage); + if (changedPkgSetting == null) { + // It's possible for the overlay mapper to know that an actor + // package changed via an explicit reference, even if the actor + // isn't installed, so skip if that's the case. + continue; } + + updateShouldFilterCacheForPackage(mShouldFilterCache, null, + changedPkgSetting, settings, users, USER_ALL, + settings.size()); } - } // else, rebuild entire cache when system is ready + } } }); } finally { @@ -752,9 +841,11 @@ public class AppsFilter implements Watchable, Snappable { mSystemSigningDetails = newPkgSetting.getSigningDetails(); // and since we add overlays before we add the framework, let's revisit already added // packages for signature matches - for (PackageStateInternal setting : existingSettings.values()) { - if (isSystemSigned(mSystemSigningDetails, setting)) { - mForceQueryable.add(setting.getAppId()); + synchronized (mLock) { + for (PackageStateInternal setting : existingSettings.values()) { + if (isSystemSigned(mSystemSigningDetails, setting)) { + mForceQueryable.add(setting.getAppId()); + } } } } @@ -764,67 +855,74 @@ public class AppsFilter implements Watchable, Snappable { return null; } - if (mProtectedBroadcasts.addAll(newPkg.getProtectedBroadcasts())) { - mQueriesViaComponentRequireRecompute = true; - } - - final boolean newIsForceQueryable = - mForceQueryable.contains(newPkgSetting.getAppId()) - /* shared user that is already force queryable */ - || newPkgSetting.isForceQueryableOverride() /* adb override */ - || (newPkgSetting.isSystem() && (mSystemAppsQueryable - || newPkg.isForceQueryable() - || ArrayUtils.contains(mForceQueryableByDevicePackageNames, - newPkg.getPackageName()))); - if (newIsForceQueryable - || (mSystemSigningDetails != null - && isSystemSigned(mSystemSigningDetails, newPkgSetting))) { - mForceQueryable.add(newPkgSetting.getAppId()); - } + synchronized (mLock) { + if (mProtectedBroadcasts.addAll(newPkg.getProtectedBroadcasts())) { + mQueriesViaComponentRequireRecompute = true; + } - for (int i = existingSettings.size() - 1; i >= 0; i--) { - final PackageStateInternal existingSetting = existingSettings.valueAt(i); - if (existingSetting.getAppId() == newPkgSetting.getAppId() || existingSetting.getPkg() - == null) { - continue; + final boolean newIsForceQueryable = + mForceQueryable.contains(newPkgSetting.getAppId()) + /* shared user that is already force queryable */ + || newPkgSetting.isForceQueryableOverride() /* adb override */ + || (newPkgSetting.isSystem() && (mSystemAppsQueryable + || newPkg.isForceQueryable() + || ArrayUtils.contains(mForceQueryableByDevicePackageNames, + newPkg.getPackageName()))); + if (newIsForceQueryable + || (mSystemSigningDetails != null + && isSystemSigned(mSystemSigningDetails, newPkgSetting))) { + mForceQueryable.add(newPkgSetting.getAppId()); } - final AndroidPackage existingPkg = existingSetting.getPkg(); - // let's evaluate the ability of already added packages to see this new package - if (!newIsForceQueryable) { - if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(existingPkg, - newPkg, mProtectedBroadcasts)) { - mQueriesViaComponent.add(existingSetting.getAppId(), newPkgSetting.getAppId()); - } - if (canQueryViaPackage(existingPkg, newPkg) - || canQueryAsInstaller(existingSetting, newPkg)) { - mQueriesViaPackage.add(existingSetting.getAppId(), newPkgSetting.getAppId()); + + for (int i = existingSettings.size() - 1; i >= 0; i--) { + final PackageStateInternal existingSetting = existingSettings.valueAt(i); + if (existingSetting.getAppId() == newPkgSetting.getAppId() + || existingSetting.getPkg() + == null) { + continue; } - if (canQueryViaUsesLibrary(existingPkg, newPkg)) { - mQueryableViaUsesLibrary.add(existingSetting.getAppId(), - newPkgSetting.getAppId()); + final AndroidPackage existingPkg = existingSetting.getPkg(); + // let's evaluate the ability of already added packages to see this new package + if (!newIsForceQueryable) { + if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(existingPkg, + newPkg, mProtectedBroadcasts)) { + mQueriesViaComponent.add(existingSetting.getAppId(), + newPkgSetting.getAppId()); + } + if (canQueryViaPackage(existingPkg, newPkg) + || canQueryAsInstaller(existingSetting, newPkg)) { + mQueriesViaPackage.add(existingSetting.getAppId(), + newPkgSetting.getAppId()); + } + if (canQueryViaUsesLibrary(existingPkg, newPkg)) { + mQueryableViaUsesLibrary.add(existingSetting.getAppId(), + newPkgSetting.getAppId()); + } } - } - // now we'll evaluate our new package's ability to see existing packages - if (!mForceQueryable.contains(existingSetting.getAppId())) { - if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(newPkg, - existingPkg, mProtectedBroadcasts)) { - mQueriesViaComponent.add(newPkgSetting.getAppId(), existingSetting.getAppId()); + // now we'll evaluate our new package's ability to see existing packages + if (!mForceQueryable.contains(existingSetting.getAppId())) { + if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(newPkg, + existingPkg, mProtectedBroadcasts)) { + mQueriesViaComponent.add(newPkgSetting.getAppId(), + existingSetting.getAppId()); + } + if (canQueryViaPackage(newPkg, existingPkg) + || canQueryAsInstaller(newPkgSetting, existingPkg)) { + mQueriesViaPackage.add(newPkgSetting.getAppId(), + existingSetting.getAppId()); + } + if (canQueryViaUsesLibrary(newPkg, existingPkg)) { + mQueryableViaUsesLibrary.add(newPkgSetting.getAppId(), + existingSetting.getAppId()); + } } - if (canQueryViaPackage(newPkg, existingPkg) - || canQueryAsInstaller(newPkgSetting, existingPkg)) { + // if either package instruments the other, mark both as visible to one another + if (newPkgSetting.getPkg() != null && existingSetting.getPkg() != null + && (pkgInstruments(newPkgSetting.getPkg(), existingSetting.getPkg()) + || pkgInstruments(existingSetting.getPkg(), newPkgSetting.getPkg()))) { mQueriesViaPackage.add(newPkgSetting.getAppId(), existingSetting.getAppId()); + mQueriesViaPackage.add(existingSetting.getAppId(), newPkgSetting.getAppId()); } - if (canQueryViaUsesLibrary(newPkg, existingPkg)) { - mQueryableViaUsesLibrary.add(newPkgSetting.getAppId(), - existingSetting.getAppId()); - } - } - // if either package instruments the other, mark both as visible to one another - if (newPkgSetting.getPkg() != null && existingSetting.getPkg() != null - && (pkgInstruments(newPkgSetting.getPkg(), existingSetting.getPkg()) - || pkgInstruments(existingSetting.getPkg(), newPkgSetting.getPkg()))) { - mQueriesViaPackage.add(newPkgSetting.getAppId(), existingSetting.getAppId()); - mQueriesViaPackage.add(existingSetting.getAppId(), newPkgSetting.getAppId()); } } @@ -845,19 +943,17 @@ public class AppsFilter implements Watchable, Snappable { return changedPackages; } - @GuardedBy("mCacheLock") private void removeAppIdFromVisibilityCache(int appId) { - if (mShouldFilterCache == null) { - return; - } - for (int i = 0; i < mShouldFilterCache.size(); i++) { - if (UserHandle.getAppId(mShouldFilterCache.keyAt(i)) == appId) { - mShouldFilterCache.removeAt(i); - // 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--; + synchronized (mCacheLock) { + for (int i = 0; i < mShouldFilterCache.size(); i++) { + if (UserHandle.getAppId(mShouldFilterCache.keyAt(i)) == appId) { + mShouldFilterCache.removeAt(i); + // 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--; + } } } } @@ -883,11 +979,12 @@ public class AppsFilter implements Watchable, Snappable { WatchedSparseBooleanMatrix cache = updateEntireShouldFilterCacheInner(settings, users, userId); synchronized (mCacheLock) { - mShouldFilterCache = cache; + mShouldFilterCache.copyFrom(cache); } }); } + @NonNull private WatchedSparseBooleanMatrix updateEntireShouldFilterCacheInner( ArrayMap<String, ? extends PackageStateInternal> settings, UserInfo[] users, int subjectUserId) { @@ -948,36 +1045,25 @@ public class AppsFilter implements Watchable, Snappable { } } else { synchronized (mCacheLock) { - mShouldFilterCache = cache; + mShouldFilterCache.copyFrom(cache); } } }); } public void onUserCreated(int newUserId) { - synchronized (mCacheLock) { - if (mShouldFilterCache != null) { - updateEntireShouldFilterCache(newUserId); - onChanged(); - } - } + updateEntireShouldFilterCache(newUserId); + onChanged(); } public void onUserDeleted(@UserIdInt int userId) { - synchronized (mCacheLock) { - if (mShouldFilterCache != null) { - removeShouldFilterCacheForUser(userId); - onChanged(); - } - } + removeShouldFilterCacheForUser(userId); + onChanged(); } private void updateShouldFilterCacheForPackage(String packageName) { mStateProvider.runWithState((settings, users) -> { synchronized (mCacheLock) { - if (mShouldFilterCache == null) { - return; - } updateShouldFilterCacheForPackage(mShouldFilterCache, null /* skipPackage */, settings.get(packageName), settings, users, USER_ALL, settings.size() /*maxIndex*/); @@ -985,7 +1071,7 @@ public class AppsFilter implements Watchable, Snappable { }); } - private void updateShouldFilterCacheForPackage(WatchedSparseBooleanMatrix cache, + private void updateShouldFilterCacheForPackage(@NonNull WatchedSparseBooleanMatrix cache, @Nullable String skipPackageName, PackageStateInternal subjectSetting, ArrayMap<String, ? extends PackageStateInternal> allSettings, UserInfo[] allUsers, int subjectUserId, int maxIndex) { @@ -1011,7 +1097,7 @@ public class AppsFilter implements Watchable, Snappable { } } - private void updateShouldFilterCacheForUser(WatchedSparseBooleanMatrix cache, + private void updateShouldFilterCacheForUser(@NonNull WatchedSparseBooleanMatrix cache, PackageStateInternal subjectSetting, UserInfo[] allUsers, PackageStateInternal otherSetting, int subjectUserId) { for (int ou = 0; ou < allUsers.length; ou++) { @@ -1027,27 +1113,28 @@ public class AppsFilter implements Watchable, Snappable { } } - @GuardedBy("mCacheLock") private void removeShouldFilterCacheForUser(int userId) { - // Sorted uids with the ascending order - final int[] cacheUids = mShouldFilterCache.keys(); - final int size = cacheUids.length; - int pos = Arrays.binarySearch(cacheUids, UserHandle.getUid(userId, 0)); - final int fromIndex = (pos >= 0 ? pos : ~pos); - if (fromIndex >= size || UserHandle.getUserId(cacheUids[fromIndex]) != userId) { - Slog.w(TAG, "Failed to remove should filter cache for user " + userId - + ", fromIndex=" + fromIndex); - return; - } - pos = Arrays.binarySearch(cacheUids, UserHandle.getUid(userId + 1, 0) - 1); - final int toIndex = (pos >= 0 ? pos + 1 : ~pos); - if (fromIndex >= toIndex || UserHandle.getUserId(cacheUids[toIndex - 1]) != userId) { - Slog.w(TAG, "Failed to remove should filter cache for user " + userId - + ", fromIndex=" + fromIndex + ", toIndex=" + toIndex); - return; + synchronized (mCacheLock) { + // Sorted uids with the ascending order + final int[] cacheUids = mShouldFilterCache.keys(); + final int size = cacheUids.length; + int pos = Arrays.binarySearch(cacheUids, UserHandle.getUid(userId, 0)); + final int fromIndex = (pos >= 0 ? pos : ~pos); + if (fromIndex >= size || UserHandle.getUserId(cacheUids[fromIndex]) != userId) { + Slog.w(TAG, "Failed to remove should filter cache for user " + userId + + ", fromIndex=" + fromIndex); + return; + } + pos = Arrays.binarySearch(cacheUids, UserHandle.getUid(userId + 1, 0) - 1); + final int toIndex = (pos >= 0 ? pos + 1 : ~pos); + if (fromIndex >= toIndex || UserHandle.getUserId(cacheUids[toIndex - 1]) != userId) { + Slog.w(TAG, "Failed to remove should filter cache for user " + userId + + ", fromIndex=" + fromIndex + ", toIndex=" + toIndex); + return; + } + mShouldFilterCache.removeRange(fromIndex, toIndex); + mShouldFilterCache.compact(); } - mShouldFilterCache.removeRange(fromIndex, toIndex); - mShouldFilterCache.compact(); } private static boolean isSystemSigned(@NonNull SigningDetails sysSigningDetails, @@ -1056,28 +1143,30 @@ public class AppsFilter implements Watchable, Snappable { && pkgSetting.getSigningDetails().signaturesMatchExactly(sysSigningDetails); } - private ArraySet<String> collectProtectedBroadcasts( + private void collectProtectedBroadcasts( ArrayMap<String, ? extends PackageStateInternal> existingSettings, @Nullable String excludePackage) { - ArraySet<String> ret = new ArraySet<>(); - for (int i = existingSettings.size() - 1; i >= 0; i--) { - PackageStateInternal setting = existingSettings.valueAt(i); - if (setting.getPkg() == null || setting.getPkg().getPackageName().equals( - excludePackage)) { - continue; - } - final List<String> protectedBroadcasts = setting.getPkg().getProtectedBroadcasts(); - if (!protectedBroadcasts.isEmpty()) { - ret.addAll(protectedBroadcasts); + synchronized (mLock) { + mProtectedBroadcasts.clear(); + for (int i = existingSettings.size() - 1; i >= 0; i--) { + PackageStateInternal setting = existingSettings.valueAt(i); + if (setting.getPkg() == null || setting.getPkg().getPackageName().equals( + excludePackage)) { + continue; + } + final List<String> protectedBroadcasts = setting.getPkg().getProtectedBroadcasts(); + if (!protectedBroadcasts.isEmpty()) { + mProtectedBroadcasts.addAll(protectedBroadcasts); + } } } - return ret; } /** * This method recomputes all component / intent-based visibility and is intended to match the * relevant logic of {@link #addPackageInternal(PackageStateInternal, ArrayMap)} */ + @GuardedBy("mLock") private void recomputeComponentVisibility( ArrayMap<String, ? extends PackageStateInternal> existingSettings) { mQueriesViaComponent.clear(); @@ -1105,24 +1194,16 @@ public class AppsFilter implements Watchable, Snappable { } /** - * Fetches all app Ids that a given setting is currently visible to, per provided user. This - * only includes UIDs >= {@link Process#FIRST_APPLICATION_UID} as all other UIDs can already see - * all applications. - * - * If the setting is visible to all UIDs, null is returned. If an app is not visible to any - * applications, the int array will be empty. - * - * @param users the set of users that should be evaluated for this calculation - * @param existingSettings the set of all package settings that currently exist on device - * @return a SparseArray mapping userIds to a sorted int array of appIds that may view the - * provided setting or null if the app is visible to all and no allow list should be - * applied. + * See {@link AppsFilterSnapshot#getVisibilityAllowList(PackageStateInternal, int[], ArrayMap)} */ + @Override @Nullable public SparseArray<int[]> getVisibilityAllowList(PackageStateInternal setting, int[] users, ArrayMap<String, ? extends PackageStateInternal> existingSettings) { - if (mForceQueryable.contains(setting.getAppId())) { - return null; + synchronized (mLock) { + if (mForceQueryable.contains(setting.getAppId())) { + return null; + } } // let's reserve max memory to limit the number of allocations SparseArray<int[]> result = new SparseArray<>(users.length); @@ -1171,7 +1252,8 @@ public class AppsFilter implements Watchable, Snappable { /** * Equivalent to calling {@link #addPackage(PackageStateInternal, boolean)} with * {@code isReplace} equal to {@code false}. - * @see AppsFilter#addPackage(PackageStateInternal, boolean) + * + * @see AppsFilterImpl#addPackage(PackageStateInternal, boolean) */ public void addPackage(PackageStateInternal newPkgSetting) { addPackage(newPkgSetting, false /* isReplace */); @@ -1180,62 +1262,68 @@ public class AppsFilter implements Watchable, Snappable { /** * Removes a package for consideration when filtering visibility between apps. * - * @param setting the setting of the package being removed. + * @param setting the setting of the package being removed. * @param isReplace if the package is being replaced. */ public void removePackage(PackageStateInternal setting, boolean isReplace) { mStateProvider.runWithState((settings, users) -> { - final int userCount = users.length; - for (int u = 0; u < userCount; u++) { - final int userId = users[u].id; - final int removingUid = UserHandle.getUid(userId, setting.getAppId()); - mImplicitlyQueryable.remove(removingUid); - for (int i = mImplicitlyQueryable.size() - 1; i >= 0; i--) { - mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i), removingUid); - } + final ArraySet<String> additionalChangedPackages; + synchronized (mLock) { + final int userCount = users.length; + for (int u = 0; u < userCount; u++) { + final int userId = users[u].id; + final int removingUid = UserHandle.getUid(userId, setting.getAppId()); + mImplicitlyQueryable.remove(removingUid); + for (int i = mImplicitlyQueryable.size() - 1; i >= 0; i--) { + mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i), removingUid); + } - if (isReplace) { - continue; - } + if (isReplace) { + continue; + } - mRetainedImplicitlyQueryable.remove(removingUid); - for (int i = mRetainedImplicitlyQueryable.size() - 1; i >= 0; i--) { - mRetainedImplicitlyQueryable.remove( - mRetainedImplicitlyQueryable.keyAt(i), removingUid); + mRetainedImplicitlyQueryable.remove(removingUid); + for (int i = mRetainedImplicitlyQueryable.size() - 1; i >= 0; i--) { + mRetainedImplicitlyQueryable.remove( + mRetainedImplicitlyQueryable.keyAt(i), removingUid); + } } - } - if (!mQueriesViaComponentRequireRecompute) { - mQueriesViaComponent.remove(setting.getAppId()); - for (int i = mQueriesViaComponent.size() - 1; i >= 0; i--) { - mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i), setting.getAppId()); + if (!mQueriesViaComponentRequireRecompute) { + mQueriesViaComponent.remove(setting.getAppId()); + for (int i = mQueriesViaComponent.size() - 1; i >= 0; i--) { + mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i), + setting.getAppId()); + } + } + mQueriesViaPackage.remove(setting.getAppId()); + for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) { + mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), setting.getAppId()); + } + mQueryableViaUsesLibrary.remove(setting.getAppId()); + for (int i = mQueryableViaUsesLibrary.size() - 1; i >= 0; i--) { + mQueryableViaUsesLibrary.remove(mQueryableViaUsesLibrary.keyAt(i), + setting.getAppId()); } - } - mQueriesViaPackage.remove(setting.getAppId()); - for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) { - mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), setting.getAppId()); - } - mQueryableViaUsesLibrary.remove(setting.getAppId()); - for (int i = mQueryableViaUsesLibrary.size() - 1; i >= 0; i--) { - mQueryableViaUsesLibrary.remove(mQueryableViaUsesLibrary.keyAt(i), - setting.getAppId()); - } - mForceQueryable.remove(setting.getAppId()); + mForceQueryable.remove(setting.getAppId()); - if (setting.getPkg() != null && !setting.getPkg().getProtectedBroadcasts().isEmpty()) { - final String removingPackageName = setting.getPkg().getPackageName(); - final Set<String> protectedBroadcasts = mProtectedBroadcasts; - mProtectedBroadcasts = collectProtectedBroadcasts(settings, removingPackageName); - if (!mProtectedBroadcasts.containsAll(protectedBroadcasts)) { - mQueriesViaComponentRequireRecompute = true; + if (setting.getPkg() != null + && !setting.getPkg().getProtectedBroadcasts().isEmpty()) { + final String removingPackageName = setting.getPkg().getPackageName(); + final ArrayList<String> protectedBroadcasts = new ArrayList<>(); + protectedBroadcasts.addAll(mProtectedBroadcasts.untrackedStorage()); + collectProtectedBroadcasts(settings, removingPackageName); + if (!mProtectedBroadcasts.containsAll(protectedBroadcasts)) { + mQueriesViaComponentRequireRecompute = true; + } } - } - ArraySet<String> additionalChangedPackages = - mOverlayReferenceMapper.removePkg(setting.getPackageName()); + additionalChangedPackages = + mOverlayReferenceMapper.removePkg(setting.getPackageName()); - mFeatureConfig.updatePackageState(setting, true /*removed*/); + mFeatureConfig.updatePackageState(setting, true /*removed*/); + } // After removing all traces of the package, if it's part of a shared user, re-add other // shared user members to re-establish visibility between them and other packages. @@ -1253,56 +1341,49 @@ public class AppsFilter implements Watchable, Snappable { } } - synchronized (mCacheLock) { - removeAppIdFromVisibilityCache(setting.getAppId()); - if (mShouldFilterCache != null && setting.hasSharedUser()) { - final ArraySet<PackageStateInternal> sharedUserPackages = - mPmInternal.getSharedUserPackages(setting.getSharedUserAppId()); - for (int i = sharedUserPackages.size() - 1; i >= 0; i--) { - PackageStateInternal siblingSetting = - sharedUserPackages.valueAt(i); - if (siblingSetting == setting) { - continue; - } + removeAppIdFromVisibilityCache(setting.getAppId()); + if (setting.hasSharedUser()) { + final ArraySet<PackageStateInternal> sharedUserPackages = + mPmInternal.getSharedUserPackages(setting.getSharedUserAppId()); + for (int i = sharedUserPackages.size() - 1; i >= 0; i--) { + PackageStateInternal siblingSetting = + sharedUserPackages.valueAt(i); + if (siblingSetting == setting) { + continue; + } + synchronized (mCacheLock) { updateShouldFilterCacheForPackage(mShouldFilterCache, setting.getPackageName(), siblingSetting, settings, users, USER_ALL, settings.size()); } } + } - if (mShouldFilterCache != null) { - if (additionalChangedPackages != null) { - for (int index = 0; index < additionalChangedPackages.size(); index++) { - String changedPackage = additionalChangedPackages.valueAt(index); - PackageStateInternal changedPkgSetting = settings.get(changedPackage); - if (changedPkgSetting == null) { - // It's possible for the overlay mapper to know that an actor - // package changed via an explicit reference, even if the actor - // isn't installed, so skip if that's the case. - continue; - } - - updateShouldFilterCacheForPackage(mShouldFilterCache, null, - changedPkgSetting, settings, users, USER_ALL, settings.size()); - } + if (additionalChangedPackages != null) { + for (int index = 0; index < additionalChangedPackages.size(); index++) { + String changedPackage = additionalChangedPackages.valueAt(index); + PackageStateInternal changedPkgSetting = settings.get(changedPackage); + if (changedPkgSetting == null) { + // It's possible for the overlay mapper to know that an actor + // package changed via an explicit reference, even if the actor + // isn't installed, so skip if that's the case. + continue; + } + synchronized (mCacheLock) { + updateShouldFilterCacheForPackage(mShouldFilterCache, null, + changedPkgSetting, settings, users, USER_ALL, settings.size()); } } - - onChanged(); } + onChanged(); }); } /** - * Returns true if the calling package should not be able to see the target package, false if no - * filtering should be done. - * - * @param callingUid the uid of the caller attempting to access a package - * @param callingSetting the setting attempting to access a package or null if it could not be - * found - * @param targetPkgSetting the package being accessed - * @param userId the user in which this access is being attempted + * See {@link AppsFilterSnapshot#shouldFilterApplication(int, Object, PackageStateInternal, + * int)} */ + @Override public boolean shouldFilterApplication(int callingUid, @Nullable Object callingSetting, PackageStateInternal targetPkgSetting, int userId) { if (DEBUG_TRACING) { @@ -1315,31 +1396,22 @@ public class AppsFilter implements Watchable, Snappable { || callingAppId == targetPkgSetting.getAppId()) { return false; } + final boolean shouldUseCache; synchronized (mCacheLock) { - if (mShouldFilterCache != null) { // use cache - final int callingIndex = mShouldFilterCache.indexOfKey(callingUid); - if (callingIndex < 0) { - Slog.wtf(TAG, "Encountered calling uid with no cached rules: " - + callingUid); - return true; - } - final int targetUid = UserHandle.getUid(userId, targetPkgSetting.getAppId()); - final int targetIndex = mShouldFilterCache.indexOfKey(targetUid); - if (targetIndex < 0) { - Slog.w(TAG, "Encountered calling -> target with no cached rules: " - + callingUid + " -> " + targetUid); - return true; - } - if (!mShouldFilterCache.valueAt(callingIndex, targetIndex)) { - return false; - } - } else { - if (!shouldFilterApplicationInternal( - callingUid, callingSetting, targetPkgSetting, userId)) { - return false; - } + shouldUseCache = mShouldFilterCache.size() != 0; + } + if (shouldUseCache) { // use cache + if (!shouldFilterApplicationUsingCache(callingUid, targetPkgSetting.getAppId(), + userId)) { + return false; + } + } else { + if (!shouldFilterApplicationInternal( + callingUid, callingSetting, targetPkgSetting, userId)) { + return false; } } + if (DEBUG_LOGGING || mFeatureConfig.isLoggingEnabled(callingAppId)) { log(callingSetting, targetPkgSetting, "BLOCKED"); } @@ -1351,6 +1423,25 @@ public class AppsFilter implements Watchable, Snappable { } } + private boolean shouldFilterApplicationUsingCache(int callingUid, int appId, int userId) { + synchronized (mCacheLock) { + final int callingIndex = mShouldFilterCache.indexOfKey(callingUid); + if (callingIndex < 0) { + Slog.wtf(TAG, "Encountered calling uid with no cached rules: " + + callingUid); + return true; + } + final int targetUid = UserHandle.getUid(userId, appId); + final int targetIndex = mShouldFilterCache.indexOfKey(targetUid); + if (targetIndex < 0) { + Slog.w(TAG, "Encountered calling -> target with no cached rules: " + + callingUid + " -> " + targetUid); + return true; + } + return mShouldFilterCache.valueAt(callingIndex, targetIndex); + } + } + private boolean shouldFilterApplicationInternal(int callingUid, Object callingSetting, PackageStateInternal targetPkgSetting, int targetUserId) { if (DEBUG_TRACING) { @@ -1437,10 +1528,10 @@ public class AppsFilter implements Watchable, Snappable { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "requestsQueryAllPackages"); } if (callingPkgSetting != null) { - if (callingPkgSetting.getPkg() != null - && requestsQueryAllPackages(callingPkgSetting.getPkg())) { - return false; - } + if (callingPkgSetting.getPkg() != null + && requestsQueryAllPackages(callingPkgSetting.getPkg())) { + return false; + } } else { for (int i = callingSharedPkgSettings.size() - 1; i >= 0; i--) { AndroidPackage pkg = callingSharedPkgSettings.valueAt(i).getPkg(); @@ -1469,142 +1560,147 @@ public class AppsFilter implements Watchable, Snappable { return false; } - try { - if (DEBUG_TRACING) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mForceQueryable"); - } - if (mForceQueryable.contains(targetAppId)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "force queryable"); + synchronized (mLock) { + try { + if (DEBUG_TRACING) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mForceQueryable"); } - return false; - } - } finally { - if (DEBUG_TRACING) { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - } - try { - if (DEBUG_TRACING) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueriesViaPackage"); - } - if (mQueriesViaPackage.contains(callingAppId, targetAppId)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "queries package"); + if (mForceQueryable.contains(targetAppId)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "force queryable"); + } + return false; + } + } finally { + if (DEBUG_TRACING) { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } - return false; - } - } finally { - if (DEBUG_TRACING) { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - } - try { - if (DEBUG_TRACING) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueriesViaComponent"); - } - if (mQueriesViaComponentRequireRecompute) { - mStateProvider.runWithState((settings, users) -> { - recomputeComponentVisibility(settings); - }); } - if (mQueriesViaComponent.contains(callingAppId, targetAppId)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "queries component"); + try { + if (DEBUG_TRACING) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueriesViaPackage"); + } + if (mQueriesViaPackage.contains(callingAppId, targetAppId)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "queries package"); + } + return false; + } + } finally { + if (DEBUG_TRACING) { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } - return false; } - } finally { - if (DEBUG_TRACING) { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + try { + if (DEBUG_TRACING) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueriesViaComponent"); + } + if (mQueriesViaComponentRequireRecompute) { + mStateProvider.runWithState((settings, users) -> { + synchronized (mLock) { + recomputeComponentVisibility(settings); + } + }); + } + if (mQueriesViaComponent.contains(callingAppId, targetAppId)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "queries component"); + } + return false; + } + } finally { + if (DEBUG_TRACING) { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } } - } - try { - if (DEBUG_TRACING) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mImplicitlyQueryable"); - } - final int targetUid = UserHandle.getUid(targetUserId, targetAppId); - if (mImplicitlyQueryable.contains(callingUid, targetUid)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "implicitly queryable for user"); + try { + if (DEBUG_TRACING) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mImplicitlyQueryable"); + } + final int targetUid = UserHandle.getUid(targetUserId, targetAppId); + if (mImplicitlyQueryable.contains(callingUid, targetUid)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "implicitly queryable for user"); + } + return false; + } + } finally { + if (DEBUG_TRACING) { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } - return false; - } - } finally { - if (DEBUG_TRACING) { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } - } - try { - if (DEBUG_TRACING) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mRetainedImplicitlyQueryable"); - } - final int targetUid = UserHandle.getUid(targetUserId, targetAppId); - if (mRetainedImplicitlyQueryable.contains(callingUid, targetUid)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, - "retained implicitly queryable for user"); + try { + if (DEBUG_TRACING) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mRetainedImplicitlyQueryable"); + } + final int targetUid = UserHandle.getUid(targetUserId, targetAppId); + if (mRetainedImplicitlyQueryable.contains(callingUid, targetUid)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, + "retained implicitly queryable for user"); + } + return false; + } + } finally { + if (DEBUG_TRACING) { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } - return false; - } - } finally { - if (DEBUG_TRACING) { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } - } - try { - if (DEBUG_TRACING) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mOverlayReferenceMapper"); - } - final String targetName = targetPkg.getPackageName(); - if (callingSharedPkgSettings != null) { - int size = callingSharedPkgSettings.size(); - for (int index = 0; index < size; index++) { - PackageStateInternal pkgSetting = callingSharedPkgSettings.valueAt(index); + try { + if (DEBUG_TRACING) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mOverlayReferenceMapper"); + } + final String targetName = targetPkg.getPackageName(); + if (callingSharedPkgSettings != null) { + int size = callingSharedPkgSettings.size(); + for (int index = 0; index < size; index++) { + PackageStateInternal pkgSetting = callingSharedPkgSettings.valueAt( + index); + if (mOverlayReferenceMapper.isValidActor(targetName, + pkgSetting.getPackageName())) { + if (DEBUG_LOGGING) { + log(callingPkgSetting, targetPkgSetting, + "matches shared user of package that acts on target of " + + "overlay"); + } + return false; + } + } + } else { if (mOverlayReferenceMapper.isValidActor(targetName, - pkgSetting.getPackageName())) { + callingPkgSetting.getPackageName())) { if (DEBUG_LOGGING) { log(callingPkgSetting, targetPkgSetting, - "matches shared user of package that acts on target of " - + "overlay"); + "acts on target of overlay"); } return false; } } - } else { - if (mOverlayReferenceMapper.isValidActor(targetName, - callingPkgSetting.getPackageName())) { + } finally { + if (DEBUG_TRACING) { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + + try { + if (DEBUG_TRACING) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueryableViaUsesLibrary"); + } + if (mQueryableViaUsesLibrary.contains(callingAppId, targetAppId)) { if (DEBUG_LOGGING) { - log(callingPkgSetting, targetPkgSetting, "acts on target of overlay"); + log(callingSetting, targetPkgSetting, "queryable for library users"); } return false; } - } - } finally { - if (DEBUG_TRACING) { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - } - - try { - if (DEBUG_TRACING) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueryableViaUsesLibrary"); - } - if (mQueryableViaUsesLibrary.contains(callingAppId, targetAppId)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "queryable for library users"); + } finally { + if (DEBUG_TRACING) { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } - return false; - } - } finally { - if (DEBUG_TRACING) { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } - return true; } finally { if (DEBUG_TRACING) { @@ -1613,7 +1709,11 @@ public class AppsFilter implements Watchable, Snappable { } } - boolean canQueryPackage(@NonNull AndroidPackage querying, String potentialTarget) { + /** + * See {@link AppsFilterSnapshot#canQueryPackage(AndroidPackage, String)} + */ + @Override + public boolean canQueryPackage(@NonNull AndroidPackage querying, String potentialTarget) { int appId = UserHandle.getAppId(querying.getUid()); if (appId < Process.FIRST_APPLICATION_UID) { return true; @@ -1668,6 +1768,11 @@ public class AppsFilter implements Watchable, Snappable { + targetPkgSetting + " " + description); } + /** + * See {@link AppsFilterSnapshot#dumpQueries(PrintWriter, Integer, DumpState, int[], + * QuadFunction)} + */ + @Override public void dumpQueries( PrintWriter pw, @Nullable Integer filteringAppId, DumpState dumpState, int[] users, QuadFunction<Integer, Integer, Integer, Boolean, String[]> getPackagesForUid) { @@ -1702,27 +1807,30 @@ public class AppsFilter implements Watchable, Snappable { } } pw.println(" system apps queryable: " + mSystemAppsQueryable); - dumpPackageSet(pw, filteringAppId, mForceQueryable, "forceQueryable", " ", expandPackages); - pw.println(" queries via package name:"); - dumpQueriesMap(pw, filteringAppId, mQueriesViaPackage, " ", expandPackages); - pw.println(" queries via component:"); - dumpQueriesMap(pw, filteringAppId, mQueriesViaComponent, " ", expandPackages); - pw.println(" queryable via interaction:"); - for (int user : users) { - pw.append(" User ").append(Integer.toString(user)).println(":"); - dumpQueriesMap(pw, - filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId), - mImplicitlyQueryable, " ", expandPackages); - dumpQueriesMap(pw, - filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId), - mRetainedImplicitlyQueryable, " ", expandPackages); + synchronized (mLock) { + dumpPackageSet(pw, filteringAppId, mForceQueryable.untrackedStorage(), + "forceQueryable", " ", expandPackages); + pw.println(" queries via package name:"); + dumpQueriesMap(pw, filteringAppId, mQueriesViaPackage, " ", expandPackages); + pw.println(" queries via component:"); + dumpQueriesMap(pw, filteringAppId, mQueriesViaComponent, " ", expandPackages); + pw.println(" queryable via interaction:"); + for (int user : users) { + pw.append(" User ").append(Integer.toString(user)).println(":"); + dumpQueriesMap(pw, + filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId), + mImplicitlyQueryable, " ", expandPackages); + dumpQueriesMap(pw, + filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId), + mRetainedImplicitlyQueryable, " ", expandPackages); + } + pw.println(" queryable via uses-library:"); + dumpQueriesMap(pw, filteringAppId, mQueryableViaUsesLibrary, " ", expandPackages); } - pw.println(" queryable via uses-library:"); - dumpQueriesMap(pw, filteringAppId, mQueryableViaUsesLibrary, " ", expandPackages); } private static void dumpQueriesMap(PrintWriter pw, @Nullable Integer filteringId, - SparseSetArray<Integer> queriesMap, String spacing, + WatchedSparseSetArray<Integer> queriesMap, String spacing, @Nullable ToString<Integer> toString) { for (int i = 0; i < queriesMap.size(); i++) { Integer callingId = queriesMap.keyAt(i); @@ -1750,7 +1858,7 @@ public class AppsFilter implements Watchable, Snappable { } private static <T> void dumpPackageSet(PrintWriter pw, @Nullable T filteringId, - Set<T> targetPkgSet, String subTitle, String spacing, + ArraySet<T> targetPkgSet, String subTitle, String spacing, @Nullable ToString<T> toString) { if (targetPkgSet != null && targetPkgSet.size() > 0 && (filteringId == null || targetPkgSet.contains(filteringId))) { diff --git a/services/core/java/com/android/server/pm/AppsFilterSnapshot.java b/services/core/java/com/android/server/pm/AppsFilterSnapshot.java new file mode 100644 index 000000000000..cb8c649ded00 --- /dev/null +++ b/services/core/java/com/android/server/pm/AppsFilterSnapshot.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2022 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 com.android.server.pm; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Process; +import android.util.ArrayMap; +import android.util.SparseArray; + +import com.android.internal.util.function.QuadFunction; +import com.android.server.pm.parsing.pkg.AndroidPackage; +import com.android.server.pm.pkg.PackageStateInternal; + +import java.io.PrintWriter; + +/** + * Read-only interface used by computer and snapshots to query the visibility of packages + */ +public interface AppsFilterSnapshot { + /** + * Fetches all app Ids that a given setting is currently visible to, per provided user. This + * only includes UIDs >= {@link Process#FIRST_APPLICATION_UID} as all other UIDs can already see + * all applications. + * + * If the setting is visible to all UIDs, null is returned. If an app is not visible to any + * applications, the int array will be empty. + * + * @param users the set of users that should be evaluated for this calculation + * @param existingSettings the set of all package settings that currently exist on device + * @return a SparseArray mapping userIds to a sorted int array of appIds that may view the + * provided setting or null if the app is visible to all and no allow list should be + * applied. + */ + SparseArray<int[]> getVisibilityAllowList(PackageStateInternal setting, int[] users, + ArrayMap<String, ? extends PackageStateInternal> existingSettings); + + /** + * Returns true if the calling package should not be able to see the target package, false if no + * filtering should be done. + * + * @param callingUid the uid of the caller attempting to access a package + * @param callingSetting the setting attempting to access a package or null if it could not be + * found + * @param targetPkgSetting the package being accessed + * @param userId the user in which this access is being attempted + */ + boolean shouldFilterApplication(int callingUid, @Nullable Object callingSetting, + PackageStateInternal targetPkgSetting, int userId); + + /** + * Returns whether the querying package is allowed to see the target package. + * + * @param querying the querying package + * @param potentialTarget the package name of the target package + */ + boolean canQueryPackage(@NonNull AndroidPackage querying, String potentialTarget); + + /** + * Dump the packages that are queryable by the querying package. + * + * @param pw the output print writer + * @param filteringAppId the querying package's app ID + * @param dumpState the state of the dumping + * @param users the users for which the packages are installed + * @param getPackagesForUid the function that produces the package names for given uids + */ + void dumpQueries(PrintWriter pw, @Nullable Integer filteringAppId, DumpState dumpState, + int[] users, + QuadFunction<Integer, Integer, Integer, Boolean, String[]> getPackagesForUid); + +} diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index 8432f48e269e..8ac3dca1991d 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -380,7 +380,7 @@ public class ComputerEngine implements Computer { private final ResolveInfo mInstantAppInstallerInfo; private final InstantAppRegistry mInstantAppRegistry; private final ApplicationInfo mLocalAndroidApplication; - private final AppsFilter mAppsFilter; + private final AppsFilterSnapshot mAppsFilter; private final WatchedArrayMap<String, Integer> mFrozenPackages; // Immutable service attribute diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 6cf63d10f1f1..55976b8d0610 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -721,7 +721,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService } @Watched - final AppsFilter mAppsFilter; + final AppsFilterImpl mAppsFilter; final PackageParser2.Callback mPackageParserCallback; @@ -979,7 +979,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService public final InstantAppRegistry instantAppRegistry; public final ApplicationInfo androidApplication; public final String appPredictionServicePackage; - public final AppsFilter appsFilter; + public final AppsFilterSnapshot appsFilter; public final ComponentResolverApi componentResolver; public final PackageManagerService service; public final WatchedArrayMap<String, Integer> frozenPackages; @@ -1431,7 +1431,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService RuntimePermissionsPersistence.createInstance(), i.getPermissionManagerServiceInternal(), domainVerificationService, lock), - (i, pm) -> AppsFilter.create(i, i.getLocalService(PackageManagerInternal.class)), + (i, pm) -> AppsFilterImpl.create(i, + i.getLocalService(PackageManagerInternal.class)), (i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"), (i, pm) -> SystemConfig.getInstance(), (i, pm) -> new PackageDexOptimizer(i.getInstaller(), i.getInstallLock(), diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java index a02237fa90ce..396994b04514 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java @@ -99,7 +99,7 @@ public class PackageManagerServiceInjector { private final Singleton<UserManagerService> mUserManagerProducer; private final Singleton<Settings> mSettingsProducer; - private final Singleton<AppsFilter> mAppsFilterProducer; + private final Singleton<AppsFilterImpl> mAppsFilterProducer; private final Singleton<PlatformCompat> mPlatformCompatProducer; private final Singleton<SystemConfig> mSystemConfigProducer; @@ -148,7 +148,7 @@ public class PackageManagerServiceInjector { Producer<PermissionManagerServiceInternal> permissionManagerServiceProducer, Producer<UserManagerService> userManagerProducer, Producer<Settings> settingsProducer, - Producer<AppsFilter> appsFilterProducer, + Producer<AppsFilterImpl> appsFilterProducer, Producer<PlatformCompat> platformCompatProducer, Producer<SystemConfig> systemConfigProducer, Producer<PackageDexOptimizer> packageDexOptimizerProducer, @@ -282,7 +282,7 @@ public class PackageManagerServiceInjector { return mSettingsProducer.get(this, mPackageManager); } - public AppsFilter getAppsFilter() { + public AppsFilterImpl getAppsFilter() { return mAppsFilterProducer.get(this, mPackageManager); } diff --git a/services/core/java/com/android/server/utils/WatchedArrayList.java b/services/core/java/com/android/server/utils/WatchedArrayList.java index bb0ba1329d86..6059f9675e34 100644 --- a/services/core/java/com/android/server/utils/WatchedArrayList.java +++ b/services/core/java/com/android/server/utils/WatchedArrayList.java @@ -273,6 +273,13 @@ public class WatchedArrayList<E> extends WatchableImpl } /** + * Return true if all the objects in the given collection are in this array list. + */ + public boolean containsAll(Collection<?> c) { + return mStorage.containsAll(c); + } + + /** * Ensure capacity. */ public void ensureCapacity(int min) { diff --git a/services/core/java/com/android/server/utils/WatchedSparseBooleanMatrix.java b/services/core/java/com/android/server/utils/WatchedSparseBooleanMatrix.java index 25ae00004b3e..c43e7f9babe6 100644 --- a/services/core/java/com/android/server/utils/WatchedSparseBooleanMatrix.java +++ b/services/core/java/com/android/server/utils/WatchedSparseBooleanMatrix.java @@ -18,6 +18,7 @@ package com.android.server.utils; import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Size; @@ -168,12 +169,19 @@ public class WatchedSparseBooleanMatrix extends WatchableImpl implements Snappab * A copy constructor that can be used for snapshotting. */ private WatchedSparseBooleanMatrix(WatchedSparseBooleanMatrix r) { - mOrder = r.mOrder; - mSize = r.mSize; - mKeys = r.mKeys.clone(); - mMap = r.mMap.clone(); - mInUse = r.mInUse.clone(); - mValues = r.mValues.clone(); + copyFrom(r); + } + + /** + * Copy from src to this. + */ + public void copyFrom(@NonNull WatchedSparseBooleanMatrix src) { + mOrder = src.mOrder; + mSize = src.mSize; + mKeys = src.mKeys.clone(); + mMap = src.mMap.clone(); + mInUse = src.mInUse.clone(); + mValues = src.mValues.clone(); } /** diff --git a/services/core/java/com/android/server/utils/WatchedSparseSetArray.java b/services/core/java/com/android/server/utils/WatchedSparseSetArray.java new file mode 100644 index 000000000000..05db12e49a13 --- /dev/null +++ b/services/core/java/com/android/server/utils/WatchedSparseSetArray.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2022 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 com.android.server.utils; + +import android.annotation.NonNull; +import android.util.ArraySet; +import android.util.SparseSetArray; + + +/** + * A watched variant of SparseSetArray. Changes to the array are notified to + * registered {@link Watcher}s. + * @param <T> The element type, stored in the SparseSetArray. + */ +public class WatchedSparseSetArray<T> extends WatchableImpl implements Snappable { + // The storage + private final SparseSetArray mStorage; + + // A private convenience function + private void onChanged() { + dispatchChange(this); + } + + public WatchedSparseSetArray() { + mStorage = new SparseSetArray(); + } + + /** + * Creates a new WatchedSparseSetArray from an existing WatchedSparseSetArray and copy its data + */ + public WatchedSparseSetArray(@NonNull WatchedSparseSetArray<T> watchedSparseSetArray) { + mStorage = new SparseSetArray(watchedSparseSetArray.untrackedStorage()); + } + + /** + * Return the underlying storage. This breaks the wrapper but is necessary when + * passing the array to distant methods. + */ + public SparseSetArray<T> untrackedStorage() { + return mStorage; + } + + /** + * Add a value for key n. + * @return FALSE when the value already existed for the given key, TRUE otherwise. + */ + public boolean add(int n, T value) { + final boolean res = mStorage.add(n, value); + onChanged(); + return res; + } + + /** + * Removes all mappings from this SparseSetArray. + */ + public void clear() { + mStorage.clear(); + onChanged(); + } + + /** + * @return whether the value exists for the key n. + */ + public boolean contains(int n, T value) { + return mStorage.contains(n, value); + } + + /** + * @return the set of items of key n + */ + public ArraySet<T> get(int n) { + return mStorage.get(n); + } + + /** + * Remove a value for key n. + * @return TRUE when the value existed for the given key and removed, FALSE otherwise. + */ + public boolean remove(int n, T value) { + if (mStorage.remove(n, value)) { + onChanged(); + return true; + } + return false; + } + + /** + * Remove all values for key n. + */ + public void remove(int n) { + mStorage.remove(n); + onChanged(); + } + + /** + * Return the size of the SparseSetArray. + */ + public int size() { + return mStorage.size(); + } + + /** + * Return the key stored at the given index. + */ + public int keyAt(int index) { + return mStorage.keyAt(index); + } + + /** + * Return the size of the array at the given index. + */ + public int sizeAt(int index) { + return mStorage.sizeAt(index); + } + + /** + * Return the value in the SetArray at the given key index and value index. + */ + public T valueAt(int intIndex, int valueIndex) { + return (T) mStorage.valueAt(intIndex, valueIndex); + } + + @NonNull + @Override + public Object snapshot() { + WatchedSparseSetArray l = new WatchedSparseSetArray(this); + l.seal(); + return l; + } + + /** + * Make <this> a snapshot of the argument. Note that <this> is immutable when the + * method returns. <this> must be empty when the function is called. + * @param r The source array, which is copied into <this> + */ + public void snapshot(@NonNull WatchedSparseSetArray<T> r) { + snapshot(this, r); + } + + /** + * Make the destination a copy of the source. If the element is a subclass of Snapper then the + * copy contains snapshots of the elements. Otherwise the copy contains references to the + * elements. The destination must be initially empty. Upon return, the destination is + * immutable. + * @param dst The destination array. It must be empty. + * @param src The source array. It is not modified. + */ + public static void snapshot(@NonNull WatchedSparseSetArray dst, + @NonNull WatchedSparseSetArray src) { + if (dst.size() != 0) { + throw new IllegalArgumentException("snapshot destination is not empty"); + } + final int arraySize = src.size(); + for (int i = 0; i < arraySize; i++) { + final ArraySet set = src.get(i); + final int setSize = set.size(); + for (int j = 0; j < setSize; j++) { + dst.add(src.keyAt(i), set.valueAt(j)); + } + } + dst.seal(); + } +} diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt index 7017440a86bb..7b152247eb9c 100644 --- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt +++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt @@ -34,7 +34,6 @@ import com.android.server.pm.test.override.PackageManagerComponentLabelIconOverr import com.android.server.testutils.TestHandler import com.android.server.testutils.mock import com.android.server.testutils.mockThrowOnUnmocked -import com.android.server.testutils.spy import com.android.server.testutils.whenever import com.android.server.wm.ActivityTaskManagerInternal import com.google.common.truth.Truth.assertThat @@ -46,13 +45,9 @@ import org.junit.runner.RunWith import org.junit.runners.Parameterized import org.mockito.Mockito.any import org.mockito.Mockito.anyInt -import org.mockito.Mockito.clearInvocations -import org.mockito.Mockito.doAnswer import org.mockito.Mockito.doReturn import org.mockito.Mockito.intThat -import org.mockito.Mockito.never import org.mockito.Mockito.same -import org.mockito.Mockito.verify import org.testng.Assert.assertThrows import java.io.File import java.util.UUID @@ -365,7 +360,7 @@ class PackageManagerComponentLabelIconOverrideTest { val mockActivityTaskManager: ActivityTaskManagerInternal = mockThrowOnUnmocked { whenever(this.isCallerRecents(anyInt())) { false } } - val mockAppsFilter: AppsFilter = mockThrowOnUnmocked { + val mockAppsFilter: AppsFilterImpl = mockThrowOnUnmocked { whenever(this.shouldFilterApplication(anyInt(), any<PackageSetting>(), any<PackageSetting>(), anyInt())) { false } whenever(this.snapshot()) { this@mockThrowOnUnmocked } diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt index 0567f58b1bbe..353c8e22cceb 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt @@ -194,7 +194,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { val packageParser: PackageParser2 = mock() val keySetManagerService: KeySetManagerService = mock() val packageAbiHelper: PackageAbiHelper = mock() - val appsFilter: AppsFilter = mock { + val appsFilter: AppsFilterImpl = mock { whenever(snapshot()) { this@mock } } val dexManager: DexManager = mock() diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java index b72b8d2ec6e8..d8f4349b95bf 100644 --- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java @@ -77,7 +77,7 @@ import java.util.concurrent.Executor; @Presubmit @RunWith(JUnit4.class) -public class AppsFilterTest { +public class AppsFilterImplTest { private static final int DUMMY_CALLING_APPID = 10345; private static final int DUMMY_TARGET_APPID = 10556; @@ -98,9 +98,9 @@ public class AppsFilterTest { } @Mock - AppsFilter.FeatureConfig mFeatureConfigMock; + AppsFilterImpl.FeatureConfig mFeatureConfigMock; @Mock - AppsFilter.StateProvider mStateProvider; + AppsFilterImpl.StateProvider mStateProvider; @Mock Executor mMockExecutor; @Mock @@ -204,11 +204,11 @@ public class AppsFilterTest { MockitoAnnotations.initMocks(this); doAnswer(invocation -> { - ((AppsFilter.StateProvider.CurrentStateCallback) invocation.getArgument(0)) + ((AppsFilterImpl.StateProvider.CurrentStateCallback) invocation.getArgument(0)) .currentState(mExisting, USER_INFO_LIST); return new Object(); }).when(mStateProvider) - .runWithState(any(AppsFilter.StateProvider.CurrentStateCallback.class)); + .runWithState(any(AppsFilterImpl.StateProvider.CurrentStateCallback.class)); doAnswer(invocation -> { ((Runnable) invocation.getArgument(0)).run(); @@ -218,14 +218,14 @@ public class AppsFilterTest { when(mFeatureConfigMock.isGloballyEnabled()).thenReturn(true); when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class))).thenAnswer( (Answer<Boolean>) invocation -> - ((AndroidPackage)invocation.getArgument(SYSTEM_USER)).getTargetSdkVersion() + ((AndroidPackage) invocation.getArgument(SYSTEM_USER)).getTargetSdkVersion() >= Build.VERSION_CODES.R); } @Test public void testSystemReadyPropogates() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); @@ -236,8 +236,8 @@ public class AppsFilterTest { @Test public void testQueriesAction_FilterMatches() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); @@ -259,8 +259,8 @@ public class AppsFilterTest { } @Test public void testQueriesProtectedAction_FilterDoesNotMatch() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); @@ -308,8 +308,8 @@ public class AppsFilterTest { @Test public void testQueriesProvider_FilterMatches() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); @@ -333,8 +333,8 @@ public class AppsFilterTest { @Test public void testOnUserUpdated_FilterMatches() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); @@ -356,11 +356,11 @@ public class AppsFilterTest { // adds new user doAnswer(invocation -> { - ((AppsFilter.StateProvider.CurrentStateCallback) invocation.getArgument(0)) + ((AppsFilterImpl.StateProvider.CurrentStateCallback) invocation.getArgument(0)) .currentState(mExisting, USER_INFO_LIST_WITH_ADDED); return new Object(); }).when(mStateProvider) - .runWithState(any(AppsFilter.StateProvider.CurrentStateCallback.class)); + .runWithState(any(AppsFilterImpl.StateProvider.CurrentStateCallback.class)); appsFilter.onUserCreated(ADDED_USER); for (int subjectUserId : USER_ARRAY_WITH_ADDED) { @@ -373,11 +373,11 @@ public class AppsFilterTest { // delete user doAnswer(invocation -> { - ((AppsFilter.StateProvider.CurrentStateCallback) invocation.getArgument(0)) + ((AppsFilterImpl.StateProvider.CurrentStateCallback) invocation.getArgument(0)) .currentState(mExisting, USER_INFO_LIST); return new Object(); }).when(mStateProvider) - .runWithState(any(AppsFilter.StateProvider.CurrentStateCallback.class)); + .runWithState(any(AppsFilterImpl.StateProvider.CurrentStateCallback.class)); appsFilter.onUserDeleted(ADDED_USER); for (int subjectUserId : USER_ARRAY) { @@ -391,8 +391,8 @@ public class AppsFilterTest { @Test public void testQueriesDifferentProvider_Filters() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); @@ -416,8 +416,8 @@ public class AppsFilterTest { @Test public void testQueriesProviderWithSemiColon_FilterMatches() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -435,8 +435,8 @@ public class AppsFilterTest { @Test public void testQueriesAction_NoMatchingAction_Filters() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -452,8 +452,8 @@ public class AppsFilterTest { @Test public void testQueriesAction_NoMatchingActionFilterLowSdk_DoesntFilter() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -473,8 +473,8 @@ public class AppsFilterTest { @Test public void testNoQueries_Filters() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -490,7 +490,7 @@ public class AppsFilterTest { @Test public void testNoUsesLibrary_Filters() throws Exception { - final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, + final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, mMockExecutor, mMockPmInternal); @@ -516,7 +516,7 @@ public class AppsFilterTest { @Test public void testUsesLibrary_DoesntFilter() throws Exception { - final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, + final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, mMockExecutor, mMockPmInternal); @@ -543,7 +543,7 @@ public class AppsFilterTest { @Test public void testUsesOptionalLibrary_DoesntFilter() throws Exception { - final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, + final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, mMockExecutor, mMockPmInternal); @@ -570,7 +570,7 @@ public class AppsFilterTest { @Test public void testUsesLibrary_ShareUid_DoesntFilter() throws Exception { - final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, + final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, mMockExecutor, mMockPmInternal); @@ -602,8 +602,8 @@ public class AppsFilterTest { @Test public void testForceQueryable_SystemDoesntFilter() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -621,8 +621,8 @@ public class AppsFilterTest { @Test public void testForceQueryable_NonSystemFilters() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -638,9 +638,10 @@ public class AppsFilterTest { @Test public void testForceQueryableByDevice_SystemCaller_DoesntFilter() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"}, - false, null, mMockExecutor, mMockPmInternal); + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, + new String[]{"com.some.package"}, false, null, + mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -657,8 +658,8 @@ public class AppsFilterTest { @Test public void testSystemSignedTarget_DoesntFilter() throws CertificateException { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); appsFilter.onSystemReady(); @@ -686,9 +687,10 @@ public class AppsFilterTest { @Test public void testForceQueryableByDevice_NonSystemCaller_Filters() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"}, - false, null, mMockExecutor, mMockPmInternal); + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, + new String[]{"com.some.package"}, false, null, + mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -704,8 +706,8 @@ public class AppsFilterTest { @Test public void testSystemQueryable_DoesntFilter() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, true /* system force queryable */, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); @@ -723,8 +725,8 @@ public class AppsFilterTest { @Test public void testQueriesPackage_DoesntFilter() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -742,8 +744,8 @@ public class AppsFilterTest { public void testNoQueries_FeatureOff_DoesntFilter() throws Exception { when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class))) .thenReturn(false); - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -759,8 +761,8 @@ public class AppsFilterTest { @Test public void testSystemUid_DoesntFilter() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -775,8 +777,8 @@ public class AppsFilterTest { @Test public void testSystemUidSecondaryUser_DoesntFilter() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -792,8 +794,8 @@ public class AppsFilterTest { @Test public void testNonSystemUid_NoCallingSetting_Filters() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -807,8 +809,8 @@ public class AppsFilterTest { @Test public void testNoTargetPackage_filters() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -838,7 +840,7 @@ public class AppsFilterTest { .setOverlayTargetOverlayableName("overlayableName"); ParsingPackage actor = pkg("com.some.package.actor"); - final AppsFilter appsFilter = new AppsFilter( + final AppsFilterImpl appsFilter = new AppsFilterImpl( mStateProvider, mFeatureConfigMock, new String[]{}, @@ -933,7 +935,7 @@ public class AppsFilterTest { when(mMockPmInternal.getSharedUserPackages(any(Integer.class))).thenReturn( actorSharedSettingPackages ); - final AppsFilter appsFilter = new AppsFilter( + final AppsFilterImpl appsFilter = new AppsFilterImpl( mStateProvider, mFeatureConfigMock, new String[]{}, @@ -985,8 +987,8 @@ public class AppsFilterTest { @Test public void testInitiatingApp_DoesntFilter() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -1003,8 +1005,8 @@ public class AppsFilterTest { @Test public void testUninstalledInitiatingApp_Filters() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -1021,8 +1023,8 @@ public class AppsFilterTest { @Test public void testOriginatingApp_Filters() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); @@ -1046,8 +1048,8 @@ public class AppsFilterTest { @Test public void testInstallingApp_DoesntFilter() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); @@ -1071,8 +1073,8 @@ public class AppsFilterTest { @Test public void testInstrumentation_DoesntFilter() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); @@ -1100,8 +1102,8 @@ public class AppsFilterTest { @Test public void testWhoCanSee() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); @@ -1173,8 +1175,8 @@ public class AppsFilterTest { @Test public void testOnChangeReport() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); @@ -1246,8 +1248,8 @@ public class AppsFilterTest { @Test public void testOnChangeReportedFilter() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -1270,6 +1272,53 @@ public class AppsFilterTest { watcher.verifyNoChangeReported("shouldFilterApplication"); } + @Test + public void testAppsFilterRead() throws Exception { + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor, mMockPmInternal); + simulateAddBasicAndroid(appsFilter); + appsFilter.onSystemReady(); + + PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"), + DUMMY_TARGET_APPID); + PackageSetting instrumentation = simulateAddPackage(appsFilter, + pkgWithInstrumentation("com.some.other.package", "com.some.package"), + DUMMY_CALLING_APPID); + + final int hasProviderAppId = Process.FIRST_APPLICATION_UID + 1; + final int queriesProviderAppId = Process.FIRST_APPLICATION_UID + 2; + PackageSetting queriesProvider = simulateAddPackage(appsFilter, + pkgQueriesProvider("com.yet.some.other.package", "com.some.authority"), + queriesProviderAppId); + appsFilter.grantImplicitAccess( + hasProviderAppId, queriesProviderAppId, false /* retainOnUpdate */); + + AppsFilterSnapshot snapshot = appsFilter.snapshot(); + assertFalse( + snapshot.shouldFilterApplication(DUMMY_CALLING_APPID, instrumentation, target, + SYSTEM_USER)); + assertFalse( + snapshot.shouldFilterApplication(DUMMY_TARGET_APPID, target, instrumentation, + SYSTEM_USER)); + + SparseArray<int[]> queriesProviderFilter = + snapshot.getVisibilityAllowList(queriesProvider, USER_ARRAY, mExisting); + assertThat(toList(queriesProviderFilter.get(SYSTEM_USER)), contains(queriesProviderAppId)); + assertTrue(snapshot.canQueryPackage(instrumentation.getPkg(), + target.getPackageName())); + + // New changes don't affect the snapshot + appsFilter.removePackage(target, false); + assertTrue( + appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, instrumentation, target, + SYSTEM_USER)); + assertFalse( + snapshot.shouldFilterApplication(DUMMY_CALLING_APPID, instrumentation, target, + SYSTEM_USER)); + + } + private List<Integer> toList(int[] array) { ArrayList<Integer> ret = new ArrayList<>(array.length); for (int i = 0; i < array.length; i++) { @@ -1282,7 +1331,7 @@ public class AppsFilterTest { PackageSettingBuilder withBuilder(PackageSettingBuilder builder); } - private void simulateAddBasicAndroid(AppsFilter appsFilter) throws Exception { + private void simulateAddBasicAndroid(AppsFilterImpl appsFilter) throws Exception { final Signature frameworkSignature = Mockito.mock(Signature.class); final SigningDetails frameworkSigningDetails = new SigningDetails(new Signature[]{frameworkSignature}, 1); @@ -1291,17 +1340,17 @@ public class AppsFilterTest { b -> b.setSigningDetails(frameworkSigningDetails)); } - private PackageSetting simulateAddPackage(AppsFilter filter, + private PackageSetting simulateAddPackage(AppsFilterImpl filter, ParsingPackage newPkgBuilder, int appId) { return simulateAddPackage(filter, newPkgBuilder, appId, null /*settingBuilder*/); } - private PackageSetting simulateAddPackage(AppsFilter filter, + private PackageSetting simulateAddPackage(AppsFilterImpl filter, ParsingPackage newPkgBuilder, int appId, @Nullable WithSettingBuilder action) { return simulateAddPackage(filter, newPkgBuilder, appId, action, null /*sharedUserSetting*/); } - private PackageSetting simulateAddPackage(AppsFilter filter, + private PackageSetting simulateAddPackage(AppsFilterImpl filter, ParsingPackage newPkgBuilder, int appId, @Nullable WithSettingBuilder action, @Nullable SharedUserSetting sharedUserSetting) { final PackageSetting setting = @@ -1324,7 +1373,7 @@ public class AppsFilterTest { return setting; } - private void simulateAddPackage(PackageSetting setting, AppsFilter filter, + private void simulateAddPackage(PackageSetting setting, AppsFilterImpl filter, @Nullable SharedUserSetting sharedUserSetting) { mExisting.put(setting.getPackageName(), setting); if (sharedUserSetting != null) { diff --git a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java index 4ed4c236535f..37c95f735d89 100644 --- a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java +++ b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java @@ -17,6 +17,7 @@ package com.android.server.utils; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -860,6 +861,54 @@ public class WatcherTest { } } + @Test + public void testWatchedSparseSetArray() { + final String name = "WatchedSparseSetArray"; + WatchableTester tester; + + // Test WatchedSparseSetArray + WatchedSparseSetArray array = new WatchedSparseSetArray(); + tester = new WatchableTester(array, name); + tester.verify(0, "Initial array - no registration"); + array.add(INDEX_A, 1); + tester.verify(0, "Updates with no registration"); + tester.register(); + tester.verify(0, "Updates with no registration"); + array.add(INDEX_B, 2); + tester.verify(1, "Updates with registration"); + array.add(INDEX_B, 4); + array.add(INDEX_C, 5); + tester.verify(3, "Updates with registration"); + // Special methods + assertTrue(array.remove(INDEX_C, 5)); + tester.verify(4, "Removed 5 from key 3"); + array.remove(INDEX_B); + tester.verify(5, "Removed everything for key 2"); + + // Snapshot + { + WatchedSparseSetArray arraySnap = (WatchedSparseSetArray) array.snapshot(); + tester.verify(5, "Generate snapshot"); + // Verify that the snapshot is a proper copy of the source. + assertEquals("WatchedSparseSetArray snap same size", + array.size(), arraySnap.size()); + for (int i = 0; i < array.size(); i++) { + ArraySet set = array.get(array.keyAt(i)); + ArraySet setSnap = arraySnap.get(arraySnap.keyAt(i)); + assertNotNull(set); + assertTrue(set.equals(setSnap)); + } + array.add(INDEX_D, 9); + tester.verify(6, "Tick after snapshot"); + // Verify that the array is sealed + verifySealed(name, ()->arraySnap.add(INDEX_D, 10)); + assertTrue(!array.isSealed()); + assertTrue(arraySnap.isSealed()); + } + array.clear(); + tester.verify(7, "Cleared all entries"); + } + private static class IndexGenerator { private final int mSeed; private final Random mRandom; @@ -1084,6 +1133,18 @@ public class WatcherTest { assertEquals(a.equals(s), true); a.put(rowIndex, colIndex, !a.get(rowIndex, colIndex)); assertEquals(a.equals(s), false); + + // Verify copy-in/out + { + final String msg = name + " copy"; + WatchedSparseBooleanMatrix copy = new WatchedSparseBooleanMatrix(); + copy.copyFrom(matrix); + final int end = copy.size(); + assertTrue(msg + " size mismatch " + end + " " + matrix.size(), end == matrix.size()); + for (int i = 0; i < end; i++) { + assertEquals(copy.keyAt(i), keys[i]); + } + } } @Test |