Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7b47cfaf authored by Pinyao Ting's avatar Pinyao Ting Committed by Android (Google) Code Review
Browse files

Merge "Limit the number of shortcuts per app that can be retained by system" into tm-dev

parents c5034c21 be9d9c04
Loading
Loading
Loading
Loading
+88 −11
Original line number Diff line number Diff line
@@ -430,6 +430,7 @@ class ShortcutPackage extends ShortcutPackageItem {
            @NonNull List<ShortcutInfo> changedShortcuts) {
        Preconditions.checkArgument(newShortcut.isEnabled(),
                "pushDynamicShortcuts() cannot publish disabled shortcuts");
        ensureShortcutCountBeforePush();

        newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);

@@ -437,7 +438,7 @@ class ShortcutPackage extends ShortcutPackageItem {
        final ShortcutInfo oldShortcut = findShortcutById(newShortcut.getId());
        boolean deleted = false;

        if (oldShortcut == null) {
        if (oldShortcut == null || !oldShortcut.isDynamic()) {
            final ShortcutService service = mShortcutUser.mService;
            final int maxShortcuts = service.getMaxActivityShortcuts();

@@ -446,18 +447,12 @@ class ShortcutPackage extends ShortcutPackageItem {
            final ArrayList<ShortcutInfo> activityShortcuts = all.get(newShortcut.getActivity());

            if (activityShortcuts != null && activityShortcuts.size() > maxShortcuts) {
                Slog.e(TAG, "Error pushing shortcut. There are already "
                        + activityShortcuts.size() + " shortcuts, exceeding the " + maxShortcuts
                        + " shortcuts limit when pushing the new shortcut " + newShortcut
                        + ". Id of shortcuts currently available in system memory are "
                        + activityShortcuts.stream().map(ShortcutInfo::getId)
                        .collect(Collectors.joining(",", "[", "]")));
                // TODO: This should not have happened. If it does, identify the root cause where
                //  possible, otherwise bail-out early to prevent memory issue.
                // Root cause was discovered in b/233155034, so this should not be happening.
                service.wtf("Error pushing shortcut. There are already "
                        + activityShortcuts.size() + " shortcuts.");
            }
            if (activityShortcuts != null && activityShortcuts.size() == maxShortcuts) {
                // Max has reached. Delete the shortcut with lowest rank.

                // Sort by isManifestShortcut() and getRank().
                Collections.sort(activityShortcuts, mShortcutTypeAndRankComparator);

@@ -473,7 +468,8 @@ class ShortcutPackage extends ShortcutPackageItem {
                deleted = deleteDynamicWithId(shortcut.getId(), /* ignoreInvisible =*/ true,
                        /*ignorePersistedShortcuts=*/ true) != null;
            }
        } else {
        }
        if (oldShortcut != null) {
            // It's an update case.
            // Make sure the target is updatable. (i.e. should be mutable.)
            oldShortcut.ensureUpdatableWith(newShortcut, /*isUpdating=*/ false);
@@ -505,6 +501,32 @@ class ShortcutPackage extends ShortcutPackageItem {
        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();
        synchronized (mLock) {
            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.
     *
@@ -1366,6 +1388,61 @@ class ShortcutPackage extends ShortcutPackageItem {
        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 people tile,
            if (a.hasFlags(ShortcutInfo.FLAG_CACHED_PEOPLE_TILE)
                    && !b.hasFlags(ShortcutInfo.FLAG_CACHED_PEOPLE_TILE)) {
                return -1;
            } else if (!a.hasFlags(ShortcutInfo.FLAG_CACHED_PEOPLE_TILE)
                    && b.hasFlags(ShortcutInfo.FLAG_CACHED_PEOPLE_TILE)) {
                return 1;
            }
            // followed 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
     * contain "floating" shortcuts because they don't belong on any activities.
+24 −1
Original line number Diff line number Diff line
@@ -180,6 +180,9 @@ public class ShortcutService extends IShortcutService.Stub {
    @VisibleForTesting
    static final int DEFAULT_MAX_SHORTCUTS_PER_ACTIVITY = 15;

    @VisibleForTesting
    static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 100;

    @VisibleForTesting
    static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96;

@@ -256,6 +259,11 @@ public class ShortcutService extends IShortcutService.Stub {
         */
        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.
         */
@@ -328,10 +336,15 @@ public class ShortcutService extends IShortcutService.Stub {
            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;

    /**
     * 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.
     */
@@ -806,6 +819,9 @@ public class ShortcutService extends IShortcutService.Stub {
        mMaxShortcuts = 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,
@@ -1757,6 +1773,13 @@ public class ShortcutService extends IShortcutService.Stub {
        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
     * - Write to file