diff --git a/api/current.txt b/api/current.txt index fbbb2e379743c..53738dcd8887b 100644 --- a/api/current.txt +++ b/api/current.txt @@ -9498,9 +9498,10 @@ package android.content.pm { public class LauncherApps { method public java.util.List getActivityList(java.lang.String, android.os.UserHandle); method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle); - method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo, android.os.UserHandle); - method public int getShortcutIconResId(android.content.pm.ShortcutInfo, android.os.UserHandle); - method public java.util.List getShortcutInfo(java.lang.String, java.util.List, android.os.UserHandle); + method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo); + method public android.os.ParcelFileDescriptor getShortcutIconFd(java.lang.String, java.lang.String, android.os.UserHandle); + method public int getShortcutIconResId(android.content.pm.ShortcutInfo); + method public int getShortcutIconResId(java.lang.String, java.lang.String, android.os.UserHandle); method public java.util.List getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle); method public boolean hasShortcutHostPermission(); method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle); @@ -9512,6 +9513,7 @@ package android.content.pm { method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle); method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle); method public boolean startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle); + method public boolean startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle); method public void unregisterCallback(android.content.pm.LauncherApps.Callback); } @@ -9533,6 +9535,7 @@ package android.content.pm { method public void setChangedSince(long); method public void setPackage(java.lang.String); method public void setQueryFlags(int); + method public void setShortcutIds(java.util.List); field public static final int FLAG_GET_DYNAMIC = 1; // 0x1 field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4 field public static final int FLAG_GET_PINNED = 2; // 0x2 @@ -10046,6 +10049,7 @@ package android.content.pm { method public java.lang.String getPackageName(); method public java.lang.String getText(); method public java.lang.String getTitle(); + method public android.os.UserHandle getUserHandle(); method public int getWeight(); method public boolean hasIconFile(); method public boolean hasIconResource(); diff --git a/api/system-current.txt b/api/system-current.txt index d91f5d18a52c6..0e9baa6ba504c 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -9836,9 +9836,10 @@ package android.content.pm { public class LauncherApps { method public java.util.List getActivityList(java.lang.String, android.os.UserHandle); method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle); - method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo, android.os.UserHandle); - method public int getShortcutIconResId(android.content.pm.ShortcutInfo, android.os.UserHandle); - method public java.util.List getShortcutInfo(java.lang.String, java.util.List, android.os.UserHandle); + method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo); + method public android.os.ParcelFileDescriptor getShortcutIconFd(java.lang.String, java.lang.String, android.os.UserHandle); + method public int getShortcutIconResId(android.content.pm.ShortcutInfo); + method public int getShortcutIconResId(java.lang.String, java.lang.String, android.os.UserHandle); method public java.util.List getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle); method public boolean hasShortcutHostPermission(); method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle); @@ -9850,6 +9851,7 @@ package android.content.pm { method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle); method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle); method public boolean startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle); + method public boolean startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle); method public void unregisterCallback(android.content.pm.LauncherApps.Callback); } @@ -9871,6 +9873,7 @@ package android.content.pm { method public void setChangedSince(long); method public void setPackage(java.lang.String); method public void setQueryFlags(int); + method public void setShortcutIds(java.util.List); field public static final int FLAG_GET_DYNAMIC = 1; // 0x1 field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4 field public static final int FLAG_GET_PINNED = 2; // 0x2 @@ -10446,6 +10449,7 @@ package android.content.pm { method public java.lang.String getPackageName(); method public java.lang.String getText(); method public java.lang.String getTitle(); + method public android.os.UserHandle getUserHandle(); method public int getWeight(); method public boolean hasIconFile(); method public boolean hasIconResource(); diff --git a/api/test-current.txt b/api/test-current.txt index bad973359337a..caf3231fd22ab 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -9507,9 +9507,10 @@ package android.content.pm { public class LauncherApps { method public java.util.List getActivityList(java.lang.String, android.os.UserHandle); method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle); - method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo, android.os.UserHandle); - method public int getShortcutIconResId(android.content.pm.ShortcutInfo, android.os.UserHandle); - method public java.util.List getShortcutInfo(java.lang.String, java.util.List, android.os.UserHandle); + method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo); + method public android.os.ParcelFileDescriptor getShortcutIconFd(java.lang.String, java.lang.String, android.os.UserHandle); + method public int getShortcutIconResId(android.content.pm.ShortcutInfo); + method public int getShortcutIconResId(java.lang.String, java.lang.String, android.os.UserHandle); method public java.util.List getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle); method public boolean hasShortcutHostPermission(); method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle); @@ -9521,6 +9522,7 @@ package android.content.pm { method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle); method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle); method public boolean startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle); + method public boolean startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle); method public void unregisterCallback(android.content.pm.LauncherApps.Callback); } @@ -9542,6 +9544,7 @@ package android.content.pm { method public void setChangedSince(long); method public void setPackage(java.lang.String); method public void setQueryFlags(int); + method public void setShortcutIds(java.util.List); field public static final int FLAG_GET_DYNAMIC = 1; // 0x1 field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4 field public static final int FLAG_GET_PINNED = 2; // 0x2 @@ -10056,6 +10059,7 @@ package android.content.pm { method public java.lang.String getPackageName(); method public java.lang.String getText(); method public java.lang.String getTitle(); + method public android.os.UserHandle getUserHandle(); method public int getWeight(); method public boolean hasIconFile(); method public boolean hasIconResource(); diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl index 6b3d4f1326ead..46321a4d7027f 100644 --- a/core/java/android/content/pm/ILauncherApps.aidl +++ b/core/java/android/content/pm/ILauncherApps.aidl @@ -47,17 +47,16 @@ interface ILauncherApps { ApplicationInfo getApplicationInfo(String packageName, int flags, in UserHandle user); ParceledListSlice getShortcuts(String callingPackage, long changedSince, String packageName, - in ComponentName componentName, int flags, in UserHandle user); - ParceledListSlice getShortcutInfo(String callingPackage, String packageName, in List ids, - in UserHandle user); + in List shortcutIds, in ComponentName componentName, int flags, in UserHandle user); void pinShortcuts(String callingPackage, String packageName, in List shortcutIds, in UserHandle user); boolean startShortcut(String callingPackage, String packageName, String id, - in Rect sourceBounds, in Bundle startActivityOptions, in UserHandle user); + in Rect sourceBounds, in Bundle startActivityOptions, int userId); - int getShortcutIconResId(String callingPackage, in ShortcutInfo shortcut, in UserHandle user); - ParcelFileDescriptor getShortcutIconFd(String callingPackage, in ShortcutInfo shortcut, - in UserHandle user); + int getShortcutIconResId(String callingPackage, String packageName, String id, + int userId); + ParcelFileDescriptor getShortcutIconFd(String callingPackage, String packageName, String id, + int userId); boolean hasShortcutHostPermission(String callingPackage); } diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index d865f34568fab..abe1aaf96c36e 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -204,6 +204,9 @@ public class LauncherApps { @Nullable String mPackage; + @Nullable + List mShortcutIds; + @Nullable ComponentName mActivity; @@ -228,6 +231,14 @@ public class LauncherApps { mPackage = packageName; } + /** + * If non-null, return only the specified shortcuts by ID. When setting this field, + * a packange name must also be set with {@link #setPackage}. + */ + public void setShortcutIds(@Nullable List shortcutIds) { + mShortcutIds = shortcutIds; + } + /** * If non-null, returns only shortcuts associated with the activity. */ @@ -429,7 +440,8 @@ public class LauncherApps { @NonNull UserHandle user) { try { return mService.getShortcuts(mContext.getPackageName(), - query.mChangedSince, query.mPackage, query.mActivity, query.mQueryFlags, user) + query.mChangedSince, query.mPackage, query.mShortcutIds, query.mActivity, + query.mQueryFlags, user) .getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -437,29 +449,18 @@ public class LauncherApps { } /** - * Returns {@link ShortcutInfo}s with the given IDs from a package. - * - *

Callers must be allowed to access the shortcut information, as defined in {@link - * #hasShortcutHostPermission()}. - * - * @param packageName The target package. - * @param ids IDs of the shortcuts to retrieve. - * @param user The UserHandle of the profile. - * - * @return list of {@link ShortcutInfo} associated with the package. + * @hide // No longer used. Use getShortcuts() instead. Kept for unit tests. */ @Nullable public List getShortcutInfo(@NonNull String packageName, @NonNull List ids, @NonNull UserHandle user) { - try { - return mService.getShortcutInfo(mContext.getPackageName(), packageName, ids, user) - .getList(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + final ShortcutQuery q = new ShortcutQuery(); + q.setPackage(packageName); + q.setShortcutIds(ids); + q.setQueryFlags(ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED); + return getShortcuts(q, user); } - /** * Pin shortcuts on a package. * @@ -490,11 +491,33 @@ public class LauncherApps { * #hasShortcutHostPermission()}. * * @param shortcut The target shortcut. + */ + public int getShortcutIconResId(@NonNull ShortcutInfo shortcut) { + return getShortcutIconResId(shortcut.getPackageName(), shortcut.getId(), + shortcut.getUserId()); + } + + /** + * Return the icon resource ID, if {@code shortcut} has one + * (i.e. when {@link ShortcutInfo#hasIconResource()} returns {@code true}). + * + *

Callers must be allowed to access the shortcut information, as defined in {@link + * #hasShortcutHostPermission()}. + * + * @param packageName The target package name. + * @param shortcutId The ID of the shortcut to lad rom. * @param user The UserHandle of the profile. */ - public int getShortcutIconResId(@NonNull ShortcutInfo shortcut, @NonNull UserHandle user) { + public int getShortcutIconResId(@NonNull String packageName, @NonNull String shortcutId, + @NonNull UserHandle user) { + return getShortcutIconResId(packageName, shortcutId, user.getIdentifier()); + } + + private int getShortcutIconResId(@NonNull String packageName, @NonNull String shortcutId, + int userId) { try { - return mService.getShortcutIconResId(mContext.getPackageName(), shortcut, user); + return mService.getShortcutIconResId(mContext.getPackageName(), + packageName, shortcutId, userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -508,12 +531,34 @@ public class LauncherApps { * #hasShortcutHostPermission()}. * * @param shortcut The target shortcut. + */ + public ParcelFileDescriptor getShortcutIconFd( + @NonNull ShortcutInfo shortcut) { + return getShortcutIconFd(shortcut.getPackageName(), shortcut.getId(), + shortcut.getUserId()); + } + + /** + * Return the icon as {@link ParcelFileDescriptor}, when it's stored as a file + * (i.e. when {@link ShortcutInfo#hasIconFile()} returns {@code true}). + * + *

Callers must be allowed to access the shortcut information, as defined in {@link + * #hasShortcutHostPermission()}. + * + * @param packageName The target package name. + * @param shortcutId The ID of the shortcut to lad rom. * @param user The UserHandle of the profile. */ public ParcelFileDescriptor getShortcutIconFd( - @NonNull ShortcutInfo shortcut, @NonNull UserHandle user) { + @NonNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user) { + return getShortcutIconFd(packageName, shortcutId, user.getIdentifier()); + } + + private ParcelFileDescriptor getShortcutIconFd( + @NonNull String packageName, @NonNull String shortcutId, int userId) { try { - return mService.getShortcutIconFd(mContext.getPackageName(), shortcut, user); + return mService.getShortcutIconFd(mContext.getPackageName(), + packageName, shortcutId, userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -536,9 +581,35 @@ public class LauncherApps { public boolean startShortcut(@NonNull String packageName, @NonNull String shortcutId, @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions, @NonNull UserHandle user) { + return startShortcut(packageName, shortcutId, sourceBounds, startActivityOptions, + user.getIdentifier()); + } + + /** + * Launches a shortcut. + * + *

Callers must be allowed to access the shortcut information, as defined in {@link + * #hasShortcutHostPermission()}. + * + * @param shortcut The target shortcut. + * @param sourceBounds The Rect containing the source bounds of the clicked icon. + * @param startActivityOptions Options to pass to startActivity. + * @return {@code false} when the shortcut is no longer valid (e.g. the creator application + * has been uninstalled). {@code true} when the shortcut is still valid. + */ + public boolean startShortcut(@NonNull ShortcutInfo shortcut, + @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions) { + return startShortcut(shortcut.getPackageName(), shortcut.getId(), + sourceBounds, startActivityOptions, + shortcut.getUserId()); + } + + private boolean startShortcut(@NonNull String packageName, @NonNull String shortcutId, + @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions, + int userId) { try { return mService.startShortcut(mContext.getPackageName(), packageName, shortcutId, - sourceBounds, startActivityOptions, user); + sourceBounds, startActivityOptions, userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index 1812575a2272d..7a807c4cf949b 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -18,6 +18,7 @@ package android.content.pm; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; @@ -153,7 +154,11 @@ public final class ShortcutInfo implements Parcelable { @Nullable private String mBitmapPath; + private final int mUserId; + private ShortcutInfo(Builder b) { + mUserId = b.mContext.getUserId(); + mId = Preconditions.checkStringNotEmpty(b.mId, "Shortcut ID must be provided"); // Note we can't do other null checks here because SM.updateShortcuts() takes partial @@ -191,6 +196,7 @@ public final class ShortcutInfo implements Parcelable { * Copy constructor. */ private ShortcutInfo(ShortcutInfo source, @CloneFlags int cloneFlags) { + mUserId = source.mUserId; mId = source.mId; mPackageName = source.mPackageName; mFlags = source.mFlags; @@ -238,6 +244,7 @@ public final class ShortcutInfo implements Parcelable { * @hide */ public void copyNonNullFieldsFrom(ShortcutInfo source) { + Preconditions.checkState(mUserId == source.mUserId, "Owner User ID must match"); Preconditions.checkState(mId.equals(source.mId), "ID must match"); Preconditions.checkState(mPackageName.equals(source.mPackageName), "Package name must match"); @@ -362,8 +369,8 @@ public final class ShortcutInfo implements Parcelable { * *

For performance reasons, icons will NOT be available on instances * returned by {@link ShortcutManager} or {@link LauncherApps}. Launcher applications - * need to use {@link LauncherApps#getShortcutIconFd(ShortcutInfo, UserHandle)} - * and {@link LauncherApps#getShortcutIconResId(ShortcutInfo, UserHandle)}. + * need to use {@link LauncherApps#getShortcutIconFd(ShortcutInfo)} + * and {@link LauncherApps#getShortcutIconResId(ShortcutInfo)}. */ @NonNull public Builder setIcon(Icon icon) { @@ -544,6 +551,18 @@ public final class ShortcutInfo implements Parcelable { return mExtras; } + /** @hide */ + public int getUserId() { + return mUserId; + } + + /** + * {@link UserHandle} on which the publisher created shortcuts. + */ + public UserHandle getUserHandle() { + return UserHandle.of(mUserId); + } + /** * Last time when any of the fields was updated. */ @@ -590,7 +609,7 @@ public final class ShortcutInfo implements Parcelable { /** * Return whether a shortcut's icon is a resource in the owning package. * - * @see LauncherApps#getShortcutIconResId(ShortcutInfo, UserHandle) + * @see LauncherApps#getShortcutIconResId(ShortcutInfo) */ public boolean hasIconResource() { return hasFlags(FLAG_HAS_ICON_RES); @@ -599,7 +618,7 @@ public final class ShortcutInfo implements Parcelable { /** * Return whether a shortcut's icon is stored as a file. * - * @see LauncherApps#getShortcutIconFd(ShortcutInfo, UserHandle) + * @see LauncherApps#getShortcutIconFd(ShortcutInfo) */ public boolean hasIconFile() { return hasFlags(FLAG_HAS_ICON_FILE); @@ -661,6 +680,7 @@ public final class ShortcutInfo implements Parcelable { private ShortcutInfo(Parcel source) { final ClassLoader cl = getClass().getClassLoader(); + mUserId = source.readInt(); mId = source.readString(); mPackageName = source.readString(); mActivityComponent = source.readParcelable(cl); @@ -679,6 +699,7 @@ public final class ShortcutInfo implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mUserId); dest.writeString(mId); dest.writeString(mPackageName); dest.writeParcelable(mActivityComponent, flags); @@ -784,11 +805,13 @@ public final class ShortcutInfo implements Parcelable { } /** @hide */ - public ShortcutInfo(String id, String packageName, ComponentName activityComponent, + public ShortcutInfo( + @UserIdInt int userId, String id, String packageName, ComponentName activityComponent, Icon icon, String title, String text, Intent intent, PersistableBundle intentPersistableExtras, int weight, PersistableBundle extras, long lastChangedTimestamp, int flags, int iconResId, String bitmapPath) { + mUserId = userId; mId = id; mPackageName = packageName; mActivityComponent = activityComponent; diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java index d57f2e6e5de5f..dc3d317533a69 100644 --- a/core/java/android/content/pm/ShortcutServiceInternal.java +++ b/core/java/android/content/pm/ShortcutServiceInternal.java @@ -42,15 +42,10 @@ public abstract class ShortcutServiceInternal { public abstract List getShortcuts(int launcherUserId, @NonNull String callingPackage, long changedSince, - @Nullable String packageName, @Nullable ComponentName componentName, - @ShortcutQuery.QueryFlags int flags, + @Nullable String packageName, @Nullable List shortcutIds, + @Nullable ComponentName componentName, @ShortcutQuery.QueryFlags int flags, int userId); - public abstract List - getShortcutInfo(int launcherUserId, @NonNull String callingPackage, - @NonNull String packageName, @Nullable List ids, int userId); - - public abstract boolean isPinnedByCaller(int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull String id, int userId); @@ -65,11 +60,11 @@ public abstract class ShortcutServiceInternal { public abstract void addListener(@NonNull ShortcutChangeListener listener); public abstract int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage, - @NonNull ShortcutInfo shortcut, int userId); + @NonNull String packageName, @NonNull String shortcutId, int userId); public abstract ParcelFileDescriptor getShortcutIconFd(int launcherUserId, @NonNull String callingPackage, - @NonNull ShortcutInfo shortcut, int userId); + @NonNull String packageName, @NonNull String shortcutId, int userId); public abstract boolean hasShortcutHostPermission(int launcherUserId, @NonNull String callingPackage); diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 4c18e15e912ab..79d9c869d2b47 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 @@ public class LauncherAppsService extends SystemService { * 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 @@ public class LauncherAppsService extends SystemService { * 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 @@ public class LauncherAppsService extends SystemService { } 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,31 +368,23 @@ public class LauncherAppsService extends SystemService { @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 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 ids, UserHandle user) { @@ -396,27 +399,27 @@ public class LauncherAppsService extends SystemService { } @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 @@ public class LauncherAppsService extends SystemService { @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 @@ public class LauncherAppsService extends SystemService { 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 @@ public class LauncherAppsService extends SystemService { final List 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 1076a7a0ae883..58559a53cb5a6 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 @@ package com.android.server.pm; 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 @@ class ShortcutPackage extends ShortcutPackageItem { 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 @@ class ShortcutPackage extends ShortcutPackageItem { 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 @@ class ShortcutPackage extends ShortcutPackageItem { 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 5c1e7a8dde9bd..c12425575485a 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 @@ public class ShortcutService extends IShortcutService.Stub { return; // has no icon } - Bitmap bitmap = null; + Bitmap bitmap; + Bitmap bitmapToRecycle = null; try { switch (icon.getType()) { case Icon.TYPE_RESOURCE: { @@ -979,7 +980,7 @@ public class ShortcutService extends IShortcutService.Stub { 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 @@ public class ShortcutService extends IShortcutService.Stub { 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 @@ public class ShortcutService extends IShortcutService.Stub { 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 @@ public class ShortcutService extends IShortcutService.Stub { } } } 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 @@ public class ShortcutService extends IShortcutService.Stub { c.drawBitmap(in, /*src=*/ null, dst, /* paint =*/ null); - in.recycle(); - return scaledBitmap; } @@ -1580,13 +1586,17 @@ public class ShortcutService extends IShortcutService.Stub { @Override public List getShortcuts(int launcherUserId, @NonNull String callingPackage, long changedSince, - @Nullable String packageName, @Nullable ComponentName componentName, + @Nullable String packageName, @Nullable List shortcutIds, + @Nullable ComponentName componentName, int queryFlags, int userId) { final ArrayList 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 @@ public class ShortcutService extends IShortcutService.Stub { if (packageName != null) { getShortcutsInnerLocked(launcherUserId, - callingPackage, packageName, changedSince, + callingPackage, packageName, shortcutIds, changedSince, componentName, queryFlags, userId, ret, cloneFlag); } else { final ArrayMap 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 @@ public class ShortcutService extends IShortcutService.Stub { } private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage, - @Nullable String packageName,long changedSince, + @Nullable String packageName, @Nullable List shortcutIds, long changedSince, @Nullable ComponentName componentName, int queryFlags, int userId, ArrayList ret, int cloneFlag) { + final ArraySet 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; @@ -1632,27 +1648,6 @@ public class ShortcutService extends IShortcutService.Stub { }, cloneFlag, callingPackage, launcherUserId); } - @Override - public List getShortcutInfo(int launcherUserId, - @NonNull String callingPackage, - @NonNull String packageName, @Nullable List ids, int userId) { - // Calling permission must be checked by LauncherAppsImpl. - Preconditions.checkStringNotEmpty(packageName, "packageName"); - - final ArrayList ret = new ArrayList<>(ids.size()); - final ArraySet 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) { @@ -1733,17 +1728,18 @@ public class ShortcutService extends IShortcutService.Stub { } @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 @@ public class ShortcutService extends IShortcutService.Stub { @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 baa5d361c1a13..b08bd724a985b 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 class ShortcutManagerTest extends InstrumentationTestCase { public String getPackageName() { return mInjectedClientPackage; } + + @Override + public int getUserId() { + return getCallingUserId(); + } } /** Context used in the service side */ @@ -190,6 +195,11 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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 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 @@ public class ShortcutManagerTest extends InstrumentationTestCase { return getLauncherShortcuts(launcher, userId, ShortcutQuery.FLAG_GET_PINNED); } + private ShortcutInfo getShortcutInfoAsLauncher(String packageName, String shortcutId, + int userId) { + final List 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 @@ public class ShortcutManagerTest extends InstrumentationTestCase { "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 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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 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 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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 class ShortcutManagerTest extends InstrumentationTestCase { } 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 class ShortcutManagerTest extends InstrumentationTestCase { } 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 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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 class ShortcutManagerTest extends InstrumentationTestCase { } 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 @@ public class ShortcutManagerTest extends InstrumentationTestCase { // 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());