diff options
| author | 2020-01-21 17:27:26 -0800 | |
|---|---|---|
| committer | 2020-02-06 22:58:14 +0000 | |
| commit | 0de8c29c87c07a19f2078a43e252b2d905fa0c2a (patch) | |
| tree | 91135691bded336e4a8dc5264906d57dbe610961 | |
| parent | 8db4c7bd521269c2f0457239eb4893d3806cc241 (diff) | |
Adds methods in LauncherApps to allow cache/uncache shortcuts
Bug: 148084870
Test: atest com.android.server.pm.ShortcutManagerTest1 \
com.android.server.pm.ShortcutManagerTest2 \
com.android.server.pm.ShortcutManagerTest3 \
com.android.server.pm.ShortcutManagerTest4 \
com.android.server.pm.ShortcutManagerTest5 \
com.android.server.pm.ShortcutManagerTest6 \
com.android.server.pm.ShortcutManagerTest7 \
com.android.server.pm.ShortcutManagerTest8 \
com.android.server.pm.ShortcutManagerTest9 \
com.android.server.pm.ShortcutManagerTest10
Change-Id: I012bcc39194616a895606cdb9beb57a0f4af11d5
7 files changed, 239 insertions, 35 deletions
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl index 04923590b413..38a9ac4a0d05 100644 --- a/core/java/android/content/pm/ILauncherApps.aidl +++ b/core/java/android/content/pm/ILauncherApps.aidl @@ -98,4 +98,9 @@ interface ILauncherApps { in ComponentName componentName, int flags, in IShortcutChangeCallback callback, int callbackId); void unregisterShortcutChangeCallback(String callingPackage, int callbackId); + + void cacheShortcuts(String callingPackage, String packageName, in List<String> shortcutIds, + in UserHandle user); + void uncacheShortcuts(String callingPackage, String packageName, in List<String> shortcutIds, + in UserHandle user); } diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 73c9e4d843b7..d2532783f47c 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -20,6 +20,7 @@ import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; @@ -1089,6 +1090,61 @@ public class LauncherApps { } /** + * Mark shortcuts as cached for a package. + * + * <p>Only dynamic long lived shortcuts can be cached. None dynamic or non long lived shortcuts + * in the list will be ignored. + * + * <p>Unlike pinned shortcuts, where different callers can have different sets of pinned + * shortcuts, cached state is per shortcut only, and even if multiple callers cache the same + * shortcut, it can be uncached by any valid caller. + * + * @param packageName The target package name. + * @param shortcutIds The IDs of the shortcut to be cached. + * @param user The UserHandle of the profile. + * @throws IllegalStateException when the user is locked, or when the {@code user} user + * is locked or not running. + * + * @see ShortcutManager + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS) + public void cacheShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds, + @NonNull UserHandle user) { + logErrorForInvalidProfileAccess(user); + try { + mService.cacheShortcuts(mContext.getPackageName(), packageName, shortcutIds, user); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Remove cached flag from shortcuts for a package. + * + * @param packageName The target package name. + * @param shortcutIds The IDs of the shortcut to be uncached. + * @param user The UserHandle of the profile. + * @throws IllegalStateException when the user is locked, or when the {@code user} user + * is locked or not running. + * + * @see ShortcutManager + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS) + public void uncacheShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds, + @NonNull UserHandle user) { + logErrorForInvalidProfileAccess(user); + try { + mService.uncacheShortcuts(mContext.getPackageName(), packageName, shortcutIds, user); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * @hide kept for testing. */ @Deprecated diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java index a11a1dd5a68b..a69905eb3de4 100644 --- a/core/java/android/content/pm/ShortcutServiceInternal.java +++ b/core/java/android/content/pm/ShortcutServiceInternal.java @@ -85,4 +85,11 @@ public abstract class ShortcutServiceInternal { public abstract boolean isForegroundDefaultLauncher(@NonNull String callingPackage, int callingUid); + + public abstract void cacheShortcuts(int launcherUserId, + @NonNull String callingPackage, @NonNull String packageName, + @NonNull List<String> shortcutIds, int userId); + public abstract void uncacheShortcuts(int launcherUserId, + @NonNull String callingPackage, @NonNull String packageName, + @NonNull List<String> shortcutIds, int userId); } diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 3e64e9828c3e..0cd1c259dd9e 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -661,6 +661,23 @@ public class LauncherAppsService extends SystemService { } } + private void ensureStrictAccessShortcutsPermission(@NonNull String callingPackage) { + verifyCallingPackage(callingPackage); + if (!injectHasAccessShortcutsPermission(injectBinderCallingPid(), + injectBinderCallingUid())) { + throw new SecurityException("Caller can't access shortcut information"); + } + } + + /** + * Returns true if the caller has the "ACCESS_SHORTCUTS" permission. + */ + @VisibleForTesting + boolean injectHasAccessShortcutsPermission(int callingPid, int callingUid) { + return mContext.checkPermission(android.Manifest.permission.ACCESS_SHORTCUTS, + callingPid, callingUid) == PackageManager.PERMISSION_GRANTED; + } + @Override public ParceledListSlice getShortcuts(String callingPackage, long changedSince, String packageName, List shortcutIds, List<LocusId> locusIds, @@ -710,6 +727,30 @@ public class LauncherAppsService extends SystemService { } @Override + public void cacheShortcuts(String callingPackage, String packageName, List<String> ids, + UserHandle targetUser) { + ensureStrictAccessShortcutsPermission(callingPackage); + if (!canAccessProfile(targetUser.getIdentifier(), "Cannot cache shortcuts")) { + return; + } + + mShortcutServiceInternal.cacheShortcuts(getCallingUserId(), + callingPackage, packageName, ids, targetUser.getIdentifier()); + } + + @Override + public void uncacheShortcuts(String callingPackage, String packageName, List<String> ids, + UserHandle targetUser) { + ensureStrictAccessShortcutsPermission(callingPackage); + if (!canAccessProfile(targetUser.getIdentifier(), "Cannot uncache shortcuts")) { + return; + } + + mShortcutServiceInternal.uncacheShortcuts(getCallingUserId(), + callingPackage, packageName, ids, targetUser.getIdentifier()); + } + + @Override public int getShortcutIconResId(String callingPackage, String packageName, String id, int targetUserId) { ensureShortcutPermission(callingPackage); diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index d16c0748ef0e..377fd16d4e19 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -1704,7 +1704,7 @@ public class ShortcutService extends IShortcutService.Stub { ShortcutInfo.validateIcon(shortcut.getIcon()); } - shortcut.replaceFlags(0); + shortcut.replaceFlags(shortcut.getFlags() & ShortcutInfo.FLAG_LONG_LIVED); } private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) { @@ -2758,6 +2758,68 @@ public class ShortcutService extends IShortcutService.Stub { } @Override + public void cacheShortcuts(int launcherUserId, + @NonNull String callingPackage, @NonNull String packageName, + @NonNull List<String> shortcutIds, int userId) { + updateCachedShortcutsInternal(launcherUserId, callingPackage, packageName, shortcutIds, + userId, /* doCache= */ true); + } + + @Override + public void uncacheShortcuts(int launcherUserId, + @NonNull String callingPackage, @NonNull String packageName, + @NonNull List<String> shortcutIds, int userId) { + updateCachedShortcutsInternal(launcherUserId, callingPackage, packageName, shortcutIds, + userId, /* doCache= */ false); + } + + private void updateCachedShortcutsInternal(int launcherUserId, + @NonNull String callingPackage, @NonNull String packageName, + @NonNull List<String> shortcutIds, int userId, boolean doCache) { + // Calling permission must be checked by LauncherAppsImpl. + Preconditions.checkStringNotEmpty(packageName, "packageName"); + Objects.requireNonNull(shortcutIds, "shortcutIds"); + + synchronized (mLock) { + throwIfUserLockedL(userId); + throwIfUserLockedL(launcherUserId); + + final int idSize = shortcutIds.size(); + final ShortcutPackage sp = getUserShortcutsLocked(userId) + .getPackageShortcutsIfExists(packageName); + if (idSize == 0 || sp == null) { + return; + } + + for (int i = 0; i < idSize; i++) { + final String id = Preconditions.checkStringNotEmpty(shortcutIds.get(i)); + final ShortcutInfo si = sp.findShortcutById(id); + if (si == null || doCache == si.isCached()) { + continue; + } + + if (doCache) { + if (si.isDynamic() && si.isLongLived()) { + si.addFlags(ShortcutInfo.FLAG_CACHED); + } else { + Log.w(TAG, "Only dynamic long lived shortcuts can get cached. Ignoring" + + "shortcut " + si.getId()); + } + } else { + if (si.isDynamic()) { + si.clearFlags(ShortcutInfo.FLAG_CACHED); + } else { + sp.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true); + } + } + } + } + packageShortcutsChanged(packageName, userId); + + verifyStates(); + } + + @Override public Intent[] createShortcutIntents(int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull String shortcutId, int userId, 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 3d190be8888b..77f842aa503f 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -556,6 +556,11 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { void injectRestoreCallingIdentity(long token) { mInjectedCallingUid = (int) token; } + + @Override + boolean injectHasAccessShortcutsPermission(int callingPid, int callingUid) { + return true; + } } protected class LauncherAppsTestable extends LauncherApps { @@ -1617,6 +1622,22 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } /** + * Make a long lived shortcut with an ID. + */ + protected ShortcutInfo makeLongLivedShortcut(String id) { + final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext, id) + .setActivity(new ComponentName(mClientContext.getPackageName(), "main")) + .setShortLabel("title-" + id) + .setIntent(makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class)) + .setLongLived(true); + final ShortcutInfo s = b.build(); + + s.setTimestamp(mInjectedCurrentTimeMillis); // HACK + + return s; + } + + /** * Make an intent. */ protected Intent makeIntent(String action, Class<?> clazz, Object... bundleKeysAndValues) { 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 63da5fbab122..f03670843f3a 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -1240,7 +1240,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { assertTrue(mManager.setDynamicShortcuts(list( - makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3")))); + makeLongLivedShortcut("s1"), makeLongLivedShortcut("s2"), makeShortcut("s3")))); }); // Pin 2 and 3 @@ -1250,9 +1250,12 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); // Cache 1 and 2 + runWithCaller(LAUNCHER_1, USER_0, () -> { + mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2"), + HANDLE_USER_0); + }); + setCaller(CALLING_PACKAGE_1); - getCallerShortcut("s1").setCached(); - getCallerShortcut("s2").setCached(); // Get manifest shortcuts assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_MANIFEST), @@ -1315,8 +1318,9 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { public void testCachedShortcuts() { runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { - assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"), makeShortcut("s2"), - makeShortcut("s3"), makeShortcut("s4")))); + assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"), + makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"), + makeLongLivedShortcut("s4")))); }); // Pin s2 @@ -1325,11 +1329,13 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { HANDLE_USER_0); }); - // Cache 2, 3 and 4 + // Cache some, but non long lived shortcuts will be ignored. + runWithCaller(LAUNCHER_1, USER_0, () -> { + mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2", "s4"), + HANDLE_USER_0); + }); + setCaller(CALLING_PACKAGE_1); - getCallerShortcut("s2").setCached(); - getCallerShortcut("s3").setCached(); - getCallerShortcut("s4").setCached(); // Get dynamic shortcuts assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_DYNAMIC), @@ -1339,27 +1345,37 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { "s2"); // Get cached shortcuts assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), - "s2", "s3", "s4"); + "s2", "s4"); // Remove a dynamic cached shortcut - mManager.removeDynamicShortcuts(list("s3")); + mManager.removeDynamicShortcuts(list("s4")); assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_DYNAMIC), - "s1", "s2", "s4"); + "s1", "s2", "s3"); assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), - "s2", "s3", "s4"); + "s2", "s4"); - // Remove dynamic cached long lived shortcuts - mManager.removeLongLivedShortcuts(list("s3", "s4")); - assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_DYNAMIC), - "s1", "s2"); + // uncache a non-dynamic shortcut. Should be removed. + runWithCaller(LAUNCHER_1, USER_0, () -> { + mLauncherApps.uncacheShortcuts(CALLING_PACKAGE_1, list("s4"), + HANDLE_USER_0); + }); assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), "s2"); + // Cache another shortcut + runWithCaller(LAUNCHER_1, USER_0, () -> { + mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s3"), + HANDLE_USER_0); + }); + assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), + "s2", "s3"); + // Remove a dynamic cached pinned long lived shortcut mManager.removeLongLivedShortcuts(list("s2")); assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_DYNAMIC), - "s1"); - assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED)); + "s1", "s3"); + assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), + "s3"); assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_PINNED), "s2"); } @@ -1371,7 +1387,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // Set up shortcuts. setCaller(CALLING_PACKAGE_1); - final ShortcutInfo s1_1 = makeShortcut("s1"); + final ShortcutInfo s1_1 = makeLongLivedShortcut("s1"); final ShortcutInfo s1_2 = makeShortcutWithLocusId("s2", makeLocusId("l1")); assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2))); @@ -1395,6 +1411,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { setCaller(CALLING_PACKAGE_3); final ShortcutInfo s3_2 = makeShortcutWithLocusId("s3", makeLocusId("l2")); + s3_2.setLongLived(); + assertTrue(mManager.setDynamicShortcuts(list(s3_2))); getCallerShortcut("s3").setTimestamp(START_TIME + 5000); @@ -1535,26 +1553,20 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // TODO More tests: pinned but dynamic. - // Cache some shortcuts - setCaller(CALLING_PACKAGE_1); - getCallerShortcut("s1").setCached(); - - setCaller(CALLING_PACKAGE_2); - getCallerShortcut("s4").setCached(); - - setCaller(CALLING_PACKAGE_3); - getCallerShortcut("s3").setCached(); - setCaller(LAUNCHER_1); + // Cache some shortcuts. Only long lived shortcuts can get cached. + mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1"), getCallingUser()); + mLauncherApps.cacheShortcuts(CALLING_PACKAGE_3, list("s3"), getCallingUser()); + // Cached ones only assertShortcutIds(assertAllNotKeyFieldsOnly( mLauncherApps.getShortcuts(buildQuery( - /* time =*/ 0, CALLING_PACKAGE_2, + /* time =*/ 0, CALLING_PACKAGE_3, /* activity =*/ null, ShortcutQuery.FLAG_MATCH_CACHED), getCallingUser())), - "s4"); + "s3"); // All packages. assertShortcutIds(assertAllNotKeyFieldsOnly( @@ -1563,7 +1575,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /* activity =*/ null, ShortcutQuery.FLAG_MATCH_CACHED), getCallingUser())), - "s1", "s4", "s3"); + "s1", "s3"); assertExpectException( IllegalArgumentException.class, "package name must also be set", () -> { @@ -1581,7 +1593,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /* activity =*/ null, ShortcutQuery.FLAG_MATCH_CACHED), getCallingUser())), - "s1", "s4", "s3"); + "s1", "s3"); } public void testGetShortcuts_shortcutKinds() throws Exception { |