diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 4df54b74bb1d2..fa0c6c393cdc5 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -166,18 +166,19 @@ class ShortcutPackage extends ShortcutPackageItem { * An in-memory copy of shortcuts for this package that was loaded from xml, keyed on IDs. */ @GuardedBy("mLock") - final ArrayMap mShortcuts = new ArrayMap<>(); + private final ArrayMap mShortcuts = new ArrayMap<>(); /** * A temporary copy of shortcuts that are to be cleared once persisted into AppSearch, keyed on * IDs. */ @GuardedBy("mLock") - private ArrayMap mTransientShortcuts = new ArrayMap<>(0); + private final ArrayMap mTransientShortcuts = new ArrayMap<>(0); /** * All the share targets from the package */ + @GuardedBy("mLock") private final ArrayList mShareTargets = new ArrayList<>(0); /** @@ -231,7 +232,9 @@ class ShortcutPackage extends ShortcutPackageItem { } public int getShortcutCount() { - return mShortcuts.size(); + synchronized (mLock) { + return mShortcuts.size(); + } } @Override @@ -272,7 +275,9 @@ class ShortcutPackage extends ShortcutPackageItem { @Nullable public ShortcutInfo findShortcutById(@Nullable final String id) { if (id == null) return null; - return mShortcuts.get(id); + synchronized (mLock) { + return mShortcuts.get(id); + } } public boolean isShortcutExistsAndInvisibleToPublisher(String id) { @@ -347,11 +352,14 @@ class ShortcutPackage extends ShortcutPackageItem { * Delete a shortcut by ID. This will *always* remove it even if it's immutable or invisible. */ private ShortcutInfo forceDeleteShortcutInner(@NonNull String id) { - final ShortcutInfo shortcut = mShortcuts.remove(id); - if (shortcut != null) { - removeIcon(shortcut); - shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED - | ShortcutInfo.FLAG_MANIFEST | ShortcutInfo.FLAG_CACHED_ALL); + final ShortcutInfo shortcut; + synchronized (mLock) { + shortcut = mShortcuts.remove(id); + if (shortcut != null) { + removeIcon(shortcut); + shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED + | ShortcutInfo.FLAG_MANIFEST | ShortcutInfo.FLAG_CACHED_ALL); + } } return shortcut; } @@ -524,14 +532,16 @@ class ShortcutPackage extends ShortcutPackageItem { public List deleteAllDynamicShortcuts() { final long now = mShortcutUser.mService.injectCurrentTimeMillis(); boolean changed = false; - for (int i = mShortcuts.size() - 1; i >= 0; i--) { - ShortcutInfo si = mShortcuts.valueAt(i); - if (si.isDynamic() && si.isVisibleToPublisher()) { - changed = true; + synchronized (mLock) { + for (int i = mShortcuts.size() - 1; i >= 0; i--) { + ShortcutInfo si = mShortcuts.valueAt(i); + if (si.isDynamic() && si.isVisibleToPublisher()) { + changed = true; - si.setTimestamp(now); - si.clearFlags(ShortcutInfo.FLAG_DYNAMIC); - si.setRank(0); // It may still be pinned, so clear the rank. + si.setTimestamp(now); + si.clearFlags(ShortcutInfo.FLAG_DYNAMIC); + si.setRank(0); // It may still be pinned, so clear the rank. + } } } removeAllShortcutsAsync(); @@ -874,59 +884,63 @@ class ShortcutPackage extends ShortcutPackageItem { */ public List getMatchingShareTargets( @NonNull IntentFilter filter) { - final List matchedTargets = new ArrayList<>(); - for (int i = 0; i < mShareTargets.size(); i++) { - final ShareTargetInfo target = mShareTargets.get(i); - for (ShareTargetInfo.TargetData data : target.mTargetData) { - if (filter.hasDataType(data.mMimeType)) { - // Matched at least with one data type - matchedTargets.add(target); - break; - } - } - } - - if (matchedTargets.isEmpty()) { - return new ArrayList<>(); - } - - // Get the list of all dynamic shortcuts in this package. - final ArrayList shortcuts = new ArrayList<>(); - // Pass callingLauncher to ensure pinned flag marked by system ui, e.g. ShareSheet, are - // included in the result - findAll(shortcuts, ShortcutInfo::isNonManifestVisible, - ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION, - mShortcutUser.mService.mContext.getPackageName(), - 0, /*getPinnedByAnyLauncher=*/ false); - - final List result = new ArrayList<>(); - for (int i = 0; i < shortcuts.size(); i++) { - final Set categories = shortcuts.get(i).getCategories(); - if (categories == null || categories.isEmpty()) { - continue; - } - for (int j = 0; j < matchedTargets.size(); j++) { - // Shortcut must have all of share target categories - boolean hasAllCategories = true; - final ShareTargetInfo target = matchedTargets.get(j); - for (int q = 0; q < target.mCategories.length; q++) { - if (!categories.contains(target.mCategories[q])) { - hasAllCategories = false; + synchronized (mLock) { + final List matchedTargets = new ArrayList<>(); + for (int i = 0; i < mShareTargets.size(); i++) { + final ShareTargetInfo target = mShareTargets.get(i); + for (ShareTargetInfo.TargetData data : target.mTargetData) { + if (filter.hasDataType(data.mMimeType)) { + // Matched at least with one data type + matchedTargets.add(target); break; } } - if (hasAllCategories) { - result.add(new ShortcutManager.ShareShortcutInfo(shortcuts.get(i), - new ComponentName(getPackageName(), target.mTargetClass))); - break; + } + + if (matchedTargets.isEmpty()) { + return new ArrayList<>(); + } + + // Get the list of all dynamic shortcuts in this package. + final ArrayList shortcuts = new ArrayList<>(); + // Pass callingLauncher to ensure pinned flag marked by system ui, e.g. ShareSheet, are + // included in the result + findAll(shortcuts, ShortcutInfo::isNonManifestVisible, + ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION, + mShortcutUser.mService.mContext.getPackageName(), + 0, /*getPinnedByAnyLauncher=*/ false); + + final List result = new ArrayList<>(); + for (int i = 0; i < shortcuts.size(); i++) { + final Set categories = shortcuts.get(i).getCategories(); + if (categories == null || categories.isEmpty()) { + continue; + } + for (int j = 0; j < matchedTargets.size(); j++) { + // Shortcut must have all of share target categories + boolean hasAllCategories = true; + final ShareTargetInfo target = matchedTargets.get(j); + for (int q = 0; q < target.mCategories.length; q++) { + if (!categories.contains(target.mCategories[q])) { + hasAllCategories = false; + break; + } + } + if (hasAllCategories) { + result.add(new ShortcutManager.ShareShortcutInfo(shortcuts.get(i), + new ComponentName(getPackageName(), target.mTargetClass))); + break; + } } } + return result; } - return result; } public boolean hasShareTargets() { - return !mShareTargets.isEmpty(); + synchronized (mLock) { + return !mShareTargets.isEmpty(); + } } /** @@ -935,38 +949,40 @@ class ShortcutPackage extends ShortcutPackageItem { * the app's Xml resource. */ int getSharingShortcutCount() { - if (mShareTargets.isEmpty()) { - return 0; - } - - // Get the list of all dynamic shortcuts in this package - final ArrayList shortcuts = new ArrayList<>(); - findAll(shortcuts, ShortcutInfo::isNonManifestVisible, - ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER); - - int sharingShortcutCount = 0; - for (int i = 0; i < shortcuts.size(); i++) { - final Set categories = shortcuts.get(i).getCategories(); - if (categories == null || categories.isEmpty()) { - continue; + synchronized (mLock) { + if (mShareTargets.isEmpty()) { + return 0; } - for (int j = 0; j < mShareTargets.size(); j++) { - // A SharingShortcut must have all of share target categories - boolean hasAllCategories = true; - final ShareTargetInfo target = mShareTargets.get(j); - for (int q = 0; q < target.mCategories.length; q++) { - if (!categories.contains(target.mCategories[q])) { - hasAllCategories = false; + + // Get the list of all dynamic shortcuts in this package + final ArrayList shortcuts = new ArrayList<>(); + findAll(shortcuts, ShortcutInfo::isNonManifestVisible, + ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER); + + int sharingShortcutCount = 0; + for (int i = 0; i < shortcuts.size(); i++) { + final Set categories = shortcuts.get(i).getCategories(); + if (categories == null || categories.isEmpty()) { + continue; + } + for (int j = 0; j < mShareTargets.size(); j++) { + // A SharingShortcut must have all of share target categories + boolean hasAllCategories = true; + final ShareTargetInfo target = mShareTargets.get(j); + for (int q = 0; q < target.mCategories.length; q++) { + if (!categories.contains(target.mCategories[q])) { + hasAllCategories = false; + break; + } + } + if (hasAllCategories) { + sharingShortcutCount++; break; } } - if (hasAllCategories) { - sharingShortcutCount++; - break; - } } + return sharingShortcutCount; } - return sharingShortcutCount; } /** @@ -1090,19 +1106,25 @@ class ShortcutPackage extends ShortcutPackageItem { // Now prepare to publish manifest shortcuts. List newManifestShortcutList = null; - try { - newManifestShortcutList = ShortcutParser.parseShortcuts(mShortcutUser.mService, - getPackageName(), getPackageUserId(), mShareTargets); - } catch (IOException|XmlPullParserException e) { - Slog.e(TAG, "Failed to load shortcuts from AndroidManifest.xml.", e); + final int shareTargetSize; + synchronized (mLock) { + try { + shareTargetSize = mShareTargets.size(); + newManifestShortcutList = ShortcutParser.parseShortcuts(mShortcutUser.mService, + getPackageName(), getPackageUserId(), mShareTargets); + } catch (IOException | XmlPullParserException e) { + Slog.e(TAG, "Failed to load shortcuts from AndroidManifest.xml.", e); + } } final int manifestShortcutSize = newManifestShortcutList == null ? 0 : newManifestShortcutList.size(); if (ShortcutService.DEBUG || ShortcutService.DEBUG_REBOOT) { Slog.d(TAG, - String.format("Package %s has %d manifest shortcut(s), and %d share target(s)", - getPackageName(), manifestShortcutSize, mShareTargets.size())); + String.format( + "Package %s has %d manifest shortcut(s), and %d share target(s)", + getPackageName(), manifestShortcutSize, shareTargetSize)); } + if (isNewApp && (manifestShortcutSize == 0)) { // If it's a new app, and it doesn't have manifest shortcuts, then nothing to do. @@ -1701,37 +1723,38 @@ class ShortcutPackage extends ShortcutPackageItem { @Override public void saveToXml(@NonNull TypedXmlSerializer out, boolean forBackup) throws IOException, XmlPullParserException { - final int size = mShortcuts.size(); - final int shareTargetSize = mShareTargets.size(); + synchronized (mLock) { + final int size = mShortcuts.size(); + final int shareTargetSize = mShareTargets.size(); - if (hasNoShortcut() && shareTargetSize == 0 && mApiCallCount == 0) { - return; // nothing to write. - } + if (hasNoShortcut() && shareTargetSize == 0 && mApiCallCount == 0) { + return; // nothing to write. + } - out.startTag(null, TAG_ROOT); + out.startTag(null, TAG_ROOT); - ShortcutService.writeAttr(out, ATTR_NAME, getPackageName()); - ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount); - ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime); - if (!forBackup) { - synchronized (mLock) { - ShortcutService.writeAttr(out, ATTR_SCHEMA_VERSON, (mIsAppSearchSchemaUpToDate) + ShortcutService.writeAttr(out, ATTR_NAME, getPackageName()); + ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount); + ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime); + if (!forBackup) { + ShortcutService.writeAttr(out, ATTR_SCHEMA_VERSON, mIsAppSearchSchemaUpToDate ? AppSearchShortcutInfo.SCHEMA_VERSION : 0); } - } - getPackageInfo().saveToXml(mShortcutUser.mService, out, forBackup); + getPackageInfo().saveToXml(mShortcutUser.mService, out, forBackup); - for (int j = 0; j < size; j++) { - saveShortcut(out, mShortcuts.valueAt(j), forBackup, getPackageInfo().isBackupAllowed()); - } - - if (!forBackup) { - for (int j = 0; j < shareTargetSize; j++) { - mShareTargets.get(j).saveToXml(out); + for (int j = 0; j < size; j++) { + saveShortcut( + out, mShortcuts.valueAt(j), forBackup, getPackageInfo().isBackupAllowed()); } - } - out.endTag(null, TAG_ROOT); + if (!forBackup) { + for (int j = 0; j < shareTargetSize; j++) { + mShareTargets.get(j).saveToXml(out); + } + } + + out.endTag(null, TAG_ROOT); + } } private void saveShortcut(TypedXmlSerializer out, ShortcutInfo si, boolean forBackup, @@ -1917,38 +1940,38 @@ class ShortcutPackage extends ShortcutPackageItem { synchronized (ret.mLock) { ret.mIsAppSearchSchemaUpToDate = ShortcutService.parseIntAttribute( parser, ATTR_SCHEMA_VERSON, 0) == AppSearchShortcutInfo.SCHEMA_VERSION; - } - ret.mApiCallCount = ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT); - ret.mLastResetTime = ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET); + ret.mApiCallCount = ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT); + ret.mLastResetTime = ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET); - final int outerDepth = parser.getDepth(); - int type; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { - if (type != XmlPullParser.START_TAG) { - continue; - } - final int depth = parser.getDepth(); - final String tag = parser.getName(); - if (depth == outerDepth + 1) { - switch (tag) { - case ShortcutPackageInfo.TAG_ROOT: - ret.getPackageInfo().loadFromXml(parser, fromBackup); - - continue; - case TAG_SHORTCUT: - final ShortcutInfo si = parseShortcut(parser, packageName, - shortcutUser.getUserId(), fromBackup); - // Don't use addShortcut(), we don't need to save the icon. - ret.mShortcuts.put(si.getId(), si); - continue; - case TAG_SHARE_TARGET: - ret.mShareTargets.add(ShareTargetInfo.loadFromXml(parser)); - continue; + final int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type != XmlPullParser.START_TAG) { + continue; } + final int depth = parser.getDepth(); + final String tag = parser.getName(); + if (depth == outerDepth + 1) { + switch (tag) { + case ShortcutPackageInfo.TAG_ROOT: + ret.getPackageInfo().loadFromXml(parser, fromBackup); + + continue; + case TAG_SHORTCUT: + final ShortcutInfo si = parseShortcut(parser, packageName, + shortcutUser.getUserId(), fromBackup); + // Don't use addShortcut(), we don't need to save the icon. + ret.mShortcuts.put(si.getId(), si); + continue; + case TAG_SHARE_TARGET: + ret.mShareTargets.add(ShareTargetInfo.loadFromXml(parser)); + continue; + } + } + ShortcutService.warnForInvalidTag(depth, tag); } - ShortcutService.warnForInvalidTag(depth, tag); } return ret; } @@ -2152,7 +2175,9 @@ class ShortcutPackage extends ShortcutPackageItem { @VisibleForTesting List getAllShareTargetsForTest() { - return new ArrayList<>(mShareTargets); + synchronized (mLock) { + return new ArrayList<>(mShareTargets); + } } @Override @@ -2291,15 +2316,19 @@ class ShortcutPackage extends ShortcutPackageItem { private void saveShortcut(@NonNull final Collection shortcuts) { Objects.requireNonNull(shortcuts); - for (ShortcutInfo si : shortcuts) { - mShortcuts.put(si.getId(), si); + synchronized (mLock) { + for (ShortcutInfo si : shortcuts) { + mShortcuts.put(si.getId(), si); + } } } @Nullable List findAll(@NonNull final Collection ids) { - return ids.stream().map(mShortcuts::get) - .filter(Objects::nonNull).collect(Collectors.toList()); + synchronized (mLock) { + return ids.stream().map(mShortcuts::get) + .filter(Objects::nonNull).collect(Collectors.toList()); + } } private void forEachShortcut(@NonNull final Consumer cb) { @@ -2318,10 +2347,12 @@ class ShortcutPackage extends ShortcutPackageItem { private void forEachShortcutStopWhen( @NonNull final Function cb) { - for (int i = mShortcuts.size() - 1; i >= 0; i--) { - final ShortcutInfo si = mShortcuts.valueAt(i); - if (cb.apply(si)) { - return; + synchronized (mLock) { + for (int i = mShortcuts.size() - 1; i >= 0; i--) { + final ShortcutInfo si = mShortcuts.valueAt(i); + if (cb.apply(si)) { + return; + } } } } @@ -2461,6 +2492,7 @@ class ShortcutPackage extends ShortcutPackageItem { }))); } + @GuardedBy("mLock") @Override void scheduleSaveToAppSearchLocked() { final Map copy = new ArrayMap<>(mShortcuts);