Merge changes I301a7f78,Id9dbd4e5

* changes:
  Improve the cache rebuild latency of AppsFilter on boot #2
  Improve the cache rebuild latency of AppsFilter on boot
diff --git a/services/core/java/com/android/server/pm/AppsFilterBase.java b/services/core/java/com/android/server/pm/AppsFilterBase.java
index b4792c6..1021e07 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.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 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 @@
     protected SnapshotCache<WatchedSparseSetArray<Integer>> 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 @@
                 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 @@
                         + 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)}
diff --git a/services/core/java/com/android/server/pm/AppsFilterImpl.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java
index 5b837f1..5ff1909b 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.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.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 java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.List;
 import java.util.Objects;
 
@@ -185,13 +185,13 @@
             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 @@
         }
         AppsFilterImpl appsFilter = new AppsFilterImpl(featureConfig,
                 forcedQueryablePackageNames, forceSystemAppsQueryable, null,
-                injector.getBackgroundHandler());
+                injector.getHandler());
         featureConfig.setAppsFilter(appsFilter);
         return appsFilter;
     }
@@ -797,7 +797,7 @@
 
     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 @@
      */
     private void recomputeComponentVisibility(
             ArrayMap<String, ? extends PackageStateInternal> existingSettings) {
+        final WatchedArraySet<String> 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 @@
         final ArrayMap<String, ? extends PackageStateInternal> settings =
                 snapshot.getPackageStates();
         final UserInfo[] users = snapshot.getUserInfos();
-        final Collection<SharedUserSetting> sharedUserSettings = snapshot.getAllSharedUsers();
         final int userCount = users.length;
         if (!isReplace || !retainImplicitGrantOnReplace) {
             synchronized (mImplicitlyQueryableLock) {
@@ -1175,9 +1155,11 @@
         // 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<? extends PackageStateInternal> 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 @@
         if (mCacheReady) {
             removeAppIdFromVisibilityCache(setting.getAppId());
 
-            if (setting.hasSharedUser()) {
+            if (sharedUserApi != null) {
                 final ArraySet<? extends PackageStateInternal> 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 4e268a2..9bfcabb 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 @@
         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 bf28479..8da1d21 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 @@
                 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<String, ? extends PackageStateInternal> mExistingSettings;
+        private final WatchedArraySet<Integer> mForceQueryable;
+        private final WatchedArraySet<String> mProtectedBroadcasts;
+
+        ParallelComputeComponentVisibility(
+                @NonNull ArrayMap<String, ? extends PackageStateInternal> existingSettings,
+                @NonNull WatchedArraySet<Integer> forceQueryable,
+                @NonNull WatchedArraySet<String> protectedBroadcasts) {
+            mExistingSettings = existingSettings;
+            mForceQueryable = forceQueryable;
+            mProtectedBroadcasts = protectedBroadcasts;
+        }
+
+        /**
+         * Computes component visibility of all packages in parallel from a thread pool.
+         */
+        void execute(@NonNull WatchedSparseSetArray<Integer> outQueriesViaComponent) {
+            final ExecutorService pool = ConcurrentUtils.newFixedThreadPool(
+                    MAX_THREADS, ParallelComputeComponentVisibility.class.getSimpleName(),
+                    THREAD_PRIORITY_DEFAULT);
+            try {
+                final List<Pair<PackageState, Future<ArraySet<Integer>>>> 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<ArraySet<Integer>> future = futures.get(i).second;
+                    try {
+                        final ArraySet<Integer> 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<Integer> getVisibleListOfQueryViaComponents(
+                @NonNull PackageStateInternal setting) {
+            final ArraySet<Integer> 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 77750ed..0386e66 100644
--- a/services/core/java/com/android/server/utils/WatchedSparseSetArray.java
+++ b/services/core/java/com/android/server/utils/WatchedSparseSetArray.java
@@ -65,6 +65,14 @@
     }
 
     /**
+     * Add a set of values for key n.
+     */
+    public void addAll(int n, ArraySet<T> values) {
+        mStorage.addAll(n, values);
+        onChanged();
+    }
+
+    /**
      * Removes all mappings from this SparseSetArray.
      */
     public void clear() {
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 3575b57..7c4b9f8 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.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 @@
         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.