Remove snapshot flood by update all overlays in one commitment

The iteration n updates some PackageUserState instances for the
specified user. Once OverlayManagerService(OMS) changes some
PackageUserState instance, it triggers a snapshot rebuild in the
next iteration.

OMS has prepared all changes in switching users. PackageManagerService
compute all changes of PackageUserStates and then use the mutator to
modified all changes of PackageUserStates in one
commitPackageStateMutation.

Test: run perfetto.ui and follow the commands on perfetto.
    b/174206591#comment14
Test: TC="PtsConfigTestCases:com.google.android.config.pts"; \
    atest ${TC}.PixelSetupWizardOverlayTest ${TC}.ConfigTestCase

Bug: 174206591
Change-Id: I2b58b5e6164a72008c284adf05f6ff0240a5a33a
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 68cd288..f26fb59 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -596,18 +596,23 @@
     /**
      * Set which overlay to use for a package.
      * @param userId The user for which to update the overlays.
-     * @param targetPackageName The package name of the package for which to update the overlays.
-     * @param overlayPaths  The complete list of overlay paths that should be enabled for
+     * @param pendingChanges is a map to describe all overlay targets and their related overlay
+     *                      paths. Its key is the overlay target package and its value is the
+     *                      complete list of overlay paths that should be enabled for
      *                      the target. Previously enabled overlays not specified in the list
      *                      will be disabled. Pass in null or empty paths to disable all overlays.
      *                      The order of the items is significant if several overlays modify the
-     *                      same resource.
+     *                      same resource. To pass the concrete ArrayMap type is to reduce the
+     *                      overheads of system server.
      * @param outUpdatedPackageNames An output list that contains the package names of packages
      *                               affected by the update of enabled overlays.
-     * @return true if all packages names were known by the package manager, false otherwise
+     * @param outInvalidPackageNames An output list that contains the package names of packages
+     *                               are not valid.
      */
