Add UserHandle to ShortcutInfo, and simplify LauncherApps APIs.
- Also fixes the bitmap recycle bug in the service.
Fixes 28053541
Change-Id: I2b244feda0f85c60e2c15af427fcad95ad5e6da5
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 4c18e15..79d9c86 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -213,8 +213,11 @@
* Checks if the caller is in the same group as the userToCheck.
*/
private void ensureInUserProfiles(UserHandle userToCheck, String message) {
+ ensureInUserProfiles(userToCheck.getIdentifier(), message);
+ }
+
+ private void ensureInUserProfiles(int targetUserId, String message) {
final int callingUserId = injectCallingUserId();
- final int targetUserId = userToCheck.getIdentifier();
if (targetUserId == callingUserId) return;
@@ -253,9 +256,13 @@
* Checks if the user is enabled.
*/
private boolean isUserEnabled(UserHandle user) {
+ return isUserEnabled(user.getIdentifier());
+ }
+
+ private boolean isUserEnabled(int userId) {
long ident = injectClearCallingIdentity();
try {
- UserInfo targetUserInfo = mUm.getUserInfo(user.getIdentifier());
+ UserInfo targetUserInfo = mUm.getUserInfo(userId);
return targetUserInfo != null && targetUserInfo.isEnabled();
} finally {
injectRestoreCallingIdentity(ident);
@@ -346,8 +353,12 @@
}
private void ensureShortcutPermission(@NonNull String callingPackage, UserHandle user) {
+ ensureShortcutPermission(callingPackage, user.getIdentifier());
+ }
+
+ private void ensureShortcutPermission(@NonNull String callingPackage, int userId) {
verifyCallingPackage(callingPackage);
- ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
+ ensureInUserProfiles(userId, "Cannot start activity for unrelated profile " + userId);
if (!mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(),
callingPackage)) {
@@ -357,32 +368,24 @@
@Override
public ParceledListSlice getShortcuts(String callingPackage, long changedSince,
- String packageName, ComponentName componentName, int flags, UserHandle user) {
+ String packageName, List shortcutIds, ComponentName componentName, int flags,
+ UserHandle user) {
ensureShortcutPermission(callingPackage, user);
if (!isUserEnabled(user)) {
return new ParceledListSlice<>(new ArrayList(0));
}
+ if (shortcutIds != null && packageName == null) {
+ throw new IllegalArgumentException(
+ "To query by shortcut ID, package name must also be set");
+ }
return new ParceledListSlice<>(
mShortcutServiceInternal.getShortcuts(getCallingUserId(),
- callingPackage, changedSince, packageName,
+ callingPackage, changedSince, packageName, shortcutIds,
componentName, flags, user.getIdentifier()));
}
@Override
- public ParceledListSlice getShortcutInfo(String callingPackage, String packageName,
- List<String> ids, UserHandle user) {
- ensureShortcutPermission(callingPackage, user);
- if (!isUserEnabled(user)) {
- return new ParceledListSlice<>(new ArrayList(0));
- }
-
- return new ParceledListSlice<>(
- mShortcutServiceInternal.getShortcutInfo(getCallingUserId(),
- callingPackage, packageName, ids, user.getIdentifier()));
- }
-
- @Override
public void pinShortcuts(String callingPackage, String packageName, List<String> ids,
UserHandle user) {
ensureShortcutPermission(callingPackage, user);
@@ -396,27 +399,27 @@
}
@Override
- public int getShortcutIconResId(String callingPackage, ShortcutInfo shortcut,
- UserHandle user) {
- ensureShortcutPermission(callingPackage, user);
- if (!isUserEnabled(user)) {
+ public int getShortcutIconResId(String callingPackage, String packageName, String id,
+ int userId) {
+ ensureShortcutPermission(callingPackage, userId);
+ if (!isUserEnabled(userId)) {
return 0;
}
return mShortcutServiceInternal.getShortcutIconResId(getCallingUserId(),
- callingPackage, shortcut, user.getIdentifier());
+ callingPackage, packageName, id, userId);
}
@Override
- public ParcelFileDescriptor getShortcutIconFd(String callingPackage, ShortcutInfo shortcut,
- UserHandle user) {
- ensureShortcutPermission(callingPackage, user);
- if (!isUserEnabled(user)) {
+ public ParcelFileDescriptor getShortcutIconFd(String callingPackage,
+ String packageName, String id, int userId) {
+ ensureShortcutPermission(callingPackage, userId);
+ if (!isUserEnabled(userId)) {
return null;
}
return mShortcutServiceInternal.getShortcutIconFd(getCallingUserId(),
- callingPackage, shortcut, user.getIdentifier());
+ callingPackage, packageName, id, userId);
}
@Override
@@ -428,23 +431,23 @@
@Override
public boolean startShortcut(String callingPackage, String packageName, String shortcutId,
- Rect sourceBounds, Bundle startActivityOptions, UserHandle user) {
+ Rect sourceBounds, Bundle startActivityOptions, int userId) {
verifyCallingPackage(callingPackage);
- ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
+ ensureInUserProfiles(userId, "Cannot start activity for unrelated profile " + userId);
- if (!isUserEnabled(user)) {
+ if (!isUserEnabled(userId)) {
throw new IllegalStateException("Cannot start a shortcut for disabled profile "
- + user);
+ + userId);
}
// Even without the permission, pinned shortcuts are always launchable.
if (!mShortcutServiceInternal.isPinnedByCaller(getCallingUserId(),
- callingPackage, packageName, shortcutId, user.getIdentifier())) {
- ensureShortcutPermission(callingPackage, user);
+ callingPackage, packageName, shortcutId, userId)) {
+ ensureShortcutPermission(callingPackage, userId);
}
final Intent intent = mShortcutServiceInternal.createShortcutIntent(getCallingUserId(),
- callingPackage, packageName, shortcutId, user.getIdentifier());
+ callingPackage, packageName, shortcutId, userId);
if (intent == null) {
return false;
}
@@ -455,7 +458,7 @@
final long ident = Binder.clearCallingIdentity();
try {
- mContext.startActivityAsUser(intent, startActivityOptions, user);
+ mContext.startActivityAsUser(intent, startActivityOptions, UserHandle.of(userId));
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -768,7 +771,8 @@
final List<ShortcutInfo> list =
mShortcutServiceInternal.getShortcuts(launcherUserId,
cookie.packageName,
- /* changedSince= */ 0, packageName, /* component= */ null,
+ /* changedSince= */ 0, packageName, /* shortcutIds=*/ null,
+ /* component= */ null,
ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY
| ShortcutQuery.FLAG_GET_PINNED
| ShortcutQuery.FLAG_GET_DYNAMIC
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 1076a7a..58559a5 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
@@ -522,7 +523,7 @@
ret.getPackageInfo().loadFromXml(parser, fromBackup);
continue;
case TAG_SHORTCUT:
- final ShortcutInfo si = parseShortcut(parser, packageName);
+ final ShortcutInfo si = parseShortcut(parser, packageName, ownerUserId);
// Don't use addShortcut(), we don't need to save the icon.
ret.mShortcuts.put(si.getId(), si);
@@ -534,8 +535,8 @@
return ret;
}
- private static ShortcutInfo parseShortcut(XmlPullParser parser, String packageName)
- throws IOException, XmlPullParserException {
+ private static ShortcutInfo parseShortcut(XmlPullParser parser, String packageName,
+ @UserIdInt int userId) throws IOException, XmlPullParserException {
String id;
ComponentName activityComponent;
// Icon icon;
@@ -586,7 +587,7 @@
throw ShortcutService.throwForInvalidTag(depth, tag);
}
return new ShortcutInfo(
- id, packageName, activityComponent, /* icon =*/ null, title, text, intent,
+ userId, id, packageName, activityComponent, /* icon =*/ null, title, text, intent,
intentPersistableExtras, weight, extras, lastChangedTimestamp, flags,
iconRes, bitmapPath);
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 5c1e7a8..c124255 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -968,7 +968,8 @@
return; // has no icon
}
- Bitmap bitmap = null;
+ Bitmap bitmap;
+ Bitmap bitmapToRecycle = null;
try {
switch (icon.getType()) {
case Icon.TYPE_RESOURCE: {
@@ -979,7 +980,7 @@
return;
}
case Icon.TYPE_BITMAP: {
- bitmap = icon.getBitmap();
+ bitmap = icon.getBitmap(); // Don't recycle in this case.
break;
}
case Icon.TYPE_URI: {
@@ -987,7 +988,8 @@
try (InputStream is = mContext.getContentResolver().openInputStream(uri)) {
- bitmap = BitmapFactory.decodeStream(is);
+ bitmapToRecycle = BitmapFactory.decodeStream(is);
+ bitmap = bitmapToRecycle;
} catch (IOException e) {
Slog.e(TAG, "Unable to load icon from " + uri);
@@ -1011,8 +1013,14 @@
try {
path = out.getFile();
- shrinkBitmap(bitmap, mMaxIconDimension)
- .compress(mIconPersistFormat, mIconPersistQuality, out);
+ Bitmap shrunk = shrinkBitmap(bitmap, mMaxIconDimension);
+ try {
+ shrunk.compress(mIconPersistFormat, mIconPersistQuality, out);
+ } finally {
+ if (bitmap != shrunk) {
+ shrunk.recycle();
+ }
+ }
shortcut.setBitmapPath(out.getFile().getAbsolutePath());
shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_FILE);
@@ -1027,8 +1035,8 @@
}
}
} finally {
- if (bitmap != null) {
- bitmap.recycle();
+ if (bitmapToRecycle != null) {
+ bitmapToRecycle.recycle();
}
// Once saved, we won't use the original icon information, so null it out.
shortcut.clearIcon();
@@ -1076,8 +1084,6 @@
c.drawBitmap(in, /*src=*/ null, dst, /* paint =*/ null);
- in.recycle();
-
return scaledBitmap;
}
@@ -1580,13 +1586,17 @@
@Override
public List<ShortcutInfo> getShortcuts(int launcherUserId,
@NonNull String callingPackage, long changedSince,
- @Nullable String packageName, @Nullable ComponentName componentName,
+ @Nullable String packageName, @Nullable List<String> shortcutIds,
+ @Nullable ComponentName componentName,
int queryFlags, int userId) {
final ArrayList<ShortcutInfo> ret = new ArrayList<>();
final int cloneFlag =
((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) == 0)
? ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER
: ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO;
+ if (packageName == null) {
+ shortcutIds = null; // LauncherAppsService already threw for it though.
+ }
synchronized (mLock) {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
@@ -1594,14 +1604,14 @@
if (packageName != null) {
getShortcutsInnerLocked(launcherUserId,
- callingPackage, packageName, changedSince,
+ callingPackage, packageName, shortcutIds, changedSince,
componentName, queryFlags, userId, ret, cloneFlag);
} else {
final ArrayMap<String, ShortcutPackage> packages =
getUserShortcutsLocked(userId).getAllPackages();
for (int i = packages.size() - 1; i >= 0; i--) {
getShortcutsInnerLocked(launcherUserId,
- callingPackage, packages.keyAt(i), changedSince,
+ callingPackage, packages.keyAt(i), shortcutIds, changedSince,
componentName, queryFlags, userId, ret, cloneFlag);
}
}
@@ -1610,14 +1620,20 @@
}
private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage,
- @Nullable String packageName,long changedSince,
+ @Nullable String packageName, @Nullable List<String> shortcutIds, long changedSince,
@Nullable ComponentName componentName, int queryFlags,
int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) {
+ final ArraySet<String> ids = shortcutIds == null ? null
+ : new ArraySet<>(shortcutIds);
+
getPackageShortcutsLocked(packageName, userId).findAll(ShortcutService.this, ret,
(ShortcutInfo si) -> {
if (si.getLastChangedTimestamp() < changedSince) {
return false;
}
+ if (ids != null && !ids.contains(si.getId())) {
+ return false;
+ }
if (componentName != null
&& !componentName.equals(si.getActivityComponent())) {
return false;
@@ -1633,27 +1649,6 @@
}
@Override
- public List<ShortcutInfo> getShortcutInfo(int launcherUserId,
- @NonNull String callingPackage,
- @NonNull String packageName, @Nullable List<String> ids, int userId) {
- // Calling permission must be checked by LauncherAppsImpl.
- Preconditions.checkStringNotEmpty(packageName, "packageName");
-
- final ArrayList<ShortcutInfo> ret = new ArrayList<>(ids.size());
- final ArraySet<String> idSet = new ArraySet<>(ids);
- synchronized (mLock) {
- getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
- .attemptToRestoreIfNeededAndSave(ShortcutService.this);
-
- getPackageShortcutsLocked(packageName, userId).findAll(
- ShortcutService.this, ret,
- (ShortcutInfo si) -> idSet.contains(si.getId()),
- ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER, callingPackage, launcherUserId);
- }
- return ret;
- }
-
- @Override
public boolean isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId) {
Preconditions.checkStringNotEmpty(packageName, "packageName");
@@ -1733,17 +1728,18 @@
}
@Override
- public int getShortcutIconResId(int launcherUserId,
- @NonNull String callingPackage,
- @NonNull ShortcutInfo shortcut, int userId) {
- Preconditions.checkNotNull(shortcut, "shortcut");
+ public int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage,
+ @NonNull String packageName, @NonNull String shortcutId, int userId) {
+ Preconditions.checkNotNull(callingPackage, "callingPackage");
+ Preconditions.checkNotNull(packageName, "packageName");
+ Preconditions.checkNotNull(shortcutId, "shortcutId");
synchronized (mLock) {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave(ShortcutService.this);
final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
- shortcut.getPackageName(), userId).findShortcutById(shortcut.getId());
+ packageName, userId).findShortcutById(shortcutId);
return (shortcutInfo != null && shortcutInfo.hasIconResource())
? shortcutInfo.getIconResourceId() : 0;
}
@@ -1751,16 +1747,18 @@
@Override
public ParcelFileDescriptor getShortcutIconFd(int launcherUserId,
- @NonNull String callingPackage,
- @NonNull ShortcutInfo shortcutIn, int userId) {
- Preconditions.checkNotNull(shortcutIn, "shortcut");
+ @NonNull String callingPackage, @NonNull String packageName,
+ @NonNull String shortcutId, int userId) {
+ Preconditions.checkNotNull(callingPackage, "callingPackage");
+ Preconditions.checkNotNull(packageName, "packageName");
+ Preconditions.checkNotNull(shortcutId, "shortcutId");
synchronized (mLock) {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave(ShortcutService.this);
final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
- shortcutIn.getPackageName(), userId).findShortcutById(shortcutIn.getId());
+ packageName, userId).findShortcutById(shortcutId);
if (shortcutInfo == null || !shortcutInfo.hasIconFile()) {
return null;
}