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

Commit 99d20ad8 authored by Danny Burakov's avatar Danny Burakov Committed by Android (Google) Code Review
Browse files

Merge "[flexiglass] Wipe dialog + more refactoring of Auth/Bouncer code." into main

parents dc34f123 8c415bc8
Loading
Loading
Loading
Loading
+20 −23
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@
package com.android.systemui.bouncer.ui.composable

import android.app.AlertDialog
import android.app.Dialog
import android.content.DialogInterface
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.animateFloatAsState
@@ -135,7 +134,7 @@ fun BouncerContent(
        }

        Dialog(
            viewModel = viewModel,
            bouncerViewModel = viewModel,
            dialogFactory = dialogFactory,
        )
    }
@@ -666,29 +665,27 @@ private fun ActionArea(

@Composable
private fun Dialog(
    viewModel: BouncerViewModel,
    bouncerViewModel: BouncerViewModel,
    dialogFactory: BouncerDialogFactory,
) {
    val dialogMessage: String? by viewModel.dialogMessage.collectAsState()
    var dialog: Dialog? by remember { mutableStateOf(null) }
    val dialogViewModel by bouncerViewModel.dialogViewModel.collectAsState()
    var dialog: AlertDialog? by remember { mutableStateOf(null) }

    if (dialogMessage != null) {
    dialogViewModel?.let { viewModel ->
        if (dialog == null) {
            dialog =
                dialogFactory().apply {
                    setMessage(dialogMessage)
                    setButton(
                        DialogInterface.BUTTON_NEUTRAL,
                        context.getString(R.string.ok),
                    ) { _, _ ->
                        viewModel.onDialogDismissed()
            dialog = dialogFactory()
        }
        dialog?.apply {
            setMessage(viewModel.text)
            setButton(DialogInterface.BUTTON_NEUTRAL, context.getString(R.string.ok)) { _, _ ->
                viewModel.onDismiss()
            }
            setCancelable(false)
            setCanceledOnTouchOutside(false)
            show()
        }
    }
    } else {
        ?: {
            dialog?.dismiss()
            dialog = null
        }
@@ -772,7 +769,7 @@ private fun UserSwitcher(
}

/**
 * Renders the dropdowm menu that displays the actual users and/or user actions that can be
 * Renders the dropdown menu that displays the actual users and/or user actions that can be
 * selected.
 */
@Composable
+2 −0
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ class AuthenticationRepositoryTest : SysuiTestCase() {
    @Mock private lateinit var lockPatternUtils: LockPatternUtils
    @Mock private lateinit var getSecurityMode: Function<Int, KeyguardSecurityModel.SecurityMode>
    @Mock private lateinit var tableLogger: TableLogBuffer
    @Mock private lateinit var devicePolicyManager: DevicePolicyManager

    private val testUtils = SceneTestUtils(this)
    private val testScope = testUtils.testScope
@@ -87,6 +88,7 @@ class AuthenticationRepositoryTest : SysuiTestCase() {
                getSecurityMode = getSecurityMode,
                userRepository = userRepository,
                lockPatternUtils = lockPatternUtils,
                devicePolicyManager = devicePolicyManager,
                broadcastDispatcher = fakeBroadcastDispatcher,
                mobileConnectionsRepository = mobileConnectionsRepository,
            )
+51 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.authentication.domain.interactor
import android.app.admin.DevicePolicyManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.None
@@ -26,6 +27,7 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pattern
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pin
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
import com.android.systemui.authentication.shared.model.AuthenticationWipeModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.scene.SceneTestUtils
import com.google.common.truth.Truth.assertThat
@@ -403,6 +405,55 @@ class AuthenticationInteractorTest : SysuiTestCase() {
            assertThat(underTest.lockoutEndTimestamp).isNull()
        }

    @Test
    fun upcomingWipe() =
        testScope.runTest {
            val upcomingWipe by collectLastValue(underTest.upcomingWipe)
            utils.authenticationRepository.setAuthenticationMethod(Pin)
            val correctPin = FakeAuthenticationRepository.DEFAULT_PIN
            val wrongPin = FakeAuthenticationRepository.DEFAULT_PIN.map { it + 1 }

            underTest.authenticate(correctPin)
            assertThat(upcomingWipe).isNull()

            var expectedFailedAttempts = 0
            var remainingFailedAttempts =
                utils.authenticationRepository.getMaxFailedUnlockAttemptsForWipe()
            assertThat(remainingFailedAttempts)
                .isGreaterThan(LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE)

            // Make many wrong attempts, until wipe is triggered:
            repeat(remainingFailedAttempts) { attemptIndex ->
                underTest.authenticate(wrongPin)
                expectedFailedAttempts++
                remainingFailedAttempts--
                if (underTest.lockoutEndTimestamp != null) {
                    // If there's a lockout, wait it out:
                    advanceTimeBy(FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS.seconds)
                }

                if (attemptIndex < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
                    // No risk of wipe.
                    assertThat(upcomingWipe).isNull()
                } else {
                    // Wipe grace period started; Make additional wrong attempts, confirm the
                    // warning is shown each time:
                    assertThat(upcomingWipe)
                        .isEqualTo(
                            AuthenticationWipeModel(
                                wipeTarget = AuthenticationWipeModel.WipeTarget.WholeDevice,
                                failedAttempts = expectedFailedAttempts,
                                remainingAttempts = remainingFailedAttempts
                            )
                        )
                }
            }

            // Unlock successfully, no more risk of upcoming wipe:
            assertSucceeded(underTest.authenticate(correctPin))
            assertThat(upcomingWipe).isNull()
        }

    @Test
    fun hintedPinLength_withoutAutoConfirm_isNull() =
        testScope.runTest {
+24 −34
Original line number Diff line number Diff line
@@ -21,6 +21,11 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.None
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Password
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pattern
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pin
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Sim
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
import com.android.systemui.scene.SceneTestUtils
@@ -47,7 +52,6 @@ class BouncerViewModelTest : SysuiTestCase() {
    private val utils = SceneTestUtils(this)
    private val testScope = utils.testScope
    private val authenticationInteractor = utils.authenticationInteractor()
    private val actionButtonInteractor = utils.bouncerActionButtonInteractor()
    private val bouncerInteractor =
        utils.bouncerInteractor(
            authenticationInteractor = authenticationInteractor,
@@ -56,7 +60,6 @@ class BouncerViewModelTest : SysuiTestCase() {
        utils.bouncerViewModel(
            bouncerInteractor = bouncerInteractor,
            authenticationInteractor = authenticationInteractor,
            actionButtonInteractor = actionButtonInteractor,
        )

    @Test
@@ -136,7 +139,7 @@ class BouncerViewModelTest : SysuiTestCase() {
    fun message() =
        testScope.runTest {
            val message by collectLastValue(underTest.message)
            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
            utils.authenticationRepository.setAuthenticationMethod(Pin)
            assertThat(message?.isUpdateAnimated).isTrue()

            repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) {
@@ -154,7 +157,7 @@ class BouncerViewModelTest : SysuiTestCase() {
        testScope.runTest {
            val authMethodViewModel by collectLastValue(underTest.authMethodViewModel)
            val message by collectLastValue(underTest.message)
            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
            utils.authenticationRepository.setAuthenticationMethod(Pin)
            assertThat(utils.authenticationRepository.lockoutEndTimestamp).isNull()
            assertThat(authMethodViewModel?.lockoutMessageId).isNotNull()

@@ -189,7 +192,7 @@ class BouncerViewModelTest : SysuiTestCase() {
                        authViewModel?.isInputEnabled ?: emptyFlow()
                    }
                )
            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
            utils.authenticationRepository.setAuthenticationMethod(Pin)
            assertThat(isInputEnabled).isTrue()

            repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) {
@@ -203,21 +206,22 @@ class BouncerViewModelTest : SysuiTestCase() {
        }

    @Test
    fun dialogMessage() =
    fun dialogViewModel() =
        testScope.runTest {
            val authMethodViewModel by collectLastValue(underTest.authMethodViewModel)
            val dialogMessage by collectLastValue(underTest.dialogMessage)
            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
            val dialogViewModel by collectLastValue(underTest.dialogViewModel)
            utils.authenticationRepository.setAuthenticationMethod(Pin)
            assertThat(authMethodViewModel?.lockoutMessageId).isNotNull()

            repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) {
                assertThat(dialogMessage).isNull()
                assertThat(dialogViewModel).isNull()
                bouncerInteractor.authenticate(WRONG_PIN)
            }
            assertThat(dialogMessage).isNotEmpty()
            assertThat(dialogViewModel).isNotNull()
            assertThat(dialogViewModel?.text).isNotEmpty()

            underTest.onDialogDismissed()
            assertThat(dialogMessage).isNull()
            dialogViewModel?.onDismiss?.invoke()
            assertThat(dialogViewModel).isNull()
        }

    @Test
@@ -225,20 +229,16 @@ class BouncerViewModelTest : SysuiTestCase() {
        testScope.runTest {
            val isSideBySideSupported by collectLastValue(underTest.isSideBySideSupported)
            utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
            utils.authenticationRepository.setAuthenticationMethod(Pin)
            assertThat(isSideBySideSupported).isTrue()
            utils.authenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.Password
            )
            utils.authenticationRepository.setAuthenticationMethod(Password)
            assertThat(isSideBySideSupported).isTrue()

            utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, false)
            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
            utils.authenticationRepository.setAuthenticationMethod(Pin)
            assertThat(isSideBySideSupported).isTrue()

            utils.authenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.Password
            )
            utils.authenticationRepository.setAuthenticationMethod(Password)
            assertThat(isSideBySideSupported).isFalse()
        }

@@ -246,27 +246,17 @@ class BouncerViewModelTest : SysuiTestCase() {
    fun isFoldSplitRequired() =
        testScope.runTest {
            val isFoldSplitRequired by collectLastValue(underTest.isFoldSplitRequired)
            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
            utils.authenticationRepository.setAuthenticationMethod(Pin)
            assertThat(isFoldSplitRequired).isTrue()
            utils.authenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.Password
            )
            utils.authenticationRepository.setAuthenticationMethod(Password)
            assertThat(isFoldSplitRequired).isFalse()

            utils.authenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.Pattern
            )
            utils.authenticationRepository.setAuthenticationMethod(Pattern)
            assertThat(isFoldSplitRequired).isTrue()
        }

    private fun authMethodsToTest(): List<AuthenticationMethodModel> {
        return listOf(
            AuthenticationMethodModel.None,
            AuthenticationMethodModel.Pin,
            AuthenticationMethodModel.Password,
            AuthenticationMethodModel.Pattern,
            AuthenticationMethodModel.Sim,
        )
        return listOf(None, Pin, Password, Pattern, Sim)
    }

    private fun assertTryAgainMessage(
+2 −2
Original line number Diff line number Diff line
@@ -303,7 +303,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
    fun onDragEnd_whenPatternTooShort() =
        testScope.runTest {
            val message by collectLastValue(bouncerViewModel.message)
            val dialogMessage by collectLastValue(bouncerViewModel.dialogMessage)
            val dialogViewModel by collectLastValue(bouncerViewModel.dialogViewModel)
            lockDeviceAndOpenPatternBouncer()

            // Enter a pattern that's too short more than enough times that would normally trigger
@@ -326,7 +326,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
                underTest.onDragEnd()

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

Loading