Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cab2462e authored by Makoto Onuki's avatar Makoto Onuki Committed by android-build-merger
Browse files

Prevent shortcut access when user is locked

am: 9c850012

Change-Id: I0aac4593da85c26107a2ba333b6af9477d5f3983
parents 7fd0752e 9c850012
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -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() {
@@ -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.
     *
@@ -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.
@@ -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}.
     *
@@ -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.
     *
@@ -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.
@@ -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.
+32 −0
Original line number Diff line number Diff line
@@ -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";
@@ -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 {
@@ -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() {
@@ -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() {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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() {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 {
+1 −1
Original line number Diff line number Diff line
@@ -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) {
+78 −2
Original line number Diff line number Diff line
@@ -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) {
@@ -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);
@@ -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();
@@ -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();
@@ -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();
@@ -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);
@@ -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);
@@ -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);
@@ -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);
@@ -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,
@@ -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,
@@ -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,
@@ -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);
@@ -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();
@@ -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);

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

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

@@ -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
@@ -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();
@@ -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) {
@@ -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);
@@ -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();
@@ -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();
@@ -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();
@@ -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);
        }
    }
@@ -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);
@@ -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");
                            }
+22 −10
Original line number Diff line number Diff line
@@ -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);
@@ -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.
     */
@@ -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