Shortcut integration with AppSearch (Part 10)
1. Persists enabled pinned shortcuts in xml.
Before the integration there are existing logic which persists shortcuts
in xml file, which makes it a good candidate for a cache layer on top of
AppSearch so that when launcher is loading the workspace, the later no
longer needs to wait for the former to finish initialization.
2. Postpone package rescan
When the device is rebooted for the very first time after the initial
restore, it would trigger a rescan on all the packages that was
installed on the device before the reboot. Since the device just
finished a restore, the number of new packages is large and
re-publishing manifest shortcuts from these packages could take a while.
In this CL we move the rescan to a background thread to prevent launcher
from being blocked when loading the workspace.
Bug: 151359749
Test: atest ShortcutManagerTest1 ShortcutManagerTest2
ShortcutManagerTest3 ShortcutManagerTest4 ShortcutManagerTest5
ShortcutManagerTest6 ShortcutManagerTest7 ShortcutManagerTest8
ShortcutManagerTest9 ShortcutManagerTest10 ShortcutManagerTest11
ShortcutManagerTest12
Test: atest CtsShortcutManagerTestCases
Change-Id: I612c8ad49f3408d80fbb6d64f948e95ae6641899
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 17516cc..e222df0 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -160,7 +160,7 @@
/**
* An temp in-memory copy of shortcuts for this package that was loaded from xml, keyed on IDs.
*/
- final ArraySet<ShortcutInfo> mShortcuts = new ArraySet<>();
+ final ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
/**
* All the share targets from the package
@@ -832,6 +832,26 @@
}
}
+ /**
+ * Find all pinned shortcuts that match {@code query}.
+ */
+ public void findAllPinned(@NonNull List<ShortcutInfo> result,
+ @Nullable Predicate<ShortcutInfo> query, int cloneFlag,
+ @Nullable String callingLauncher, int launcherUserId, boolean getPinnedByAnyLauncher) {
+ if (getPackageInfo().isShadow()) {
+ // Restored and the app not installed yet, so don't return any.
+ return;
+ }
+ final ShortcutService s = mShortcutUser.mService;
+
+ // Set of pinned shortcuts by the calling launcher.
+ final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null
+ : s.getLauncherShortcutsLocked(callingLauncher, getPackageUserId(), launcherUserId)
+ .getPinnedShortcutIds(getPackageName(), getPackageUserId());
+ mShortcuts.values().forEach(si -> filter(result, query, cloneFlag, callingLauncher,
+ pinnedByCallerSet, getPinnedByAnyLauncher, si));
+ }
+
private void filter(@NonNull final List<ShortcutInfo> result,
@Nullable final Predicate<ShortcutInfo> query, final int cloneFlag,
@Nullable final String callingLauncher,
@@ -1672,10 +1692,10 @@
@Override
public void saveToXml(@NonNull TypedXmlSerializer out, boolean forBackup)
throws IOException, XmlPullParserException {
- final int size = getShortcutCount();
+ final int size = mShortcuts.size();
final int shareTargetSize = mShareTargets.size();
- if (size == 0 && shareTargetSize == 0 && mApiCallCount == 0) {
+ if (size == 0 && shareTargetSize == 0 && mApiCallCount == 0 && getShortcutCount() == 0) {
return; // nothing to write.
}
@@ -1695,15 +1715,8 @@
}
getPackageInfo().saveToXml(mShortcutUser.mService, out, forBackup);
- if (forBackup) {
- // Shortcuts are persisted in AppSearch, xml is only needed for backup.
- forEachShortcut(AppSearchShortcutInfo.QUERY_IS_PINNED_AND_ENABLED, si -> {
- try {
- saveShortcut(out, si, forBackup, getPackageInfo().isBackupAllowed());
- } catch (IOException | XmlPullParserException e) {
- throw new RuntimeException(e);
- }
- });
+ for (int j = 0; j < size; j++) {
+ saveShortcut(out, mShortcuts.valueAt(j), forBackup, getPackageInfo().isBackupAllowed());
}
if (!forBackup) {
@@ -1916,7 +1929,7 @@
final ShortcutInfo si = parseShortcut(parser, packageName,
shortcutUser.getUserId(), fromBackup);
// Don't use addShortcut(), we don't need to save the icon.
- ret.mShortcuts.add(si);
+ ret.mShortcuts.put(si.getId(), si);
continue;
case TAG_SHARE_TARGET:
ret.mShareTargets.add(ShareTargetInfo.loadFromXml(parser));
@@ -2268,11 +2281,23 @@
private void saveShortcut(@NonNull final Collection<ShortcutInfo> shortcuts) {
Objects.requireNonNull(shortcuts);
+ shortcuts.forEach(si -> {
+ if (si.isPinned()) {
+ mShortcuts.put(si.getId(), si);
+ } else {
+ mShortcuts.remove(si.getId());
+ }
+ });
+ saveToAppSearch(shortcuts);
+ }
+
+ private void saveToAppSearch(@NonNull final Collection<ShortcutInfo> shortcuts) {
+ Objects.requireNonNull(shortcuts);
if (shortcuts.isEmpty()) {
// No need to invoke AppSearch when there's nothing to save.
return;
}
- awaitInAppSearch("Saving shortcut", session -> {
+ awaitInAppSearch("Saving shortcuts", session -> {
final AndroidFuture<Boolean> future = new AndroidFuture<>();
session.put(new PutDocumentsRequest.Builder()
.addGenericDocuments(
@@ -2314,6 +2339,7 @@
private void removeShortcut(@NonNull final String id) {
Objects.requireNonNull(id);
+ mShortcuts.remove(id);
awaitInAppSearch("Removing shortcut with id=" + id, session -> {
final AndroidFuture<Boolean> future = new AndroidFuture<>();
session.remove(new RemoveByUriRequest.Builder(getPackageName()).addUris(id).build(),
@@ -2474,11 +2500,15 @@
.detectAll()
.penaltyLog() // TODO: change this to penaltyDeath to fix the call-site
.build());
- if (!mIsInitilized || forceReset) {
+ final boolean wasInitialized = mIsInitilized;
+ if (!wasInitialized || forceReset) {
ConcurrentUtils.waitForFutureNoInterrupt(
setupSchema(session), "Setting up schema");
}
mIsInitilized = true;
+ if (!wasInitialized) {
+ restoreParsedShortcuts(false);
+ }
return ConcurrentUtils.waitForFutureNoInterrupt(cb.apply(session), description);
} catch (Exception e) {
Slog.e(TAG, "Failed to initiate app search for shortcut package "
@@ -2526,13 +2556,17 @@
}
/**
- * Merge/replace shortcuts parsed from xml file.
+ * Replace shortcuts parsed from xml file.
*/
- void restoreParsedShortcuts(final boolean replace) {
+ void restoreParsedShortcuts() {
+ restoreParsedShortcuts(true);
+ }
+
+ private void restoreParsedShortcuts(final boolean replace) {
if (replace) {
removeShortcuts();
}
- saveShortcut(mShortcuts);
+ saveToAppSearch(mShortcuts.values());
}
private boolean verifyRanksSequential(List<ShortcutInfo> list) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 9e26ccd..dcf730d 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1306,7 +1306,7 @@
mUsers.put(userId, userPackages);
// Also when a user's data is first accessed, scan all packages.
- checkPackageChanges(userId);
+ injectPostToHandler(() -> checkPackageChanges(userId));
}
return userPackages;
}
@@ -3006,9 +3006,18 @@
((queryFlags & ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER) != 0);
queryFlags |= (getPinnedByAnyLauncher ? ShortcutQuery.FLAG_MATCH_PINNED : 0);
+ final boolean matchPinnedOnly =
+ ((queryFlags & ShortcutQuery.FLAG_MATCH_PINNED) != 0)
+ && ((queryFlags & ShortcutQuery.FLAG_MATCH_CACHED) == 0)
+ && ((queryFlags & ShortcutQuery.FLAG_MATCH_DYNAMIC) == 0)
+ && ((queryFlags & ShortcutQuery.FLAG_MATCH_MANIFEST) == 0);
+
final Predicate<ShortcutInfo> filter = getFilterFromQuery(ids, locusIds, changedSince,
componentName, queryFlags, getPinnedByAnyLauncher);
- if (ids != null && !ids.isEmpty()) {
+ if (matchPinnedOnly) {
+ p.findAllPinned(ret, filter, cloneFlag, callingPackage, launcherUserId,
+ getPinnedByAnyLauncher);
+ } else if (ids != null && !ids.isEmpty()) {
p.findAllByIds(ret, ids, filter, cloneFlag, callingPackage, launcherUserId,
getPinnedByAnyLauncher);
} else {
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 5f0aa03..069944d 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -457,7 +457,6 @@
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);
@@ -492,7 +491,6 @@
final ShortcutPackage sp = ShortcutPackage.loadFromFile(s, ret, f, fromBackup);
if (sp != null) {
ret.mPackages.put(sp.getPackageName(), sp);
- sp.restoreParsedShortcuts(false);
}
});
@@ -575,7 +573,7 @@
Log.w(TAG, "Shortcuts for package " + sp.getPackageName() + " are being restored."
+ " Existing non-manifeset shortcuts will be overwritten.");
}
- sp.restoreParsedShortcuts(true);
+ sp.restoreParsedShortcuts();
addPackage(sp);
restoredPackages[0]++;
restoredShortcuts[0] += sp.getShortcutCount();