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();