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;
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
index baa5d36..b08bd72 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
@@ -172,6 +172,11 @@
public String getPackageName() {
return mInjectedClientPackage;
}
+
+ @Override
+ public int getUserId() {
+ return getCallingUserId();
+ }
}
/** Context used in the service side */
@@ -190,6 +195,11 @@
public void startActivityAsUser(@RequiresPermission Intent intent, @Nullable Bundle options,
UserHandle userId) {
}
+
+ @Override
+ public int getUserId() {
+ return UserHandle.USER_SYSTEM;
+ }
}
/** ShortcutService with injection override methods. */
@@ -1152,9 +1162,25 @@
return intentCaptor.getValue();
}
+ private Intent launchShortcutAndGetIntent_withShortcutInfo(
+ @NonNull String packageName, @NonNull String shortcutId, int userId) {
+ reset(mServiceContext);
+
+ assertTrue(mLauncherApps.startShortcut(
+ getShortcutInfoAsLauncher(packageName, shortcutId, userId), null, null));
+
+ final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mServiceContext).startActivityAsUser(
+ intentCaptor.capture(),
+ any(Bundle.class),
+ eq(UserHandle.of(userId)));
+ return intentCaptor.getValue();
+ }
+
private void assertShortcutLaunchable(@NonNull String packageName, @NonNull String shortcutId,
int userId) {
assertNotNull(launchShortcutAndGetIntent(packageName, shortcutId, userId));
+ assertNotNull(launchShortcutAndGetIntent_withShortcutInfo(packageName, shortcutId, userId));
}
private void assertShortcutNotLaunchable(@NonNull String packageName,
@@ -1193,6 +1219,14 @@
return getLauncherShortcuts(launcher, userId, ShortcutQuery.FLAG_GET_PINNED);
}
+ private ShortcutInfo getShortcutInfoAsLauncher(String packageName, String shortcutId,
+ int userId) {
+ final List<ShortcutInfo> infoList =
+ mLauncherApps.getShortcutInfo(packageName, list(shortcutId),
+ UserHandle.of(userId));
+ assertEquals("No shortcutInfo found (or too many of them)", 1, infoList.size());
+ return infoList.get(0);
+ }
private Intent genPackageDeleteIntent(String pakcageName, int userId) {
Intent i = new Intent(Intent.ACTION_PACKAGE_REMOVED);
@@ -1728,6 +1762,16 @@
"res64x64",
"none");
+ // Different profile. Note the names and the contents don't match.
+ setCaller(CALLING_PACKAGE_1, USER_P0);
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcutWithIcon("res32x32", res512x512),
+ makeShortcutWithIcon("bmp32x32", bmp512x512)
+ )));
+ assertShortcutIds(assertAllNotHaveIcon(mManager.getDynamicShortcuts()),
+ "res32x32",
+ "bmp32x32");
+
// Re-initialize and load from the files.
mService.saveDirtyInfo();
initService();
@@ -1737,61 +1781,90 @@
setCaller(LAUNCHER_1);
// Check hasIconResource()/hasIconFile().
- assertShortcutIds(assertAllHaveIconResId(mLauncherApps.getShortcutInfo(
- CALLING_PACKAGE_1, list("res32x32"),
- getCallingUser())), "res32x32");
+ assertShortcutIds(assertAllHaveIconResId(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res32x32", USER_0))),
+ "res32x32");
- assertShortcutIds(assertAllHaveIconResId(mLauncherApps.getShortcutInfo(
- CALLING_PACKAGE_1, list("res64x64"), getCallingUser())),
+ assertShortcutIds(assertAllHaveIconResId(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res64x64", USER_0))),
"res64x64");
- assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
- CALLING_PACKAGE_1, list("bmp32x32"), getCallingUser())),
+ assertShortcutIds(assertAllHaveIconFile(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_0))),
"bmp32x32");
- assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
- CALLING_PACKAGE_1, list("bmp64x64"), getCallingUser())),
+ assertShortcutIds(assertAllHaveIconFile(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp64x64", USER_0))),
"bmp64x64");
- assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
- CALLING_PACKAGE_1, list("bmp512x512"), getCallingUser())),
+ assertShortcutIds(assertAllHaveIconFile(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp512x512", USER_0))),
"bmp512x512");
+ assertShortcutIds(assertAllHaveIconResId(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res32x32", USER_P0))),
+ "res32x32");
+ assertShortcutIds(assertAllHaveIconFile(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_P0))),
+ "bmp32x32");
+
// Check
assertEquals(
R.drawable.black_32x32,
mLauncherApps.getShortcutIconResId(
- makePackageShortcut(CALLING_PACKAGE_1, "res32x32"), getCallingUser()));
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res32x32", USER_0)));
assertEquals(
R.drawable.black_64x64,
mLauncherApps.getShortcutIconResId(
-
- makePackageShortcut(CALLING_PACKAGE_1, "res64x64"), getCallingUser()));
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res64x64", USER_0)));
assertEquals(
0, // because it's not a resource
mLauncherApps.getShortcutIconResId(
- makePackageShortcut(CALLING_PACKAGE_1, "bmp32x32"), getCallingUser()));
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_0)));
assertEquals(
0, // because it's not a resource
mLauncherApps.getShortcutIconResId(
- makePackageShortcut(CALLING_PACKAGE_1, "bmp64x64"), getCallingUser()));
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp64x64", USER_0)));
assertEquals(
0, // because it's not a resource
mLauncherApps.getShortcutIconResId(
- makePackageShortcut(CALLING_PACKAGE_1, "bmp512x512"), getCallingUser()));
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp512x512", USER_0)));
bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
- makePackageShortcut(CALLING_PACKAGE_1, "bmp32x32"), getCallingUser()));
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_0)));
assertBitmapSize(32, 32, bmp);
bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
- makePackageShortcut(CALLING_PACKAGE_1, "bmp64x64"), getCallingUser()));
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp64x64", USER_0)));
assertBitmapSize(64, 64, bmp);
bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
- makePackageShortcut(CALLING_PACKAGE_1, "bmp512x512"), getCallingUser()));
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp512x512", USER_0)));
+ assertBitmapSize(128, 128, bmp);
+
+ assertEquals(
+ R.drawable.black_512x512,
+ mLauncherApps.getShortcutIconResId(
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res32x32", USER_P0)));
+ // Should be 512x512, so shrunk.
+ bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_P0)));
+ assertBitmapSize(128, 128, bmp);
+
+ // Also check the overload APIs too.
+ assertEquals(
+ R.drawable.black_32x32,
+ mLauncherApps.getShortcutIconResId(CALLING_PACKAGE_1, "res32x32", HANDLE_USER_0));
+ assertEquals(
+ R.drawable.black_64x64,
+ mLauncherApps.getShortcutIconResId(CALLING_PACKAGE_1, "res64x64", HANDLE_USER_0));
+ assertEquals(
+ R.drawable.black_512x512,
+ mLauncherApps.getShortcutIconResId(CALLING_PACKAGE_1, "res32x32", HANDLE_USER_P0));
+ bmp = pfdToBitmap(
+ mLauncherApps.getShortcutIconFd(CALLING_PACKAGE_1, "bmp32x32", HANDLE_USER_P0));
assertBitmapSize(128, 128, bmp);
// TODO Test the content URI case too.
@@ -2034,9 +2107,16 @@
private static ShortcutQuery buildQuery(long changedSince,
String packageName, ComponentName componentName,
/* @ShortcutQuery.QueryFlags */ int flags) {
+ return buildQuery(changedSince, packageName, null, componentName, flags);
+ }
+
+ private static ShortcutQuery buildQuery(long changedSince,
+ String packageName, List<String> shortcutIds, ComponentName componentName,
+ /* @ShortcutQuery.QueryFlags */ int flags) {
final ShortcutQuery q = new ShortcutQuery();
q.setChangedSince(changedSince);
q.setPackage(packageName);
+ q.setShortcutIds(shortcutIds);
q.setActivity(componentName);
q.setQueryFlags(flags);
return q;
@@ -2110,6 +2190,36 @@
getCallingUser())),
"s2", "s3"))));
+ // With ID.
+ assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+ assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 1000, CALLING_PACKAGE_2, list("s3"),
+ /* activity =*/ null,
+ ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+ getCallingUser())),
+ "s3"))));
+ assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+ assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 1000, CALLING_PACKAGE_2, list("s3", "s2", "ss"),
+ /* activity =*/ null,
+ ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+ getCallingUser())),
+ "s2", "s3"))));
+ assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+ assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 1000, CALLING_PACKAGE_2, list("s3x", "s2x"),
+ /* activity =*/ null,
+ ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+ getCallingUser()))
+ /* empty */))));
+ assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+ assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 1000, CALLING_PACKAGE_2, list(),
+ /* activity =*/ null,
+ ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+ getCallingUser()))
+ /* empty */))));
+
// Pin some shortcuts.
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
list("s3", "s4"), getCallingUser());
@@ -2132,6 +2242,13 @@
getCallingUser())),
"s1", "s3");
+ TestUtils.assertExpectException(
+ IllegalArgumentException.class, "package name must also be set", () -> {
+ mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 0, /* package= */ null, list("id"),
+ /* activity =*/ null, /* flags */ 0), getCallingUser());
+ });
+
// TODO More tests: pinned but dynamic, filter by activity
}
@@ -5063,12 +5180,15 @@
}
public void testShortcutInfoParcel() {
- ShortcutInfo si = parceled(new ShortcutInfo.Builder(getTestContext())
+ setCaller(CALLING_PACKAGE_1, USER_10);
+ ShortcutInfo si = parceled(new ShortcutInfo.Builder(mClientContext)
.setId("id")
.setTitle("title")
.setIntent(makeIntent("action", ShortcutActivity.class))
.build());
- assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals(mClientContext.getPackageName(), si.getPackageName());
+ assertEquals(USER_10, si.getUserId());
+ assertEquals(HANDLE_USER_10, si.getUserHandle());
assertEquals("id", si.getId());
assertEquals("title", si.getTitle());
assertEquals("action", si.getIntent().getAction());
@@ -5109,9 +5229,11 @@
}
public void testShortcutInfoClone() {
+ setCaller(CALLING_PACKAGE_1, USER_11);
+
PersistableBundle pb = new PersistableBundle();
pb.putInt("k", 1);
- ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext())
+ ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
.setId("id")
.setActivityComponent(new ComponentName("a", "b"))
.setIcon(Icon.createWithContentUri("content://a.b.c/"))
@@ -5127,7 +5249,9 @@
ShortcutInfo si = sorig.clone(/* clone flags*/ 0);
- assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals(USER_11, si.getUserId());
+ assertEquals(HANDLE_USER_11, si.getUserHandle());
+ assertEquals(mClientContext.getPackageName(), si.getPackageName());
assertEquals("id", si.getId());
assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
assertEquals("content://a.b.c/", si.getIcon().getUriString());
@@ -5144,7 +5268,7 @@
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
- assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals(mClientContext.getPackageName(), si.getPackageName());
assertEquals("id", si.getId());
assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
assertEquals(null, si.getIcon());
@@ -5161,7 +5285,7 @@
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
- assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals(mClientContext.getPackageName(), si.getPackageName());
assertEquals("id", si.getId());
assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
assertEquals(null, si.getIcon());
@@ -5177,7 +5301,7 @@
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
- assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals(mClientContext.getPackageName(), si.getPackageName());
assertEquals("id", si.getId());
assertEquals(null, si.getActivityComponent());
assertEquals(null, si.getIcon());
@@ -5267,7 +5391,7 @@
}
public void testShortcutInfoSaveAndLoad() throws InterruptedException {
- setCaller(CALLING_PACKAGE_1, USER_0);
+ setCaller(CALLING_PACKAGE_1, USER_10);
final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
getTestContext().getResources(), R.drawable.black_32x32));
@@ -5293,11 +5417,13 @@
// Save and load.
mService.saveDirtyInfo();
initService();
- mService.handleUnlockUser(USER_0);
+ mService.handleUnlockUser(USER_10);
ShortcutInfo si;
- si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_0);
+ si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_10);
+ assertEquals(USER_10, si.getUserId());
+ assertEquals(HANDLE_USER_10, si.getUserHandle());
assertEquals(CALLING_PACKAGE_1, si.getPackageName());
assertEquals("id", si.getId());
assertEquals(ShortcutActivity2.class.getName(), si.getActivityComponent().getClassName());