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

Commit 4fbf54f9 authored by Pinyao Ting's avatar Pinyao Ting Committed by Automerger Merge Worker
Browse files

Merge "Limit the number of shortcuts per app that can be retained by system"...

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

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21566820



Change-Id: I9712c24ddfb897753b3ee2d9c0791174aeb6c5dd
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 3a84321d 7b47cfaf
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