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

Commit 70ec7531 authored by Alejandro Nijamkin's avatar Alejandro Nijamkin
Browse files

[flexiglass] Hydrates biometrics displayState with proper state

When Flexiglass is on, takes device entry state into account to
calculate the correct displayState for biometrics (currently only used
for side FPS).

Bug: 332771243
Test: manually verified that side FPS unlock works (from
bouncer and from lockscreen)
Test: unit tests added
Flag: com.android.systemui.scene_container

Change-Id: If61ac8f06ab66b311409a50abee8653824913949
parent c3184204
Loading
Loading
Loading
Loading
+49 −12
Original line number Diff line number Diff line
@@ -14,6 +14,8 @@
 * limitations under the License.
 */

@file:OptIn(ExperimentalCoroutinesApi::class)

package com.android.systemui.biometrics.domain.interactor

import android.hardware.biometrics.AuthenticateOptions
@@ -21,16 +23,22 @@ import android.hardware.biometrics.IBiometricContextListener
import android.util.Log
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.display.data.repository.DeviceStateRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
@@ -76,20 +84,34 @@ constructor(
    deviceStateRepository: DeviceStateRepository,
    keyguardTransitionInteractor: KeyguardTransitionInteractor,
    udfpsOverlayInteractor: UdfpsOverlayInteractor,
    deviceEntryInteractor: Lazy<DeviceEntryInteractor>,
) : LogContextInteractor {

    override val displayState =
        keyguardTransitionInteractor.startedKeyguardTransitionStep.map {
            when (it.to) {
                KeyguardState.LOCKSCREEN,
                KeyguardState.OCCLUDED,
                KeyguardState.ALTERNATE_BOUNCER,
                KeyguardState.PRIMARY_BOUNCER -> AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN
                KeyguardState.AOD -> AuthenticateOptions.DISPLAY_STATE_AOD
                KeyguardState.OFF,
                KeyguardState.DOZING -> AuthenticateOptions.DISPLAY_STATE_NO_UI
                KeyguardState.DREAMING -> AuthenticateOptions.DISPLAY_STATE_SCREENSAVER
                else -> AuthenticateOptions.DISPLAY_STATE_UNKNOWN
    override val displayState: Flow<Int> by lazy {
        if (SceneContainerFlag.isEnabled) {
            combine(
                deviceEntryInteractor.get().isDeviceEntered,
                keyguardTransitionInteractor.startedKeyguardTransitionStep,
            ) { isDeviceEntered, transitionStep ->
                if (isDeviceEntered) {
                    AuthenticateOptions.DISPLAY_STATE_UNKNOWN
                } else {
                    transitionStep.toAuthenticateOptions(
                        // Here when isDeviceEntered=false which always means that the device is on
                        // top of the keyguard. Therefore, any KeyguardState that doesn't have a
                        // more specific mapping as a sub-state of keyguard, maps to LOCKSCREEN
                        // instead of UNKNOWN, because it _is_ a known display state and that
                        // display state is undeniably LOCKSCREEN.
                        default = AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN
                    )
                }
            }
        } else {
            keyguardTransitionInteractor.startedKeyguardTransitionStep.map { transitionStep ->
                transitionStep.toAuthenticateOptions(
                    default = AuthenticateOptions.DISPLAY_STATE_UNKNOWN
                )
            }
        }
    }

@@ -152,6 +174,21 @@ constructor(
        }
    }

    private fun TransitionStep.toAuthenticateOptions(default: Int): Int {
        return when (this.to) {
            KeyguardState.LOCKSCREEN,
            KeyguardState.OCCLUDED,
            KeyguardState.ALTERNATE_BOUNCER,
            KeyguardState.PRIMARY_BOUNCER -> AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN
            KeyguardState.AOD -> AuthenticateOptions.DISPLAY_STATE_AOD
            KeyguardState.OFF,
            KeyguardState.DOZING -> AuthenticateOptions.DISPLAY_STATE_NO_UI
            KeyguardState.DREAMING -> AuthenticateOptions.DISPLAY_STATE_SCREENSAVER
            KeyguardState.GONE -> AuthenticateOptions.DISPLAY_STATE_UNKNOWN
            else -> default
        }
    }

    companion object {
        private const val TAG = "ContextRepositoryImpl"
    }
+63 −5
Original line number Diff line number Diff line
@@ -23,15 +23,22 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.display.data.repository.DeviceStateRepository
import com.android.systemui.display.data.repository.fakeDeviceStateRepository
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
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.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -61,10 +68,11 @@ class LogContextInteractorImplTest : SysuiTestCase() {
    fun setup() {
        interactor =
            LogContextInteractorImpl(
                testScope.backgroundScope,
                deviceStateRepository,
                kosmos.keyguardTransitionInteractor,
                udfpsOverlayInteractor,
                applicationScope = testScope.backgroundScope,
                deviceStateRepository = deviceStateRepository,
                keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor,
                udfpsOverlayInteractor = udfpsOverlayInteractor,
                deviceEntryInteractor = { kosmos.deviceEntryInteractor },
            )
    }

@@ -135,7 +143,57 @@ class LogContextInteractorImplTest : SysuiTestCase() {
        }

    @Test
    fun displayStateChanges() =
    @EnableSceneContainer
    fun displayStateChanges_withSceneContainer() =
        testScope.runTest {
            val displayState = collectLastValue(interactor.displayState)

            keyguardTransitionRepository.startTransitionTo(KeyguardState.OFF)
            assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_NO_UI)

            keyguardTransitionRepository.startTransitionTo(KeyguardState.DOZING)
            assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_NO_UI)

            keyguardTransitionRepository.startTransitionTo(KeyguardState.DREAMING)
            assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_SCREENSAVER)

            keyguardTransitionRepository.startTransitionTo(KeyguardState.AOD)
            assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_AOD)

            keyguardTransitionRepository.startTransitionTo(KeyguardState.ALTERNATE_BOUNCER)
            assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN)

            keyguardTransitionRepository.startTransitionTo(KeyguardState.PRIMARY_BOUNCER)
            assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN)

            keyguardTransitionRepository.startTransitionTo(KeyguardState.LOCKSCREEN)
            assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN)

            keyguardTransitionRepository.startTransitionTo(KeyguardState.OCCLUDED)
            assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN)

            // Unlock the device.
            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
                SuccessFingerprintAuthenticationStatus(0, true)
            )
            runCurrent()
            kosmos.sceneInteractor.snapToScene(Scenes.Gone, "")

            keyguardTransitionRepository.startTransitionTo(KeyguardState.GONE)
            assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_UNKNOWN)

            keyguardTransitionRepository.startTransitionTo(KeyguardState.UNDEFINED)
            assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_UNKNOWN)

            // Relock the device
            kosmos.sceneInteractor.snapToScene(Scenes.Lockscreen, "")
            keyguardTransitionRepository.startTransitionTo(KeyguardState.AOD)
            assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_AOD)
        }

    @Test
    @DisableSceneContainer
    fun displayStateChanges_withoutSceneContainer() =
        testScope.runTest {
            val displayState = collectLastValue(interactor.displayState)