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

Commit 592c8541 authored by Ale Nijamkin's avatar Ale Nijamkin Committed by Android (Google) Code Review
Browse files

Merge "[flexiglass] Too-short lock pattern attempts don't trigger throttling." into main

parents b21c2cc8 c343c457
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -109,6 +109,9 @@ interface AuthenticationRepository {
     */
    val authenticationMethod: Flow<AuthenticationMethodModel>

    /** The minimal length of a pattern. */
    val minPatternLength: Int

    /**
     * Returns the currently-configured authentication method. This determines how the
     * authentication challenge needs to be completed in order to unlock an otherwise locked device.
@@ -227,6 +230,8 @@ constructor(
                }
            }

    override val minPatternLength: Int = LockPatternUtils.MIN_LOCK_PATTERN_SIZE

    override suspend fun getAuthenticationMethod(): AuthenticationMethodModel {
        return withContext(backgroundDispatcher) {
            blockingAuthenticationMethodInternal(userRepository.selectedUserId)
+3 −0
Original line number Diff line number Diff line
@@ -185,6 +185,9 @@ constructor(
    /** Whether the pattern should be visible for the currently-selected user. */
    val isPatternVisible: StateFlow<Boolean> = repository.isPatternVisible

    /** The minimal length of a pattern. */
    val minPatternLength: Int = repository.minPatternLength

    private var throttlingCountdownJob: Job? = null

    init {
+16 −1
Original line number Diff line number Diff line
@@ -92,6 +92,9 @@ constructor(
    /** Whether the pattern should be visible for the currently-selected user. */
    val isPatternVisible: StateFlow<Boolean> = authenticationInteractor.isPatternVisible

    /** The minimal length of a pattern. */
    val minPatternLength = authenticationInteractor.minPatternLength

    init {
        if (flags.isEnabled()) {
            // Clear the message if moved from throttling to no-longer throttling.
@@ -204,12 +207,24 @@ constructor(
                loggingReason = "successful authentication",
            )
        } else {
            repository.setMessage(errorMessage(authenticationInteractor.getAuthenticationMethod()))
            showErrorMessage()
        }

        return isAuthenticated
    }

    /**
     * Shows the error message.
     *
     * Callers should use this instead of [authenticate] when they know ahead of time that an auth
     * attempt will fail but aren't interested in the other side effects like triggering throttling.
     * For example, if the user entered a pattern that's too short, the system can show the error
     * message without having the attempt trigger throttling.
     */
    suspend fun showErrorMessage() {
        repository.setMessage(errorMessage(authenticationInteractor.getAuthenticationMethod()))
    }

    private fun promptMessage(authMethod: AuthenticationMethodModel): String {
        return when (authMethod) {
            is AuthenticationMethodModel.Pin ->
+3 −1
Original line number Diff line number Diff line
@@ -170,7 +170,9 @@ class PatternBouncerViewModel(
        _selectedDots.value = linkedSetOf()

        applicationScope.launch {
            if (interactor.authenticate(pattern) != true) {
            if (pattern.size < interactor.minPatternLength) {
                interactor.showErrorMessage()
            } else if (interactor.authenticate(pattern) != true) {
                showFailureAnimation()
            }
        }
+43 −0
Original line number Diff line number Diff line
@@ -202,6 +202,49 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
            assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
        }

    @Test
    fun onDragEnd_whenPatternTooShort() =
        testScope.runTest {
            val currentScene by collectLastValue(sceneInteractor.desiredScene)
            val message by collectLastValue(bouncerViewModel.message)
            val selectedDots by collectLastValue(underTest.selectedDots)
            val currentDot by collectLastValue(underTest.currentDot)
            val throttlingDialogMessage by
                collectLastValue(bouncerViewModel.throttlingDialogMessage)
            utils.authenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.Pattern
            )
            utils.authenticationRepository.setUnlocked(false)
            sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
            assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
            underTest.onShown()

            // Enter a pattern that's too short more than enough times that would normally trigger
            // throttling if the pattern were not too short and wrong:
            val attempts = FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING + 1
            repeat(attempts) { attempt ->
                underTest.onDragStart()
                CORRECT_PATTERN.subList(
                        0,
                        authenticationInteractor.minPatternLength - 1,
                    )
                    .forEach { coordinate ->
                        underTest.onDrag(
                            xPx = 30f * coordinate.x + 15,
                            yPx = 30f * coordinate.y + 15,
                            containerSizePx = 90,
                            verticalOffsetPx = 0f,
                        )
                    }

                underTest.onDragEnd()

                assertWithMessage("Attempt #$attempt").that(message?.text).isEqualTo(WRONG_PATTERN)
                assertWithMessage("Attempt #$attempt").that(throttlingDialogMessage).isNull()
            }
        }

    @Test
    fun onDragEnd_correctAfterWrong() =
        testScope.runTest {
Loading