Merge "Properly protect ShortcutPackage#mShortcuts with synchronization lock." into tm-dev am: 645f51d337

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18188747

Change-Id: Ie7669580df29be8a33a18febb2f484c9ab8879f7
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
TreeHugger Robot
2022-05-06 23:21:55 +00:00
committed by Automerger Merge Worker

View File

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