diff options
| author | 2022-05-03 17:26:33 +0000 | |
|---|---|---|
| committer | 2022-05-03 17:26:33 +0000 | |
| commit | 4c71401de5aea3cab2c74e22b24a0d3c0a97c82d (patch) | |
| tree | 09c57217742106ab706d4f71939a3a9e025f2d57 | |
| parent | e26bcbb81185c937a2e5bb67d32c18350486ceca (diff) | |
| parent | 6b941dcc3b5b3613d2d7b0581e1133db00568220 (diff) | |
Merge "[AppsFilter] separate classes for read and write" into tm-dev
8 files changed, 1208 insertions, 947 deletions
diff --git a/services/core/java/com/android/server/pm/AppsFilterBase.java b/services/core/java/com/android/server/pm/AppsFilterBase.java new file mode 100644 index 000000000000..7004c7363122 --- /dev/null +++ b/services/core/java/com/android/server/pm/AppsFilterBase.java @@ -0,0 +1,773 @@ +/* + * 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 static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; + +import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; +import static com.android.server.pm.AppsFilterUtils.canQueryViaComponents; +import static com.android.server.pm.AppsFilterUtils.requestsQueryAllPackages; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.pm.SigningDetails; +import android.os.Binder; +import android.os.Process; +import android.os.Trace; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Slog; +import android.util.SparseArray; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.function.QuadFunction; +import com.android.server.om.OverlayReferenceMapper; +import com.android.server.pm.parsing.pkg.AndroidPackage; +import com.android.server.pm.pkg.PackageStateInternal; +import com.android.server.pm.snapshot.PackageDataSnapshot; +import com.android.server.utils.SnapshotCache; +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 java.io.PrintWriter; +import java.util.Arrays; +import java.util.Collection; +import java.util.Objects; +import java.util.concurrent.Executor; + +/** + * AppsFilter is the entity responsible for filtering visibility between apps based on declarations + * in their manifests. This class implements the unlocked, read-only methods of AppsFilter. + * See {@link AppsFilterImpl} for the write methods that updates the internal structures. + */ +public abstract class AppsFilterBase implements AppsFilterSnapshot { + protected static final String TAG = "AppsFilter"; + + // Logs all filtering instead of enforcing + protected static final boolean DEBUG_ALLOW_ALL = false; + protected static final boolean DEBUG_LOGGING = false; + public static final boolean DEBUG_TRACING = false; + + /** + * This contains a list of app UIDs that are implicitly queryable because another app explicitly + * interacted with it. For example, if application A starts a service in application B, + * application B is implicitly allowed to query for application A; regardless of any manifest + * entries. + */ + @NonNull + @Watched + protected WatchedSparseSetArray<Integer> mImplicitlyQueryable; + @NonNull + protected 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. + */ + @NonNull + @Watched + protected WatchedSparseSetArray<Integer> mRetainedImplicitlyQueryable; + @NonNull + protected 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. + */ + @NonNull + @Watched + protected WatchedSparseSetArray<Integer> mQueriesViaPackage; + @NonNull + protected 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. + */ + @NonNull + @Watched + protected WatchedSparseSetArray<Integer> mQueriesViaComponent; + @NonNull + protected 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. + */ + @NonNull + @Watched + protected WatchedSparseSetArray<Integer> mQueryableViaUsesLibrary; + @NonNull + protected SnapshotCache<WatchedSparseSetArray<Integer>> mQueryableViaUsesLibrarySnapshot; + + /** + * Executor for running reasonably short background tasks such as building the initial + * visibility cache. + */ + protected Executor mBackgroundExecutor; + + /** + * Pending full recompute of mQueriesViaComponent. Occurs when a package adds a new set of + * protected broadcast. This in turn invalidates all prior additions and require a very + * computationally expensive recomputing. + * Full recompute is done lazily at the point when we use mQueriesViaComponent to filter apps. + */ + protected boolean mQueriesViaComponentRequireRecompute = false; + + /** + * A set of App IDs that are always queryable by any package, regardless of their manifest + * content. + */ + @NonNull + @Watched + protected WatchedArraySet<Integer> mForceQueryable; + @NonNull + protected SnapshotCache<WatchedArraySet<Integer>> mForceQueryableSnapshot; + + /** + * The set of package names provided by the device that should be force queryable regardless of + * their manifest contents. + */ + @NonNull + protected String[] mForceQueryableByDevicePackageNames; + @NonNull + /** True if all system apps should be made queryable by default. */ + protected boolean mSystemAppsQueryable; + @NonNull + protected FeatureConfig mFeatureConfig; + @NonNull + protected OverlayReferenceMapper mOverlayReferenceMapper; + @Nullable + protected SigningDetails mSystemSigningDetails; + + @NonNull + @Watched + protected WatchedArrayList<String> mProtectedBroadcasts; + @NonNull + protected SnapshotCache<WatchedArrayList<String>> mProtectedBroadcastsSnapshot; + + /** + * This structure maps uid -> uid and indicates whether access from the first should be + * filtered to the second. It's essentially a cache of the + * {@link #shouldFilterApplicationInternal(PackageDataSnapshot, 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 empty until {@link #mSystemReady} is true. + */ + @NonNull + @Watched + protected WatchedSparseBooleanMatrix mShouldFilterCache; + @NonNull + protected SnapshotCache<WatchedSparseBooleanMatrix> mShouldFilterCacheSnapshot; + + protected volatile boolean mSystemReady = false; + + protected boolean isForceQueryable(int callingAppId) { + return mForceQueryable.contains(callingAppId); + } + + protected boolean isQueryableViaPackage(int callingAppId, int targetAppId) { + return mQueriesViaPackage.contains(callingAppId, targetAppId); + } + + protected boolean isQueryableViaComponent(int callingAppId, int targetAppId) { + return mQueriesViaComponent.contains(callingAppId, targetAppId); + } + + protected boolean isImplicitlyQueryable(int callingAppId, int targetAppId) { + return mImplicitlyQueryable.contains(callingAppId, targetAppId); + } + + protected boolean isRetainedImplicitlyQueryable(int callingAppId, int targetAppId) { + return mRetainedImplicitlyQueryable.contains(callingAppId, targetAppId); + } + + protected boolean isQueryableViaUsesLibrary(int callingAppId, int targetAppId) { + return mQueryableViaUsesLibrary.contains(callingAppId, targetAppId); + } + + protected boolean isQueryableViaComponentWhenRequireRecompute( + ArrayMap<String, ? extends PackageStateInternal> existingSettings, + PackageStateInternal callingPkgSetting, + ArraySet<PackageStateInternal> callingSharedPkgSettings, + AndroidPackage targetPkg, + int callingAppId, int targetAppId) { + // Do no recompute or use mQueriesViaComponent if it's stale in snapshot + // Since we know we are in the snapshot, no need to acquire mLock because + // mProtectedBroadcasts will not change + if (callingPkgSetting != null) { + if (callingPkgSetting.getPkg() != null + && canQueryViaComponents(callingPkgSetting.getPkg(), targetPkg, + mProtectedBroadcasts)) { + return true; + } + } else { + for (int i = callingSharedPkgSettings.size() - 1; i >= 0; i--) { + final AndroidPackage pkg = + callingSharedPkgSettings.valueAt(i).getPkg(); + if (pkg != null && canQueryViaComponents(pkg, targetPkg, + mProtectedBroadcasts)) { + return true; + } + } + } + return false; + } + + /** + * See {@link AppsFilterSnapshot#getVisibilityAllowList(PackageDataSnapshot, + * PackageStateInternal, int[], ArrayMap)} + */ + @Override + @Nullable + public SparseArray<int[]> getVisibilityAllowList(PackageDataSnapshot snapshot, + PackageStateInternal setting, int[] users, + ArrayMap<String, ? extends PackageStateInternal> existingSettings) { + if (isForceQueryable(setting.getAppId())) { + return null; + } + // let's reserve max memory to limit the number of allocations + SparseArray<int[]> result = new SparseArray<>(users.length); + for (int u = 0; u < users.length; u++) { + final int userId = users[u]; + int[] appIds = new int[existingSettings.size()]; + int[] buffer = null; + int allowListSize = 0; + for (int i = existingSettings.size() - 1; i >= 0; i--) { + final PackageStateInternal existingSetting = existingSettings.valueAt(i); + final int existingAppId = existingSetting.getAppId(); + if (existingAppId < Process.FIRST_APPLICATION_UID) { + continue; + } + final int loc = Arrays.binarySearch(appIds, 0, allowListSize, existingAppId); + if (loc >= 0) { + continue; + } + final int existingUid = UserHandle.getUid(userId, existingAppId); + if (!shouldFilterApplication(snapshot, existingUid, existingSetting, setting, + userId)) { + if (buffer == null) { + buffer = new int[appIds.length]; + } + final int insert = ~loc; + System.arraycopy(appIds, insert, buffer, 0, allowListSize - insert); + appIds[insert] = existingAppId; + System.arraycopy(buffer, 0, appIds, insert + 1, allowListSize - insert); + allowListSize++; + } + } + result.put(userId, Arrays.copyOf(appIds, allowListSize)); + } + return result; + } + + /** + * This api does type conversion on the <existingSettings> parameter. + */ + @VisibleForTesting(visibility = PRIVATE) + @Nullable + SparseArray<int[]> getVisibilityAllowList(PackageDataSnapshot snapshot, + PackageStateInternal setting, int[] users, + WatchedArrayMap<String, ? extends PackageStateInternal> existingSettings) { + return getVisibilityAllowList(snapshot, setting, users, + existingSettings.untrackedStorage()); + } + + /** + * See + * {@link AppsFilterSnapshot#shouldFilterApplication(PackageDataSnapshot, int, Object, + * PackageStateInternal, int)} + */ + @Override + public boolean shouldFilterApplication(PackageDataSnapshot snapshot, int callingUid, + @Nullable Object callingSetting, PackageStateInternal targetPkgSetting, int userId) { + if (DEBUG_TRACING) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplication"); + } + try { + int callingAppId = UserHandle.getAppId(callingUid); + if (callingAppId < Process.FIRST_APPLICATION_UID + || targetPkgSetting.getAppId() < Process.FIRST_APPLICATION_UID + || callingAppId == targetPkgSetting.getAppId()) { + return false; + } + if (mSystemReady) { // use cache + if (!shouldFilterApplicationUsingCache(callingUid, + targetPkgSetting.getAppId(), + userId)) { + return false; + } + } else { + if (!shouldFilterApplicationInternal(snapshot, + callingUid, callingSetting, targetPkgSetting, userId)) { + return false; + } + } + if (DEBUG_LOGGING || mFeatureConfig.isLoggingEnabled(callingAppId)) { + log(callingSetting, targetPkgSetting, "BLOCKED"); + } + return !DEBUG_ALLOW_ALL; + } finally { + if (DEBUG_TRACING) { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + } + + protected boolean shouldFilterApplicationUsingCache(int callingUid, int appId, int userId) { + 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); + } + + protected boolean shouldFilterApplicationInternal(PackageDataSnapshot snapshot, int callingUid, + Object callingSetting, PackageStateInternal targetPkgSetting, int targetUserId) { + if (DEBUG_TRACING) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplicationInternal"); + } + try { + final boolean featureEnabled = mFeatureConfig.isGloballyEnabled(); + if (!featureEnabled) { + if (DEBUG_LOGGING) { + Slog.d(TAG, "filtering disabled; skipped"); + } + return false; + } + if (callingSetting == null) { + Slog.wtf(TAG, "No setting found for non system uid " + callingUid); + return true; + } + final PackageStateInternal callingPkgSetting; + if (DEBUG_TRACING) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "callingSetting instanceof"); + } + final ArraySet<PackageStateInternal> callingSharedPkgSettings = new ArraySet<>(); + + if (callingSetting instanceof PackageStateInternal) { + final PackageStateInternal packageState = (PackageStateInternal) callingSetting; + if (packageState.hasSharedUser()) { + callingPkgSetting = null; + callingSharedPkgSettings.addAll(getSharedUserPackages( + packageState.getSharedUserAppId(), snapshot.getAllSharedUsers())); + + } else { + callingPkgSetting = packageState; + } + } else { + callingPkgSetting = null; + callingSharedPkgSettings.addAll( + ((SharedUserSetting) callingSetting).getPackageStates()); + } + if (DEBUG_TRACING) { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + + if (callingPkgSetting != null) { + if (callingPkgSetting.getPkg() != null + && !mFeatureConfig.packageIsEnabled(callingPkgSetting.getPkg())) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "DISABLED"); + } + return false; + } + } else { + for (int i = callingSharedPkgSettings.size() - 1; i >= 0; i--) { + final AndroidPackage pkg = callingSharedPkgSettings.valueAt(i).getPkg(); + if (pkg != null && !mFeatureConfig.packageIsEnabled(pkg)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "DISABLED"); + } + return false; + } + } + } + + if (DEBUG_TRACING) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "getAppId"); + } + final int callingAppId; + if (callingPkgSetting != null) { + callingAppId = callingPkgSetting.getAppId(); + } else { + // all should be the same + callingAppId = callingSharedPkgSettings.valueAt(0).getAppId(); + } + final int targetAppId = targetPkgSetting.getAppId(); + if (DEBUG_TRACING) { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + if (callingAppId == targetAppId) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "same app id"); + } + return false; + } + + try { + if (DEBUG_TRACING) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "requestsQueryAllPackages"); + } + if (callingPkgSetting != null) { + 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(); + if (pkg != null && requestsQueryAllPackages(pkg)) { + return false; + } + } + } + } finally { + if (DEBUG_TRACING) { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + + // This package isn't technically installed and won't be written to settings, so we can + // treat it as filtered until it's available again. + final AndroidPackage targetPkg = targetPkgSetting.getPkg(); + if (targetPkg == null) { + if (DEBUG_LOGGING) { + Slog.wtf(TAG, "shouldFilterApplication: " + "targetPkg is null"); + } + return true; + } + if (targetPkg.isStaticSharedLibrary()) { + // not an app, this filtering takes place at a higher level + return false; + } + + try { + if (DEBUG_TRACING) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mForceQueryable"); + } + if (isForceQueryable(targetAppId)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "force queryable"); + } + return false; + } + } finally { + if (DEBUG_TRACING) { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + try { + if (DEBUG_TRACING) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueriesViaPackage"); + } + if (isQueryableViaPackage(callingAppId, targetAppId)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "queries package"); + } + 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) { + if (isQueryableViaComponent(callingAppId, targetAppId)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "queries component"); + } + return false; + } + } else { // mQueriesViaComponent is stale + if (isQueryableViaComponentWhenRequireRecompute(snapshot.getPackageStates(), + callingPkgSetting, callingSharedPkgSettings, targetPkg, + 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 (isImplicitlyQueryable(callingUid, targetUid)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "implicitly queryable for user"); + } + 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 (isRetainedImplicitlyQueryable(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); + } + } + + try { + if (DEBUG_TRACING) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mOverlayReferenceMapper"); + } + final String targetName = targetPkg.getPackageName(); + if (!callingSharedPkgSettings.isEmpty()) { + 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, + callingPkgSetting.getPackageName())) { + if (DEBUG_LOGGING) { + log(callingPkgSetting, targetPkgSetting, "acts on target of overlay"); + } + return false; + } + } + } finally { + if (DEBUG_TRACING) { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + + try { + if (DEBUG_TRACING) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueryableViaUsesLibrary"); + } + if (isQueryableViaUsesLibrary(callingAppId, targetAppId)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "queryable for library users"); + } + return false; + } + } finally { + if (DEBUG_TRACING) { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + + return true; + } finally { + if (DEBUG_TRACING) { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + } + + /** + * 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; + } + + // Check if FILTER_APPLICATION_QUERY is enabled on the given package. + if (!mFeatureConfig.packageIsEnabled(querying)) { + return true; + } + + if (requestsQueryAllPackages(querying)) { + return true; + } + + return !querying.getQueriesPackages().isEmpty() + && querying.getQueriesPackages().contains(potentialTarget); + } + + private static void log(Object callingSetting, PackageStateInternal targetPkgSetting, + String description) { + Slog.i(TAG, + "interaction: " + (callingSetting == null ? "system" : callingSetting) + " -> " + + targetPkgSetting + " " + description); + } + + protected ArraySet<? extends PackageStateInternal> getSharedUserPackages(int sharedUserAppId, + Collection<SharedUserSetting> sharedUserSettings) { + for (SharedUserSetting setting : sharedUserSettings) { + if (setting.mAppId != sharedUserAppId) { + continue; + } + return setting.getPackageStates(); + } + return new ArraySet<>(); + } + + /** + * 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) { + final SparseArray<String> cache = new SparseArray<>(); + ToString<Integer> expandPackages = input -> { + String cachedValue = cache.get(input); + if (cachedValue == null) { + final int callingUid = Binder.getCallingUid(); + final int appId = UserHandle.getAppId(input); + String[] packagesForUid = null; + for (int i = 0, size = users.length; packagesForUid == null && i < size; i++) { + packagesForUid = getPackagesForUid.apply(callingUid, users[i], appId, + false /*isCallerInstantApp*/); + } + if (packagesForUid == null) { + cachedValue = "[app id " + input + " not installed]"; + } else { + cachedValue = packagesForUid.length == 1 ? packagesForUid[0] + : "[" + TextUtils.join(",", packagesForUid) + "]"; + } + cache.put(input, cachedValue); + } + return cachedValue; + }; + pw.println(); + pw.println("Queries:"); + dumpState.onTitlePrinted(); + if (!mFeatureConfig.isGloballyEnabled()) { + pw.println(" DISABLED"); + if (!DEBUG_LOGGING) { + return; + } + } + pw.println(" system apps queryable: " + mSystemAppsQueryable); + dumpQueryables(pw, filteringAppId, users, expandPackages); + } + + protected void dumpQueryables(PrintWriter pw, @Nullable Integer filteringAppId, int[] users, + ToString<Integer> expandPackages) { + 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); + } + + private static void dumpQueriesMap(PrintWriter pw, @Nullable Integer filteringId, + WatchedSparseSetArray<Integer> queriesMap, String spacing, + @Nullable ToString<Integer> toString) { + for (int i = 0; i < queriesMap.size(); i++) { + Integer callingId = queriesMap.keyAt(i); + if (Objects.equals(callingId, filteringId)) { + // don't filter target package names if the calling is filteringId + dumpPackageSet( + pw, null /*filteringId*/, queriesMap.get(callingId), + toString == null + ? callingId.toString() + : toString.toString(callingId), + spacing, toString); + } else { + dumpPackageSet( + pw, filteringId, queriesMap.get(callingId), + toString == null + ? callingId.toString() + : toString.toString(callingId), + spacing, toString); + } + } + } + + protected interface ToString<T> { + String toString(T input); + } + + private static <T> void dumpPackageSet(PrintWriter pw, @Nullable T filteringId, + ArraySet<T> targetPkgSet, String subTitle, String spacing, + @Nullable ToString<T> toString) { + if (targetPkgSet != null && targetPkgSet.size() > 0 + && (filteringId == null || targetPkgSet.contains(filteringId))) { + pw.append(spacing).append(subTitle).println(":"); + for (T item : targetPkgSet) { + if (filteringId == null || Objects.equals(filteringId, item)) { + pw.append(spacing).append(" ") + .println(toString == null ? item : toString.toString(item)); + } + } + } + } +} diff --git a/services/core/java/com/android/server/pm/AppsFilterImpl.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java index f757405645b9..952711db1735 100644 --- a/services/core/java/com/android/server/pm/AppsFilterImpl.java +++ b/services/core/java/com/android/server/pm/AppsFilterImpl.java @@ -22,210 +22,68 @@ import static android.os.UserHandle.USER_NULL; import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE; import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; +import static com.android.server.pm.AppsFilterUtils.canQueryAsInstaller; +import static com.android.server.pm.AppsFilterUtils.canQueryViaComponents; +import static com.android.server.pm.AppsFilterUtils.canQueryViaPackage; +import static com.android.server.pm.AppsFilterUtils.canQueryViaUsesLibrary; +import static com.android.server.pm.AppsFilterUtils.requestsQueryAllPackages; -import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; -import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.SigningDetails; import android.content.pm.UserInfo; -import android.os.Binder; -import android.os.Process; import android.os.Trace; import android.os.UserHandle; import android.provider.DeviceConfig; -import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; -import android.util.SparseArray; import android.util.SparseBooleanArray; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; 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.compat.CompatChange; import com.android.server.om.OverlayReferenceMapper; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; import com.android.server.pm.pkg.PackageStateInternal; -import com.android.server.pm.pkg.component.ParsedComponent; import com.android.server.pm.pkg.component.ParsedInstrumentation; -import com.android.server.pm.pkg.component.ParsedIntentInfo; -import com.android.server.pm.pkg.component.ParsedMainComponent; -import com.android.server.pm.pkg.component.ParsedProvider; import com.android.server.pm.snapshot.PackageDataSnapshot; import com.android.server.utils.Snappable; import com.android.server.utils.SnapshotCache; 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.Collection; import java.util.List; import java.util.Objects; -import java.util.Set; -import java.util.StringTokenizer; import java.util.concurrent.Executor; /** - * The entity responsible for filtering visibility between apps based on declarations in their - * manifests. + * Implementation of the methods that update the internal structures of AppsFilter. Because of the + * mutations, all the read accesses to those internal structures need to be locked, thus extending + * {@link AppsFilterLocked}. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) -public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable { - - private static final String TAG = "AppsFilter"; - - // Logs all filtering instead of enforcing - private static final boolean DEBUG_ALLOW_ALL = false; - private static final boolean DEBUG_LOGGING = false; - private static final boolean DEBUG_TRACING = false; - - /** - * This contains a list of app UIDs that are implicitly queryable because another app explicitly - * interacted with it. For example, if application A starts a service in application B, - * application B is implicitly allowed to query for application A; regardless of any manifest - * entries. - */ - @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. - */ - @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. - */ - @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. - */ - @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. - */ - @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 - * visibility cache. - */ - private final Executor mBackgroundExecutor; - - /** - * Pending full recompute of mQueriesViaComponent. Occurs when a package adds a new set of - * protected broadcast. This in turn invalidates all prior additions and require a very - * computationally expensive recomputing. - * Full recompute is done lazily at the point when we use mQueriesViaComponent to filter apps. - */ - private boolean mQueriesViaComponentRequireRecompute = false; - - /** - * A set of App IDs that are always queryable by any package, regardless of their manifest - * content. - */ - @GuardedBy("mLock") - @Watched - 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 - * their manifest contents. - */ - private final String[] mForceQueryableByDevicePackageNames; - - /** True if all system apps should be made queryable by default. */ - private final boolean mSystemAppsQueryable; - private final FeatureConfig mFeatureConfig; - private final OverlayReferenceMapper mOverlayReferenceMapper; - private SigningDetails mSystemSigningDetails; - - @GuardedBy("mLock") - @Watched - private final WatchedArrayList<String> mProtectedBroadcasts; - private final SnapshotCache<WatchedArrayList<String>> mProtectedBroadcastsSnapshot; - - private final Object mCacheLock = new Object(); - - private final boolean mIsSnapshot; - - /** - * This structure maps uid -> uid and indicates whether access from the first should be - * filtered to the second. It's essentially a cache of the - * {@link #shouldFilterApplicationInternal(PackageDataSnapshot, 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 empty until {@link #mSystemReady} is true. - */ - @GuardedBy("mCacheLock") - @NonNull - private final WatchedSparseBooleanMatrix mShouldFilterCache; - private final SnapshotCache<WatchedSparseBooleanMatrix> mShouldFilterCacheSnapshot; - - private volatile boolean mSystemReady = false; - - /** - * Guards the accesses for the list/set fields except for {@link #mShouldFilterCache} - */ - private final Object mLock = new Object(); - +public final class AppsFilterImpl extends AppsFilterLocked implements Watchable, Snappable { /** * A cached snapshot. */ - private final SnapshotCache<AppsFilterImpl> mSnapshot; - - private SnapshotCache<AppsFilterImpl> makeCache() { - return new SnapshotCache<AppsFilterImpl>(this, this) { - @Override - public AppsFilterImpl createSnapshot() { - AppsFilterImpl s = new AppsFilterImpl(mSource); - s.mWatchable.seal(); - return s; - } - }; - } + @NonNull + private SnapshotCache<AppsFilterSnapshot> mSnapshot; /** * Watchable machinery @@ -323,47 +181,12 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable mProtectedBroadcastsSnapshot = new SnapshotCache.Auto<>( mProtectedBroadcasts, mProtectedBroadcasts, "AppsFilter.mProtectedBroadcasts"); - mSnapshot = makeCache(); - mIsSnapshot = false; - } - - /** - * The copy constructor is used by PackageManagerService to construct a snapshot. - */ - 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; - mForceQueryableByDevicePackageNames = - Arrays.copyOf(orig.mForceQueryableByDevicePackageNames, - orig.mForceQueryableByDevicePackageNames.length); - mSystemAppsQueryable = orig.mSystemAppsQueryable; - mFeatureConfig = orig.mFeatureConfig; - mOverlayReferenceMapper = orig.mOverlayReferenceMapper; - mSystemSigningDetails = orig.mSystemSigningDetails; - synchronized (orig.mCacheLock) { - mShouldFilterCache = orig.mShouldFilterCacheSnapshot.snapshot(); - mShouldFilterCacheSnapshot = new SnapshotCache.Sealed<>(); - } - - mBackgroundExecutor = null; - mSnapshot = new SnapshotCache.Sealed<>(); - mSystemReady = orig.mSystemReady; - mIsSnapshot = true; + mSnapshot = new SnapshotCache<AppsFilterSnapshot>(this, this) { + @Override + public AppsFilterSnapshot createSnapshot() { + return new AppsFilterSnapshotImpl(AppsFilterImpl.this); + } + }; } /** @@ -375,38 +198,8 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable return mSnapshot.snapshot(); } - @VisibleForTesting(visibility = PRIVATE) - public interface FeatureConfig { - - /** Called when the system is ready and components can be queried. */ - void onSystemReady(); - - /** @return true if we should filter apps at all. */ - boolean isGloballyEnabled(); - - /** @return true if the feature is enabled for the given package. */ - boolean packageIsEnabled(AndroidPackage pkg); - - /** @return true if debug logging is enabled for the given package. */ - boolean isLoggingEnabled(int appId); - - /** - * Turns on logging for the given appId - * - * @param enable true if logging should be enabled, false if disabled. - */ - void enableLogging(int appId, boolean enable); - - /** - * Initializes the package enablement state for the given package. This gives opportunity - * to do any expensive operations ahead of the actual checks. - * - * @param removed true if adding, false if removing - */ - void updatePackageState(PackageStateInternal setting, boolean removed); - } - - private static class FeatureConfigImpl implements FeatureConfig, CompatChange.ChangeListener { + private static class FeatureConfigImpl implements FeatureConfig, + CompatChange.ChangeListener { private static final String FILTERING_ENABLED_NAME = "package_query_filtering_enabled"; private final PackageManagerServiceInjector mInjector; private final PackageManagerInternal mPmInternal; @@ -424,6 +217,14 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable mInjector = injector; } + FeatureConfigImpl(FeatureConfigImpl orig) { + mInjector = null; + mPmInternal = null; + mFeatureEnabled = orig.mFeatureEnabled; + mDisabledPackages.addAll(orig.mDisabledPackages); + mLoggingEnabled = orig.mLoggingEnabled; + } + public void setAppsFilter(AppsFilterImpl filter) { mAppsFilter = filter; } @@ -535,10 +336,18 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable enableLogging(setting.getAppId(), enableLogging); if (removed) { mDisabledPackages.remove(setting.getPackageName()); + if (mAppsFilter != null) { + mAppsFilter.onChanged(); + } } else if (setting.getPkg() != null) { updateEnabledState(setting.getPkg()); } } + + @Override + public FeatureConfig snapshot() { + return new FeatureConfigImpl(this); + } } /** Builder method for an AppsFilter */ @@ -571,133 +380,6 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable return mFeatureConfig; } - /** Returns true if the querying package may query for the potential target package */ - private static boolean canQueryViaComponents(AndroidPackage querying, - AndroidPackage potentialTarget, WatchedArrayList<String> protectedBroadcasts) { - if (!querying.getQueriesIntents().isEmpty()) { - for (Intent intent : querying.getQueriesIntents()) { - if (matchesPackage(intent, potentialTarget, protectedBroadcasts)) { - return true; - } - } - } - if (!querying.getQueriesProviders().isEmpty() - && matchesProviders(querying.getQueriesProviders(), potentialTarget)) { - return true; - } - return false; - } - - private static boolean canQueryViaPackage(AndroidPackage querying, - AndroidPackage potentialTarget) { - return !querying.getQueriesPackages().isEmpty() - && querying.getQueriesPackages().contains(potentialTarget.getPackageName()); - } - - private static boolean canQueryAsInstaller(PackageStateInternal querying, - AndroidPackage potentialTarget) { - final InstallSource installSource = querying.getInstallSource(); - if (potentialTarget.getPackageName().equals(installSource.installerPackageName)) { - return true; - } - if (!installSource.isInitiatingPackageUninstalled - && potentialTarget.getPackageName().equals(installSource.initiatingPackageName)) { - return true; - } - return false; - } - - private static boolean canQueryViaUsesLibrary(AndroidPackage querying, - AndroidPackage potentialTarget) { - if (potentialTarget.getLibraryNames().isEmpty()) { - return false; - } - final List<String> libNames = potentialTarget.getLibraryNames(); - for (int i = 0, size = libNames.size(); i < size; i++) { - final String libName = libNames.get(i); - if (querying.getUsesLibraries().contains(libName) - || querying.getUsesOptionalLibraries().contains(libName)) { - return true; - } - } - return false; - } - - private static boolean matchesProviders( - Set<String> queriesAuthorities, AndroidPackage potentialTarget) { - for (int p = ArrayUtils.size(potentialTarget.getProviders()) - 1; p >= 0; p--) { - ParsedProvider provider = potentialTarget.getProviders().get(p); - if (!provider.isExported()) { - continue; - } - if (provider.getAuthority() == null) { - continue; - } - StringTokenizer authorities = new StringTokenizer(provider.getAuthority(), ";", - false); - while (authorities.hasMoreElements()) { - if (queriesAuthorities.contains(authorities.nextToken())) { - return true; - } - } - } - return false; - } - - private static boolean matchesPackage(Intent intent, AndroidPackage potentialTarget, - WatchedArrayList<String> protectedBroadcasts) { - if (matchesAnyComponents( - intent, potentialTarget.getServices(), null /*protectedBroadcasts*/)) { - return true; - } - if (matchesAnyComponents( - intent, potentialTarget.getActivities(), null /*protectedBroadcasts*/)) { - return true; - } - if (matchesAnyComponents(intent, potentialTarget.getReceivers(), protectedBroadcasts)) { - return true; - } - if (matchesAnyComponents( - intent, potentialTarget.getProviders(), null /*protectedBroadcasts*/)) { - return true; - } - return false; - } - - private static boolean matchesAnyComponents(Intent intent, - List<? extends ParsedMainComponent> components, - WatchedArrayList<String> protectedBroadcasts) { - for (int i = ArrayUtils.size(components) - 1; i >= 0; i--) { - ParsedMainComponent component = components.get(i); - if (!component.isExported()) { - continue; - } - if (matchesAnyFilter(intent, component, protectedBroadcasts)) { - return true; - } - } - return false; - } - - private static boolean matchesAnyFilter(Intent intent, ParsedComponent component, - WatchedArrayList<String> protectedBroadcasts) { - List<ParsedIntentInfo> intents = component.getIntents(); - for (int i = ArrayUtils.size(intents) - 1; i >= 0; i--) { - IntentFilter intentFilter = intents.get(i).getIntentFilter(); - if (matchesIntentFilter(intent, intentFilter, protectedBroadcasts)) { - return true; - } - } - return false; - } - - private static boolean matchesIntentFilter(Intent intent, IntentFilter intentFilter, - @Nullable WatchedArrayList<String> protectedBroadcasts) { - return intentFilter.match(intent.getAction(), intent.getType(), intent.getScheme(), - 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. @@ -921,10 +603,6 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable } } - private void updateEntireShouldFilterCache(PackageDataSnapshot snapshot) { - updateEntireShouldFilterCache(snapshot, USER_ALL); - } - private void updateEntireShouldFilterCache(PackageDataSnapshot snapshot, int subjectUserId) { final ArrayMap<String, ? extends PackageStateInternal> settings = snapshot.getPackageStates(); @@ -1110,6 +788,18 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable } } + @Override + protected boolean isQueryableViaComponentWhenRequireRecompute( + ArrayMap<String, ? extends PackageStateInternal> existingSettings, + PackageStateInternal callingPkgSetting, + ArraySet<PackageStateInternal> callingSharedPkgSettings, + AndroidPackage targetPkg, + int callingAppId, int targetAppId) { + // Recompute the whole mQueriesViaComponent because mProtectedBroadcasts have changed + recomputeComponentVisibility(existingSettings); + return isQueryableViaComponent(callingAppId, targetAppId); + } + /** * This method recomputes all component / intent-based visibility and is intended to match the * relevant logic of {@link #addPackageInternal(PackageStateInternal, ArrayMap)} @@ -1144,67 +834,6 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable } /** - * See {@link AppsFilterSnapshot#getVisibilityAllowList(PackageDataSnapshot, - * PackageStateInternal, int[], ArrayMap)} - */ - @Override - @Nullable - public SparseArray<int[]> getVisibilityAllowList(PackageDataSnapshot snapshot, - PackageStateInternal setting, int[] users, - ArrayMap<String, ? extends PackageStateInternal> existingSettings) { - 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); - for (int u = 0; u < users.length; u++) { - final int userId = users[u]; - int[] appIds = new int[existingSettings.size()]; - int[] buffer = null; - int allowListSize = 0; - for (int i = existingSettings.size() - 1; i >= 0; i--) { - final PackageStateInternal existingSetting = existingSettings.valueAt(i); - final int existingAppId = existingSetting.getAppId(); - if (existingAppId < Process.FIRST_APPLICATION_UID) { - continue; - } - final int loc = Arrays.binarySearch(appIds, 0, allowListSize, existingAppId); - if (loc >= 0) { - continue; - } - final int existingUid = UserHandle.getUid(userId, existingAppId); - if (!shouldFilterApplication(snapshot, existingUid, existingSetting, setting, - userId)) { - if (buffer == null) { - buffer = new int[appIds.length]; - } - final int insert = ~loc; - System.arraycopy(appIds, insert, buffer, 0, allowListSize - insert); - appIds[insert] = existingAppId; - System.arraycopy(buffer, 0, appIds, insert + 1, allowListSize - insert); - allowListSize++; - } - } - result.put(userId, Arrays.copyOf(appIds, allowListSize)); - } - return result; - } - - /** - * This api does type conversion on the <existingSettings> parameter. - */ - @VisibleForTesting(visibility = PRIVATE) - @Nullable - SparseArray<int[]> getVisibilityAllowList(PackageDataSnapshot snapshot, - PackageStateInternal setting, int[] users, - WatchedArrayMap<String, ? extends PackageStateInternal> existingSettings) { - return getVisibilityAllowList(snapshot, setting, users, - existingSettings.untrackedStorage()); - } - - /** * Equivalent to calling {@link #addPackage(PackageDataSnapshot, PackageStateInternal, boolean)} * with {@code isReplace} equal to {@code false}. * @@ -1339,420 +968,6 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable onChanged(); } - private ArraySet<? extends PackageStateInternal> getSharedUserPackages(int sharedUserAppId, - Collection<SharedUserSetting> sharedUserSettings) { - for (SharedUserSetting setting : sharedUserSettings) { - if (setting.mAppId != sharedUserAppId) { - continue; - } - return setting.getPackageStates(); - } - return new ArraySet<>(); - } - - /** - * See - * {@link AppsFilterSnapshot#shouldFilterApplication(PackageDataSnapshot, int, Object, - * PackageStateInternal, int)} - */ - @Override - public boolean shouldFilterApplication(PackageDataSnapshot snapshot, int callingUid, - @Nullable Object callingSetting, PackageStateInternal targetPkgSetting, int userId) { - if (DEBUG_TRACING) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplication"); - } - try { - int callingAppId = UserHandle.getAppId(callingUid); - if (callingAppId < Process.FIRST_APPLICATION_UID - || targetPkgSetting.getAppId() < Process.FIRST_APPLICATION_UID - || callingAppId == targetPkgSetting.getAppId()) { - return false; - } - if (mSystemReady) { // use cache - if (!shouldFilterApplicationUsingCache(callingUid, - targetPkgSetting.getAppId(), - userId)) { - return false; - } - } else { - if (!shouldFilterApplicationInternal(snapshot, - callingUid, callingSetting, targetPkgSetting, userId)) { - return false; - } - } - if (DEBUG_LOGGING || mFeatureConfig.isLoggingEnabled(callingAppId)) { - log(callingSetting, targetPkgSetting, "BLOCKED"); - } - return !DEBUG_ALLOW_ALL; - } finally { - if (DEBUG_TRACING) { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - } - } - - 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); - } - } - - @SuppressWarnings("GuardedBy") - private boolean shouldFilterApplicationInternal(PackageDataSnapshot snapshot, int callingUid, - Object callingSetting, PackageStateInternal targetPkgSetting, int targetUserId) { - if (DEBUG_TRACING) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplicationInternal"); - } - try { - final boolean featureEnabled = mFeatureConfig.isGloballyEnabled(); - if (!featureEnabled) { - if (DEBUG_LOGGING) { - Slog.d(TAG, "filtering disabled; skipped"); - } - return false; - } - if (callingSetting == null) { - Slog.wtf(TAG, "No setting found for non system uid " + callingUid); - return true; - } - final PackageStateInternal callingPkgSetting; - if (DEBUG_TRACING) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "callingSetting instanceof"); - } - final ArraySet<PackageStateInternal> callingSharedPkgSettings = new ArraySet<>(); - - if (callingSetting instanceof PackageStateInternal) { - final PackageStateInternal packageState = (PackageStateInternal) callingSetting; - if (packageState.hasSharedUser()) { - callingPkgSetting = null; - callingSharedPkgSettings.addAll(getSharedUserPackages( - packageState.getSharedUserAppId(), snapshot.getAllSharedUsers())); - - } else { - callingPkgSetting = packageState; - } - } else { - callingPkgSetting = null; - callingSharedPkgSettings.addAll( - ((SharedUserSetting) callingSetting).getPackageStates()); - } - if (DEBUG_TRACING) { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - - if (callingPkgSetting != null) { - if (callingPkgSetting.getPkg() != null - && !mFeatureConfig.packageIsEnabled(callingPkgSetting.getPkg())) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "DISABLED"); - } - return false; - } - } else { - for (int i = callingSharedPkgSettings.size() - 1; i >= 0; i--) { - final AndroidPackage pkg = callingSharedPkgSettings.valueAt(i).getPkg(); - if (pkg != null && !mFeatureConfig.packageIsEnabled(pkg)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "DISABLED"); - } - return false; - } - } - } - - if (DEBUG_TRACING) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "getAppId"); - } - final int callingAppId; - if (callingPkgSetting != null) { - callingAppId = callingPkgSetting.getAppId(); - } else { - // all should be the same - callingAppId = callingSharedPkgSettings.valueAt(0).getAppId(); - } - final int targetAppId = targetPkgSetting.getAppId(); - if (DEBUG_TRACING) { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - if (callingAppId == targetAppId) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "same app id"); - } - return false; - } - - try { - if (DEBUG_TRACING) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "requestsQueryAllPackages"); - } - if (callingPkgSetting != null) { - 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(); - if (pkg != null && requestsQueryAllPackages(pkg)) { - return false; - } - } - } - } finally { - if (DEBUG_TRACING) { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - } - - // This package isn't technically installed and won't be written to settings, so we can - // treat it as filtered until it's available again. - final AndroidPackage targetPkg = targetPkgSetting.getPkg(); - if (targetPkg == null) { - if (DEBUG_LOGGING) { - Slog.wtf(TAG, "shouldFilterApplication: " + "targetPkg is null"); - } - return true; - } - if (targetPkg.isStaticSharedLibrary()) { - // not an app, this filtering takes place at a higher level - return false; - } - - try { - if (DEBUG_TRACING) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mForceQueryable"); - } - synchronized (mLock) { - if (mForceQueryable.contains(targetAppId)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "force queryable"); - } - return false; - } - } - } finally { - if (DEBUG_TRACING) { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - } - try { - if (DEBUG_TRACING) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueriesViaPackage"); - } - synchronized (mLock) { - 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); - } - } - try { - if (DEBUG_TRACING) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueriesViaComponent"); - } - if (!mQueriesViaComponentRequireRecompute) { - synchronized (mLock) { - if (mQueriesViaComponent.contains(callingAppId, targetAppId)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "queries component"); - } - return false; - } - } - } else { // mQueriesViaComponent is stale - if (!mIsSnapshot) { - // Only recompute mQueriesViaComponent if not in snapshot - recomputeComponentVisibility(snapshot.getPackageStates()); - synchronized (mLock) { - if (mQueriesViaComponent.contains(callingAppId, targetAppId)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "queries component"); - } - return false; - } - } - } else { - // Do no recompute or use mQueriesViaComponent if it's stale in snapshot - // Since we know we are in the snapshot, no need to acquire mLock because - // mProtectedBroadcasts will not change - if (callingPkgSetting != null) { - if (callingPkgSetting.getPkg() != null - && canQueryViaComponents(callingPkgSetting.getPkg(), targetPkg, - mProtectedBroadcasts)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "queries component"); - } - return false; - } - } else { - for (int i = callingSharedPkgSettings.size() - 1; i >= 0; i--) { - final AndroidPackage pkg = - callingSharedPkgSettings.valueAt(i).getPkg(); - if (pkg != null && canQueryViaComponents(pkg, targetPkg, - mProtectedBroadcasts)) { - 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); - synchronized (mLock) { - 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); - } - } - - try { - if (DEBUG_TRACING) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mRetainedImplicitlyQueryable"); - } - final int targetUid = UserHandle.getUid(targetUserId, targetAppId); - synchronized (mLock) { - 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); - } - } - - try { - if (DEBUG_TRACING) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mOverlayReferenceMapper"); - } - final String targetName = targetPkg.getPackageName(); - if (!callingSharedPkgSettings.isEmpty()) { - 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, - callingPkgSetting.getPackageName())) { - if (DEBUG_LOGGING) { - log(callingPkgSetting, targetPkgSetting, "acts on target of overlay"); - } - return false; - } - } - } finally { - if (DEBUG_TRACING) { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - } - - try { - if (DEBUG_TRACING) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueryableViaUsesLibrary"); - } - synchronized (mLock) { - if (mQueryableViaUsesLibrary.contains(callingAppId, targetAppId)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "queryable for library users"); - } - return false; - } - } - } finally { - if (DEBUG_TRACING) { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - } - - return true; - } finally { - if (DEBUG_TRACING) { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - } - } - - /** - * 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; - } - - // Check if FILTER_APPLICATION_QUERY is enabled on the given package. - if (!mFeatureConfig.packageIsEnabled(querying)) { - return true; - } - - if (requestsQueryAllPackages(querying)) { - return true; - } - - return !querying.getQueriesPackages().isEmpty() - && querying.getQueriesPackages().contains(potentialTarget); - } - - private static boolean requestsQueryAllPackages(@NonNull AndroidPackage pkg) { - // we're not guaranteed to have permissions yet analyzed at package add, so we inspect the - // package directly - return pkg.getRequestedPermissions().contains( - Manifest.permission.QUERY_ALL_PACKAGES); - } - /** Returns {@code true} if the source package instruments the target package. */ private static boolean pkgInstruments( @NonNull AndroidPackage source, @NonNull AndroidPackage target) { @@ -1774,117 +989,4 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable } } } - - private static void log(Object callingSetting, PackageStateInternal targetPkgSetting, - String description) { - Slog.i(TAG, - "interaction: " + (callingSetting == null ? "system" : callingSetting) + " -> " - + 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) { - final SparseArray<String> cache = new SparseArray<>(); - ToString<Integer> expandPackages = input -> { - String cachedValue = cache.get(input); - if (cachedValue == null) { - final int callingUid = Binder.getCallingUid(); - final int appId = UserHandle.getAppId(input); - String[] packagesForUid = null; - for (int i = 0, size = users.length; packagesForUid == null && i < size; i++) { - packagesForUid = getPackagesForUid.apply(callingUid, users[i], appId, - false /*isCallerInstantApp*/); - } - if (packagesForUid == null) { - cachedValue = "[app id " + input + " not installed]"; - } else { - cachedValue = packagesForUid.length == 1 ? packagesForUid[0] - : "[" + TextUtils.join(",", packagesForUid) + "]"; - } - cache.put(input, cachedValue); - } - return cachedValue; - }; - pw.println(); - pw.println("Queries:"); - dumpState.onTitlePrinted(); - if (!mFeatureConfig.isGloballyEnabled()) { - pw.println(" DISABLED"); - if (!DEBUG_LOGGING) { - return; - } - } - pw.println(" system apps queryable: " + mSystemAppsQueryable); - dumpPackageSet(pw, filteringAppId, mForceQueryable.untrackedStorage(), - "forceQueryable", " ", expandPackages); - synchronized (mLock) { - 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); - } - } - - private static void dumpQueriesMap(PrintWriter pw, @Nullable Integer filteringId, - WatchedSparseSetArray<Integer> queriesMap, String spacing, - @Nullable ToString<Integer> toString) { - for (int i = 0; i < queriesMap.size(); i++) { - Integer callingId = queriesMap.keyAt(i); - if (Objects.equals(callingId, filteringId)) { - // don't filter target package names if the calling is filteringId - dumpPackageSet( - pw, null /*filteringId*/, queriesMap.get(callingId), - toString == null - ? callingId.toString() - : toString.toString(callingId), - spacing, toString); - } else { - dumpPackageSet( - pw, filteringId, queriesMap.get(callingId), - toString == null - ? callingId.toString() - : toString.toString(callingId), - spacing, toString); - } - } - } - - private interface ToString<T> { - String toString(T input); - - } - - private static <T> void dumpPackageSet(PrintWriter pw, @Nullable T filteringId, - ArraySet<T> targetPkgSet, String subTitle, String spacing, - @Nullable ToString<T> toString) { - if (targetPkgSet != null && targetPkgSet.size() > 0 - && (filteringId == null || targetPkgSet.contains(filteringId))) { - pw.append(spacing).append(subTitle).println(":"); - for (T item : targetPkgSet) { - if (filteringId == null || Objects.equals(filteringId, item)) { - pw.append(spacing).append(" ") - .println(toString == null ? item : toString.toString(item)); - } - } - } - } } diff --git a/services/core/java/com/android/server/pm/AppsFilterLocked.java b/services/core/java/com/android/server/pm/AppsFilterLocked.java new file mode 100644 index 000000000000..e8e6cd9e8edb --- /dev/null +++ b/services/core/java/com/android/server/pm/AppsFilterLocked.java @@ -0,0 +1,93 @@ +/* + * 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.Nullable; + +import java.io.PrintWriter; + +/** + * Overrides the unlocked methods in {@link AppsFilterBase} and guards them with locks. + * These are used by {@link AppsFilterImpl} which contains modifications to the class members + */ +abstract class AppsFilterLocked extends AppsFilterBase { + /** + * Guards the accesses for the list/set class members + */ + protected final Object mLock = new Object(); + /** + * Guards the access for {@link AppsFilterBase#mShouldFilterCache}; + */ + protected Object mCacheLock = new Object(); + + @Override + protected boolean isForceQueryable(int appId) { + synchronized (mLock) { + return super.isForceQueryable(appId); + } + } + + @Override + protected boolean isQueryableViaPackage(int callingAppId, int targetAppId) { + synchronized (mLock) { + return super.isQueryableViaPackage(callingAppId, targetAppId); + } + } + + @Override + protected boolean isQueryableViaComponent(int callingAppId, int targetAppId) { + synchronized (mLock) { + return super.isQueryableViaComponent(callingAppId, targetAppId); + } + } + + @Override + protected boolean isImplicitlyQueryable(int callingAppId, int targetAppId) { + synchronized (mLock) { + return super.isImplicitlyQueryable(callingAppId, targetAppId); + } + } + + @Override + protected boolean isRetainedImplicitlyQueryable(int callingAppId, int targetAppId) { + synchronized (mLock) { + return super.isRetainedImplicitlyQueryable(callingAppId, targetAppId); + } + } + + @Override + protected boolean isQueryableViaUsesLibrary(int callingAppId, int targetAppId) { + synchronized (mLock) { + return super.isQueryableViaUsesLibrary(callingAppId, targetAppId); + } + } + + @Override + protected boolean shouldFilterApplicationUsingCache(int callingUid, int appId, int userId) { + synchronized (mCacheLock) { + return super.shouldFilterApplicationUsingCache(callingUid, appId, userId); + } + } + + @Override + protected void dumpQueryables(PrintWriter pw, @Nullable Integer filteringAppId, int[] users, + ToString<Integer> expandPackages) { + synchronized (mLock) { + dumpQueryables(pw, filteringAppId, users, expandPackages); + } + } +} diff --git a/services/core/java/com/android/server/pm/AppsFilterSnapshotImpl.java b/services/core/java/com/android/server/pm/AppsFilterSnapshotImpl.java new file mode 100644 index 000000000000..c12aa6d485a4 --- /dev/null +++ b/services/core/java/com/android/server/pm/AppsFilterSnapshotImpl.java @@ -0,0 +1,60 @@ +/* + * 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 com.android.server.utils.SnapshotCache; + +import java.util.Arrays; + +/** + * Implements the copy-constructor that generates a snapshot of AppsFilter + */ +public final class AppsFilterSnapshotImpl extends AppsFilterBase { + AppsFilterSnapshotImpl(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; + mForceQueryableByDevicePackageNames = + Arrays.copyOf(orig.mForceQueryableByDevicePackageNames, + orig.mForceQueryableByDevicePackageNames.length); + mSystemAppsQueryable = orig.mSystemAppsQueryable; + mFeatureConfig = orig.mFeatureConfig.snapshot(); + mOverlayReferenceMapper = orig.mOverlayReferenceMapper; + mSystemSigningDetails = orig.mSystemSigningDetails; + synchronized (orig.mCacheLock) { + mShouldFilterCache = orig.mShouldFilterCacheSnapshot.snapshot(); + mShouldFilterCacheSnapshot = new SnapshotCache.Sealed<>(); + } + + mBackgroundExecutor = null; + mSystemReady = orig.mSystemReady; + } +} diff --git a/services/core/java/com/android/server/pm/AppsFilterUtils.java b/services/core/java/com/android/server/pm/AppsFilterUtils.java new file mode 100644 index 000000000000..3a105c09d92a --- /dev/null +++ b/services/core/java/com/android/server/pm/AppsFilterUtils.java @@ -0,0 +1,172 @@ +/* + * 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.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Intent; +import android.content.IntentFilter; + +import com.android.internal.util.ArrayUtils; +import com.android.server.pm.parsing.pkg.AndroidPackage; +import com.android.server.pm.pkg.PackageStateInternal; +import com.android.server.pm.pkg.component.ParsedComponent; +import com.android.server.pm.pkg.component.ParsedIntentInfo; +import com.android.server.pm.pkg.component.ParsedMainComponent; +import com.android.server.pm.pkg.component.ParsedProvider; +import com.android.server.utils.WatchedArrayList; + +import java.util.List; +import java.util.Set; +import java.util.StringTokenizer; + +final class AppsFilterUtils { + public static boolean requestsQueryAllPackages(@NonNull AndroidPackage pkg) { + // we're not guaranteed to have permissions yet analyzed at package add, so we inspect the + // package directly + return pkg.getRequestedPermissions().contains( + Manifest.permission.QUERY_ALL_PACKAGES); + } + + /** Returns true if the querying package may query for the potential target package */ + public static boolean canQueryViaComponents(AndroidPackage querying, + AndroidPackage potentialTarget, WatchedArrayList<String> protectedBroadcasts) { + if (!querying.getQueriesIntents().isEmpty()) { + for (Intent intent : querying.getQueriesIntents()) { + if (matchesPackage(intent, potentialTarget, protectedBroadcasts)) { + return true; + } + } + } + if (!querying.getQueriesProviders().isEmpty() + && matchesProviders(querying.getQueriesProviders(), potentialTarget)) { + return true; + } + return false; + } + + public static boolean canQueryViaPackage(AndroidPackage querying, + AndroidPackage potentialTarget) { + return !querying.getQueriesPackages().isEmpty() + && querying.getQueriesPackages().contains(potentialTarget.getPackageName()); + } + + public static boolean canQueryAsInstaller(PackageStateInternal querying, + AndroidPackage potentialTarget) { + final InstallSource installSource = querying.getInstallSource(); + if (potentialTarget.getPackageName().equals(installSource.installerPackageName)) { + return true; + } + if (!installSource.isInitiatingPackageUninstalled + && potentialTarget.getPackageName().equals(installSource.initiatingPackageName)) { + return true; + } + return false; + } + + public static boolean canQueryViaUsesLibrary(AndroidPackage querying, + AndroidPackage potentialTarget) { + if (potentialTarget.getLibraryNames().isEmpty()) { + return false; + } + final List<String> libNames = potentialTarget.getLibraryNames(); + for (int i = 0, size = libNames.size(); i < size; i++) { + final String libName = libNames.get(i); + if (querying.getUsesLibraries().contains(libName) + || querying.getUsesOptionalLibraries().contains(libName)) { + return true; + } + } + return false; + } + + private static boolean matchesProviders( + Set<String> queriesAuthorities, AndroidPackage potentialTarget) { + for (int p = ArrayUtils.size(potentialTarget.getProviders()) - 1; p >= 0; p--) { + ParsedProvider provider = potentialTarget.getProviders().get(p); + if (!provider.isExported()) { + continue; + } + if (provider.getAuthority() == null) { + continue; + } + StringTokenizer authorities = new StringTokenizer(provider.getAuthority(), ";", + false); + while (authorities.hasMoreElements()) { + if (queriesAuthorities.contains(authorities.nextToken())) { + return true; + } + } + } + return false; + } + + private static boolean matchesPackage(Intent intent, AndroidPackage potentialTarget, + WatchedArrayList<String> protectedBroadcasts) { + if (matchesAnyComponents( + intent, potentialTarget.getServices(), null /*protectedBroadcasts*/)) { + return true; + } + if (matchesAnyComponents( + intent, potentialTarget.getActivities(), null /*protectedBroadcasts*/)) { + return true; + } + if (matchesAnyComponents(intent, potentialTarget.getReceivers(), protectedBroadcasts)) { + return true; + } + if (matchesAnyComponents( + intent, potentialTarget.getProviders(), null /*protectedBroadcasts*/)) { + return true; + } + return false; + } + + private static boolean matchesAnyComponents(Intent intent, + List<? extends ParsedMainComponent> components, + WatchedArrayList<String> protectedBroadcasts) { + for (int i = ArrayUtils.size(components) - 1; i >= 0; i--) { + ParsedMainComponent component = components.get(i); + if (!component.isExported()) { + continue; + } + if (matchesAnyFilter(intent, component, protectedBroadcasts)) { + return true; + } + } + return false; + } + + private static boolean matchesAnyFilter(Intent intent, ParsedComponent component, + WatchedArrayList<String> protectedBroadcasts) { + List<ParsedIntentInfo> intents = component.getIntents(); + for (int i = ArrayUtils.size(intents) - 1; i >= 0; i--) { + IntentFilter intentFilter = intents.get(i).getIntentFilter(); + if (matchesIntentFilter(intent, intentFilter, protectedBroadcasts)) { + return true; + } + } + return false; + } + + private static boolean matchesIntentFilter(Intent intent, IntentFilter intentFilter, + @Nullable WatchedArrayList<String> protectedBroadcasts) { + return intentFilter.match(intent.getAction(), intent.getType(), intent.getScheme(), + intent.getData(), intent.getCategories(), "AppsFilter", true, + protectedBroadcasts != null ? protectedBroadcasts.untrackedStorage() : null) > 0; + } +} diff --git a/services/core/java/com/android/server/pm/FeatureConfig.java b/services/core/java/com/android/server/pm/FeatureConfig.java new file mode 100644 index 000000000000..6e356de5083e --- /dev/null +++ b/services/core/java/com/android/server/pm/FeatureConfig.java @@ -0,0 +1,56 @@ +/* + * 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 static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.pm.parsing.pkg.AndroidPackage; +import com.android.server.pm.pkg.PackageStateInternal; + +@VisibleForTesting(visibility = PRIVATE) +interface FeatureConfig { + + /** Called when the system is ready and components can be queried. */ + void onSystemReady(); + + /** @return true if we should filter apps at all. */ + boolean isGloballyEnabled(); + + /** @return true if the feature is enabled for the given package. */ + boolean packageIsEnabled(AndroidPackage pkg); + + /** @return true if debug logging is enabled for the given package. */ + boolean isLoggingEnabled(int appId); + + /** + * Turns on logging for the given appId + * + * @param enable true if logging should be enabled, false if disabled. + */ + void enableLogging(int appId, boolean enable); + + /** + * Initializes the package enablement state for the given package. This gives opportunity + * to do any expensive operations ahead of the actual checks. + * + * @param removed true if adding, false if removing + */ + void updatePackageState(PackageStateInternal setting, boolean removed); + + FeatureConfig snapshot(); +} 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 55745cd254a4..63939c9f2695 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt @@ -195,8 +195,9 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { val packageParser: PackageParser2 = mock() val keySetManagerService: KeySetManagerService = mock() val packageAbiHelper: PackageAbiHelper = mock() + val appsFilterSnapshot: AppsFilterSnapshotImpl = mock() val appsFilter: AppsFilterImpl = mock { - whenever(snapshot()) { this@mock } + whenever(snapshot()) { appsFilterSnapshot } } val dexManager: DexManager = mock() val installer: Installer = mock() @@ -332,6 +333,9 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { // everything visible by default whenever(mocks.appsFilter.shouldFilterApplication(any(PackageDataSnapshot::class.java), anyInt(), nullable(), nullable(), anyInt())) { false } + whenever(mocks.appsFilterSnapshot.shouldFilterApplication( + any(PackageDataSnapshot::class.java), + anyInt(), nullable(), nullable(), anyInt())) { false } val displayManager: DisplayManager = mock() whenever(mocks.context.getSystemService(DisplayManager::class.java)) diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java index c43e6ab6658a..7974718512a8 100644 --- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java @@ -99,7 +99,7 @@ public class AppsFilterImplTest { } @Mock - AppsFilterImpl.FeatureConfig mFeatureConfigMock; + FeatureConfig mFeatureConfigMock; @Mock PackageDataSnapshot mSnapshot; @Mock @@ -1283,6 +1283,7 @@ public class AppsFilterImplTest { @Test public void testAppsFilterRead() throws Exception { + when(mFeatureConfigMock.snapshot()).thenReturn(mFeatureConfigMock); final AppsFilterImpl appsFilter = new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null, mMockExecutor); |