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

Commit 3f0ebe72 authored by Andreas Miko's avatar Andreas Miko Committed by Android (Google) Code Review
Browse files

Merge "[KTF integration] Fix AlternateBouncerInteractor" into main

parents 7aa222d7 b0152712
Loading
Loading
Loading
Loading
+95 −87
Original line number Diff line number Diff line
@@ -16,90 +16,93 @@

package com.android.systemui.bouncer.domain.interactor

import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.keyguardUpdateMonitor
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepositoryImpl
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFingerprintAuthInteractor
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.util.time.SystemClock
import dagger.Lazy
import kotlinx.coroutines.test.TestScope
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.policy.keyguardStateController
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.whenever

@SmallTest
@RunWith(AndroidJUnit4::class)
class AlternateBouncerInteractorTest : SysuiTestCase() {
    private val kosmos = testKosmos()

    private lateinit var underTest: AlternateBouncerInteractor
    private lateinit var bouncerRepository: KeyguardBouncerRepository
    private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
    private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
    @Mock private lateinit var statusBarStateController: StatusBarStateController
    @Mock private lateinit var keyguardStateController: KeyguardStateController
    @Mock private lateinit var systemClock: SystemClock
    @Mock private lateinit var bouncerLogger: TableLogBuffer
    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor

    @Before
    fun setup() {
        MockitoAnnotations.initMocks(this)
        bouncerRepository =
            KeyguardBouncerRepositoryImpl(
                FakeSystemClock(),
                TestScope().backgroundScope,
                bouncerLogger,
            )
        biometricSettingsRepository = FakeBiometricSettingsRepository()
        fingerprintPropertyRepository = FakeFingerprintPropertyRepository()

        mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
        initializeUnderTest()
    }

    private fun initializeUnderTest() {
        // Set any feature flags before creating the alternateBouncerInteractor
        underTest =
            AlternateBouncerInteractor(
                statusBarStateController,
                keyguardStateController,
                bouncerRepository,
                fingerprintPropertyRepository,
                biometricSettingsRepository,
                systemClock,
                keyguardUpdateMonitor,
                Lazy { mock(DeviceEntryFingerprintAuthInteractor::class.java) },
                Lazy { mock(KeyguardInteractor::class.java) },
                Lazy { mock(KeyguardTransitionInteractor::class.java) },
                TestScope().backgroundScope,
            )
        underTest = kosmos.alternateBouncerInteractor
    }

    @Test(expected = IllegalStateException::class)
    @EnableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
    fun enableUdfpsRefactor_deprecatedShowMethod_throwsIllegalStateException() {
        mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
        underTest.show()
    }

    @Test
    @DisableSceneContainer
    fun canShowAlternateBouncer_false_dueToTransitionState() =
        kosmos.testScope.runTest {
            givenAlternateBouncerSupported()
            val canShowAlternateBouncer by collectLastValue(underTest.canShowAlternateBouncer)
            kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(
                from = KeyguardState.AOD,
                to = KeyguardState.GONE,
                validateStep = false,
            )
            assertFalse(canShowAlternateBouncer!!)
        }

    @Test
    @EnableSceneContainer
    fun canShowAlternateBouncer_false_dueToTransitionState_scene_container() =
        kosmos.testScope.runTest {
            givenAlternateBouncerSupported()
            val canShowAlternateBouncer by collectLastValue(underTest.canShowAlternateBouncer)
            val isDeviceUnlocked by
                collectLastValue(
                    kosmos.deviceUnlockedInteractor.deviceUnlockStatus.map { it.isUnlocked }
                )
            assertThat(isDeviceUnlocked).isFalse()

            kosmos.authenticationInteractor.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)
            assertThat(isDeviceUnlocked).isTrue()
            kosmos.sceneInteractor.changeScene(Scenes.Gone, "")

            assertThat(canShowAlternateBouncer).isFalse()
        }

    @Test
    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
    fun canShowAlternateBouncerForFingerprint_givenCanShow() {
        givenCanShowAlternateBouncer()
        assertTrue(underTest.canShowAlternateBouncerForFingerprint())
@@ -108,7 +111,7 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
    @Test
    fun canShowAlternateBouncerForFingerprint_alternateBouncerUIUnavailable() {
        givenCanShowAlternateBouncer()
        bouncerRepository.setAlternateBouncerUIAvailable(false)
        kosmos.keyguardBouncerRepository.setAlternateBouncerUIAvailable(false)

        assertFalse(underTest.canShowAlternateBouncerForFingerprint())
    }
@@ -116,7 +119,7 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
    @Test
    fun canShowAlternateBouncerForFingerprint_ifFingerprintIsNotUsuallyAllowed() {
        givenCanShowAlternateBouncer()
        biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
        kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)

        assertFalse(underTest.canShowAlternateBouncerForFingerprint())
    }
