diff options
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; - } } |