summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author TreeHugger Robot <treehugger-gerrit@google.com> 2022-05-03 17:26:33 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2022-05-03 17:26:33 +0000
commit4c71401de5aea3cab2c74e22b24a0d3c0a97c82d (patch)
tree09c57217742106ab706d4f71939a3a9e025f2d57
parente26bcbb81185c937a2e5bb67d32c18350486ceca (diff)
parent6b941dcc3b5b3613d2d7b0581e1133db00568220 (diff)
Merge "[AppsFilter] separate classes for read and write" into tm-dev
-rw-r--r--services/core/java/com/android/server/pm/AppsFilterBase.java773
-rw-r--r--services/core/java/com/android/server/pm/AppsFilterImpl.java992
-rw-r--r--services/core/java/com/android/server/pm/AppsFilterLocked.java93
-rw-r--r--services/core/java/com/android/server/pm/AppsFilterSnapshotImpl.java60
-rw-r--r--services/core/java/com/android/server/pm/AppsFilterUtils.java172
-rw-r--r--services/core/java/com/android/server/pm/FeatureConfig.java56
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt6
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java3
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);