Loading core/java/android/content/pm/LauncherApps.java +22 −0 Original line number Diff line number Diff line Loading @@ -492,6 +492,8 @@ public class LauncherApps { * If the calling launcher application contains pinned shortcuts, they will still work, * even though the caller no longer has the shortcut host permission. * * <p>Returns {@code false} when the user is locked. * * @see ShortcutManager */ public boolean hasShortcutHostPermission() { Loading @@ -508,6 +510,9 @@ public class LauncherApps { * <p>Callers must be allowed to access the shortcut information, as defined in {@link * #hasShortcutHostPermission()}. * * <p>Returns am empty list when the user is locked, or when the {@code user} user * is locked or not running. * * @param query result includes shortcuts matching this query. * @param user The UserHandle of the profile. * Loading Loading @@ -551,6 +556,9 @@ public class LauncherApps { * <p>The calling launcher application must be allowed to access the shortcut information, * as defined in {@link #hasShortcutHostPermission()}. * * <p>Call will be ignored when the user is locked, or when the {@code user} user * is locked or not running. * * @param packageName The target package name. * @param shortcutIds The IDs of the shortcut to be pinned. * @param user The UserHandle of the profile. Loading Loading @@ -622,6 +630,9 @@ public class LauncherApps { * <p>The calling launcher application must be allowed to access the shortcut information, * as defined in {@link #hasShortcutHostPermission()}. * * <p>Returns {@code null} when the user is locked, or when the user owning the shortcut * is locked or not running. * * @param density The preferred density of the icon, zero for default density. Use * density DPI values from {@link DisplayMetrics}. * Loading Loading @@ -670,6 +681,9 @@ public class LauncherApps { * <p>The calling launcher application must be allowed to access the shortcut information, * as defined in {@link #hasShortcutHostPermission()}. * * <p>Returns {@code 0} when the user is locked, or when the user owning the shortcut * is locked or not running. * * @param density Optional density for the icon, or 0 to use the default density. Use * @return A badged icon for the shortcut. * Loading @@ -690,6 +704,10 @@ public class LauncherApps { * <p>The calling launcher application must be allowed to access the shortcut information, * as defined in {@link #hasShortcutHostPermission()}. * * <p>Throws {@link android.content.ActivityNotFoundException} * when the user is locked, or when the {@code user} user * is locked or not running. * * @param packageName The target shortcut package name. * @param shortcutId The target shortcut ID. * @param sourceBounds The Rect containing the source bounds of the clicked icon. Loading @@ -712,6 +730,10 @@ public class LauncherApps { * <p>The calling launcher application must be allowed to access the shortcut information, * as defined in {@link #hasShortcutHostPermission()}. * * <p>Throws {@link android.content.ActivityNotFoundException} * when the user is locked, or when the user owning the shortcut * is locked or not running. * * @param shortcut The target shortcut. * @param sourceBounds The Rect containing the source bounds of the clicked icon. * @param startActivityOptions Options to pass to startActivity. Loading core/java/android/content/pm/ShortcutManager.java +32 −0 Original line number Diff line number Diff line Loading @@ -434,6 +434,12 @@ import java.util.List; * <h3>Launcher API</h3> * * The {@link LauncherApps} class provides APIs for launcher applications to access shortcuts. * * * <h3>Direct Boot and Shortcuts</h3> * * All shortcut information is stored in credential encrypted storage, so no shortcuts can be * accessed when the user is locked. */ public class ShortcutManager { private static final String TAG = "ShortcutManager"; Loading Loading @@ -469,6 +475,8 @@ public class ShortcutManager { * * @throws IllegalArgumentException if {@link #getMaxShortcutCountPerActivity()} is exceeded, * or when trying to update immutable shortcuts. * * @throws IllegalStateException when the user is locked. */ public boolean setDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) { try { Loading @@ -481,6 +489,8 @@ public class ShortcutManager { /** * Return all dynamic shortcuts from the caller application. * * @throws IllegalStateException when the user is locked. */ @NonNull public List<ShortcutInfo> getDynamicShortcuts() { Loading @@ -494,6 +504,8 @@ public class ShortcutManager { /** * Return all manifest shortcuts from the caller application. * * @throws IllegalStateException when the user is locked. */ @NonNull public List<ShortcutInfo> getManifestShortcuts() { Loading @@ -515,6 +527,8 @@ public class ShortcutManager { * * @throws IllegalArgumentException if {@link #getMaxShortcutCountPerActivity()} is exceeded, * or when trying to update immutable shortcuts. * * @throws IllegalStateException when the user is locked. */ public boolean addDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) { try { Loading @@ -527,6 +541,8 @@ public class ShortcutManager { /** * Delete dynamic shortcuts by ID. * * @throws IllegalStateException when the user is locked. */ public void removeDynamicShortcuts(@NonNull List<String> shortcutIds) { try { Loading @@ -539,6 +555,8 @@ public class ShortcutManager { /** * Delete all dynamic shortcuts from the caller application. * * @throws IllegalStateException when the user is locked. */ public void removeAllDynamicShortcuts() { try { Loading @@ -550,6 +568,8 @@ public class ShortcutManager { /** * Return all pinned shortcuts from the caller application. * * @throws IllegalStateException when the user is locked. */ @NonNull public List<ShortcutInfo> getPinnedShortcuts() { Loading @@ -570,6 +590,8 @@ public class ShortcutManager { * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited. * * @throws IllegalArgumentException If trying to update immutable shortcuts. * * @throws IllegalStateException when the user is locked. */ public boolean updateShortcuts(List<ShortcutInfo> shortcutInfoList) { try { Loading @@ -585,6 +607,8 @@ public class ShortcutManager { * class. * * @throws IllegalArgumentException If trying to disable immutable shortcuts. * * @throws IllegalStateException when the user is locked. */ public void disableShortcuts(@NonNull List<String> shortcutIds) { try { Loading Loading @@ -622,6 +646,8 @@ public class ShortcutManager { * For more details, see the Javadoc for the {@link ShortcutManager} class. * * @throws IllegalArgumentException If trying to disable immutable shortcuts. * * @throws IllegalStateException when the user is locked. */ public void disableShortcuts(@NonNull List<String> shortcutIds, CharSequence disabledMessage) { try { Loading @@ -638,6 +664,8 @@ public class ShortcutManager { * already enabled, this method does nothing. * * @throws IllegalArgumentException If trying to enable immutable shortcuts. * * @throws IllegalStateException when the user is locked. */ public void enableShortcuts(@NonNull List<String> shortcutIds) { try { Loading Loading @@ -704,6 +732,8 @@ public class ShortcutManager { * Return {@code true} when rate-limiting is active for the caller application. * * <p>See the class level javadoc for details. * * @throws IllegalStateException when the user is locked. */ public boolean isRateLimitingActive() { try { Loading Loading @@ -747,6 +777,8 @@ public class ShortcutManager { * <p>The information is accessible via {@link UsageStatsManager#queryEvents} * Typically, launcher applications use this information to build a prediction model * so that they can promote the shortcuts that are likely to be used at the moment. * * @throws IllegalStateException when the user is locked. */ public void reportShortcutUsed(String shortcutId) { try { Loading services/core/java/com/android/server/pm/ShortcutParser.java +1 −1 Original line number Diff line number Diff line Loading @@ -259,7 +259,7 @@ public class ShortcutParser { continue; } ShortcutService.warnForInvalidTag(depth, tag); Log.w(TAG, String.format("Invalid tag '%s' found at depth %d", tag, depth)); } } finally { if (parser != null) { Loading services/core/java/com/android/server/pm/ShortcutService.java +78 −2 Original line number Diff line number Diff line Loading @@ -1037,6 +1037,24 @@ public class ShortcutService extends IShortcutService.Stub { } } private boolean isUserUnlocked(@UserIdInt int userId) { final long token = injectClearCallingIdentity(); try { // Weird: when SystemService.onUnlockUser() is called, the user state is still // unlocking, as opposed to unlocked. So we need to accept the "unlocking" state too. // We know when the user is unlocking, the CE storage is already unlocked. return mUserManager.isUserUnlockingOrUnlocked(userId); } finally { injectRestoreCallingIdentity(token); } } void throwIfUserLocked(@UserIdInt int userId) { if (!isUserUnlocked(userId)) { throw new IllegalStateException("User " + userId + " is locked or not running"); } } @GuardedBy("mLock") @NonNull private boolean isUserLoadedLocked(@UserIdInt int userId) { Loading @@ -1047,6 +1065,11 @@ public class ShortcutService extends IShortcutService.Stub { @GuardedBy("mLock") @NonNull ShortcutUser getUserShortcutsLocked(@UserIdInt int userId) { if (!isUserUnlocked(userId)) { wtf("User still locked"); return new ShortcutUser(this, userId); } ShortcutUser userPackages = mUsers.get(userId); if (userPackages == null) { userPackages = loadUserLocked(userId); Loading Loading @@ -1535,6 +1558,7 @@ public class ShortcutService extends IShortcutService.Stub { public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, @UserIdInt int userId) { verifyCaller(packageName, userId); throwIfUserLocked(userId); final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); final int size = newShortcuts.size(); Loading Loading @@ -1585,6 +1609,7 @@ public class ShortcutService extends IShortcutService.Stub { public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList, @UserIdInt int userId) { verifyCaller(packageName, userId); throwIfUserLocked(userId); final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); final int size = newShortcuts.size(); Loading Loading @@ -1664,6 +1689,7 @@ public class ShortcutService extends IShortcutService.Stub { public boolean addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, @UserIdInt int userId) { verifyCaller(packageName, userId); throwIfUserLocked(userId); final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); final int size = newShortcuts.size(); Loading Loading @@ -1715,6 +1741,7 @@ public class ShortcutService extends IShortcutService.Stub { CharSequence disabledMessage, int disabledMessageResId, @UserIdInt int userId) { verifyCaller(packageName, userId); Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided"); throwIfUserLocked(userId); synchronized (mLock) { final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); Loading Loading @@ -1743,6 +1770,7 @@ public class ShortcutService extends IShortcutService.Stub { public void enableShortcuts(String packageName, List shortcutIds, @UserIdInt int userId) { verifyCaller(packageName, userId); Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided"); throwIfUserLocked(userId); synchronized (mLock) { final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); Loading @@ -1764,6 +1792,7 @@ public class ShortcutService extends IShortcutService.Stub { @UserIdInt int userId) { verifyCaller(packageName, userId); Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided"); throwIfUserLocked(userId); synchronized (mLock) { final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); Loading @@ -1787,6 +1816,7 @@ public class ShortcutService extends IShortcutService.Stub { @Override public void removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) { verifyCaller(packageName, userId); throwIfUserLocked(userId); synchronized (mLock) { final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); Loading @@ -1802,6 +1832,8 @@ public class ShortcutService extends IShortcutService.Stub { public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName, @UserIdInt int userId) { verifyCaller(packageName, userId); throwIfUserLocked(userId); synchronized (mLock) { return getShortcutsWithQueryLocked( packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, Loading @@ -1813,6 +1845,8 @@ public class ShortcutService extends IShortcutService.Stub { public ParceledListSlice<ShortcutInfo> getManifestShortcuts(String packageName, @UserIdInt int userId) { verifyCaller(packageName, userId); throwIfUserLocked(userId); synchronized (mLock) { return getShortcutsWithQueryLocked( packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, Loading @@ -1824,6 +1858,8 @@ public class ShortcutService extends IShortcutService.Stub { public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName, @UserIdInt int userId) { verifyCaller(packageName, userId); throwIfUserLocked(userId); synchronized (mLock) { return getShortcutsWithQueryLocked( packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, Loading Loading @@ -1854,6 +1890,7 @@ public class ShortcutService extends IShortcutService.Stub { @Override public int getRemainingCallCount(String packageName, @UserIdInt int userId) { verifyCaller(packageName, userId); throwIfUserLocked(userId); synchronized (mLock) { final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); Loading @@ -1865,6 +1902,7 @@ public class ShortcutService extends IShortcutService.Stub { @Override public long getRateLimitResetTime(String packageName, @UserIdInt int userId) { verifyCaller(packageName, userId); throwIfUserLocked(userId); synchronized (mLock) { return getNextResetTimeLocked(); Loading @@ -1883,6 +1921,7 @@ public class ShortcutService extends IShortcutService.Stub { @Override public void reportShortcutUsed(String packageName, String shortcutId, int userId) { verifyCaller(packageName, userId); throwIfUserLocked(userId); Preconditions.checkNotNull(shortcutId); Loading Loading @@ -1951,6 +1990,10 @@ public class ShortcutService extends IShortcutService.Stub { Slog.d(TAG, "onApplicationActive: package=" + packageName + " userid=" + userId); } enforceResetThrottlingPermission(); if (!isUserUnlocked(userId)) { // This is called by system UI, so no need to throw. Just ignore. return; } resetPackageThrottling(packageName, userId); } Loading @@ -1968,6 +2011,8 @@ public class ShortcutService extends IShortcutService.Stub { // even when hasShortcutPermission() is overridden. @VisibleForTesting boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) { throwIfUserLocked(userId); synchronized (mLock) { final ShortcutUser user = getUserShortcutsLocked(userId); Loading Loading @@ -2125,6 +2170,10 @@ public class ShortcutService extends IShortcutService.Stub { @Nullable ComponentName componentName, int queryFlags, int userId) { final ArrayList<ShortcutInfo> ret = new ArrayList<>(); if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) { return ret; } final boolean cloneKeyFieldOnly = ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0); final int cloneFlag = cloneKeyFieldOnly ? ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO Loading Loading @@ -2202,6 +2251,10 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkStringNotEmpty(packageName, "packageName"); Preconditions.checkStringNotEmpty(shortcutId, "shortcutId"); if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) { return false; } synchronized (mLock) { getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) .attemptToRestoreIfNeededAndSave(); Loading @@ -2218,6 +2271,10 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkStringNotEmpty(packageName, "packageName"); Preconditions.checkStringNotEmpty(shortcutId, "shortcutId"); if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) { return null; } final ShortcutPackage p = getUserShortcutsLocked(userId) .getPackageShortcutsIfExists(packageName); if (p == null) { Loading @@ -2239,6 +2296,10 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkStringNotEmpty(packageName, "packageName"); Preconditions.checkNotNull(shortcutIds, "shortcutIds"); if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) { return; } synchronized (mLock) { final ShortcutLauncher launcher = getLauncherShortcutsLocked(callingPackage, userId, launcherUserId); Loading @@ -2259,6 +2320,10 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty"); Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty"); if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) { return null; } synchronized (mLock) { getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) .attemptToRestoreIfNeededAndSave(); Loading Loading @@ -2289,6 +2354,10 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkNotNull(packageName, "packageName"); Preconditions.checkNotNull(shortcutId, "shortcutId"); if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) { return 0; } synchronized (mLock) { getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) .attemptToRestoreIfNeededAndSave(); Loading @@ -2313,6 +2382,10 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkNotNull(packageName, "packageName"); Preconditions.checkNotNull(shortcutId, "shortcutId"); if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) { return null; } synchronized (mLock) { getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) .attemptToRestoreIfNeededAndSave(); Loading Loading @@ -2345,6 +2418,9 @@ public class ShortcutService extends IShortcutService.Stub { @Override public boolean hasShortcutHostPermission(int launcherUserId, @NonNull String callingPackage) { if (!isUserUnlocked(launcherUserId)) { return false; } return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId); } } Loading Loading @@ -2395,7 +2471,7 @@ public class ShortcutService extends IShortcutService.Stub { final long token = injectClearCallingIdentity(); try { if (!mUserManager.isUserUnlocked(userId)) { if (!isUserUnlocked(userId)) { if (DEBUG) { Slog.d(TAG, "Ignoring package broadcast " + action + " for locked/stopped user " + userId); Loading Loading @@ -3208,7 +3284,7 @@ public class ShortcutService extends IShortcutService.Stub { case "--user": if (takeUser) { mUserId = UserHandle.parseUserArg(getNextArgRequired()); if (!mUserManager.isUserUnlocked(mUserId)) { if (!isUserUnlocked(mUserId)) { throw new CommandException( "User " + mUserId + " is not running or locked"); } Loading services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +22 −10 Original line number Diff line number Diff line Loading @@ -687,19 +687,25 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { // Set up isUserRunning and isUserUnlocked. when(mMockUserManager.isUserRunning(anyInt())).thenAnswer(new AnswerWithSystemCheck<>( inv -> mRunningUsers.get((Integer) inv.getArguments()[0]))); inv -> b(mRunningUsers.get((Integer) inv.getArguments()[0])))); when(mMockUserManager.isUserUnlocked(anyInt())).thenAnswer(new AnswerWithSystemCheck<>( inv -> { when(mMockUserManager.isUserUnlocked(anyInt())) .thenAnswer(new AnswerWithSystemCheck<>(inv -> { final int userId = (Integer) inv.getArguments()[0]; return mRunningUsers.get(userId) && mUnlockedUsers.get(userId); return b(mRunningUsers.get(userId)) && b(mUnlockedUsers.get(userId)); })); // isUserUnlockingOrUnlocked() return the same value as isUserUnlocked(). when(mMockUserManager.isUserUnlockingOrUnlocked(anyInt())) .thenAnswer(new AnswerWithSystemCheck<>(inv -> { final int userId = (Integer) inv.getArguments()[0]; return b(mRunningUsers.get(userId)) && b(mUnlockedUsers.get(userId)); })); // User 0 is always running // User 0 and P0 are always running mRunningUsers.put(USER_0, true); mRunningUsers.put(USER_10, false); mRunningUsers.put(USER_11, false); mRunningUsers.put(USER_P0, false); mRunningUsers.put(USER_P0, true); // Unlock all users by default. mUnlockedUsers.put(USER_0, true); Loading @@ -715,6 +721,10 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { setCaller(CALLING_PACKAGE_1); } private static boolean b(Boolean value) { return (value != null && value); } /** * Returns a boolean but also checks if the current UID is SYSTEM_UID. */ Loading Loading @@ -1726,6 +1736,8 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } protected void prepareCrossProfileDataSet() { mRunningUsers.put(USER_10, true); // this test needs user 10. runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { assertTrue(mManager.setDynamicShortcuts(list( makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"), Loading Loading
core/java/android/content/pm/LauncherApps.java +22 −0 Original line number Diff line number Diff line Loading @@ -492,6 +492,8 @@ public class LauncherApps { * If the calling launcher application contains pinned shortcuts, they will still work, * even though the caller no longer has the shortcut host permission. * * <p>Returns {@code false} when the user is locked. * * @see ShortcutManager */ public boolean hasShortcutHostPermission() { Loading @@ -508,6 +510,9 @@ public class LauncherApps { * <p>Callers must be allowed to access the shortcut information, as defined in {@link * #hasShortcutHostPermission()}. * * <p>Returns am empty list when the user is locked, or when the {@code user} user * is locked or not running. * * @param query result includes shortcuts matching this query. * @param user The UserHandle of the profile. * Loading Loading @@ -551,6 +556,9 @@ public class LauncherApps { * <p>The calling launcher application must be allowed to access the shortcut information, * as defined in {@link #hasShortcutHostPermission()}. * * <p>Call will be ignored when the user is locked, or when the {@code user} user * is locked or not running. * * @param packageName The target package name. * @param shortcutIds The IDs of the shortcut to be pinned. * @param user The UserHandle of the profile. Loading Loading @@ -622,6 +630,9 @@ public class LauncherApps { * <p>The calling launcher application must be allowed to access the shortcut information, * as defined in {@link #hasShortcutHostPermission()}. * * <p>Returns {@code null} when the user is locked, or when the user owning the shortcut * is locked or not running. * * @param density The preferred density of the icon, zero for default density. Use * density DPI values from {@link DisplayMetrics}. * Loading Loading @@ -670,6 +681,9 @@ public class LauncherApps { * <p>The calling launcher application must be allowed to access the shortcut information, * as defined in {@link #hasShortcutHostPermission()}. * * <p>Returns {@code 0} when the user is locked, or when the user owning the shortcut * is locked or not running. * * @param density Optional density for the icon, or 0 to use the default density. Use * @return A badged icon for the shortcut. * Loading @@ -690,6 +704,10 @@ public class LauncherApps { * <p>The calling launcher application must be allowed to access the shortcut information, * as defined in {@link #hasShortcutHostPermission()}. * * <p>Throws {@link android.content.ActivityNotFoundException} * when the user is locked, or when the {@code user} user * is locked or not running. * * @param packageName The target shortcut package name. * @param shortcutId The target shortcut ID. * @param sourceBounds The Rect containing the source bounds of the clicked icon. Loading @@ -712,6 +730,10 @@ public class LauncherApps { * <p>The calling launcher application must be allowed to access the shortcut information, * as defined in {@link #hasShortcutHostPermission()}. * * <p>Throws {@link android.content.ActivityNotFoundException} * when the user is locked, or when the user owning the shortcut * is locked or not running. * * @param shortcut The target shortcut. * @param sourceBounds The Rect containing the source bounds of the clicked icon. * @param startActivityOptions Options to pass to startActivity. Loading
core/java/android/content/pm/ShortcutManager.java +32 −0 Original line number Diff line number Diff line Loading @@ -434,6 +434,12 @@ import java.util.List; * <h3>Launcher API</h3> * * The {@link LauncherApps} class provides APIs for launcher applications to access shortcuts. * * * <h3>Direct Boot and Shortcuts</h3> * * All shortcut information is stored in credential encrypted storage, so no shortcuts can be * accessed when the user is locked. */ public class ShortcutManager { private static final String TAG = "ShortcutManager"; Loading Loading @@ -469,6 +475,8 @@ public class ShortcutManager { * * @throws IllegalArgumentException if {@link #getMaxShortcutCountPerActivity()} is exceeded, * or when trying to update immutable shortcuts. * * @throws IllegalStateException when the user is locked. */ public boolean setDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) { try { Loading @@ -481,6 +489,8 @@ public class ShortcutManager { /** * Return all dynamic shortcuts from the caller application. * * @throws IllegalStateException when the user is locked. */ @NonNull public List<ShortcutInfo> getDynamicShortcuts() { Loading @@ -494,6 +504,8 @@ public class ShortcutManager { /** * Return all manifest shortcuts from the caller application. * * @throws IllegalStateException when the user is locked. */ @NonNull public List<ShortcutInfo> getManifestShortcuts() { Loading @@ -515,6 +527,8 @@ public class ShortcutManager { * * @throws IllegalArgumentException if {@link #getMaxShortcutCountPerActivity()} is exceeded, * or when trying to update immutable shortcuts. * * @throws IllegalStateException when the user is locked. */ public boolean addDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) { try { Loading @@ -527,6 +541,8 @@ public class ShortcutManager { /** * Delete dynamic shortcuts by ID. * * @throws IllegalStateException when the user is locked. */ public void removeDynamicShortcuts(@NonNull List<String> shortcutIds) { try { Loading @@ -539,6 +555,8 @@ public class ShortcutManager { /** * Delete all dynamic shortcuts from the caller application. * * @throws IllegalStateException when the user is locked. */ public void removeAllDynamicShortcuts() { try { Loading @@ -550,6 +568,8 @@ public class ShortcutManager { /** * Return all pinned shortcuts from the caller application. * * @throws IllegalStateException when the user is locked. */ @NonNull public List<ShortcutInfo> getPinnedShortcuts() { Loading @@ -570,6 +590,8 @@ public class ShortcutManager { * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited. * * @throws IllegalArgumentException If trying to update immutable shortcuts. * * @throws IllegalStateException when the user is locked. */ public boolean updateShortcuts(List<ShortcutInfo> shortcutInfoList) { try { Loading @@ -585,6 +607,8 @@ public class ShortcutManager { * class. * * @throws IllegalArgumentException If trying to disable immutable shortcuts. * * @throws IllegalStateException when the user is locked. */ public void disableShortcuts(@NonNull List<String> shortcutIds) { try { Loading Loading @@ -622,6 +646,8 @@ public class ShortcutManager { * For more details, see the Javadoc for the {@link ShortcutManager} class. * * @throws IllegalArgumentException If trying to disable immutable shortcuts. * * @throws IllegalStateException when the user is locked. */ public void disableShortcuts(@NonNull List<String> shortcutIds, CharSequence disabledMessage) { try { Loading @@ -638,6 +664,8 @@ public class ShortcutManager { * already enabled, this method does nothing. * * @throws IllegalArgumentException If trying to enable immutable shortcuts. * * @throws IllegalStateException when the user is locked. */ public void enableShortcuts(@NonNull List<String> shortcutIds) { try { Loading Loading @@ -704,6 +732,8 @@ public class ShortcutManager { * Return {@code true} when rate-limiting is active for the caller application. * * <p>See the class level javadoc for details. * * @throws IllegalStateException when the user is locked. */ public boolean isRateLimitingActive() { try { Loading Loading @@ -747,6 +777,8 @@ public class ShortcutManager { * <p>The information is accessible via {@link UsageStatsManager#queryEvents} * Typically, launcher applications use this information to build a prediction model * so that they can promote the shortcuts that are likely to be used at the moment. * * @throws IllegalStateException when the user is locked. */ public void reportShortcutUsed(String shortcutId) { try { Loading
services/core/java/com/android/server/pm/ShortcutParser.java +1 −1 Original line number Diff line number Diff line Loading @@ -259,7 +259,7 @@ public class ShortcutParser { continue; } ShortcutService.warnForInvalidTag(depth, tag); Log.w(TAG, String.format("Invalid tag '%s' found at depth %d", tag, depth)); } } finally { if (parser != null) { Loading
services/core/java/com/android/server/pm/ShortcutService.java +78 −2 Original line number Diff line number Diff line Loading @@ -1037,6 +1037,24 @@ public class ShortcutService extends IShortcutService.Stub { } } private boolean isUserUnlocked(@UserIdInt int userId) { final long token = injectClearCallingIdentity(); try { // Weird: when SystemService.onUnlockUser() is called, the user state is still // unlocking, as opposed to unlocked. So we need to accept the "unlocking" state too. // We know when the user is unlocking, the CE storage is already unlocked. return mUserManager.isUserUnlockingOrUnlocked(userId); } finally { injectRestoreCallingIdentity(token); } } void throwIfUserLocked(@UserIdInt int userId) { if (!isUserUnlocked(userId)) { throw new IllegalStateException("User " + userId + " is locked or not running"); } } @GuardedBy("mLock") @NonNull private boolean isUserLoadedLocked(@UserIdInt int userId) { Loading @@ -1047,6 +1065,11 @@ public class ShortcutService extends IShortcutService.Stub { @GuardedBy("mLock") @NonNull ShortcutUser getUserShortcutsLocked(@UserIdInt int userId) { if (!isUserUnlocked(userId)) { wtf("User still locked"); return new ShortcutUser(this, userId); } ShortcutUser userPackages = mUsers.get(userId); if (userPackages == null) { userPackages = loadUserLocked(userId); Loading Loading @@ -1535,6 +1558,7 @@ public class ShortcutService extends IShortcutService.Stub { public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, @UserIdInt int userId) { verifyCaller(packageName, userId); throwIfUserLocked(userId); final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); final int size = newShortcuts.size(); Loading Loading @@ -1585,6 +1609,7 @@ public class ShortcutService extends IShortcutService.Stub { public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList, @UserIdInt int userId) { verifyCaller(packageName, userId); throwIfUserLocked(userId); final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); final int size = newShortcuts.size(); Loading Loading @@ -1664,6 +1689,7 @@ public class ShortcutService extends IShortcutService.Stub { public boolean addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, @UserIdInt int userId) { verifyCaller(packageName, userId); throwIfUserLocked(userId); final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); final int size = newShortcuts.size(); Loading Loading @@ -1715,6 +1741,7 @@ public class ShortcutService extends IShortcutService.Stub { CharSequence disabledMessage, int disabledMessageResId, @UserIdInt int userId) { verifyCaller(packageName, userId); Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided"); throwIfUserLocked(userId); synchronized (mLock) { final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); Loading Loading @@ -1743,6 +1770,7 @@ public class ShortcutService extends IShortcutService.Stub { public void enableShortcuts(String packageName, List shortcutIds, @UserIdInt int userId) { verifyCaller(packageName, userId); Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided"); throwIfUserLocked(userId); synchronized (mLock) { final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); Loading @@ -1764,6 +1792,7 @@ public class ShortcutService extends IShortcutService.Stub { @UserIdInt int userId) { verifyCaller(packageName, userId); Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided"); throwIfUserLocked(userId); synchronized (mLock) { final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); Loading @@ -1787,6 +1816,7 @@ public class ShortcutService extends IShortcutService.Stub { @Override public void removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) { verifyCaller(packageName, userId); throwIfUserLocked(userId); synchronized (mLock) { final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); Loading @@ -1802,6 +1832,8 @@ public class ShortcutService extends IShortcutService.Stub { public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName, @UserIdInt int userId) { verifyCaller(packageName, userId); throwIfUserLocked(userId); synchronized (mLock) { return getShortcutsWithQueryLocked( packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, Loading @@ -1813,6 +1845,8 @@ public class ShortcutService extends IShortcutService.Stub { public ParceledListSlice<ShortcutInfo> getManifestShortcuts(String packageName, @UserIdInt int userId) { verifyCaller(packageName, userId); throwIfUserLocked(userId); synchronized (mLock) { return getShortcutsWithQueryLocked( packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, Loading @@ -1824,6 +1858,8 @@ public class ShortcutService extends IShortcutService.Stub { public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName, @UserIdInt int userId) { verifyCaller(packageName, userId); throwIfUserLocked(userId); synchronized (mLock) { return getShortcutsWithQueryLocked( packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, Loading Loading @@ -1854,6 +1890,7 @@ public class ShortcutService extends IShortcutService.Stub { @Override public int getRemainingCallCount(String packageName, @UserIdInt int userId) { verifyCaller(packageName, userId); throwIfUserLocked(userId); synchronized (mLock) { final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); Loading @@ -1865,6 +1902,7 @@ public class ShortcutService extends IShortcutService.Stub { @Override public long getRateLimitResetTime(String packageName, @UserIdInt int userId) { verifyCaller(packageName, userId); throwIfUserLocked(userId); synchronized (mLock) { return getNextResetTimeLocked(); Loading @@ -1883,6 +1921,7 @@ public class ShortcutService extends IShortcutService.Stub { @Override public void reportShortcutUsed(String packageName, String shortcutId, int userId) { verifyCaller(packageName, userId); throwIfUserLocked(userId); Preconditions.checkNotNull(shortcutId); Loading Loading @@ -1951,6 +1990,10 @@ public class ShortcutService extends IShortcutService.Stub { Slog.d(TAG, "onApplicationActive: package=" + packageName + " userid=" + userId); } enforceResetThrottlingPermission(); if (!isUserUnlocked(userId)) { // This is called by system UI, so no need to throw. Just ignore. return; } resetPackageThrottling(packageName, userId); } Loading @@ -1968,6 +2011,8 @@ public class ShortcutService extends IShortcutService.Stub { // even when hasShortcutPermission() is overridden. @VisibleForTesting boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) { throwIfUserLocked(userId); synchronized (mLock) { final ShortcutUser user = getUserShortcutsLocked(userId); Loading Loading @@ -2125,6 +2170,10 @@ public class ShortcutService extends IShortcutService.Stub { @Nullable ComponentName componentName, int queryFlags, int userId) { final ArrayList<ShortcutInfo> ret = new ArrayList<>(); if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) { return ret; } final boolean cloneKeyFieldOnly = ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0); final int cloneFlag = cloneKeyFieldOnly ? ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO Loading Loading @@ -2202,6 +2251,10 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkStringNotEmpty(packageName, "packageName"); Preconditions.checkStringNotEmpty(shortcutId, "shortcutId"); if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) { return false; } synchronized (mLock) { getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) .attemptToRestoreIfNeededAndSave(); Loading @@ -2218,6 +2271,10 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkStringNotEmpty(packageName, "packageName"); Preconditions.checkStringNotEmpty(shortcutId, "shortcutId"); if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) { return null; } final ShortcutPackage p = getUserShortcutsLocked(userId) .getPackageShortcutsIfExists(packageName); if (p == null) { Loading @@ -2239,6 +2296,10 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkStringNotEmpty(packageName, "packageName"); Preconditions.checkNotNull(shortcutIds, "shortcutIds"); if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) { return; } synchronized (mLock) { final ShortcutLauncher launcher = getLauncherShortcutsLocked(callingPackage, userId, launcherUserId); Loading @@ -2259,6 +2320,10 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty"); Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty"); if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) { return null; } synchronized (mLock) { getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) .attemptToRestoreIfNeededAndSave(); Loading Loading @@ -2289,6 +2354,10 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkNotNull(packageName, "packageName"); Preconditions.checkNotNull(shortcutId, "shortcutId"); if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) { return 0; } synchronized (mLock) { getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) .attemptToRestoreIfNeededAndSave(); Loading @@ -2313,6 +2382,10 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkNotNull(packageName, "packageName"); Preconditions.checkNotNull(shortcutId, "shortcutId"); if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) { return null; } synchronized (mLock) { getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) .attemptToRestoreIfNeededAndSave(); Loading Loading @@ -2345,6 +2418,9 @@ public class ShortcutService extends IShortcutService.Stub { @Override public boolean hasShortcutHostPermission(int launcherUserId, @NonNull String callingPackage) { if (!isUserUnlocked(launcherUserId)) { return false; } return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId); } } Loading Loading @@ -2395,7 +2471,7 @@ public class ShortcutService extends IShortcutService.Stub { final long token = injectClearCallingIdentity(); try { if (!mUserManager.isUserUnlocked(userId)) { if (!isUserUnlocked(userId)) { if (DEBUG) { Slog.d(TAG, "Ignoring package broadcast " + action + " for locked/stopped user " + userId); Loading Loading @@ -3208,7 +3284,7 @@ public class ShortcutService extends IShortcutService.Stub { case "--user": if (takeUser) { mUserId = UserHandle.parseUserArg(getNextArgRequired()); if (!mUserManager.isUserUnlocked(mUserId)) { if (!isUserUnlocked(mUserId)) { throw new CommandException( "User " + mUserId + " is not running or locked"); } Loading
services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +22 −10 Original line number Diff line number Diff line Loading @@ -687,19 +687,25 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { // Set up isUserRunning and isUserUnlocked. when(mMockUserManager.isUserRunning(anyInt())).thenAnswer(new AnswerWithSystemCheck<>( inv -> mRunningUsers.get((Integer) inv.getArguments()[0]))); inv -> b(mRunningUsers.get((Integer) inv.getArguments()[0])))); when(mMockUserManager.isUserUnlocked(anyInt())).thenAnswer(new AnswerWithSystemCheck<>( inv -> { when(mMockUserManager.isUserUnlocked(anyInt())) .thenAnswer(new AnswerWithSystemCheck<>(inv -> { final int userId = (Integer) inv.getArguments()[0]; return mRunningUsers.get(userId) && mUnlockedUsers.get(userId); return b(mRunningUsers.get(userId)) && b(mUnlockedUsers.get(userId)); })); // isUserUnlockingOrUnlocked() return the same value as isUserUnlocked(). when(mMockUserManager.isUserUnlockingOrUnlocked(anyInt())) .thenAnswer(new AnswerWithSystemCheck<>(inv -> { final int userId = (Integer) inv.getArguments()[0]; return b(mRunningUsers.get(userId)) && b(mUnlockedUsers.get(userId)); })); // User 0 is always running // User 0 and P0 are always running mRunningUsers.put(USER_0, true); mRunningUsers.put(USER_10, false); mRunningUsers.put(USER_11, false); mRunningUsers.put(USER_P0, false); mRunningUsers.put(USER_P0, true); // Unlock all users by default. mUnlockedUsers.put(USER_0, true); Loading @@ -715,6 +721,10 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { setCaller(CALLING_PACKAGE_1); } private static boolean b(Boolean value) { return (value != null && value); } /** * Returns a boolean but also checks if the current UID is SYSTEM_UID. */ Loading Loading @@ -1726,6 +1736,8 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } protected void prepareCrossProfileDataSet() { mRunningUsers.put(USER_10, true); // this test needs user 10. runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { assertTrue(mManager.setDynamicShortcuts(list( makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"), Loading