diff options
| author | 2023-10-10 22:50:51 +0000 | |
|---|---|---|
| committer | 2023-10-10 23:01:45 +0000 | |
| commit | 1c95758cf9d6a72a493ec410ff585028adc2f4fb (patch) | |
| tree | 58cbd88afa82fd1137375235f05f580d4386c1c3 | |
| parent | 008cc90195926f9aa2b7223721e6ccb714bd85bf (diff) | |
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
(cherry picked from commit 5ebbd7d30dca21980550780beb7135526f78db86)
7 files changed, 214 insertions, 63 deletions
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index 6d2feb8f0ee3..2d554031ab48 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -126,6 +126,15 @@ public class KeyguardManager { "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}. * @hide diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index aaa3a773ab01..d5b8f62aaf2b 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 PASSWORD_TYPE_KEY = "lockscreen.password_type"; @Deprecated public final static String PASSWORD_TYPE_ALTERNATE_KEY = "lockscreen.password_type_alternate"; @@ -399,7 +404,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); @@ -408,7 +413,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); @@ -416,21 +421,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( @@ -750,6 +755,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. * @@ -982,7 +998,7 @@ public class LockPatternUtils { } @Override public boolean shouldBypassCache(Integer userHandle) { - return userHandle == USER_FRP; + return isSpecialUserId(userHandle); } }; @@ -1089,9 +1105,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); @@ -1861,6 +1878,33 @@ public class LockPatternUtils { } /** + * 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 * additional user authentication. 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 6167c4b80cee..1a668f7bdc8f 100644 --- a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java @@ -37,10 +37,23 @@ public class LockPatternUtilsTest { } @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); assertNotEquals(UserHandle.USER_ALL, LockPatternUtils.USER_FRP); 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 f7a3b7ca9b63..f0988f148592 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; @@ -1315,8 +1317,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); @@ -1332,29 +1334,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; } @@ -1596,8 +1575,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; } @@ -2218,15 +2197,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 1ace6e5452ad..1c5ecb70a3ae 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 3df05c5a9e5f..8e9c21f5f35f 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; @@ -768,11 +773,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); } /** @@ -1057,10 +1081,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); @@ -1071,14 +1095,14 @@ 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) { final IWeaver weaver = getWeaverService(); if (weaver == null) { - 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 dc47b87cad2d..70150c507460 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; - } } |