From 95982a5f17d2c0a656a591409d2a87631decd8d7 Mon Sep 17 00:00:00 2001 From: Rhed Jao Date: Tue, 11 Apr 2023 12:32:09 +0000 Subject: [PATCH] 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 --- core/java/android/app/KeyguardManager.java | 9 ++ .../internal/widget/LockPatternUtils.java | 62 +++++++++-- .../internal/widget/LockPatternUtilsTest.java | 13 +++ .../locksettings/LockSettingsService.java | 45 +++----- .../locksettings/LockSettingsStorage.java | 7 +- .../SyntheticPasswordManager.java | 40 +++++-- .../LockscreenRepairModeTest.java | 101 +++++++++++++++--- 7 files changed, 214 insertions(+), 63 deletions(-) diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index b71cb4a388ab8..e7a2b61ecaf9e 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -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}. diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 66b69c53a6847..361bdce715de5 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -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 diff --git a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java index 6167c4b80ceea..1a668f7bdc8fc 100644 --- a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java @@ -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); + } } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 38f153b222a35..a96e4adf1fee9 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -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); diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java index 1ace6e5452ad4..1c5ecb70a3aef 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java @@ -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); diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java index d74d304be730c..7349088c2b522 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java @@ -16,9 +16,14 @@ package com.android.server.locksettings; +import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; +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.EscrowTokenStateChangeCallback; import static com.android.internal.widget.LockPatternUtils.PIN_LENGTH_UNAVAILABLE; +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.pinOrPasswordQualityToCredentialType; import android.annotation.IntDef; import android.annotation.NonNull; @@ -718,11 +723,30 @@ class SyntheticPasswordManager { return PasswordData.fromBytes(passwordData).credentialType; } - static int getFrpCredentialType(byte[] payload) { - if (payload == null) { + int getSpecialUserCredentialType(int userId) { + final PersistentData data = getSpecialUserPersistentData(userId); + if (data.type != PersistentData.TYPE_SP_GATEKEEPER + && data.type != PersistentData.TYPE_SP_WEAVER) { + return CREDENTIAL_TYPE_NONE; + } + if (data.payload == null) { return LockPatternUtils.CREDENTIAL_TYPE_NONE; } - return PasswordData.fromBytes(payload).credentialType; + final int credentialType = PasswordData.fromBytes(data.payload).credentialType; + if (credentialType != CREDENTIAL_TYPE_PASSWORD_OR_PIN) { + return credentialType; + } + return pinOrPasswordQualityToCredentialType(data.qualityForUi); + } + + private PersistentData getSpecialUserPersistentData(int userId) { + if (userId == USER_FRP) { + return mStorage.readPersistentDataBlock(); + } + if (userId == USER_REPAIR_MODE) { + return mStorage.readRepairModePersistentData(); + } + throw new IllegalArgumentException("Unknown special user id " + userId); } /** @@ -1005,10 +1029,10 @@ class SyntheticPasswordManager { return sizeOfCredential; } - public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper, - LockscreenCredential userCredential, + public VerifyCredentialResponse verifySpecialUserCredential(int sourceUserId, + IGateKeeperService gatekeeper, LockscreenCredential userCredential, ICheckCredentialProgressCallback progressCallback) { - PersistentData persistentData = mStorage.readPersistentDataBlock(); + final PersistentData persistentData = getSpecialUserPersistentData(sourceUserId); if (persistentData.type == PersistentData.TYPE_SP_GATEKEEPER) { PasswordData pwd = PasswordData.fromBytes(persistentData.payload); byte[] stretchedLskf = stretchLskf(userCredential, pwd); @@ -1019,13 +1043,13 @@ class SyntheticPasswordManager { 0 /* challenge */, pwd.passwordHandle, stretchedLskfToGkPassword(stretchedLskf)); } catch (RemoteException e) { - Slog.e(TAG, "FRP verifyChallenge failed", e); + Slog.e(TAG, "Persistent data credential verifyChallenge failed", e); return VerifyCredentialResponse.ERROR; } return VerifyCredentialResponse.fromGateKeeperResponse(response); } else if (persistentData.type == PersistentData.TYPE_SP_WEAVER) { if (!isWeaverAvailable()) { - Slog.e(TAG, "No weaver service to verify SP-based FRP credential"); + Slog.e(TAG, "No weaver service to verify SP-based persistent data credential"); return VerifyCredentialResponse.ERROR; } PasswordData pwd = PasswordData.fromBytes(persistentData.payload); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java index dc47b87cad2d1..70150c507460d 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java @@ -16,6 +16,7 @@ package com.android.server.locksettings; +import static com.android.internal.widget.LockPatternUtils.USER_REPAIR_MODE; import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW; import static org.junit.Assert.assertEquals; @@ -31,7 +32,6 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.VerifyCredentialResponse; import com.android.server.locksettings.LockSettingsStorage.PersistentData; -import com.android.server.locksettings.SyntheticPasswordManager.PasswordData; import org.junit.Before; import org.junit.Test; @@ -58,7 +58,7 @@ public class LockscreenRepairModeTest extends BaseLockSettingsServiceTests { newPin("1234"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW) .getResponseCode()); assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PIN, - getCredentialType(mStorage.readRepairModePersistentData())); + mService.getCredentialType(USER_REPAIR_MODE)); } @Test @@ -71,7 +71,7 @@ public class LockscreenRepairModeTest extends BaseLockSettingsServiceTests { newPattern("4321"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW) .getResponseCode()); assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN, - getCredentialType(mStorage.readRepairModePersistentData())); + mService.getCredentialType(USER_REPAIR_MODE)); } @Test @@ -84,7 +84,7 @@ public class LockscreenRepairModeTest extends BaseLockSettingsServiceTests { newPassword("4321"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW) .getResponseCode()); assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, - getCredentialType(mStorage.readRepairModePersistentData())); + mService.getCredentialType(USER_REPAIR_MODE)); } @Test @@ -109,21 +109,98 @@ public class LockscreenRepairModeTest extends BaseLockSettingsServiceTests { newPin("1234"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW) .getResponseCode()); assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PIN, - getCredentialType(mStorage.readRepairModePersistentData())); + mService.getCredentialType(USER_REPAIR_MODE)); mService.deleteRepairModePersistentDataIfNeeded(); assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData()); } + @Test + public void verifyPin_userRepairMode() { + mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID); + assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData()); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential( + newPin("1234"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW) + .getResponseCode()); + setRepairModeActive(true); + + assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PIN, + mService.getCredentialType(USER_REPAIR_MODE)); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential(newPin("1234"), USER_REPAIR_MODE, 0 /* flags */) + .getResponseCode()); + } + + @Test + public void verifyPattern_userRepairMode() { + mService.setLockCredential(newPattern("4321"), nonePassword(), PRIMARY_USER_ID); + assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData()); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential( + newPattern("4321"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW) + .getResponseCode()); + setRepairModeActive(true); + + assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN, + mService.getCredentialType(USER_REPAIR_MODE)); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential(newPattern("4321"), USER_REPAIR_MODE, 0 /* flags */) + .getResponseCode()); + } + + @Test + public void verifyPassword_userRepairMode() { + mService.setLockCredential(newPassword("4321"), nonePassword(), PRIMARY_USER_ID); + assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData()); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential( + newPassword("4321"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW) + .getResponseCode()); + setRepairModeActive(true); + + assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, + mService.getCredentialType(USER_REPAIR_MODE)); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential(newPassword("4321"), USER_REPAIR_MODE, 0 /* flags */) + .getResponseCode()); + } + + @Test + public void verifyCredential_userRepairMode_repairModeIsNotActive() { + mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID); + assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData()); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential( + newPin("1234"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW) + .getResponseCode()); + + assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PIN, + mService.getCredentialType(USER_REPAIR_MODE)); + assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, + mService.verifyCredential(newPin("1234"), USER_REPAIR_MODE, 0 /* flags */) + .getResponseCode()); + } + + @Test + public void verifyCredential_userRepairMode_wrongPin() { + mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID); + assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData()); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential( + newPin("1234"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW) + .getResponseCode()); + setRepairModeActive(true); + + assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PIN, + mService.getCredentialType(USER_REPAIR_MODE)); + assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, + mService.verifyCredential(newPin("5678"), USER_REPAIR_MODE, 0 /* flags */) + .getResponseCode()); + } + private void setRepairModeActive(boolean active) { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.REPAIR_MODE_ACTIVE, active ? 1 : 0); } - - private static int getCredentialType(PersistentData persistentData) { - if (persistentData == null || persistentData.payload == null) { - return LockPatternUtils.CREDENTIAL_TYPE_NONE; - } - return PasswordData.fromBytes(persistentData.payload).credentialType; - } }