Loading services/core/java/com/android/server/pm/ShortcutPackage.java +76 −3 Original line number Original line Diff line number Diff line Loading @@ -68,6 +68,7 @@ import java.util.List; import java.util.Objects; import java.util.Objects; import java.util.Set; import java.util.Set; import java.util.function.Predicate; import java.util.function.Predicate; import java.util.stream.Collectors; /** /** * Package information used by {@link ShortcutService}. * Package information used by {@link ShortcutService}. Loading Loading @@ -350,6 +351,7 @@ class ShortcutPackage extends ShortcutPackageItem { @NonNull List<ShortcutInfo> changedShortcuts) { @NonNull List<ShortcutInfo> changedShortcuts) { Preconditions.checkArgument(newShortcut.isEnabled(), Preconditions.checkArgument(newShortcut.isEnabled(), "pushDynamicShortcuts() cannot publish disabled shortcuts"); "pushDynamicShortcuts() cannot publish disabled shortcuts"); ensureShortcutCountBeforePush(); newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC); newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC); Loading @@ -357,7 +359,7 @@ class ShortcutPackage extends ShortcutPackageItem { final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId()); final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId()); boolean deleted = false; boolean deleted = false; if (oldShortcut == null) { if (oldShortcut == null || !oldShortcut.isDynamic()) { final ShortcutService service = mShortcutUser.mService; final ShortcutService service = mShortcutUser.mService; final int maxShortcuts = service.getMaxActivityShortcuts(); final int maxShortcuts = service.getMaxActivityShortcuts(); Loading @@ -367,7 +369,6 @@ class ShortcutPackage extends ShortcutPackageItem { if (activityShortcuts != null && activityShortcuts.size() == maxShortcuts) { if (activityShortcuts != null && activityShortcuts.size() == maxShortcuts) { // Max has reached. Delete the shortcut with lowest rank. // Max has reached. Delete the shortcut with lowest rank. // Sort by isManifestShortcut() and getRank(). // Sort by isManifestShortcut() and getRank(). Collections.sort(activityShortcuts, mShortcutTypeAndRankComparator); Collections.sort(activityShortcuts, mShortcutTypeAndRankComparator); Loading @@ -382,7 +383,8 @@ class ShortcutPackage extends ShortcutPackageItem { changedShortcuts.add(shortcut); changedShortcuts.add(shortcut); deleted = deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true) != null; deleted = deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true) != null; } } } else { } if (oldShortcut != null) { // It's an update case. // It's an update case. // Make sure the target is updatable. (i.e. should be mutable.) // Make sure the target is updatable. (i.e. should be mutable.) oldShortcut.ensureUpdatableWith(newShortcut, /*isUpdating=*/ false); oldShortcut.ensureUpdatableWith(newShortcut, /*isUpdating=*/ false); Loading @@ -396,6 +398,30 @@ class ShortcutPackage extends ShortcutPackageItem { return deleted; return deleted; } } private void ensureShortcutCountBeforePush() { final ShortcutService service = mShortcutUser.mService; // Ensure the total number of shortcuts doesn't exceed the hard limit per app. final int maxShortcutPerApp = service.getMaxAppShortcuts(); final List<ShortcutInfo> appShortcuts = mShortcuts.values().stream().filter(si -> !si.isPinned()).collect(Collectors.toList()); if (appShortcuts.size() >= maxShortcutPerApp) { // Max has reached. Removes shortcuts until they fall within the hard cap. // Sort by isManifestShortcut(), isDynamic() and getLastChangedTimestamp(). Collections.sort(appShortcuts, mShortcutTypeRankAndTimeComparator); while (appShortcuts.size() >= maxShortcutPerApp) { final ShortcutInfo shortcut = appShortcuts.remove(appShortcuts.size() - 1); if (shortcut.isDeclaredInManifest()) { // All shortcuts are manifest shortcuts and cannot be removed. throw new IllegalArgumentException(getPackageName() + " has published " + appShortcuts.size() + " manifest shortcuts across different" + " activities."); } forceDeleteShortcutInner(shortcut.getId()); } } } /** /** * Remove all shortcuts that aren't pinned, cached nor dynamic. * Remove all shortcuts that aren't pinned, cached nor dynamic. * * Loading Loading @@ -1222,6 +1248,53 @@ class ShortcutPackage extends ShortcutPackageItem { return Integer.compare(a.getRank(), b.getRank()); return Integer.compare(a.getRank(), b.getRank()); }; }; /** * To sort by isManifestShortcut(), isDynamic(), getRank() and * getLastChangedTimestamp(). i.e. manifest shortcuts come before non-manifest shortcuts, * dynamic shortcuts come before floating shortcuts, then sort by last changed timestamp. * * This is used to decide which shortcuts to remove when the total number of shortcuts retained * for the app exceeds the limit defined in {@link ShortcutService#getMaxAppShortcuts()}. * * (Note the number of manifest shortcuts is always <= the max number, because if there are * more, ShortcutParser would ignore the rest.) */ final Comparator<ShortcutInfo> mShortcutTypeRankAndTimeComparator = (ShortcutInfo a, ShortcutInfo b) -> { if (a.isDeclaredInManifest() && !b.isDeclaredInManifest()) { return -1; } if (!a.isDeclaredInManifest() && b.isDeclaredInManifest()) { return 1; } if (a.isDynamic() && b.isDynamic()) { return Integer.compare(a.getRank(), b.getRank()); } if (a.isDynamic()) { return -1; } if (b.isDynamic()) { return 1; } if (a.isCached() && b.isCached()) { // if both shortcuts are cached, prioritize shortcuts cached by bubbles. if (a.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES) && !b.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES)) { return -1; } else if (!a.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES) && b.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES)) { return 1; } } if (a.isCached()) { return -1; } if (b.isCached()) { return 1; } return Long.compare(b.getLastChangedTimestamp(), a.getLastChangedTimestamp()); }; /** /** * Build a list of shortcuts for each target activity and return as a map. The result won't * Build a list of shortcuts for each target activity and return as a map. The result won't * contain "floating" shortcuts because they don't belong on any activities. * contain "floating" shortcuts because they don't belong on any activities. Loading services/core/java/com/android/server/pm/ShortcutService.java +24 −1 Original line number Original line Diff line number Diff line Loading @@ -171,6 +171,9 @@ public class ShortcutService extends IShortcutService.Stub { @VisibleForTesting @VisibleForTesting static final int DEFAULT_MAX_SHORTCUTS_PER_ACTIVITY = 15; static final int DEFAULT_MAX_SHORTCUTS_PER_ACTIVITY = 15; @VisibleForTesting static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 100; @VisibleForTesting @VisibleForTesting static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96; static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96; Loading Loading @@ -245,6 +248,11 @@ public class ShortcutService extends IShortcutService.Stub { */ */ String KEY_MAX_SHORTCUTS = "max_shortcuts"; String KEY_MAX_SHORTCUTS = "max_shortcuts"; /** * Key name for the max shortcuts can be retained in system ram per app. (int) */ String KEY_MAX_SHORTCUTS_PER_APP = "max_shortcuts_per_app"; /** /** * Key name for icon compression quality, 0-100. * Key name for icon compression quality, 0-100. */ */ Loading Loading @@ -302,10 +310,15 @@ public class ShortcutService extends IShortcutService.Stub { new SparseArray<>(); new SparseArray<>(); /** /** * Max number of dynamic + manifest shortcuts that each application can have at a time. * Max number of dynamic + manifest shortcuts that each activity can have at a time. */ */ private int mMaxShortcuts; private int mMaxShortcuts; /** * Max number of shortcuts that can exists in system ram for each application. */ private int mMaxShortcutsPerApp; /** /** * Max number of updating API calls that each application can make during the interval. * Max number of updating API calls that each application can make during the interval. */ */ Loading Loading @@ -732,6 +745,9 @@ public class ShortcutService extends IShortcutService.Stub { mMaxShortcuts = Math.max(0, (int) parser.getLong( mMaxShortcuts = Math.max(0, (int) parser.getLong( ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_ACTIVITY)); 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() final int iconDimensionDp = Math.max(1, injectIsLowRamDevice() ? (int) parser.getLong( ? (int) parser.getLong( ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM, ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM, Loading Loading @@ -1661,6 +1677,13 @@ public class ShortcutService extends IShortcutService.Stub { return mMaxShortcuts; return mMaxShortcuts; } } /** * Return the max number of shortcuts can be retaiend in system ram for each application. */ int getMaxAppShortcuts() { return mMaxShortcutsPerApp; } /** /** * - Sends a notification to LauncherApps * - Sends a notification to LauncherApps * - Write to file * - Write to file Loading Loading
services/core/java/com/android/server/pm/ShortcutPackage.java +76 −3 Original line number Original line Diff line number Diff line Loading @@ -68,6 +68,7 @@ import java.util.List; import java.util.Objects; import java.util.Objects; import java.util.Set; import java.util.Set; import java.util.function.Predicate; import java.util.function.Predicate; import java.util.stream.Collectors; /** /** * Package information used by {@link ShortcutService}. * Package information used by {@link ShortcutService}. Loading Loading @@ -350,6 +351,7 @@ class ShortcutPackage extends ShortcutPackageItem { @NonNull List<ShortcutInfo> changedShortcuts) { @NonNull List<ShortcutInfo> changedShortcuts) { Preconditions.checkArgument(newShortcut.isEnabled(), Preconditions.checkArgument(newShortcut.isEnabled(), "pushDynamicShortcuts() cannot publish disabled shortcuts"); "pushDynamicShortcuts() cannot publish disabled shortcuts"); ensureShortcutCountBeforePush(); newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC); newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC); Loading @@ -357,7 +359,7 @@ class ShortcutPackage extends ShortcutPackageItem { final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId()); final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId()); boolean deleted = false; boolean deleted = false; if (oldShortcut == null) { if (oldShortcut == null || !oldShortcut.isDynamic()) { final ShortcutService service = mShortcutUser.mService; final ShortcutService service = mShortcutUser.mService; final int maxShortcuts = service.getMaxActivityShortcuts(); final int maxShortcuts = service.getMaxActivityShortcuts(); Loading @@ -367,7 +369,6 @@ class ShortcutPackage extends ShortcutPackageItem { if (activityShortcuts != null && activityShortcuts.size() == maxShortcuts) { if (activityShortcuts != null && activityShortcuts.size() == maxShortcuts) { // Max has reached. Delete the shortcut with lowest rank. // Max has reached. Delete the shortcut with lowest rank. // Sort by isManifestShortcut() and getRank(). // Sort by isManifestShortcut() and getRank(). Collections.sort(activityShortcuts, mShortcutTypeAndRankComparator); Collections.sort(activityShortcuts, mShortcutTypeAndRankComparator); Loading @@ -382,7 +383,8 @@ class ShortcutPackage extends ShortcutPackageItem { changedShortcuts.add(shortcut); changedShortcuts.add(shortcut); deleted = deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true) != null; deleted = deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true) != null; } } } else { } if (oldShortcut != null) { // It's an update case. // It's an update case. // Make sure the target is updatable. (i.e. should be mutable.) // Make sure the target is updatable. (i.e. should be mutable.) oldShortcut.ensureUpdatableWith(newShortcut, /*isUpdating=*/ false); oldShortcut.ensureUpdatableWith(newShortcut, /*isUpdating=*/ false); Loading @@ -396,6 +398,30 @@ class ShortcutPackage extends ShortcutPackageItem { return deleted; return deleted; } } private void ensureShortcutCountBeforePush() { final ShortcutService service = mShortcutUser.mService; // Ensure the total number of shortcuts doesn't exceed the hard limit per app. final int maxShortcutPerApp = service.getMaxAppShortcuts(); final List<ShortcutInfo> appShortcuts = mShortcuts.values().stream().filter(si -> !si.isPinned()).collect(Collectors.toList()); if (appShortcuts.size() >= maxShortcutPerApp) { // Max has reached. Removes shortcuts until they fall within the hard cap. // Sort by isManifestShortcut(), isDynamic() and getLastChangedTimestamp(). Collections.sort(appShortcuts, mShortcutTypeRankAndTimeComparator); while (appShortcuts.size() >= maxShortcutPerApp) { final ShortcutInfo shortcut = appShortcuts.remove(appShortcuts.size() - 1); if (shortcut.isDeclaredInManifest()) { // All shortcuts are manifest shortcuts and cannot be removed. throw new IllegalArgumentException(getPackageName() + " has published " + appShortcuts.size() + " manifest shortcuts across different" + " activities."); } forceDeleteShortcutInner(shortcut.getId()); } } } /** /** * Remove all shortcuts that aren't pinned, cached nor dynamic. * Remove all shortcuts that aren't pinned, cached nor dynamic. * * Loading Loading @@ -1222,6 +1248,53 @@ class ShortcutPackage extends ShortcutPackageItem { return Integer.compare(a.getRank(), b.getRank()); return Integer.compare(a.getRank(), b.getRank()); }; }; /** * To sort by isManifestShortcut(), isDynamic(), getRank() and * getLastChangedTimestamp(). i.e. manifest shortcuts come before non-manifest shortcuts, * dynamic shortcuts come before floating shortcuts, then sort by last changed timestamp. * * This is used to decide which shortcuts to remove when the total number of shortcuts retained * for the app exceeds the limit defined in {@link ShortcutService#getMaxAppShortcuts()}. * * (Note the number of manifest shortcuts is always <= the max number, because if there are * more, ShortcutParser would ignore the rest.) */ final Comparator<ShortcutInfo> mShortcutTypeRankAndTimeComparator = (ShortcutInfo a, ShortcutInfo b) -> { if (a.isDeclaredInManifest() && !b.isDeclaredInManifest()) { return -1; } if (!a.isDeclaredInManifest() && b.isDeclaredInManifest()) { return 1; } if (a.isDynamic() && b.isDynamic()) { return Integer.compare(a.getRank(), b.getRank()); } if (a.isDynamic()) { return -1; } if (b.isDynamic()) { return 1; } if (a.isCached() && b.isCached()) { // if both shortcuts are cached, prioritize shortcuts cached by bubbles. if (a.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES) && !b.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES)) { return -1; } else if (!a.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES) && b.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES)) { return 1; } } if (a.isCached()) { return -1; } if (b.isCached()) { return 1; } return Long.compare(b.getLastChangedTimestamp(), a.getLastChangedTimestamp()); }; /** /** * Build a list of shortcuts for each target activity and return as a map. The result won't * Build a list of shortcuts for each target activity and return as a map. The result won't * contain "floating" shortcuts because they don't belong on any activities. * contain "floating" shortcuts because they don't belong on any activities. Loading
services/core/java/com/android/server/pm/ShortcutService.java +24 −1 Original line number Original line Diff line number Diff line Loading @@ -171,6 +171,9 @@ public class ShortcutService extends IShortcutService.Stub { @VisibleForTesting @VisibleForTesting static final int DEFAULT_MAX_SHORTCUTS_PER_ACTIVITY = 15; static final int DEFAULT_MAX_SHORTCUTS_PER_ACTIVITY = 15; @VisibleForTesting static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 100; @VisibleForTesting @VisibleForTesting static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96; static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96; Loading Loading @@ -245,6 +248,11 @@ public class ShortcutService extends IShortcutService.Stub { */ */ String KEY_MAX_SHORTCUTS = "max_shortcuts"; String KEY_MAX_SHORTCUTS = "max_shortcuts"; /** * Key name for the max shortcuts can be retained in system ram per app. (int) */ String KEY_MAX_SHORTCUTS_PER_APP = "max_shortcuts_per_app"; /** /** * Key name for icon compression quality, 0-100. * Key name for icon compression quality, 0-100. */ */ Loading Loading @@ -302,10 +310,15 @@ public class ShortcutService extends IShortcutService.Stub { new SparseArray<>(); new SparseArray<>(); /** /** * Max number of dynamic + manifest shortcuts that each application can have at a time. * Max number of dynamic + manifest shortcuts that each activity can have at a time. */ */ private int mMaxShortcuts; private int mMaxShortcuts; /** * Max number of shortcuts that can exists in system ram for each application. */ private int mMaxShortcutsPerApp; /** /** * Max number of updating API calls that each application can make during the interval. * Max number of updating API calls that each application can make during the interval. */ */ Loading Loading @@ -732,6 +745,9 @@ public class ShortcutService extends IShortcutService.Stub { mMaxShortcuts = Math.max(0, (int) parser.getLong( mMaxShortcuts = Math.max(0, (int) parser.getLong( ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_ACTIVITY)); 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() final int iconDimensionDp = Math.max(1, injectIsLowRamDevice() ? (int) parser.getLong( ? (int) parser.getLong( ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM, ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM, Loading Loading @@ -1661,6 +1677,13 @@ public class ShortcutService extends IShortcutService.Stub { return mMaxShortcuts; return mMaxShortcuts; } } /** * Return the max number of shortcuts can be retaiend in system ram for each application. */ int getMaxAppShortcuts() { return mMaxShortcutsPerApp; } /** /** * - Sends a notification to LauncherApps * - Sends a notification to LauncherApps * - Write to file * - Write to file Loading