diff --git a/api/current.txt b/api/current.txt index be2c71f0b46541cc2c4b7ee181cc49513782ac33..c7b2537c6733c77e29a1dac14766929ffc63a3cf 100644 --- a/api/current.txt +++ b/api/current.txt @@ -10574,6 +10574,7 @@ package android.content.pm { field public static final int FLAG_MATCH_DYNAMIC = 1; // 0x1 field public static final int FLAG_MATCH_MANIFEST = 8; // 0x8 field public static final int FLAG_MATCH_PINNED = 2; // 0x2 + field public static final int FLAG_MATCH_PINNED_BY_ANY_LAUNCHER = 1024; // 0x400 } public class PackageInfo implements android.os.Parcelable { diff --git a/api/system-current.txt b/api/system-current.txt index 5f37686c4f8b7f9d3a1a3dc8df4a33eeb4b472b2..04b94e858ca9be298f7de7d383f1e580f7d984e5 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -11192,6 +11192,7 @@ package android.content.pm { field public static final int FLAG_MATCH_DYNAMIC = 1; // 0x1 field public static final int FLAG_MATCH_MANIFEST = 8; // 0x8 field public static final int FLAG_MATCH_PINNED = 2; // 0x2 + field public static final int FLAG_MATCH_PINNED_BY_ANY_LAUNCHER = 1024; // 0x400 } public class PackageInfo implements android.os.Parcelable { diff --git a/api/test-current.txt b/api/test-current.txt index c68676b5cb5e5b2c5faa89377cce40cf266a260d..6701de3f62cc87211216477c0eb373fd06a3b6b6 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -10653,10 +10653,10 @@ package android.content.pm { method public android.content.pm.LauncherApps.ShortcutQuery setQueryFlags(int); method public android.content.pm.LauncherApps.ShortcutQuery setShortcutIds(java.util.List); field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4 - field public static final int FLAG_MATCH_ALL_PINNED = 1024; // 0x400 field public static final int FLAG_MATCH_DYNAMIC = 1; // 0x1 field public static final int FLAG_MATCH_MANIFEST = 8; // 0x8 field public static final int FLAG_MATCH_PINNED = 2; // 0x2 + field public static final int FLAG_MATCH_PINNED_BY_ANY_LAUNCHER = 1024; // 0x400 } public class PackageInfo implements android.os.Parcelable { diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 05c555685384942ce792a238084b0c36395d6682..9e54e235325c942aa764acec1bb7c56442c42272 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -282,13 +282,13 @@ public class LauncherApps { public static final int FLAG_GET_MANIFEST = FLAG_MATCH_MANIFEST; /** - * @hide include all pinned shortcuts by any launchers, not just by the caller, + * Include all pinned shortcuts by any launchers, not just by the caller, * in the result. - * If the caller doesn't havve the {@link android.Manifest.permission#ACCESS_SHORTCUTS} - * permission, this flag will be ignored. + * + * The caller must be the selected assistant app to use this flag, or have the system + * {@code ACCESS_SHORTCUTS} permission. */ - @TestApi - public static final int FLAG_MATCH_ALL_PINNED = 1 << 10; + public static final int FLAG_MATCH_PINNED_BY_ANY_LAUNCHER = 1 << 10; /** * FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST @@ -302,7 +302,7 @@ public class LauncherApps { * @hide */ public static final int FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED = - FLAG_MATCH_ALL_KINDS | FLAG_MATCH_ALL_PINNED; + FLAG_MATCH_ALL_KINDS | FLAG_MATCH_PINNED_BY_ANY_LAUNCHER; /** @hide kept for unit tests */ @Deprecated diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index a3585bc1f97d7bb50cabd18f5a7b81a0c2650911..ba97c428bb443d6508826d20d6739ee819914258 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -615,10 +615,16 @@ class ShortcutPackage extends ShortcutPackageItem { // Fix up isPinned for the caller. Note we need to do it before the "test" callback, // since it may check isPinned. - if (!isPinnedByCaller) { - clone.clearFlags(ShortcutInfo.FLAG_PINNED); + // However, if getPinnedByAnyLauncher is set, we do it after the test. + if (!getPinnedByAnyLauncher) { + if (!isPinnedByCaller) { + clone.clearFlags(ShortcutInfo.FLAG_PINNED); + } } if (query == null || query.test(clone)) { + if (!isPinnedByCaller) { + clone.clearFlags(ShortcutInfo.FLAG_PINNED); + } result.add(clone); } } diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 25e92399478907a38c956eff89260ce852a701d4..0907dd7c5a9e8b05b194a4219923f0b581cff97f 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -2234,7 +2234,7 @@ public class ShortcutService extends IShortcutService.Stub { // We override this method in unit tests to do a simpler check. boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId, int callingPid, int callingUid) { - if (injectCheckAccessShortcutsPermission(callingPid, callingUid)) { + if (canSeeAnyPinnedShortcut(callingPackage, userId, callingPid, callingUid)) { return true; } final long start = injectElapsedRealtime(); @@ -2245,10 +2245,21 @@ public class ShortcutService extends IShortcutService.Stub { } } + boolean canSeeAnyPinnedShortcut(@NonNull String callingPackage, int userId, + int callingPid, int callingUid) { + if (injectHasAccessShortcutsPermission(callingPid, callingUid)) { + return true; + } + synchronized (mLock) { + return getUserShortcutsLocked(userId).hasHostPackage(callingPackage); + } + } + /** * Returns true if the caller has the "ACCESS_SHORTCUTS" permission. */ - boolean injectCheckAccessShortcutsPermission(int callingPid, int callingUid) { + @VisibleForTesting + boolean injectHasAccessShortcutsPermission(int callingPid, int callingUid) { return mContext.checkPermission(android.Manifest.permission.ACCESS_SHORTCUTS, callingPid, callingUid) == PackageManager.PERMISSION_GRANTED; } @@ -2262,10 +2273,6 @@ public class ShortcutService extends IShortcutService.Stub { final ShortcutUser user = getUserShortcutsLocked(userId); - if (user.hasHostPackage(packageName)) { - return true; - } - // Always trust the cached component. final ComponentName cached = user.getCachedLauncher(); if (cached != null) { @@ -2491,8 +2498,8 @@ public class ShortcutService extends IShortcutService.Stub { final ArraySet ids = shortcutIds == null ? null : new ArraySet<>(shortcutIds); - final ShortcutPackage p = getUserShortcutsLocked(userId) - .getPackageShortcutsIfExists(packageName); + final ShortcutUser user = getUserShortcutsLocked(userId); + final ShortcutPackage p = user.getPackageShortcutsIfExists(packageName); if (p == null) { return; // No need to instantiate ShortcutPackage. } @@ -2500,9 +2507,12 @@ public class ShortcutService extends IShortcutService.Stub { final boolean matchPinned = (queryFlags & ShortcutQuery.FLAG_MATCH_PINNED) != 0; final boolean matchManifest = (queryFlags & ShortcutQuery.FLAG_MATCH_MANIFEST) != 0; + final boolean canAccessAllShortcuts = + canSeeAnyPinnedShortcut(callingPackage, launcherUserId, callingPid, callingUid); + final boolean getPinnedByAnyLauncher = - ((queryFlags & ShortcutQuery.FLAG_MATCH_ALL_PINNED) != 0) - && injectCheckAccessShortcutsPermission(callingPid, callingUid); + canAccessAllShortcuts && + ((queryFlags & ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER) != 0); p.findAll(ret, (ShortcutInfo si) -> { @@ -2521,7 +2531,7 @@ public class ShortcutService extends IShortcutService.Stub { if (matchDynamic && si.isDynamic()) { return true; } - if ((matchPinned && si.isPinned()) || getPinnedByAnyLauncher) { + if ((matchPinned || getPinnedByAnyLauncher) && si.isPinned()) { return true; } if (matchManifest && si.isDeclaredInManifest()) { @@ -2614,14 +2624,15 @@ public class ShortcutService extends IShortcutService.Stub { .attemptToRestoreIfNeededAndSave(); final boolean getPinnedByAnyLauncher = - injectCheckAccessShortcutsPermission(callingPid, callingUid); + canSeeAnyPinnedShortcut(callingPackage, launcherUserId, + callingPid, callingUid); // Make sure the shortcut is actually visible to the launcher. final ShortcutInfo si = getShortcutInfoLocked( launcherUserId, callingPackage, packageName, shortcutId, userId, getPinnedByAnyLauncher); // "si == null" should suffice here, but check the flags too just to make sure. - if (si == null || !si.isEnabled() || !si.isAlive()) { + if (si == null || !si.isEnabled() || !(si.isAlive() || getPinnedByAnyLauncher)) { Log.e(TAG, "Shortcut " + shortcutId + " does not exist or disabled"); return null; } diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java index 6bb5bc6cd162efa5b8a1a19635b14375d7e6ffa0..025ebc36f99bb603452460253e67a7c27defd211 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -453,7 +453,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } @Override - boolean injectCheckAccessShortcutsPermission(int callingPid, int callingUid) { + boolean injectHasAccessShortcutsPermission(int callingPid, int callingUid) { return mInjectCheckAccessShortcutsPermission; } @@ -1648,7 +1648,6 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected void assertShortcutLaunchable(@NonNull String packageName, @NonNull String shortcutId, int userId) { assertNotNull(launchShortcutAndGetIntent(packageName, shortcutId, userId)); - assertNotNull(launchShortcutAndGetIntent_withShortcutInfo(packageName, shortcutId, userId)); } protected void assertShortcutNotLaunched(@NonNull String packageName, diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java index b5e8e1ca046f4d5e13c7f270e45986327a2a8489..f92b575bc976629c819390b903f5849616b76ee3 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -1918,7 +1918,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // Make sure FLAG_MATCH_ALL_PINNED will be ignored. assertWith(mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2, /* activity =*/ null, ShortcutQuery.FLAG_MATCH_PINNED - | ShortcutQuery.FLAG_MATCH_ALL_PINNED), getCallingUser())) + | ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER), getCallingUser())) .isEmpty(); // Make sure the special permission works. @@ -1928,14 +1928,18 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertWith(mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2, /* activity =*/ null, ShortcutQuery.FLAG_MATCH_PINNED - | ShortcutQuery.FLAG_MATCH_ALL_PINNED), getCallingUser())) + | ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER), getCallingUser())) .haveIds("s1", "s2"); assertWith(mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2, /* activity =*/ null, ShortcutQuery.FLAG_MATCH_PINNED), getCallingUser())) .isEmpty(); + assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", getCallingUser().getIdentifier()); + mInjectCheckAccessShortcutsPermission = false; + assertShortcutNotLaunched(CALLING_PACKAGE_2, "s1", getCallingUser().getIdentifier()); + assertShortcutIds(assertAllDynamic( mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1, /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED @@ -2106,6 +2110,62 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } + public void testPinShortcutAndGetPinnedShortcuts_assistant() { + // Create some shortcuts. + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertTrue(mManager.setDynamicShortcuts(list( + makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3")))); + }); + + // Pin some. + runWithCaller(LAUNCHER_1, USER_0, () -> { + mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, + list("s3", "s4"), getCallingUser()); + }); + + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertTrue(mManager.setDynamicShortcuts(list( + makeShortcut("s1")))); + }); + + runWithCaller(LAUNCHER_2, USER_0, () -> { + final ShortcutQuery allPinned = new ShortcutQuery().setQueryFlags( + ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER); + + assertWith(mLauncherApps.getShortcuts(allPinned, HANDLE_USER_0)) + .isEmpty(); + + assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0); + assertShortcutNotLaunched(CALLING_PACKAGE_1, "s3", USER_0); + assertShortcutNotLaunched(CALLING_PACKAGE_1, "s4", USER_0); + + // Make it the assistant app. + mInternal.setShortcutHostPackage("assistant", LAUNCHER_2, USER_0); + assertWith(mLauncherApps.getShortcuts(allPinned, HANDLE_USER_0)) + .haveIds("s3"); + + assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0); + assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0); + assertShortcutNotLaunched(CALLING_PACKAGE_1, "s4", USER_0); + + mInternal.setShortcutHostPackage("another-type", LAUNCHER_3, USER_0); + assertWith(mLauncherApps.getShortcuts(allPinned, HANDLE_USER_0)) + .haveIds("s3"); + + mInternal.setShortcutHostPackage("assistant", null, USER_0); + assertWith(mLauncherApps.getShortcuts(allPinned, HANDLE_USER_0)) + .isEmpty(); + + mInternal.setShortcutHostPackage("assistant", LAUNCHER_2, USER_0); + assertWith(mLauncherApps.getShortcuts(allPinned, HANDLE_USER_0)) + .haveIds("s3"); + + mInternal.setShortcutHostPackage("assistant", LAUNCHER_1, USER_0); + assertWith(mLauncherApps.getShortcuts(allPinned, HANDLE_USER_0)) + .isEmpty(); + }); + } + public void testPinShortcutAndGetPinnedShortcuts_crossProfile_plusLaunch() { // Create some shortcuts. runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {