Merge "Allow assistant app to access any shortcuts"

This commit is contained in:
TreeHugger Robot
2017-11-14 18:41:50 +00:00
committed by Android (Google) Code Review
8 changed files with 104 additions and 26 deletions

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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<java.lang.String>);
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 {

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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<String> 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;
}

View File

@@ -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,

View File

@@ -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, () -> {