Merge "Limit the number of shortcuts per app that can be retained by system" into rvc-dev am: 15ae228ddb
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21564438 Change-Id: I9e61356ca633c6800098a5511b7330db7a4503ae Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -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}.
|
||||||
@@ -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);
|
||||||
|
|
||||||
@@ -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();
|
||||||
|
|
||||||
@@ -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);
|
||||||
|
|
||||||
@@ -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);
|
||||||
@@ -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.
|
||||||
*
|
*
|
||||||
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
@@ -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.
|
||||||
*/
|
*/
|
||||||
@@ -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.
|
||||||
*/
|
*/
|
||||||
@@ -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,
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user