Loading services/core/java/com/android/server/locksettings/LockSettingsService.java +6 −2 Original line number Diff line number Diff line Loading @@ -2549,8 +2549,12 @@ public class LockSettingsService extends ILockSettings.Stub { if (response.isMatched()) { mSoftwareRateLimiter.reportSuccess(lskfId); } else { // TODO(b/395976735): don't count transient failures Duration swTimeout = mSoftwareRateLimiter.reportWrongGuess(lskfId, credential); boolean isCertainlyWrongGuess = response.getResponseCode() == VerifyCredentialResponse.RESPONSE_CRED_INCORRECT; Duration swTimeout = mSoftwareRateLimiter.reportFailure( lskfId, credential, isCertainlyWrongGuess); // The software rate-limiter may use longer delays than the hardware one. While the // long-term solution is to update the hardware rate-limiter to match, for now this Loading services/core/java/com/android/server/locksettings/SoftwareRateLimiter.java +44 −31 Original line number Diff line number Diff line Loading @@ -224,10 +224,10 @@ class SoftwareRateLimiter { }); // Check for remaining delay. Note that the case of a positive remaining delay normally // won't be reached, since reportWrongGuess() will have returned the delay when the last // guess was made, causing the lock screen to block inputs for that amount of time. But // checking for it is still needed to cover any cases where a guess gets made anyway, for // example following a reboot which causes the lock screen to "forget" the delay. // won't be reached, since reportFailure() will have returned the delay when the last guess // was made, causing the lock screen to block inputs for that amount of time. But checking // for it is still needed to cover any cases where a guess gets made anyway, for example // following a reboot which causes the lock screen to "forget" the delay. final Duration delay = getCurrentDelay(state); final Duration now = mInjector.getTimeSinceBoot(); final Duration remainingDelay = state.timeSinceBootOfLastWrongGuess.plus(delay).minus(now); Loading Loading @@ -298,27 +298,37 @@ class SoftwareRateLimiter { } /** * Reports a new wrong guess to the software rate-limiter. * Reports a failure to the software rate-limiter. * * <p>This must be called immediately after the hardware rate-limiter reported that the given * guess is incorrect, before the credential check failure is made visible in the UI. It is * assumed that {@link #apply(LskfIdentifier, LockscreenCredential)} was previously called with * the same parameters and returned a {@code CONTINUE_TO_HARDWARE} result. * <p>This must be called immediately after the hardware rate-limiter reported a failure, before * the credential check failure is made visible in the UI. It is assumed that {@link * #apply(LskfIdentifier, LockscreenCredential)} was previously called with the same parameters * and returned a {@code CONTINUE_TO_HARDWARE} result. * * @param id the ID of the protector or special credential * @param newWrongGuess a new wrong guess for the LSKF * @param guess the LSKF that was attempted * @param isCertainlyWrongGuess true if it's certain that the failure was caused by the guess * being wrong, as opposed to e.g. a transient hardware glitch * @return the delay until when the next guess will be allowed */ synchronized Duration reportWrongGuess(LskfIdentifier id, LockscreenCredential newWrongGuess) { synchronized Duration reportFailure( LskfIdentifier id, LockscreenCredential guess, boolean isCertainlyWrongGuess) { RateLimiterState state = getExistingState(id); // In non-enforcing mode, ignore duplicate wrong guesses here since they were already // counted by apply(), including having stats written for them. In enforcing mode, this // method isn't passed duplicate wrong guesses. if (!mEnforcing && ArrayUtils.contains(state.savedWrongGuesses, newWrongGuess)) { if (!mEnforcing && ArrayUtils.contains(state.savedWrongGuesses, guess)) { return Duration.ZERO; } // Increment the failure counter regardless of whether the failure is a certainly wrong // guess or not. A generic failure might still be caused by a wrong guess. Gatekeeper only // ever returns generic failures, and some Weaver implementations prefer THROTTLE to // INCORRECT_KEY once the delay becomes nonzero. Instead of making the software rate-limiter // ineffective on all such devices, still apply it. This does mean that correct guesses that // encountered an error will be rate-limited. However, by design the rate-limiter kicks in // gradually anyway, so there will be a chance for the user to try again. state.numWrongGuesses++; state.timeSinceBootOfLastWrongGuess = mInjector.getTimeSinceBoot(); Loading @@ -329,7 +339,9 @@ class SoftwareRateLimiter { writeStats(id, state, /* success= */ false); insertNewWrongGuess(state, newWrongGuess); // Save certainly wrong guesses so that duplicates of them can be detected. if (isCertainlyWrongGuess) { insertNewWrongGuess(state, guess); // Schedule the saved wrong guesses to be forgotten after a few minutes, extending the // existing timeout if one was already running. Loading @@ -347,6 +359,7 @@ class SoftwareRateLimiter { }, /* token= */ state, SAVED_WRONG_GUESS_TIMEOUT.toMillis()); } return getCurrentDelay(state); } Loading Loading @@ -384,7 +397,7 @@ class SoftwareRateLimiter { private RateLimiterState getExistingState(LskfIdentifier id) { RateLimiterState state = mState.get(id); if (state == null) { // This should never happen, since reportSuccess() and reportWrongGuess() are always // This should never happen, since reportSuccess() and reportFailure() are always // supposed to be paired with a call to apply() that created the state if it did not // exist. Nor is it supported to call clearLskfState() or clearUserState() in between; // higher-level locking in LockSettingsService guarantees that never happens. Loading services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +0 −3 Original line number Diff line number Diff line Loading @@ -723,8 +723,6 @@ class SyntheticPasswordManager { // the credential was incorrect and there is a timeout before the next attempt will // be allowed. INCORRECT_KEY is preferred in the latter case to avoid the ambiguity, // but we still have to support implementations that use THROTTLE for both cases. // // TODO(b/395976735): needs unit testing via MockWeaverService return responseFromTimeout(weaverResponse); case WeaverReadStatus.INCORRECT_KEY: if (weaverResponse.timeout != 0) { Loading @@ -734,7 +732,6 @@ class SyntheticPasswordManager { // // TODO(b/395976735): use RESPONSE_CRED_INCORRECT in this case, and update users // of VerifyCredentialResponse to be compatible with that. // TODO(b/395976735): needs unit testing via MockWeaverService return responseFromTimeout(weaverResponse); } if (android.security.Flags.softwareRatelimiter()) { Loading services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java +106 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Intent; import android.hardware.weaver.WeaverReadStatus; import android.os.RemoteException; import android.os.UserHandle; import android.platform.test.annotations.DisableFlags; Loading @@ -57,10 +58,13 @@ import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.VerifyCredentialResponse; import org.junit.Before; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import java.time.Duration; /** * atest FrameworksServicesTests:LockSettingsServiceTests */ Loading Loading @@ -719,6 +723,108 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { assertEquals(VerifyCredentialResponse.RESPONSE_OTHER_ERROR, response.getResponseCode()); } // Tests that if verifyCredential is passed a wrong guess and Weaver reports INCORRECT_KEY with // zero timeout (which indicates a certainly wrong guess), then LockSettingsService saves that // guess as a recent wrong guess and rejects a repeat of it as a duplicate. @Test @EnableFlags(android.security.Flags.FLAG_SOFTWARE_RATELIMITER) public void testRepeatOfWrongGuessRejectedAsDuplicate_afterWeaverIncorrectKeyWithoutTimeout() throws Exception { final int userId = PRIMARY_USER_ID; final LockscreenCredential credential = newPassword("password"); final LockscreenCredential wrongGuess = newPassword("wrong"); mSpManager.enableWeaver(); setCredential(userId, credential); mSpManager.injectWeaverReadResponse(WeaverReadStatus.INCORRECT_KEY, Duration.ZERO); VerifyCredentialResponse response = mService.verifyCredential(wrongGuess, userId, /* flags= */ 0); assertEquals(VerifyCredentialResponse.RESPONSE_CRED_INCORRECT, response.getResponseCode()); assertEquals(Duration.ZERO, response.getTimeoutAsDuration()); response = mService.verifyCredential(wrongGuess, userId, /* flags= */ 0); assertEquals( VerifyCredentialResponse.RESPONSE_CRED_ALREADY_TRIED, response.getResponseCode()); assertEquals(Duration.ZERO, response.getTimeoutAsDuration()); } // Same as preceding test case, but uses a nonzero timeout. // // TODO(b/395976735): currently the behavior in this scenario is wrong, so currently this test // case is ignored. Fix the behavior and remove @Ignore. @Ignore @Test @EnableFlags(android.security.Flags.FLAG_SOFTWARE_RATELIMITER) public void testRepeatOfWrongGuessRejectedAsDuplicate_afterWeaverIncorrectKeyWithTimeout() throws Exception { final int userId = PRIMARY_USER_ID; final LockscreenCredential credential = newPassword("password"); final LockscreenCredential wrongGuess = newPassword("wrong"); final Duration timeout = Duration.ofSeconds(60); mSpManager.enableWeaver(); setCredential(userId, credential); mSpManager.injectWeaverReadResponse(WeaverReadStatus.INCORRECT_KEY, timeout); VerifyCredentialResponse response = mService.verifyCredential(wrongGuess, userId, /* flags= */ 0); assertEquals(VerifyCredentialResponse.RESPONSE_RETRY, response.getResponseCode()); assertEquals(timeout, response.getTimeoutAsDuration()); response = mService.verifyCredential(wrongGuess, userId, /* flags= */ 0); assertEquals( VerifyCredentialResponse.RESPONSE_CRED_ALREADY_TRIED, response.getResponseCode()); assertEquals(Duration.ZERO, response.getTimeoutAsDuration()); } // Tests that if verifyCredential is passed a correct guess but it fails due to Weaver reporting // a status of THROTTLE (which is the expected status when there is a remaining rate-limiting // delay in Weaver), then LockSettingsService does not block the same guess from being // re-attempted and in particular does not reject it as a duplicate wrong guess. @Test @EnableFlags(android.security.Flags.FLAG_SOFTWARE_RATELIMITER) public void testRepeatOfCorrectGuessAllowed_afterWeaverThrottle() throws Exception { final int userId = PRIMARY_USER_ID; final LockscreenCredential credential = newPassword("password"); final Duration timeout = Duration.ofSeconds(60); mSpManager.enableWeaver(); setCredential(userId, credential); mSpManager.injectWeaverReadResponse(WeaverReadStatus.THROTTLE, timeout); VerifyCredentialResponse response = mService.verifyCredential(credential, userId, /* flags= */ 0); assertEquals(VerifyCredentialResponse.RESPONSE_RETRY, response.getResponseCode()); assertEquals(timeout, response.getTimeoutAsDuration()); response = mService.verifyCredential(credential, userId, /* flags= */ 0); assertTrue(response.isMatched()); } // Tests that if verifyCredential is passed a correct guess but it fails due to Weaver reporting // a status of FAILED (which is the expected status when there is a transient error unrelated to // the guess), then LockSettingsService does not block the same guess from being re-attempted // and in particular does not reject it as a duplicate wrong guess. @Test @EnableFlags(android.security.Flags.FLAG_SOFTWARE_RATELIMITER) public void testRepeatOfCorrectGuessAllowed_afterWeaverFailed() throws Exception { final int userId = PRIMARY_USER_ID; final LockscreenCredential credential = newPassword("password"); mSpManager.enableWeaver(); setCredential(userId, credential); mSpManager.injectWeaverReadResponse(WeaverReadStatus.FAILED, Duration.ZERO); VerifyCredentialResponse response = mService.verifyCredential(credential, userId, /* flags= */ 0); assertEquals(VerifyCredentialResponse.RESPONSE_OTHER_ERROR, response.getResponseCode()); assertEquals(Duration.ZERO, response.getTimeoutAsDuration()); response = mService.verifyCredential(credential, userId, /* flags= */ 0); assertTrue(response.isMatched()); } private void checkRecordedFrpNotificationIntent() { if (android.security.Flags.frpEnforcement()) { Intent savedNotificationIntent = mService.getSavedFrpNotificationIntent(); Loading services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java +6 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import junit.framework.AssertionFailedError; import java.nio.ByteBuffer; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import java.time.Duration; import java.util.Arrays; import javax.crypto.SecretKeyFactory; Loading Loading @@ -152,4 +153,9 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager { public int getSumOfWeaverFailureCounters() { return mWeaverService.getSumOfFailureCounters(); } /** Injects a response to be returned by the next read from Weaver. */ public void injectWeaverReadResponse(int status, Duration timeout) { mWeaverService.injectReadResponse(status, timeout); } } Loading
services/core/java/com/android/server/locksettings/LockSettingsService.java +6 −2 Original line number Diff line number Diff line Loading @@ -2549,8 +2549,12 @@ public class LockSettingsService extends ILockSettings.Stub { if (response.isMatched()) { mSoftwareRateLimiter.reportSuccess(lskfId); } else { // TODO(b/395976735): don't count transient failures Duration swTimeout = mSoftwareRateLimiter.reportWrongGuess(lskfId, credential); boolean isCertainlyWrongGuess = response.getResponseCode() == VerifyCredentialResponse.RESPONSE_CRED_INCORRECT; Duration swTimeout = mSoftwareRateLimiter.reportFailure( lskfId, credential, isCertainlyWrongGuess); // The software rate-limiter may use longer delays than the hardware one. While the // long-term solution is to update the hardware rate-limiter to match, for now this Loading
services/core/java/com/android/server/locksettings/SoftwareRateLimiter.java +44 −31 Original line number Diff line number Diff line Loading @@ -224,10 +224,10 @@ class SoftwareRateLimiter { }); // Check for remaining delay. Note that the case of a positive remaining delay normally // won't be reached, since reportWrongGuess() will have returned the delay when the last // guess was made, causing the lock screen to block inputs for that amount of time. But // checking for it is still needed to cover any cases where a guess gets made anyway, for // example following a reboot which causes the lock screen to "forget" the delay. // won't be reached, since reportFailure() will have returned the delay when the last guess // was made, causing the lock screen to block inputs for that amount of time. But checking // for it is still needed to cover any cases where a guess gets made anyway, for example // following a reboot which causes the lock screen to "forget" the delay. final Duration delay = getCurrentDelay(state); final Duration now = mInjector.getTimeSinceBoot(); final Duration remainingDelay = state.timeSinceBootOfLastWrongGuess.plus(delay).minus(now); Loading Loading @@ -298,27 +298,37 @@ class SoftwareRateLimiter { } /** * Reports a new wrong guess to the software rate-limiter. * Reports a failure to the software rate-limiter. * * <p>This must be called immediately after the hardware rate-limiter reported that the given * guess is incorrect, before the credential check failure is made visible in the UI. It is * assumed that {@link #apply(LskfIdentifier, LockscreenCredential)} was previously called with * the same parameters and returned a {@code CONTINUE_TO_HARDWARE} result. * <p>This must be called immediately after the hardware rate-limiter reported a failure, before * the credential check failure is made visible in the UI. It is assumed that {@link * #apply(LskfIdentifier, LockscreenCredential)} was previously called with the same parameters * and returned a {@code CONTINUE_TO_HARDWARE} result. * * @param id the ID of the protector or special credential * @param newWrongGuess a new wrong guess for the LSKF * @param guess the LSKF that was attempted * @param isCertainlyWrongGuess true if it's certain that the failure was caused by the guess * being wrong, as opposed to e.g. a transient hardware glitch * @return the delay until when the next guess will be allowed */ synchronized Duration reportWrongGuess(LskfIdentifier id, LockscreenCredential newWrongGuess) { synchronized Duration reportFailure( LskfIdentifier id, LockscreenCredential guess, boolean isCertainlyWrongGuess) { RateLimiterState state = getExistingState(id); // In non-enforcing mode, ignore duplicate wrong guesses here since they were already // counted by apply(), including having stats written for them. In enforcing mode, this // method isn't passed duplicate wrong guesses. if (!mEnforcing && ArrayUtils.contains(state.savedWrongGuesses, newWrongGuess)) { if (!mEnforcing && ArrayUtils.contains(state.savedWrongGuesses, guess)) { return Duration.ZERO; } // Increment the failure counter regardless of whether the failure is a certainly wrong // guess or not. A generic failure might still be caused by a wrong guess. Gatekeeper only // ever returns generic failures, and some Weaver implementations prefer THROTTLE to // INCORRECT_KEY once the delay becomes nonzero. Instead of making the software rate-limiter // ineffective on all such devices, still apply it. This does mean that correct guesses that // encountered an error will be rate-limited. However, by design the rate-limiter kicks in // gradually anyway, so there will be a chance for the user to try again. state.numWrongGuesses++; state.timeSinceBootOfLastWrongGuess = mInjector.getTimeSinceBoot(); Loading @@ -329,7 +339,9 @@ class SoftwareRateLimiter { writeStats(id, state, /* success= */ false); insertNewWrongGuess(state, newWrongGuess); // Save certainly wrong guesses so that duplicates of them can be detected. if (isCertainlyWrongGuess) { insertNewWrongGuess(state, guess); // Schedule the saved wrong guesses to be forgotten after a few minutes, extending the // existing timeout if one was already running. Loading @@ -347,6 +359,7 @@ class SoftwareRateLimiter { }, /* token= */ state, SAVED_WRONG_GUESS_TIMEOUT.toMillis()); } return getCurrentDelay(state); } Loading Loading @@ -384,7 +397,7 @@ class SoftwareRateLimiter { private RateLimiterState getExistingState(LskfIdentifier id) { RateLimiterState state = mState.get(id); if (state == null) { // This should never happen, since reportSuccess() and reportWrongGuess() are always // This should never happen, since reportSuccess() and reportFailure() are always // supposed to be paired with a call to apply() that created the state if it did not // exist. Nor is it supported to call clearLskfState() or clearUserState() in between; // higher-level locking in LockSettingsService guarantees that never happens. Loading
services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +0 −3 Original line number Diff line number Diff line Loading @@ -723,8 +723,6 @@ class SyntheticPasswordManager { // the credential was incorrect and there is a timeout before the next attempt will // be allowed. INCORRECT_KEY is preferred in the latter case to avoid the ambiguity, // but we still have to support implementations that use THROTTLE for both cases. // // TODO(b/395976735): needs unit testing via MockWeaverService return responseFromTimeout(weaverResponse); case WeaverReadStatus.INCORRECT_KEY: if (weaverResponse.timeout != 0) { Loading @@ -734,7 +732,6 @@ class SyntheticPasswordManager { // // TODO(b/395976735): use RESPONSE_CRED_INCORRECT in this case, and update users // of VerifyCredentialResponse to be compatible with that. // TODO(b/395976735): needs unit testing via MockWeaverService return responseFromTimeout(weaverResponse); } if (android.security.Flags.softwareRatelimiter()) { Loading
services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java +106 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Intent; import android.hardware.weaver.WeaverReadStatus; import android.os.RemoteException; import android.os.UserHandle; import android.platform.test.annotations.DisableFlags; Loading @@ -57,10 +58,13 @@ import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.VerifyCredentialResponse; import org.junit.Before; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import java.time.Duration; /** * atest FrameworksServicesTests:LockSettingsServiceTests */ Loading Loading @@ -719,6 +723,108 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { assertEquals(VerifyCredentialResponse.RESPONSE_OTHER_ERROR, response.getResponseCode()); } // Tests that if verifyCredential is passed a wrong guess and Weaver reports INCORRECT_KEY with // zero timeout (which indicates a certainly wrong guess), then LockSettingsService saves that // guess as a recent wrong guess and rejects a repeat of it as a duplicate. @Test @EnableFlags(android.security.Flags.FLAG_SOFTWARE_RATELIMITER) public void testRepeatOfWrongGuessRejectedAsDuplicate_afterWeaverIncorrectKeyWithoutTimeout() throws Exception { final int userId = PRIMARY_USER_ID; final LockscreenCredential credential = newPassword("password"); final LockscreenCredential wrongGuess = newPassword("wrong"); mSpManager.enableWeaver(); setCredential(userId, credential); mSpManager.injectWeaverReadResponse(WeaverReadStatus.INCORRECT_KEY, Duration.ZERO); VerifyCredentialResponse response = mService.verifyCredential(wrongGuess, userId, /* flags= */ 0); assertEquals(VerifyCredentialResponse.RESPONSE_CRED_INCORRECT, response.getResponseCode()); assertEquals(Duration.ZERO, response.getTimeoutAsDuration()); response = mService.verifyCredential(wrongGuess, userId, /* flags= */ 0); assertEquals( VerifyCredentialResponse.RESPONSE_CRED_ALREADY_TRIED, response.getResponseCode()); assertEquals(Duration.ZERO, response.getTimeoutAsDuration()); } // Same as preceding test case, but uses a nonzero timeout. // // TODO(b/395976735): currently the behavior in this scenario is wrong, so currently this test // case is ignored. Fix the behavior and remove @Ignore. @Ignore @Test @EnableFlags(android.security.Flags.FLAG_SOFTWARE_RATELIMITER) public void testRepeatOfWrongGuessRejectedAsDuplicate_afterWeaverIncorrectKeyWithTimeout() throws Exception { final int userId = PRIMARY_USER_ID; final LockscreenCredential credential = newPassword("password"); final LockscreenCredential wrongGuess = newPassword("wrong"); final Duration timeout = Duration.ofSeconds(60); mSpManager.enableWeaver(); setCredential(userId, credential); mSpManager.injectWeaverReadResponse(WeaverReadStatus.INCORRECT_KEY, timeout); VerifyCredentialResponse response = mService.verifyCredential(wrongGuess, userId, /* flags= */ 0); assertEquals(VerifyCredentialResponse.RESPONSE_RETRY, response.getResponseCode()); assertEquals(timeout, response.getTimeoutAsDuration()); response = mService.verifyCredential(wrongGuess, userId, /* flags= */ 0); assertEquals( VerifyCredentialResponse.RESPONSE_CRED_ALREADY_TRIED, response.getResponseCode()); assertEquals(Duration.ZERO, response.getTimeoutAsDuration()); } // Tests that if verifyCredential is passed a correct guess but it fails due to Weaver reporting // a status of THROTTLE (which is the expected status when there is a remaining rate-limiting // delay in Weaver), then LockSettingsService does not block the same guess from being // re-attempted and in particular does not reject it as a duplicate wrong guess. @Test @EnableFlags(android.security.Flags.FLAG_SOFTWARE_RATELIMITER) public void testRepeatOfCorrectGuessAllowed_afterWeaverThrottle() throws Exception { final int userId = PRIMARY_USER_ID; final LockscreenCredential credential = newPassword("password"); final Duration timeout = Duration.ofSeconds(60); mSpManager.enableWeaver(); setCredential(userId, credential); mSpManager.injectWeaverReadResponse(WeaverReadStatus.THROTTLE, timeout); VerifyCredentialResponse response = mService.verifyCredential(credential, userId, /* flags= */ 0); assertEquals(VerifyCredentialResponse.RESPONSE_RETRY, response.getResponseCode()); assertEquals(timeout, response.getTimeoutAsDuration()); response = mService.verifyCredential(credential, userId, /* flags= */ 0); assertTrue(response.isMatched()); } // Tests that if verifyCredential is passed a correct guess but it fails due to Weaver reporting // a status of FAILED (which is the expected status when there is a transient error unrelated to // the guess), then LockSettingsService does not block the same guess from being re-attempted // and in particular does not reject it as a duplicate wrong guess. @Test @EnableFlags(android.security.Flags.FLAG_SOFTWARE_RATELIMITER) public void testRepeatOfCorrectGuessAllowed_afterWeaverFailed() throws Exception { final int userId = PRIMARY_USER_ID; final LockscreenCredential credential = newPassword("password"); mSpManager.enableWeaver(); setCredential(userId, credential); mSpManager.injectWeaverReadResponse(WeaverReadStatus.FAILED, Duration.ZERO); VerifyCredentialResponse response = mService.verifyCredential(credential, userId, /* flags= */ 0); assertEquals(VerifyCredentialResponse.RESPONSE_OTHER_ERROR, response.getResponseCode()); assertEquals(Duration.ZERO, response.getTimeoutAsDuration()); response = mService.verifyCredential(credential, userId, /* flags= */ 0); assertTrue(response.isMatched()); } private void checkRecordedFrpNotificationIntent() { if (android.security.Flags.frpEnforcement()) { Intent savedNotificationIntent = mService.getSavedFrpNotificationIntent(); Loading
services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java +6 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import junit.framework.AssertionFailedError; import java.nio.ByteBuffer; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import java.time.Duration; import java.util.Arrays; import javax.crypto.SecretKeyFactory; Loading Loading @@ -152,4 +153,9 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager { public int getSumOfWeaverFailureCounters() { return mWeaverService.getSumOfFailureCounters(); } /** Injects a response to be returned by the next read from Weaver. */ public void injectWeaverReadResponse(int status, Duration timeout) { mWeaverService.injectReadResponse(status, timeout); } }