-    public abstract boolean setEnabledOverlayPackages(int userId, String targetPackageName,
-            @Nullable OverlayPaths overlayPaths, Set<String> outUpdatedPackageNames);
+    public abstract void setEnabledOverlayPackages(int userId,
+            @NonNull ArrayMap<String, OverlayPaths> pendingChanges,
+            @NonNull Set<String> outUpdatedPackageNames,
+            @NonNull Set<String> outInvalidPackageNames);
 
     /**
      * Resolves an activity intent, allowing instant apps to be resolved.
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index ee0b3d5..0bfc091 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -1476,7 +1476,7 @@
                 targetPackageNames = pm.getTargetPackageNames(userId);
             }
 
-            final Map<String, OverlayPaths> pendingChanges =
+            final ArrayMap<String, OverlayPaths> pendingChanges =
                     new ArrayMap<>(targetPackageNames.size());
             synchronized (mLock) {
                 final OverlayPaths frameworkOverlays =
@@ -1492,6 +1492,9 @@
             }
 
             final HashSet<String> updatedPackages = new HashSet<>();
+            final HashSet<String> invalidPackages = new HashSet<>();
+            pm.setEnabledOverlayPackages(userId, pendingChanges, updatedPackages, invalidPackages);
+
             for (final String targetPackageName : targetPackageNames) {
                 if (DEBUG) {
                     Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
@@ -1499,11 +1502,10 @@
                             + "] userId=" + userId);
                 }
 
-                if (!pm.setEnabledOverlayPackages(
-                        userId, targetPackageName, pendingChanges.get(targetPackageName),
-                        updatedPackages)) {
-                    Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
-                            targetPackageName, userId));
+                if (invalidPackages.contains(targetPackageName)) {
+                    Slog.e(TAG, TextUtils.formatSimple(
+                            "Failed to change enabled overlays for %s user %d", targetPackageName,
+                            userId));
                 }
             }
             return new ArrayList<>(updatedPackages);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c06beb0..6f5cb8b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6316,11 +6316,12 @@
         }
 
         @Override
-        public boolean setEnabledOverlayPackages(int userId, @NonNull String targetPackageName,
-                @Nullable OverlayPaths overlayPaths,
-                @NonNull Set<String> outUpdatedPackageNames) {
-            return PackageManagerService.this.setEnabledOverlayPackages(userId, targetPackageName,
-                    overlayPaths, outUpdatedPackageNames);
+        public void setEnabledOverlayPackages(int userId,
+                @NonNull ArrayMap<String, OverlayPaths> pendingChanges,
+                @NonNull Set<String> outUpdatedPackageNames,
+                @NonNull Set<String> outInvalidPackageNames) {
+            PackageManagerService.this.setEnabledOverlayPackages(userId,
+                    pendingChanges, outUpdatedPackageNames, outInvalidPackageNames);
         }
 
         @Override
@@ -6528,85 +6529,119 @@
         }
     }
 
-    private boolean setEnabledOverlayPackages(@UserIdInt int userId,
-            @NonNull String targetPackageName, @Nullable OverlayPaths newOverlayPaths,
-            @NonNull Set<String> outUpdatedPackageNames) {
+    private void setEnabledOverlayPackages(@UserIdInt int userId,
+            @NonNull ArrayMap<String, OverlayPaths> pendingChanges,
+            @NonNull Set<String> outUpdatedPackageNames,
+            @NonNull Set<String> outInvalidPackageNames) {
+        final ArrayMap<String, ArrayMap<String, ArraySet<String>>>
+                targetPkgToLibNameToModifiedDependents = new ArrayMap<>();
+        final int numberOfPendingChanges = pendingChanges.size();
+
         synchronized (mOverlayPathsLock) {
-            final ArrayMap<String, ArraySet<String>> libNameToModifiedDependents = new ArrayMap<>();
             Computer computer = snapshotComputer();
-            final PackageStateInternal packageState = computer.getPackageStateInternal(
-                    targetPackageName);
-            final AndroidPackage targetPkg = packageState == null ? null : packageState.getPkg();
-            if (targetPackageName == null || targetPkg == null) {
-                Slog.e(TAG, "failed to find package " + targetPackageName);
-                return false;
-            }
+            for (int i = 0; i < numberOfPendingChanges; i++) {
+                final String targetPackageName = pendingChanges.keyAt(i);
+                final OverlayPaths newOverlayPaths = pendingChanges.valueAt(i);
+                final PackageStateInternal packageState = computer.getPackageStateInternal(
+                        targetPackageName);
+                final AndroidPackage targetPkg =
+                        packageState == null ? null : packageState.getPkg();
+                if (targetPackageName == null || targetPkg == null) {
+                    Slog.e(TAG, "failed to find package " + targetPackageName);
+                    outInvalidPackageNames.add(targetPackageName);
+                    continue;
+                }
 
-            if (Objects.equals(packageState.getUserStateOrDefault(userId).getOverlayPaths(),
-                    newOverlayPaths)) {
-                return true;
-            }
+                if (Objects.equals(packageState.getUserStateOrDefault(userId).getOverlayPaths(),
+                        newOverlayPaths)) {
+                    continue;
+                }
 
-            if (targetPkg.getLibraryNames() != null) {
-                // Set the overlay paths for dependencies of the shared library.
-                for (final String libName : targetPkg.getLibraryNames()) {
-                    ArraySet<String> modifiedDependents = null;
+                if (targetPkg.getLibraryNames() != null) {
+                    // Set the overlay paths for dependencies of the shared library.
+                    for (final String libName : targetPkg.getLibraryNames()) {
+                        ArraySet<String> modifiedDependents = null;
 
-                    final SharedLibraryInfo info = computer.getSharedLibraryInfo(libName,
-                            SharedLibraryInfo.VERSION_UNDEFINED);
-                    if (info == null) {
-                        continue;
-                    }
-                    final List<VersionedPackage> dependents = computer
-                            .getPackagesUsingSharedLibrary(info, 0, Process.SYSTEM_UID, userId);
-                    if (dependents == null) {
-                        continue;
-                    }
-                    for (final VersionedPackage dependent : dependents) {
-                        final PackageStateInternal dependentState =
-                                computer.getPackageStateInternal(dependent.getPackageName());
-                        if (dependentState == null) {
+                        final SharedLibraryInfo info = computer.getSharedLibraryInfo(libName,
+                                SharedLibraryInfo.VERSION_UNDEFINED);
+                        if (info == null) {
                             continue;
                         }
-                        if (!Objects.equals(dependentState.getUserStateOrDefault(userId)
-                                .getSharedLibraryOverlayPaths()
-                                .get(libName), newOverlayPaths)) {
-                            String dependentPackageName = dependent.getPackageName();
-                            modifiedDependents = ArrayUtils.add(modifiedDependents,
-                                    dependentPackageName);
-                            outUpdatedPackageNames.add(dependentPackageName);
+                        final List<VersionedPackage> dependents =
+                                computer.getPackagesUsingSharedLibrary(info, 0, Process.SYSTEM_UID,
+                                        userId);
+                        if (dependents == null) {
+                            continue;
+                        }
+                        for (final VersionedPackage dependent : dependents) {
+                            final PackageStateInternal dependentState =
+                                    computer.getPackageStateInternal(dependent.getPackageName());
+                            if (dependentState == null) {
+                                continue;
+                            }
+                            if (!Objects.equals(dependentState.getUserStateOrDefault(userId)
+                                    .getSharedLibraryOverlayPaths()
+                                    .get(libName), newOverlayPaths)) {
+                                String dependentPackageName = dependent.getPackageName();
+                                modifiedDependents = ArrayUtils.add(modifiedDependents,
+                                        dependentPackageName);
+                                outUpdatedPackageNames.add(dependentPackageName);
+                            }
+                        }
+
+                        if (modifiedDependents != null) {
+                            ArrayMap<String, ArraySet<String>> libNameToModifiedDependents =
+                                    targetPkgToLibNameToModifiedDependents.get(
+                                            targetPackageName);
+                            if (libNameToModifiedDependents == null) {
+                                libNameToModifiedDependents = new ArrayMap<>();
+                                targetPkgToLibNameToModifiedDependents.put(targetPackageName,
+                                        libNameToModifiedDependents);
+                            }
+                            libNameToModifiedDependents.put(libName, modifiedDependents);
                         }
                     }
-
-                    if (modifiedDependents != null) {
-                        libNameToModifiedDependents.put(libName, modifiedDependents);
-                    }
                 }
+
+                outUpdatedPackageNames.add(targetPackageName);
             }
 
-            outUpdatedPackageNames.add(targetPackageName);
-
             commitPackageStateMutation(null, mutator -> {
-                mutator.forPackage(targetPackageName)
-                        .userState(userId)
-                        .setOverlayPaths(newOverlayPaths);
+                for (int i = 0; i < numberOfPendingChanges; i++) {
+                    final String targetPackageName = pendingChanges.keyAt(i);
+                    final OverlayPaths newOverlayPaths = pendingChanges.valueAt(i);
 
-                for (int mapIndex = 0; mapIndex < libNameToModifiedDependents.size(); mapIndex++) {
-                    String libName = libNameToModifiedDependents.keyAt(mapIndex);
-                    ArraySet<String> modifiedDependents =
-                            libNameToModifiedDependents.valueAt(mapIndex);
-                    for (int setIndex = 0; setIndex < modifiedDependents.size(); setIndex++) {
-                        mutator.forPackage(modifiedDependents.valueAt(setIndex))
-                                .userState(userId)
-                                .setOverlayPathsForLibrary(libName, newOverlayPaths);
+                    if (!outUpdatedPackageNames.contains(targetPackageName)) {
+                        continue;
+                    }
+
+                    mutator.forPackage(targetPackageName)
+                            .userState(userId)
+                            .setOverlayPaths(newOverlayPaths);
+
+                    final ArrayMap<String, ArraySet<String>> libNameToModifiedDependents =
+                            targetPkgToLibNameToModifiedDependents.get(
+                                    targetPackageName);
+                    if (libNameToModifiedDependents == null) {
+                        continue;
+                    }
+
+                    for (int mapIndex = 0; mapIndex < libNameToModifiedDependents.size();
+                            mapIndex++) {
+                        String libName = libNameToModifiedDependents.keyAt(mapIndex);
+                        ArraySet<String> modifiedDependents =
+                                libNameToModifiedDependents.valueAt(mapIndex);
+                        for (int setIndex = 0; setIndex < modifiedDependents.size(); setIndex++) {
+                            mutator.forPackage(modifiedDependents.valueAt(setIndex))
+                                    .userState(userId)
+                                    .setOverlayPathsForLibrary(libName, newOverlayPaths);
+                        }
                     }
                 }
             });
         }
 
         invalidatePackageInfoCache();
-
-        return true;
     }
 
     private void enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions(