Implement a global maximum on number of shortcuts an app can publish

Currently there is a limit on the number of shortcuts an app can publish
in respect to each launcher activity. This CL further implements a
global maximum of total number of shortcuts an app can publish to
mitigate from any potential system health issue.

Bug: 250576066
Test: manual
Change-Id: I94b4e39475c7585ec67f15df1256fdf0b65f3bec
Merged-In: I94b4e39475c7585ec67f15df1256fdf0b65f3bec
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index f2bfb2a..3814d63 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -1332,9 +1332,15 @@
         }
 
         // Then make sure none of the activities have more than the max number of shortcuts.
+        int total = 0;
         for (int i = counts.size() - 1; i >= 0; i--) {
-            service.enforceMaxActivityShortcuts(counts.valueAt(i));
+            int count = counts.valueAt(i);
+            service.enforceMaxActivityShortcuts(count);
+            total += count;
         }
+
+        // Finally make sure that the app doesn't have more than the max number of shortcuts.
+        service.enforceMaxAppShortcuts(total);
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 0c42ff6..3682bf1 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -172,6 +172,9 @@
     static final int DEFAULT_MAX_SHORTCUTS_PER_ACTIVITY = 15;
 
     @VisibleForTesting
+    static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 60;
+
+    @VisibleForTesting
     static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96;
 
     @VisibleForTesting
@@ -246,6 +249,11 @@
         String KEY_MAX_SHORTCUTS = "max_shortcuts";
 
         /**
+         * Key name for the max dynamic shortcuts per app. (int)
+         */
+        String KEY_MAX_SHORTCUTS_PER_APP = "max_shortcuts_per_app";
+
+        /**
          * Key name for icon compression quality, 0-100.
          */
         String KEY_ICON_QUALITY = "icon_quality";
@@ -302,9 +310,14 @@
             new SparseArray<>();
 
     /**
+     * Max number of dynamic + manifest shortcuts that each activity can have at a time.
+     */
+    private int mMaxShortcutsPerActivity;
+
+    /**
      * Max number of dynamic + manifest shortcuts that each application can have at a time.
      */
-    private int mMaxShortcuts;
+    private int mMaxShortcutsPerApp;
 
     /**
      * Max number of updating API calls that each application can make during the interval.
@@ -729,9 +742,12 @@
         mMaxUpdatesPerInterval = Math.max(0, (int) parser.getLong(
                 ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL, DEFAULT_MAX_UPDATES_PER_INTERVAL));
 
-        mMaxShortcuts = Math.max(0, (int) parser.getLong(
+        mMaxShortcutsPerActivity = Math.max(0, (int) parser.getLong(
                 ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_ACTIVITY));
 
+        mMaxShortcutsPerApp = Math.max(0, (int) parser.getLong(
+                ConfigConstants.KEY_MAX_SHORTCUTS_PER_APP, DEFAULT_MAX_SHORTCUTS_PER_APP));
+
         final int iconDimensionDp = Math.max(1, injectIsLowRamDevice()
                 ? (int) parser.getLong(
                 ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM,
@@ -1649,16 +1665,33 @@
      *                                  {@link #getMaxActivityShortcuts()}.
      */
     void enforceMaxActivityShortcuts(int numShortcuts) {
-        if (numShortcuts > mMaxShortcuts) {
+        if (numShortcuts > mMaxShortcutsPerActivity) {
             throw new IllegalArgumentException("Max number of dynamic shortcuts exceeded");
         }
     }
 
     /**
+     * @throws IllegalArgumentException if {@code numShortcuts} is bigger than
+     *                                  {@link #getMaxAppShortcuts()}.
+     */
+    void enforceMaxAppShortcuts(int numShortcuts) {
+        if (numShortcuts > mMaxShortcutsPerApp) {
+            throw new IllegalArgumentException("Max number of dynamic shortcuts per app exceeded");
+        }
+    }
+
+    /**
      * Return the max number of dynamic + manifest shortcuts for each launcher icon.
      */
     int getMaxActivityShortcuts() {
-        return mMaxShortcuts;
+        return mMaxShortcutsPerActivity;
+    }
+
+    /**
+     * Return the max number of dynamic + manifest shortcuts for each launcher icon.
+     */
+    int getMaxAppShortcuts() {
+        return mMaxShortcutsPerApp;
     }
 
     /**
@@ -2075,6 +2108,8 @@
             ps.ensureNotImmutable(shortcut.getId(), /*ignoreInvisible=*/ true);
             fillInDefaultActivity(Arrays.asList(shortcut));
 
+            enforceMaxAppShortcuts(ps.getShortcutCount());
+
             if (!shortcut.hasRank()) {
                 shortcut.setRank(0);
             }
@@ -2493,7 +2528,7 @@
             throws RemoteException {
         verifyCaller(packageName, userId);
 
-        return mMaxShortcuts;
+        return mMaxShortcutsPerActivity;
     }
 
     @Override
@@ -4411,7 +4446,7 @@
                 pw.print("    maxUpdatesPerInterval: ");
                 pw.println(mMaxUpdatesPerInterval);
                 pw.print("    maxShortcutsPerActivity: ");
-                pw.println(mMaxShortcuts);
+                pw.println(mMaxShortcutsPerActivity);
                 pw.println();
 
                 mStatLogger.dump(pw, "  ");
@@ -4848,7 +4883,7 @@
 
     @VisibleForTesting
     int getMaxShortcutsForTest() {
-        return mMaxShortcuts;
+        return mMaxShortcutsPerActivity;
     }
 
     @VisibleForTesting