diff --git a/services/core/java/com/android/server/pm/AppsFilterBase.java b/services/core/java/com/android/server/pm/AppsFilterBase.java index b4792c65bce54..1021e07cf9464 100644 --- a/services/core/java/com/android/server/pm/AppsFilterBase.java +++ b/services/core/java/com/android/server/pm/AppsFilterBase.java @@ -41,6 +41,7 @@ import com.android.internal.util.function.QuadFunction; import com.android.server.om.OverlayReferenceMapper; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; +import com.android.server.pm.pkg.SharedUserApi; import com.android.server.pm.snapshot.PackageDataSnapshot; import com.android.server.utils.SnapshotCache; import com.android.server.utils.Watched; @@ -51,7 +52,6 @@ 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.atomic.AtomicBoolean; @@ -137,10 +137,9 @@ public abstract class AppsFilterBase implements AppsFilterSnapshot { protected SnapshotCache> mQueryableViaUsesPermissionSnapshot; /** - * Handler for running reasonably short background tasks such as building the initial - * visibility cache. + * Handler for running tasks such as building the initial visibility cache. */ - protected Handler mBackgroundHandler; + protected Handler mHandler; /** * Pending full recompute of mQueriesViaComponent. Occurs when a package adds a new set of @@ -410,9 +409,11 @@ public abstract class AppsFilterBase implements AppsFilterSnapshot { final PackageStateInternal packageState = (PackageStateInternal) callingSetting; if (packageState.hasSharedUser()) { callingPkgSetting = null; - callingSharedPkgSettings.addAll(getSharedUserPackages( - packageState.getSharedUserAppId(), snapshot.getAllSharedUsers())); - + final SharedUserApi sharedUserApi = + snapshot.getSharedUser(packageState.getSharedUserAppId()); + if (sharedUserApi != null) { + callingSharedPkgSettings.addAll(sharedUserApi.getPackageStates()); + } } else { callingPkgSetting = packageState; } @@ -699,17 +700,6 @@ public abstract class AppsFilterBase implements AppsFilterSnapshot { + targetPkgSetting + " " + description); } - protected ArraySet getSharedUserPackages(int sharedUserAppId, - Collection 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)} diff --git a/services/core/java/com/android/server/pm/AppsFilterImpl.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java index 5b837f1daa6fb..5ff1909b54e4f 100644 --- a/services/core/java/com/android/server/pm/AppsFilterImpl.java +++ b/services/core/java/com/android/server/pm/AppsFilterImpl.java @@ -35,7 +35,6 @@ 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.annotation.NonNull; import android.annotation.Nullable; @@ -63,9 +62,11 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.server.FgThread; import com.android.server.compat.CompatChange; import com.android.server.om.OverlayReferenceMapper; +import com.android.server.pm.AppsFilterUtils.ParallelComputeComponentVisibility; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; +import com.android.server.pm.pkg.SharedUserApi; import com.android.server.pm.pkg.component.ParsedInstrumentation; import com.android.server.pm.pkg.component.ParsedPermission; import com.android.server.pm.pkg.component.ParsedUsesPermission; @@ -80,7 +81,6 @@ import com.android.server.utils.Watcher; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.List; import java.util.Objects; @@ -185,13 +185,13 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable, String[] forceQueryableList, boolean systemAppsQueryable, @Nullable OverlayReferenceMapper.Provider overlayProvider, - Handler backgroundHandler) { + Handler handler) { mFeatureConfig = featureConfig; mForceQueryableByDevicePackageNames = forceQueryableList; mSystemAppsQueryable = systemAppsQueryable; mOverlayReferenceMapper = new OverlayReferenceMapper(true /*deferRebuild*/, overlayProvider); - mBackgroundHandler = backgroundHandler; + mHandler = handler; mShouldFilterCache = new WatchedSparseBooleanMatrix(); mShouldFilterCacheSnapshot = new SnapshotCache.Auto<>( mShouldFilterCache, mShouldFilterCache, "AppsFilter.mShouldFilterCache"); @@ -428,7 +428,7 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable, } AppsFilterImpl appsFilter = new AppsFilterImpl(featureConfig, forcedQueryablePackageNames, forceSystemAppsQueryable, null, - injector.getBackgroundHandler()); + injector.getHandler()); featureConfig.setAppsFilter(appsFilter); return appsFilter; } @@ -797,7 +797,7 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable, private void updateEntireShouldFilterCacheAsync(PackageManagerInternal pmInternal, long delayMs, int reason) { - mBackgroundHandler.postDelayed(() -> { + mHandler.postDelayed(() -> { if (!mCacheValid.compareAndSet(CACHE_INVALID, CACHE_VALID)) { // Cache is already valid. return; @@ -990,34 +990,15 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable, */ private void recomputeComponentVisibility( ArrayMap existingSettings) { + final WatchedArraySet protectedBroadcasts; + synchronized (mProtectedBroadcastsLock) { + protectedBroadcasts = mProtectedBroadcasts.snapshot(); + } + final ParallelComputeComponentVisibility computer = new ParallelComputeComponentVisibility( + existingSettings, mForceQueryable, protectedBroadcasts); synchronized (mQueriesViaComponentLock) { mQueriesViaComponent.clear(); - } - for (int i = existingSettings.size() - 1; i >= 0; i--) { - PackageStateInternal setting = existingSettings.valueAt(i); - if (setting.getPkg() == null || requestsQueryAllPackages(setting.getPkg())) { - continue; - } - for (int j = existingSettings.size() - 1; j >= 0; j--) { - if (i == j) { - continue; - } - final PackageStateInternal otherSetting = existingSettings.valueAt(j); - if (otherSetting.getPkg() == null || mForceQueryable.contains( - otherSetting.getAppId())) { - continue; - } - final boolean canQueryViaComponents; - synchronized (mProtectedBroadcastsLock) { - canQueryViaComponents = canQueryViaComponents(setting.getPkg(), - otherSetting.getPkg(), mProtectedBroadcasts); - } - if (canQueryViaComponents) { - synchronized (mQueriesViaComponentLock) { - mQueriesViaComponent.add(setting.getAppId(), otherSetting.getAppId()); - } - } - } + computer.execute(mQueriesViaComponent); } mQueriesViaComponentRequireRecompute.set(false); @@ -1066,7 +1047,6 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable, final ArrayMap settings = snapshot.getPackageStates(); final UserInfo[] users = snapshot.getUserInfos(); - final Collection sharedUserSettings = snapshot.getAllSharedUsers(); final int userCount = users.length; if (!isReplace || !retainImplicitGrantOnReplace) { synchronized (mImplicitlyQueryableLock) { @@ -1175,9 +1155,11 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable, // shared user members to re-establish visibility between them and other packages. // NOTE: this must come after all removals from data structures but before we update the // cache - if (setting.hasSharedUser()) { + final SharedUserApi sharedUserApi = setting.hasSharedUser() + ? snapshot.getSharedUser(setting.getSharedUserAppId()) : null; + if (sharedUserApi != null) { final ArraySet sharedUserPackages = - getSharedUserPackages(setting.getSharedUserAppId(), sharedUserSettings); + sharedUserApi.getPackageStates(); for (int i = sharedUserPackages.size() - 1; i >= 0; i--) { if (sharedUserPackages.valueAt(i) == setting) { continue; @@ -1190,9 +1172,9 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable, if (mCacheReady) { removeAppIdFromVisibilityCache(setting.getAppId()); - if (setting.hasSharedUser()) { + if (sharedUserApi != null) { final ArraySet sharedUserPackages = - getSharedUserPackages(setting.getSharedUserAppId(), sharedUserSettings); + sharedUserApi.getPackageStates(); for (int i = sharedUserPackages.size() - 1; i >= 0; i--) { PackageStateInternal siblingSetting = sharedUserPackages.valueAt(i); diff --git a/services/core/java/com/android/server/pm/AppsFilterSnapshotImpl.java b/services/core/java/com/android/server/pm/AppsFilterSnapshotImpl.java index 4e268a277102b..9bfcabb11fbaf 100644 --- a/services/core/java/com/android/server/pm/AppsFilterSnapshotImpl.java +++ b/services/core/java/com/android/server/pm/AppsFilterSnapshotImpl.java @@ -77,6 +77,6 @@ public final class AppsFilterSnapshotImpl extends AppsFilterBase { mCacheEnabled = orig.mCacheEnabled; mShouldFilterCacheSnapshot = new SnapshotCache.Sealed<>(); - mBackgroundHandler = null; + mHandler = null; } } diff --git a/services/core/java/com/android/server/pm/AppsFilterUtils.java b/services/core/java/com/android/server/pm/AppsFilterUtils.java index bf28479e74192..8da1d217b88a7 100644 --- a/services/core/java/com/android/server/pm/AppsFilterUtils.java +++ b/services/core/java/com/android/server/pm/AppsFilterUtils.java @@ -16,24 +16,36 @@ package com.android.server.pm; +import static android.os.Process.THREAD_PRIORITY_DEFAULT; + import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Intent; import android.content.IntentFilter; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Pair; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.ConcurrentUtils; import com.android.server.pm.pkg.AndroidPackage; +import com.android.server.pm.pkg.PackageState; 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.WatchedArraySet; +import com.android.server.utils.WatchedSparseSetArray; +import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.StringTokenizer; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; final class AppsFilterUtils { public static boolean requestsQueryAllPackages(@NonNull AndroidPackage pkg) { @@ -169,4 +181,93 @@ final class AppsFilterUtils { intent.getData(), intent.getCategories(), "AppsFilter", true, protectedBroadcasts != null ? protectedBroadcasts.untrackedStorage() : null) > 0; } + + /** + * A helper class for parallel computing of component visibility of all packages on the device. + */ + public static final class ParallelComputeComponentVisibility { + private static final int MAX_THREADS = 4; + + private final ArrayMap mExistingSettings; + private final WatchedArraySet mForceQueryable; + private final WatchedArraySet mProtectedBroadcasts; + + ParallelComputeComponentVisibility( + @NonNull ArrayMap existingSettings, + @NonNull WatchedArraySet forceQueryable, + @NonNull WatchedArraySet protectedBroadcasts) { + mExistingSettings = existingSettings; + mForceQueryable = forceQueryable; + mProtectedBroadcasts = protectedBroadcasts; + } + + /** + * Computes component visibility of all packages in parallel from a thread pool. + */ + void execute(@NonNull WatchedSparseSetArray outQueriesViaComponent) { + final ExecutorService pool = ConcurrentUtils.newFixedThreadPool( + MAX_THREADS, ParallelComputeComponentVisibility.class.getSimpleName(), + THREAD_PRIORITY_DEFAULT); + try { + final List>>> futures = + new ArrayList<>(); + for (int i = mExistingSettings.size() - 1; i >= 0; i--) { + final PackageStateInternal setting = mExistingSettings.valueAt(i); + final AndroidPackage pkg = setting.getPkg(); + if (pkg == null || requestsQueryAllPackages(pkg)) { + continue; + } + if (pkg.getQueriesIntents().isEmpty() + && pkg.getQueriesProviders().isEmpty()) { + continue; + } + futures.add(new Pair(setting, + pool.submit(() -> getVisibleListOfQueryViaComponents(setting)))); + } + for (int i = 0; i < futures.size(); i++) { + final int appId = futures.get(i).first.getAppId(); + final Future> future = futures.get(i).second; + try { + final ArraySet visibleList = future.get(); + if (visibleList.size() != 0) { + outQueriesViaComponent.addAll(appId, visibleList); + } + } catch (InterruptedException | ExecutionException e) { + throw new IllegalStateException(e); + } + } + } finally { + pool.shutdownNow(); + } + } + + /** + * Returns a set of app IDs that contains components resolved by the queries intent + * or provider that declared in the manifest of the querying package. + * + * @param setting The package to query. + * @return A set of app IDs. + */ + @NonNull + private ArraySet getVisibleListOfQueryViaComponents( + @NonNull PackageStateInternal setting) { + final ArraySet result = new ArraySet(); + for (int i = mExistingSettings.size() - 1; i >= 0; i--) { + final PackageStateInternal otherSetting = mExistingSettings.valueAt(i); + if (setting.getAppId() == otherSetting.getAppId()) { + continue; + } + if (otherSetting.getPkg() == null || mForceQueryable.contains( + otherSetting.getAppId())) { + continue; + } + final boolean canQuery = canQueryViaComponents( + setting.getPkg(), otherSetting.getPkg(), mProtectedBroadcasts); + if (canQuery) { + result.add(otherSetting.getAppId()); + } + } + return result; + } + } } diff --git a/services/core/java/com/android/server/utils/WatchedSparseSetArray.java b/services/core/java/com/android/server/utils/WatchedSparseSetArray.java index 77750ed92273d..0386e66aaf3df 100644 --- a/services/core/java/com/android/server/utils/WatchedSparseSetArray.java +++ b/services/core/java/com/android/server/utils/WatchedSparseSetArray.java @@ -64,6 +64,14 @@ public class WatchedSparseSetArray extends WatchableImpl implements Snappable return res; } + /** + * Add a set of values for key n. + */ + public void addAll(int n, ArraySet values) { + mStorage.addAll(n, values); + onChanged(); + } + /** * Removes all mappings from this SparseSetArray. */ diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java index 3575b57934caa..7c4b9f8688d18 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java @@ -24,6 +24,7 @@ import static org.hamcrest.Matchers.contains; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -225,6 +226,13 @@ public class AppsFilterImplTest { when(mSnapshot.getPackageStates()).thenAnswer(x -> mExisting); when(mSnapshot.getAllSharedUsers()).thenReturn(mSharedUserSettings); when(mSnapshot.getUserInfos()).thenReturn(USER_INFO_LIST); + when(mSnapshot.getSharedUser(anyInt())).thenAnswer(invocation -> { + final int sharedUserAppId = invocation.getArgument(0); + return mSharedUserSettings.stream() + .filter(sharedUserSetting -> sharedUserSetting.getAppId() == sharedUserAppId) + .findAny() + .orElse(null); + }); when(mPmInternal.snapshot()).thenReturn(mSnapshot); // Can't mock postDelayed because of some weird bug in Mockito.