@@ -124,7 +127,7 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
    @Test
    fun canShowAlternateBouncerForFingerprint_strongBiometricNotAllowed() {
        givenCanShowAlternateBouncer()
        biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(false)
        kosmos.biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(false)

        assertFalse(underTest.canShowAlternateBouncerForFingerprint())
    }
@@ -132,23 +135,24 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
    @Test
    fun canShowAlternateBouncerForFingerprint_fingerprintLockedOut() {
        givenCanShowAlternateBouncer()
        whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(true)
        whenever(kosmos.keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(true)

        assertFalse(underTest.canShowAlternateBouncerForFingerprint())
    }

    @Test
    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
    fun show_whenCanShow() {
        givenCanShowAlternateBouncer()

        assertTrue(underTest.show())
        assertTrue(bouncerRepository.alternateBouncerVisible.value)
        assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerVisible.value)
    }

    @Test
    fun canShowAlternateBouncerForFingerprint_butCanDismissLockScreen() {
        givenCanShowAlternateBouncer()
        whenever(keyguardStateController.isUnlocked).thenReturn(true)
        whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(true)

        assertFalse(underTest.canShowAlternateBouncerForFingerprint())
    }
@@ -156,82 +160,86 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
    @Test
    fun canShowAlternateBouncerForFingerprint_primaryBouncerShowing() {
        givenCanShowAlternateBouncer()
        bouncerRepository.setPrimaryShow(true)
        kosmos.keyguardBouncerRepository.setPrimaryShow(true)

        assertFalse(underTest.canShowAlternateBouncerForFingerprint())
    }

    @Test
    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
    fun show_whenCannotShow() {
        givenCannotShowAlternateBouncer()

        assertFalse(underTest.show())
        assertFalse(bouncerRepository.alternateBouncerVisible.value)
        assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerVisible.value)
    }

    @Test
    fun hide_wasPreviouslyShowing() {
        bouncerRepository.setAlternateVisible(true)
        kosmos.keyguardBouncerRepository.setAlternateVisible(true)

        assertTrue(underTest.hide())
        assertFalse(bouncerRepository.alternateBouncerVisible.value)
        assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerVisible.value)
    }

    @Test
    fun hide_wasNotPreviouslyShowing() {
        bouncerRepository.setAlternateVisible(false)
        kosmos.keyguardBouncerRepository.setAlternateVisible(false)

        assertFalse(underTest.hide())
        assertFalse(bouncerRepository.alternateBouncerVisible.value)
        assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerVisible.value)
    }

    @Test
    @EnableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
    fun canShowAlternateBouncerForFingerprint_rearFps() {
        mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
        initializeUnderTest()
        givenCanShowAlternateBouncer()
        fingerprintPropertyRepository.supportsRearFps() // does not support alternate bouncer
        kosmos.fingerprintPropertyRepository.supportsRearFps() // does not support alternate bouncer

        assertFalse(underTest.canShowAlternateBouncerForFingerprint())
    }

    @Test
    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
    fun alternateBouncerUiAvailable_fromMultipleSources() {
        initializeUnderTest()
        assertFalse(bouncerRepository.alternateBouncerUIAvailable.value)
        assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value)

        // GIVEN there are two different sources indicating the alternate bouncer is available
        underTest.setAlternateBouncerUIAvailable(true, "source1")
        underTest.setAlternateBouncerUIAvailable(true, "source2")
        assertTrue(bouncerRepository.alternateBouncerUIAvailable.value)
        assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value)

        // WHEN one of the sources no longer says the UI is available
        underTest.setAlternateBouncerUIAvailable(false, "source1")

        // THEN alternate bouncer UI is still available (from the other source)
        assertTrue(bouncerRepository.alternateBouncerUIAvailable.value)
        assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value)

        // WHEN all sources say the UI is not available
        underTest.setAlternateBouncerUIAvailable(false, "source2")

        // THEN alternate boucer UI is not available
        assertFalse(bouncerRepository.alternateBouncerUIAvailable.value)
        assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value)
    }

    private fun givenCanShowAlternateBouncer() {
    private fun givenAlternateBouncerSupported() {
        if (DeviceEntryUdfpsRefactor.isEnabled) {
            fingerprintPropertyRepository.supportsUdfps()
            kosmos.fingerprintPropertyRepository.supportsUdfps()
        } else {
            bouncerRepository.setAlternateBouncerUIAvailable(true)
            kosmos.keyguardBouncerRepository.setAlternateBouncerUIAvailable(true)
        }
    }
        bouncerRepository.setPrimaryShow(false)
        biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
        biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
        whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
        whenever(keyguardStateController.isUnlocked).thenReturn(false)

    private fun givenCanShowAlternateBouncer() {
        givenAlternateBouncerSupported()
        kosmos.keyguardBouncerRepository.setPrimaryShow(false)
        kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
        kosmos.biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
        whenever(kosmos.keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
        whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(false)
    }

    private fun givenCannotShowAlternateBouncer() {
        biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
        kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
    }
}
+37 −21
Original line number Diff line number Diff line
@@ -28,6 +28,9 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
import com.android.systemui.util.time.SystemClock
@@ -60,6 +63,7 @@ constructor(
    private val deviceEntryFingerprintAuthInteractor: Lazy<DeviceEntryFingerprintAuthInteractor>,
    private val keyguardInteractor: Lazy<KeyguardInteractor>,
    keyguardTransitionInteractor: Lazy<KeyguardTransitionInteractor>,
    sceneInteractor: Lazy<SceneInteractor>,
    @Application scope: CoroutineScope,
) {
    var receivedDownTouch = false
@@ -96,10 +100,22 @@ constructor(
        alternateBouncerSupported
            .flatMapLatest { alternateBouncerSupported ->
                if (alternateBouncerSupported) {
                    keyguardTransitionInteractor.get().currentKeyguardState.flatMapLatest {
                        currentKeyguardState ->
                    combine(
                            keyguardTransitionInteractor.get().currentKeyguardState,
                            if (SceneContainerFlag.isEnabled) {
                                sceneInteractor.get().currentScene
                            } else {
                                flowOf(Scenes.Lockscreen)
                            },
                            ::Pair
                        )
                        .flatMapLatest { (currentKeyguardState, transitionState) ->
                            if (currentKeyguardState == KeyguardState.GONE) {
                                flowOf(false)
                            } else if (
                                SceneContainerFlag.isEnabled && transitionState == Scenes.Gone
                            ) {
                                flowOf(false)
                            } else {
                                combine(
                                    deviceEntryFingerprintAuthInteractor
+1 −0
Original line number Diff line number Diff line
@@ -115,6 +115,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
                { mock(DeviceEntryFingerprintAuthInteractor::class.java) },
                { mock(KeyguardInteractor::class.java) },
                { mock(KeyguardTransitionInteractor::class.java) },
                { kosmos.sceneInteractor },
                testScope.backgroundScope,
            )

+5 −4
Original line number Diff line number Diff line
@@ -26,15 +26,15 @@ import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInterac
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.statusbar.statusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateControllerImpl
import com.android.systemui.util.mockito.mock
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.statusbar.policy.keyguardStateController
import com.android.systemui.util.time.systemClock

var Kosmos.alternateBouncerInteractor by
val Kosmos.alternateBouncerInteractor: AlternateBouncerInteractor by
    Kosmos.Fixture {
        AlternateBouncerInteractor(
            statusBarStateController = statusBarStateController,
            keyguardStateController = mock<KeyguardStateControllerImpl>(),
            keyguardStateController = keyguardStateController,
            bouncerRepository = keyguardBouncerRepository,
            fingerprintPropertyRepository = fingerprintPropertyRepository,
            biometricSettingsRepository = biometricSettingsRepository,
@@ -44,5 +44,6 @@ var Kosmos.alternateBouncerInteractor by
            keyguardInteractor = { keyguardInteractor },
            keyguardTransitionInteractor = { keyguardTransitionInteractor },
            scope = testScope.backgroundScope,
            sceneInteractor = { sceneInteractor },
        )
    }
+2 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
@@ -89,6 +90,7 @@ object KeyguardDismissInteractorFactory {
                { mock(DeviceEntryFingerprintAuthInteractor::class.java) },
                { mock(KeyguardInteractor::class.java) },
                { mock(KeyguardTransitionInteractor::class.java) },
                { mock(SceneInteractor::class.java) },
                testScope.backgroundScope,
            )
        val powerInteractorWithDeps =
Loading