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

Commit 95982a5f authored by Rhed Jao's avatar Rhed Jao
Browse files

Add a verification flow for the user to exit repair mode

Create a special user id USER_REPAIR_MODE and an intent
ACTION_CONFIRM_REPAIR_MODE_DEVICE_CREDENTIAL for the repair
mode client triggering a exiting repair mode verification flow
that verifies credentials the user enrolled in normal mode.
Functions in LockSettings are updated to handle the new
user id. Information needed to verify the repair mode user is
retrieved from the repair mode file when the verifyCredential
api is called.

Bug: 277561275
Test: atest com.android.server.locksettings
Test: am start -a android.app.action.PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL
      settings put global repair_mode_active 1
      am start -a android.app.action.CONFIRM_REPAIR_MODE_DEVICE_CREDENTIAL
      The credential is verified successfully.
Change-Id: I51699d157e9de1a57bc36797ea005bcdd51aadb3
Merged-In: I51699d157e9de1a57bc36797ea005bcdd51aadb3
parent bf44988f
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -127,6 +127,15 @@ public class KeyguardManager {
    public static final String ACTION_PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL =
            "android.app.action.PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL";

    /**
     * Intent used to prompt user for device credential that is written by
     * {@link #ACTION_PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL} for exiting
     * repair mode.
     * @hide
     */
    public static final String ACTION_CONFIRM_REPAIR_MODE_DEVICE_CREDENTIAL =
            "android.app.action.CONFIRM_REPAIR_MODE_DEVICE_CREDENTIAL";

    /**
     * A CharSequence dialog title to show to the user when used with a
     * {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
+53 −9
Original line number Diff line number Diff line
@@ -180,6 +180,11 @@ public class LockPatternUtils {
     */
    public static final int USER_FRP = UserHandle.USER_NULL + 1;

    /**
     * Special user id for triggering the exiting repair mode verification flow.
     */
    public static final int USER_REPAIR_MODE = UserHandle.USER_NULL + 2;

    public final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen";
    public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type";
    @Deprecated
@@ -400,7 +405,7 @@ public class LockPatternUtils {

    @UnsupportedAppUsage
    public void reportFailedPasswordAttempt(int userId) {
        if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
        if (isSpecialUserId(mContext, userId, /* checkDeviceSupported= */ true)) {
            return;
        }
        getDevicePolicyManager().reportFailedPasswordAttempt(userId);
@@ -409,7 +414,7 @@ public class LockPatternUtils {

    @UnsupportedAppUsage
    public void reportSuccessfulPasswordAttempt(int userId) {
        if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
        if (isSpecialUserId(mContext, userId, /* checkDeviceSupported= */ true)) {
            return;
        }
        getDevicePolicyManager().reportSuccessfulPasswordAttempt(userId);
@@ -417,21 +422,21 @@ public class LockPatternUtils {
    }

    public void reportPasswordLockout(int timeoutMs, int userId) {
        if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
        if (isSpecialUserId(mContext, userId, /* checkDeviceSupported= */ true)) {
            return;
        }
        getTrustManager().reportUnlockLockout(timeoutMs, userId);
    }

    public int getCurrentFailedPasswordAttempts(int userId) {
        if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
        if (isSpecialUserId(mContext, userId, /* checkDeviceSupported= */ true)) {
            return 0;
        }
        return getDevicePolicyManager().getCurrentFailedPasswordAttempts(userId);
    }

    public int getMaximumFailedPasswordsForWipe(int userId) {
        if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
        if (isSpecialUserId(mContext, userId, /* checkDeviceSupported= */ true)) {
            return 0;
        }
        return getDevicePolicyManager().getMaximumFailedPasswordsForWipe(
@@ -768,6 +773,17 @@ public class LockPatternUtils {
        }
    }

    /** Returns the credential type corresponding to the given PIN or password quality. */
    public static int pinOrPasswordQualityToCredentialType(int quality) {
        if (isQualityAlphabeticPassword(quality)) {
            return CREDENTIAL_TYPE_PASSWORD;
        }
        if (isQualityNumericPin(quality)) {
            return CREDENTIAL_TYPE_PIN;
        }
        throw new IllegalArgumentException("Quality is neither Pin nor password: " + quality);
    }

    /**
     * Save a new lockscreen credential.
     *
@@ -1002,7 +1018,7 @@ public class LockPatternUtils {
                }
                @Override
                public boolean shouldBypassCache(Integer userHandle) {
                    return userHandle == USER_FRP;
                    return isSpecialUserId(userHandle);
                }
            };

@@ -1116,9 +1132,10 @@ public class LockPatternUtils {
    @UnsupportedAppUsage
    public long setLockoutAttemptDeadline(int userId, int timeoutMs) {
        final long deadline = SystemClock.elapsedRealtime() + timeoutMs;
        if (userId == USER_FRP) {
            // For secure password storage (that is required for FRP), the underlying storage also
            // enforces the deadline. Since we cannot store settings for the FRP user, don't.
        if (isSpecialUserId(userId)) {
            // For secure password storage (that is required for special users such as FRP), the
            // underlying storage also enforces the deadline. Since we cannot store settings
            // for special users, don't.
            return deadline;
        }
        mLockoutDeadlines.put(userId, deadline);
@@ -1888,6 +1905,33 @@ public class LockPatternUtils {
        return SystemProperties.getInt(GSI_RUNNING_PROP, 0) > 0;
    }

    /**
     * Return {@code true} if the given user id is a special user such as {@link #USER_FRP}.
     */
    public static boolean isSpecialUserId(int userId) {
        return isSpecialUserId(/* context= */ null, userId, /* checkDeviceSupported= */ false);
    }

    /**
     * Return {@code true} if the given user id is a special user for the verification flow.
     *
     * @param checkDeviceSupported {@code true} to check the specified user is supported
     *                             by the device.
     */
    private static boolean isSpecialUserId(@Nullable Context context, int userId,
            boolean checkDeviceSupported) {
        switch (userId) {
            case USER_FRP:
                if (checkDeviceSupported) return frpCredentialEnabled(context);
                return true;

            case USER_REPAIR_MODE:
                if (checkDeviceSupported) return isRepairModeSupported(context);
                return true;
        }
        return false;
    }

    /**
     * Attempt to rederive the unified work challenge for the specified profile user and unlock the
     * user. If successful, this would allow the user to leave quiet mode automatically without
+13 −0
Original line number Diff line number Diff line
@@ -36,6 +36,11 @@ public class LockPatternUtilsTest {
        assertTrue(LockPatternUtils.USER_FRP < 0);
    }

    @Test
    public void testUserRepairMode_isNotRegularUser() {
        assertTrue(LockPatternUtils.USER_REPAIR_MODE < 0);
    }

    @Test
    public void testUserFrp_isNotAReservedSpecialUser() throws Exception {
        assertNotEquals(UserHandle.USER_NULL, LockPatternUtils.USER_FRP);
@@ -43,4 +48,12 @@ public class LockPatternUtilsTest {
        assertNotEquals(UserHandle.USER_CURRENT, LockPatternUtils.USER_FRP);
        assertNotEquals(UserHandle.USER_CURRENT_OR_SELF, LockPatternUtils.USER_FRP);
    }

    @Test
    public void testUserRepairMode_isNotAReservedSpecialUser() throws Exception {
        assertNotEquals(UserHandle.USER_NULL, LockPatternUtils.USER_REPAIR_MODE);
        assertNotEquals(UserHandle.USER_ALL, LockPatternUtils.USER_REPAIR_MODE);
        assertNotEquals(UserHandle.USER_CURRENT, LockPatternUtils.USER_REPAIR_MODE);
        assertNotEquals(UserHandle.USER_CURRENT_OR_SELF, LockPatternUtils.USER_REPAIR_MODE);
    }
}
+14 −31
Original line number Diff line number Diff line
@@ -31,7 +31,6 @@ import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_SYSTEM;

import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import static com.android.internal.widget.LockPatternUtils.CURRENT_LSKF_BASED_PROTECTOR_ID_KEY;
@@ -40,9 +39,12 @@ import static com.android.internal.widget.LockPatternUtils.PIN_LENGTH_UNAVAILABL
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
import static com.android.internal.widget.LockPatternUtils.USER_FRP;
import static com.android.internal.widget.LockPatternUtils.USER_REPAIR_MODE;
import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE;
import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW;
import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled;
import static com.android.internal.widget.LockPatternUtils.isSpecialUserId;
import static com.android.internal.widget.LockPatternUtils.pinOrPasswordQualityToCredentialType;
import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential;
import static com.android.server.locksettings.SyntheticPasswordManager.TOKEN_TYPE_STRONG;
import static com.android.server.locksettings.SyntheticPasswordManager.TOKEN_TYPE_WEAK;
@@ -1289,8 +1291,8 @@ public class LockSettingsService extends ILockSettings.Stub {
     * {@link #CREDENTIAL_TYPE_PASSWORD}
     */
    private int getCredentialTypeInternal(int userId) {
        if (userId == USER_FRP) {
            return getFrpCredentialType();
        if (isSpecialUserId(userId)) {
            return mSpManager.getSpecialUserCredentialType(userId);
        }
        synchronized (mSpManager) {
            final long protectorId = getCurrentLskfBasedProtectorId(userId);
@@ -1306,29 +1308,6 @@ public class LockSettingsService extends ILockSettings.Stub {
        }
    }

    private int getFrpCredentialType() {
        PersistentData data = mStorage.readPersistentDataBlock();
        if (data.type != PersistentData.TYPE_SP_GATEKEEPER &&
                data.type != PersistentData.TYPE_SP_WEAVER) {
            return CREDENTIAL_TYPE_NONE;
        }
        int credentialType = SyntheticPasswordManager.getFrpCredentialType(data.payload);
        if (credentialType != CREDENTIAL_TYPE_PASSWORD_OR_PIN) {
            return credentialType;
        }
        return pinOrPasswordQualityToCredentialType(data.qualityForUi);
    }

    private static int pinOrPasswordQualityToCredentialType(int quality) {
        if (LockPatternUtils.isQualityAlphabeticPassword(quality)) {
            return CREDENTIAL_TYPE_PASSWORD;
        }
        if (LockPatternUtils.isQualityNumericPin(quality)) {
            return CREDENTIAL_TYPE_PIN;
        }
        throw new IllegalArgumentException("Quality is neither Pin nor password: " + quality);
    }

    private boolean isUserSecure(int userId) {
        return getCredentialTypeInternal(userId) != CREDENTIAL_TYPE_NONE;
    }
@@ -1570,8 +1549,8 @@ public class LockSettingsService extends ILockSettings.Stub {
     * unlock operation.
     */
    private void sendCredentialsOnUnlockIfRequired(LockscreenCredential credential, int userId) {
        // Don't send credentials during the factory reset protection flow.
        if (userId == USER_FRP) {
        // Don't send credentials during the special user flow.
        if (isSpecialUserId(userId)) {
            return;
        }

@@ -2195,15 +2174,19 @@ public class LockSettingsService extends ILockSettings.Stub {
            Slog.e(TAG, "FRP credential can only be verified prior to provisioning.");
            return VerifyCredentialResponse.ERROR;
        }
        if (userId == USER_REPAIR_MODE && !LockPatternUtils.isRepairModeActive(mContext)) {
            Slog.e(TAG, "Repair mode is not active on the device.");
            return VerifyCredentialResponse.ERROR;
        }
        Slogf.i(TAG, "Verifying lockscreen credential for user %d", userId);

        final AuthenticationResult authResult;
        VerifyCredentialResponse response;

        synchronized (mSpManager) {
            if (userId == USER_FRP) {
                return mSpManager.verifyFrpCredential(getGateKeeperService(), credential,
                        progressCallback);
            if (isSpecialUserId(userId)) {
                return mSpManager.verifySpecialUserCredential(userId, getGateKeeperService(),
                        credential, progressCallback);
            }

            long protectorId = getCurrentLskfBasedProtectorId(userId);
+4 −3
Original line number Diff line number Diff line
@@ -19,7 +19,7 @@ package com.android.server.locksettings;
import static android.content.Context.USER_SERVICE;

import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import static com.android.internal.widget.LockPatternUtils.USER_FRP;
import static com.android.internal.widget.LockPatternUtils.isSpecialUserId;

import android.annotation.Nullable;
import android.app.admin.DevicePolicyManager;
@@ -536,7 +536,8 @@ class LockSettingsStorage {
    }

    public void setString(String key, String value, int userId) {
        Preconditions.checkArgument(userId != USER_FRP, "cannot store lock settings for FRP user");
        Preconditions.checkArgument(!isSpecialUserId(userId),
                "cannot store lock settings for special user: %d", userId);

        writeKeyValue(key, value, userId);
        if (ArrayUtils.contains(SETTINGS_TO_BACKUP, key)) {
@@ -561,7 +562,7 @@ class LockSettingsStorage {
    }

    public String getString(String key, String defaultValue, int userId) {
        if (userId == USER_FRP) {
        if (isSpecialUserId(userId)) {
            return null;
        }
        return readKeyValue(key, defaultValue, userId);
Loading