diff --git a/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java b/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
index 1c5f0a7fc0f34..f411c98433cf7 100644
--- a/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
+++ b/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
@@ -280,7 +280,8 @@ public class ShortcutBitmapSaver {
IoUtils.closeQuietly(out);
}
- shortcut.setBitmapPath(file.getAbsolutePath());
+ final String path = file.getAbsolutePath();
+ mService.postValue(shortcut, si -> si.setBitmapPath(path));
} catch (IOException | RuntimeException e) {
Slog.e(ShortcutService.TAG, "Unable to write bitmap to file", e);
@@ -295,12 +296,14 @@ public class ShortcutBitmapSaver {
Slog.d(TAG, "Saved bitmap.");
}
if (shortcut != null) {
- if (shortcut.getBitmapPath() == null) {
- removeIcon(shortcut);
- }
+ mService.postValue(shortcut, si -> {
+ if (si.getBitmapPath() == null) {
+ removeIcon(si);
+ }
- // Whatever happened, remove this flag.
- shortcut.clearFlags(ShortcutInfo.FLAG_ICON_FILE_PENDING_SAVE);
+ // Whatever happened, remove this flag.
+ si.clearFlags(ShortcutInfo.FLAG_ICON_FILE_PENDING_SAVE);
+ });
}
}
return true;
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index a604afc22c096..302e6572a245c 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -223,12 +223,14 @@ class ShortcutPackage extends ShortcutPackageItem {
// - Disable if needed.
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
ShortcutInfo si = mShortcuts.valueAt(i);
- si.clearFlags(ShortcutInfo.FLAG_SHADOW);
+ mutateShortcut(si.getId(), si, shortcut -> {
+ shortcut.clearFlags(ShortcutInfo.FLAG_SHADOW);
- si.setDisabledReason(restoreBlockReason);
- if (restoreBlockReason != ShortcutInfo.DISABLED_REASON_NOT_DISABLED) {
- si.addFlags(ShortcutInfo.FLAG_DISABLED);
- }
+ shortcut.setDisabledReason(restoreBlockReason);
+ if (restoreBlockReason != ShortcutInfo.DISABLED_REASON_NOT_DISABLED) {
+ shortcut.addFlags(ShortcutInfo.FLAG_DISABLED);
+ }
+ });
}
// Because some launchers may not have been restored (e.g. allowBackup=false),
// we need to re-calculate the pinned shortcuts.
@@ -460,9 +462,11 @@ class ShortcutPackage extends ShortcutPackageItem {
if (si.isDynamic() && (!ignoreInvisible || si.isVisibleToPublisher())) {
changed = true;
- si.setTimestamp(now);
- si.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
- si.setRank(0); // It may still be pinned, so clear the rank.
+ mutateShortcut(si.getId(), si, shortcut -> {
+ shortcut.setTimestamp(now);
+ shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
+ shortcut.setRank(0); // It may still be pinned, so clear the rank.
+ });
}
}
if (changed) {
@@ -506,7 +510,7 @@ class ShortcutPackage extends ShortcutPackageItem {
public ShortcutInfo deleteLongLivedWithId(@NonNull String shortcutId, boolean ignoreInvisible) {
final ShortcutInfo shortcut = mShortcuts.get(shortcutId);
if (shortcut != null) {
- shortcut.clearFlags(ShortcutInfo.FLAG_CACHED_ALL);
+ mutateShortcut(shortcutId, null, si -> si.clearFlags(ShortcutInfo.FLAG_CACHED_ALL));
}
return deleteOrDisableWithId(
shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false, ignoreInvisible,
@@ -527,15 +531,16 @@ class ShortcutPackage extends ShortcutPackageItem {
overrideImmutable, ignoreInvisible, disabledReason);
// If disabled id still exists, it is pinned and we need to update the disabled message.
- final ShortcutInfo disabled = mShortcuts.get(shortcutId);
- if (disabled != null) {
- if (disabledMessage != null) {
- disabled.setDisabledMessage(disabledMessage);
- } else if (disabledMessageResId != 0) {
- disabled.setDisabledMessageResId(disabledMessageResId);
- mShortcutUser.mService.fixUpShortcutResourceNamesAndValues(disabled);
+ mutateShortcut(shortcutId, null, disabled -> {
+ if (disabled != null) {
+ if (disabledMessage != null) {
+ disabled.setDisabledMessage(disabledMessage);
+ } else if (disabledMessageResId != 0) {
+ disabled.setDisabledMessageResId(disabledMessageResId);
+ mShortcutUser.mService.fixUpShortcutResourceNamesAndValues(disabled);
+ }
}
- }
+ });
return deleted;
}
@@ -557,21 +562,23 @@ class ShortcutPackage extends ShortcutPackageItem {
}
if (oldShortcut.isPinned() || oldShortcut.isCached()) {
- oldShortcut.setRank(0);
- oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_MANIFEST);
- if (disable) {
- oldShortcut.addFlags(ShortcutInfo.FLAG_DISABLED);
- // Do not overwrite the disabled reason if one is alreay set.
- if (oldShortcut.getDisabledReason() == ShortcutInfo.DISABLED_REASON_NOT_DISABLED) {
- oldShortcut.setDisabledReason(disabledReason);
+ mutateShortcut(oldShortcut.getId(), oldShortcut, si -> {
+ si.setRank(0);
+ si.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_MANIFEST);
+ if (disable) {
+ si.addFlags(ShortcutInfo.FLAG_DISABLED);
+ // Do not overwrite the disabled reason if one is alreay set.
+ if (si.getDisabledReason() == ShortcutInfo.DISABLED_REASON_NOT_DISABLED) {
+ si.setDisabledReason(disabledReason);
+ }
}
- }
- oldShortcut.setTimestamp(mShortcutUser.mService.injectCurrentTimeMillis());
+ si.setTimestamp(mShortcutUser.mService.injectCurrentTimeMillis());
- // See ShortcutRequestPinProcessor.directPinShortcut().
- if (mShortcutUser.mService.isDummyMainActivity(oldShortcut.getActivity())) {
- oldShortcut.setActivity(null);
- }
+ // See ShortcutRequestPinProcessor.directPinShortcut().
+ if (mShortcutUser.mService.isDummyMainActivity(si.getActivity())) {
+ si.setActivity(null);
+ }
+ });
return null;
} else {
@@ -581,12 +588,11 @@ class ShortcutPackage extends ShortcutPackageItem {
}
public void enableWithId(@NonNull String shortcutId) {
- final ShortcutInfo shortcut = mShortcuts.get(shortcutId);
- if (shortcut != null) {
- ensureNotImmutable(shortcut, /*ignoreInvisible=*/ true);
- shortcut.clearFlags(ShortcutInfo.FLAG_DISABLED);
- shortcut.setDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
- }
+ mutateShortcut(shortcutId, null, si -> {
+ ensureNotImmutable(si, /*ignoreInvisible=*/ true);
+ si.clearFlags(ShortcutInfo.FLAG_DISABLED);
+ si.setDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
+ });
}
public void updateInvisibleShortcutForPinRequestWith(@NonNull ShortcutInfo shortcut) {
@@ -609,22 +615,25 @@ class ShortcutPackage extends ShortcutPackageItem {
*
Then remove all shortcuts that are not dynamic and no longer pinned either.
*/
public void refreshPinnedFlags() {
- // First, un-pin all shortcuts
- for (int i = mShortcuts.size() - 1; i >= 0; i--) {
- mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_PINNED);
+ // TODO: rewrite this function with proper query (i.e. fetch only pinned shortcuts and
+ // unpin if it's no longer pinned by any launcher and vice versa)
+ final List shortcuts = new ArrayList<>(mShortcuts.values());
+ final Map shortcutMap = new ArrayMap<>(shortcuts.size());
+ for (ShortcutInfo si : shortcuts) {
+ shortcutMap.put(si.getId(), si);
}
+ final Set pinnedShortcuts = new ArraySet<>();
- // Then, for the pinned set for each launcher, set the pin flag one by one.
+ // First, for the pinned set for each launcher, keep track of their id one by one.
mShortcutUser.forAllLaunchers(launcherShortcuts -> {
final ArraySet pinned = launcherShortcuts.getPinnedShortcutIds(
getPackageName(), getPackageUserId());
-
if (pinned == null || pinned.size() == 0) {
return;
}
for (int i = pinned.size() - 1; i >= 0; i--) {
final String id = pinned.valueAt(i);
- final ShortcutInfo si = mShortcuts.get(id);
+ final ShortcutInfo si = shortcutMap.get(id);
if (si == null) {
// This happens if a launcher pinned shortcuts from this package, then backup&
// restored, but this package doesn't allow backing up.
@@ -632,9 +641,21 @@ class ShortcutPackage extends ShortcutPackageItem {
// That's fine, when the launcher is restored, we'll fix it.
continue;
}
- si.addFlags(ShortcutInfo.FLAG_PINNED);
+ pinnedShortcuts.add(si.getId());
}
});
+ // Then, update the pinned state if necessary
+ for (int i = shortcuts.size() - 1; i >= 0; i--) {
+ final ShortcutInfo si = shortcuts.get(i);
+ if (pinnedShortcuts.contains(si.getId()) && !si.isPinned()) {
+ mutateShortcut(si.getId(), si,
+ shortcut -> shortcut.addFlags(ShortcutInfo.FLAG_PINNED));
+ }
+ if (!pinnedShortcuts.contains(si.getId()) && si.isPinned()) {
+ mutateShortcut(si.getId(), si, shortcut ->
+ shortcut.clearFlags(ShortcutInfo.FLAG_PINNED));
+ }
+ }
// Lastly, remove the ones that are no longer pinned, cached nor dynamic.
removeOrphans();
@@ -1034,8 +1055,10 @@ class ShortcutPackage extends ShortcutPackageItem {
continue;
}
Slog.i(TAG, String.format("Restoring shortcut: %s", si.getId()));
- si.clearFlags(ShortcutInfo.FLAG_DISABLED);
- si.setDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
+ mutateShortcut(si.getId(), si, shortcut -> {
+ shortcut.clearFlags(ShortcutInfo.FLAG_DISABLED);
+ shortcut.setDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
+ });
}
// For existing shortcuts, update timestamps if they have any resources.
@@ -1065,21 +1088,24 @@ class ShortcutPackage extends ShortcutPackageItem {
}
if (si.hasAnyResources()) {
- if (!si.isOriginallyFromManifest()) {
+ if (publisherRes == null) {
+ publisherRes = getPackageResources();
if (publisherRes == null) {
- publisherRes = getPackageResources();
- if (publisherRes == null) {
- break; // Resources couldn't be loaded.
- }
+ break; // Resources couldn't be loaded.
+ }
+ }
+
+ final Resources res = publisherRes;
+ mutateShortcut(si.getId(), si, shortcut -> {
+ if (!shortcut.isOriginallyFromManifest()) {
+ shortcut.lookupAndFillInResourceIds(res);
}
- // TODO: update resource strings in AppSearch
// If this shortcut is not from a manifest, then update all resource IDs
// from resource names. (We don't allow resource strings for
// non-manifest at the moment, but icons can still be resources.)
- si.lookupAndFillInResourceIds(publisherRes);
- }
- si.setTimestamp(s.injectCurrentTimeMillis());
+ shortcut.setTimestamp(s.injectCurrentTimeMillis());
+ });
}
}
}
@@ -1382,8 +1408,11 @@ class ShortcutPackage extends ShortcutPackageItem {
}
}
- si.resolveResourceStrings(publisherRes);
- si.setTimestamp(s.injectCurrentTimeMillis());
+ final Resources res = publisherRes;
+ mutateShortcut(si.getId(), si, shortcut -> {
+ shortcut.resolveResourceStrings(res);
+ shortcut.setTimestamp(s.injectCurrentTimeMillis());
+ });
if (changedShortcuts == null) {
changedShortcuts = new ArrayList<>(1);
@@ -1400,7 +1429,7 @@ class ShortcutPackage extends ShortcutPackageItem {
public void clearAllImplicitRanks() {
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
final ShortcutInfo si = mShortcuts.valueAt(i);
- si.clearImplicitRankAndRankChangedFlag();
+ mutateShortcut(si.getId(), si, ShortcutInfo::clearImplicitRankAndRankChangedFlag);
}
}
@@ -1445,8 +1474,10 @@ class ShortcutPackage extends ShortcutPackageItem {
final ShortcutInfo si = mShortcuts.valueAt(i);
if (si.isFloating()) {
if (si.getRank() != 0) {
- si.setTimestamp(now);
- si.setRank(0);
+ mutateShortcut(si.getId(), si, shortcut -> {
+ shortcut.setTimestamp(now);
+ shortcut.setRank(0);
+ });
}
}
}
@@ -1479,8 +1510,10 @@ class ShortcutPackage extends ShortcutPackageItem {
}
final int thisRank = rank++;
if (si.getRank() != thisRank) {
- si.setTimestamp(now);
- si.setRank(thisRank);
+ mutateShortcut(si.getId(), si, shortcut -> {
+ shortcut.setTimestamp(now);
+ shortcut.setRank(thisRank);
+ });
}
}
}
@@ -2172,6 +2205,32 @@ class ShortcutPackage extends ShortcutPackageItem {
resetAppSearch(null);
}
+ void mutateShortcut(@NonNull final String id, @Nullable final ShortcutInfo shortcut,
+ @NonNull final Consumer transform) {
+ Objects.requireNonNull(id);
+ Objects.requireNonNull(transform);
+ synchronized (mLock) {
+ if (shortcut != null) {
+ transform.accept(shortcut);
+ } else {
+ transform.accept(findShortcutById(id));
+ }
+ // TODO: Load ShortcutInfo from AppSearch, apply transformation logic and save
+ }
+ }
+
+ /**
+ * Removes shortcuts from AppSearch.
+ */
+ void removeShortcuts() {
+ }
+
+ /**
+ * Merge/replace shortcuts parsed from xml file.
+ */
+ void restoreParsedShortcuts(final boolean replace) {
+ }
+
private boolean verifyRanksSequential(List list) {
boolean failed = false;
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 209a143f665d7..f84eb4437acf1 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -662,7 +662,7 @@ public class ShortcutService extends IShortcutService.Stub {
/** lifecycle event */
void handleUnlockUser(int userId) {
if (DEBUG) {
- Slog.d(TAG, "handleUnlockUser: user=" + userId);
+ Slog.d(TAG, "handleUnlockUser: user=" + userId);
}
synchronized (mUnlockedUsers) {
mUnlockedUsers.put(userId, true);
@@ -1179,6 +1179,14 @@ public class ShortcutService extends IShortcutService.Stub {
}
}
+ void postValue(@NonNull final ShortcutInfo shortcutInfo,
+ @NonNull final Consumer cb) {
+ final String pkg = shortcutInfo.getPackage();
+ final int userId = shortcutInfo.getUserId();
+ final String id = shortcutInfo.getId();
+ getPackageShortcutsLocked(pkg, userId).mutateShortcut(id, shortcutInfo, cb);
+ }
+
/** Return the last reset time. */
@GuardedBy("mLock")
long getLastResetTimeLocked() {
@@ -1566,7 +1574,6 @@ public class ShortcutService extends IShortcutService.Stub {
* resource-based strings.
*/
void fixUpShortcutResourceNamesAndValues(ShortcutInfo si) {
- // TODO: update resource names in AppSearch
final Resources publisherRes = injectGetResourcesForApplicationAsUser(
si.getPackage(), si.getUserId());
if (publisherRes != null) {
@@ -1947,7 +1954,7 @@ public class ShortcutService extends IShortcutService.Stub {
final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
injectBinderCallingPid(), injectBinderCallingUid());
- List changedShortcuts = null;
+ final List changedShortcuts = new ArrayList<>(1);
synchronized (mLock) {
throwIfUserLockedL(userId);
@@ -1975,59 +1982,57 @@ public class ShortcutService extends IShortcutService.Stub {
final ShortcutInfo source = newShortcuts.get(i);
fixUpIncomingShortcutInfo(source, /* forUpdate= */ true);
- final ShortcutInfo target = ps.findShortcutById(source.getId());
+ ps.mutateShortcut(source.getId(), null, target -> {
+ // Invisible shortcuts can't be updated.
+ if (target == null || !target.isVisibleToPublisher()) {
+ return;
+ }
- // Invisible shortcuts can't be updated.
- if (target == null || !target.isVisibleToPublisher()) {
- continue;
- }
+ if (target.isEnabled() != source.isEnabled()) {
+ Slog.w(TAG,
+ "ShortcutInfo.enabled cannot be changed with updateShortcuts()");
+ }
- if (target.isEnabled() != source.isEnabled()) {
- Slog.w(TAG,
- "ShortcutInfo.enabled cannot be changed with updateShortcuts()");
- }
+ if (target.isLongLived() != source.isLongLived()) {
+ Slog.w(TAG,
+ "ShortcutInfo.longLived cannot be changed with updateShortcuts()");
+ }
- if (target.isLongLived() != source.isLongLived()) {
- Slog.w(TAG,
- "ShortcutInfo.longLived cannot be changed with updateShortcuts()");
- }
+ // When updating the rank, we need to insert between existing ranks, so set
+ // this setRankChanged, and also copy the implicit rank fo adjustRanks().
+ if (source.hasRank()) {
+ target.setRankChanged();
+ target.setImplicitRank(source.getImplicitRank());
+ }
- // When updating the rank, we need to insert between existing ranks, so set
- // this setRankChanged, and also copy the implicit rank fo adjustRanks().
- if (source.hasRank()) {
- target.setRankChanged();
- target.setImplicitRank(source.getImplicitRank());
- }
+ final boolean replacingIcon = (source.getIcon() != null);
+ if (replacingIcon) {
+ removeIconLocked(target);
+ }
- final boolean replacingIcon = (source.getIcon() != null);
- if (replacingIcon) {
- removeIconLocked(target);
- }
+ // Note copyNonNullFieldsFrom() does the "updatable with?" check too.
+ target.copyNonNullFieldsFrom(source);
+ target.setTimestamp(injectCurrentTimeMillis());
- // Note copyNonNullFieldsFrom() does the "updatable with?" check too.
- target.copyNonNullFieldsFrom(source);
- target.setTimestamp(injectCurrentTimeMillis());
+ if (replacingIcon) {
+ saveIconAndFixUpShortcutLocked(target);
+ }
- if (replacingIcon) {
- saveIconAndFixUpShortcutLocked(target);
- }
+ // When we're updating any resource related fields, re-extract the res names and
+ // the values.
+ if (replacingIcon || source.hasStringResources()) {
+ fixUpShortcutResourceNamesAndValues(target);
+ }
- // When we're updating any resource related fields, re-extract the res names and
- // the values.
- if (replacingIcon || source.hasStringResources()) {
- fixUpShortcutResourceNamesAndValues(target);
- }
-
- if (changedShortcuts == null) {
- changedShortcuts = new ArrayList<>(1);
- }
- changedShortcuts.add(target);
+ changedShortcuts.add(target);
+ });
}
// Lastly, adjust the ranks.
ps.adjustRanks();
}
- packageShortcutsChanged(packageName, userId, changedShortcuts, null);
+ packageShortcutsChanged(packageName, userId,
+ changedShortcuts.isEmpty() ? null : changedShortcuts, null);
verifyStates();
@@ -3114,7 +3119,8 @@ public class ShortcutService extends IShortcutService.Stub {
if (doCache) {
if (si.isLongLived()) {
- si.addFlags(cacheFlags);
+ sp.mutateShortcut(si.getId(), si,
+ shortcut -> shortcut.addFlags(cacheFlags));
if (changedShortcuts == null) {
changedShortcuts = new ArrayList<>(1);
}
@@ -3125,7 +3131,8 @@ public class ShortcutService extends IShortcutService.Stub {
}
} else {
ShortcutInfo removed = null;
- si.clearFlags(cacheFlags);
+ sp.mutateShortcut(si.getId(), si, shortcut ->
+ shortcut.clearFlags(cacheFlags));
if (!si.isDynamic() && !si.isCached()) {
removed = sp.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true);
}
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 6cbc47fb59c4e..ec784d0211ddc 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -185,6 +185,9 @@ class ShortcutUser {
public ShortcutPackage removePackage(@NonNull String packageName) {
final ShortcutPackage removed = mPackages.remove(packageName);
+ if (removed != null) {
+ removed.removeShortcuts();
+ }
mService.cleanupBitmapsForPackage(mUserId, packageName);
return removed;
@@ -330,7 +333,10 @@ class ShortcutUser {
if (!shortcutPackage.rescanPackageIfNeeded(isNewApp, forceRescan)) {
if (isNewApp) {
- mPackages.remove(packageName);
+ final ShortcutPackage sp = mPackages.remove(packageName);
+ if (sp != null) {
+ sp.removeShortcuts();
+ }
}
}
}
@@ -454,6 +460,7 @@ class ShortcutUser {
case ShortcutPackage.TAG_ROOT: {
final ShortcutPackage shortcuts = ShortcutPackage.loadFromXml(
s, ret, parser, fromBackup);
+ shortcuts.restoreParsedShortcuts(false);
// Don't use addShortcut(), we don't need to save the icon.
ret.mPackages.put(shortcuts.getPackageName(), shortcuts);
@@ -488,6 +495,7 @@ class ShortcutUser {
final ShortcutPackage sp = ShortcutPackage.loadFromFile(s, ret, f, fromBackup);
if (sp != null) {
ret.mPackages.put(sp.getPackageName(), sp);
+ sp.restoreParsedShortcuts(false);
}
});
@@ -570,6 +578,7 @@ class ShortcutUser {
Log.w(TAG, "Shortcuts for package " + sp.getPackageName() + " are being restored."
+ " Existing non-manifeset shortcuts will be overwritten.");
}
+ sp.restoreParsedShortcuts(true);
addPackage(sp);
restoredPackages[0]++;
restoredShortcuts[0] += sp.getShortcutCount();