Merge "Fingerprint allowed state should update based on face lockout" into main
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
index 4fd44cc..cfe0bec 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
 import com.android.systemui.bouncer.shared.model.BouncerMessageModel
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryBiometricsAllowedInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryFingerprintAuthInteractor
 import com.android.systemui.flags.SystemPropertiesHelper
 import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
@@ -108,7 +109,9 @@
                 facePropertyRepository = kosmos.fakeFacePropertyRepository,
                 deviceEntryFingerprintAuthInteractor = kosmos.deviceEntryFingerprintAuthInteractor,
                 faceAuthRepository = kosmos.fakeDeviceEntryFaceAuthRepository,
-                securityModel = securityModel
+                securityModel = securityModel,
+                deviceEntryBiometricsAllowedInteractor =
+                    kosmos.deviceEntryBiometricsAllowedInteractor,
             )
         biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(
             fingerprintAuthCurrentlyAllowed
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractorTest.kt
index 51f9957..605d125 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractorTest.kt
@@ -21,8 +21,6 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
@@ -36,41 +34,29 @@
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val underTest = kosmos.deviceEntryFingerprintAuthInteractor
-    private val fingerprintAuthRepository = kosmos.deviceEntryFingerprintAuthRepository
     private val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository
-    private val biometricSettingsRepository = kosmos.biometricSettingsRepository
 
     @Test
-    fun isFingerprintAuthCurrentlyAllowed_allowedOnlyWhenItIsNotLockedOutAndAllowedBySettings() =
+    fun isSensorUnderDisplay() =
         testScope.runTest {
-            val currentlyAllowed by collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed)
-            biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
-            fingerprintAuthRepository.setLockedOut(true)
-
-            assertThat(currentlyAllowed).isFalse()
-
-            fingerprintAuthRepository.setLockedOut(false)
-            assertThat(currentlyAllowed).isTrue()
-
-            biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(false)
-            assertThat(currentlyAllowed).isFalse()
+            val isUdfps by collectLastValue(underTest.isSensorUnderDisplay)
+            fingerprintPropertyRepository.supportsUdfps()
+            assertThat(isUdfps).isTrue()
         }
 
     @Test
-    fun isFingerprintCurrentlyAllowedInBouncer_trueForNonUdfpsSensorTypes() =
+    fun isSensorUnderDisplay_rear() =
         testScope.runTest {
-            biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
-
-            val isFingerprintCurrentlyAllowedInBouncer by
-                collectLastValue(underTest.isFingerprintCurrentlyAllowedOnBouncer)
-
-            fingerprintPropertyRepository.supportsUdfps()
-            assertThat(isFingerprintCurrentlyAllowedInBouncer).isFalse()
-
+            val isUdfps by collectLastValue(underTest.isSensorUnderDisplay)
             fingerprintPropertyRepository.supportsRearFps()
-            assertThat(isFingerprintCurrentlyAllowedInBouncer).isTrue()
+            assertThat(isUdfps).isFalse()
+        }
 
+    @Test
+    fun isSensorUnderDisplay_side() =
+        testScope.runTest {
+            val isUdfps by collectLastValue(underTest.isSensorUnderDisplay)
             fingerprintPropertyRepository.supportsSideFps()
-            assertThat(isFingerprintCurrentlyAllowedInBouncer).isTrue()
+            assertThat(isUdfps).isFalse()
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
index 4984fc6..373671d0 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
@@ -21,7 +21,7 @@
 import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFingerprintAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
 import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -60,7 +60,8 @@
     private val biometricSettingsRepository: BiometricSettingsRepository,
     private val systemClock: SystemClock,
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
-    private val deviceEntryFingerprintAuthInteractor: Lazy<DeviceEntryFingerprintAuthInteractor>,
+    private val deviceEntryBiometricsAllowedInteractor:
+        Lazy<DeviceEntryBiometricsAllowedInteractor>,
     private val keyguardInteractor: Lazy<KeyguardInteractor>,
     keyguardTransitionInteractor: Lazy<KeyguardTransitionInteractor>,
     sceneInteractor: Lazy<SceneInteractor>,
@@ -114,7 +115,7 @@
                                 flowOf(false)
                             } else {
                                 combine(
-                                    deviceEntryFingerprintAuthInteractor
+                                    deviceEntryBiometricsAllowedInteractor
                                         .get()
                                         .isFingerprintAuthCurrentlyAllowed,
                                     keyguardInteractor.get().isKeyguardDismissible,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
index 35015e7..8e12646 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFingerprintAuthInteractor
 import com.android.systemui.flags.SystemPropertiesHelper
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
@@ -76,10 +77,11 @@
     private val deviceEntryFingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor,
     faceAuthRepository: DeviceEntryFaceAuthRepository,
     private val securityModel: KeyguardSecurityModel,
+    deviceEntryBiometricsAllowedInteractor: DeviceEntryBiometricsAllowedInteractor,
 ) {
 
     private val isFingerprintAuthCurrentlyAllowedOnBouncer =
-        deviceEntryFingerprintAuthInteractor.isFingerprintCurrentlyAllowedOnBouncer.stateIn(
+        deviceEntryBiometricsAllowedInteractor.isFingerprintCurrentlyAllowedOnBouncer.stateIn(
             applicationScope,
             SharingStarted.Eagerly,
             false
@@ -87,6 +89,7 @@
 
     private val currentSecurityMode
         get() = securityModel.getSecurityMode(currentUserId)
+
     private val currentUserId
         get() = userRepository.getSelectedUserInfo().id
 
@@ -349,6 +352,7 @@
 
 interface CountDownTimerCallback {
     fun onFinish()
+
     fun onTick(millisUntilFinished: Long)
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/log/BouncerLoggerStartable.kt b/packages/SystemUI/src/com/android/systemui/bouncer/log/BouncerLoggerStartable.kt
index 29c4f2e..87ec254 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/log/BouncerLoggerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/log/BouncerLoggerStartable.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricSettingsInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFingerprintAuthInteractor
 import com.android.systemui.log.BouncerLogger
@@ -39,6 +40,7 @@
     private val faceAuthInteractor: DeviceEntryFaceAuthInteractor,
     private val fingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor,
     private val bouncerLogger: BouncerLogger,
+    private val deviceEntryBiometricsAllowedInteractor: DeviceEntryBiometricsAllowedInteractor,
 ) : CoreStartable {
     override fun start() {
         if (!Build.isDebuggable()) {
@@ -69,13 +71,13 @@
             }
         }
         applicationScope.launch {
-            fingerprintAuthInteractor.isFingerprintCurrentlyAllowedOnBouncer.collectLatest {
-                newValue ->
-                bouncerLogger.interestedStateChanged(
-                    "fingerprintCurrentlyAllowedOnBouncer",
-                    newValue
-                )
-            }
+            deviceEntryBiometricsAllowedInteractor.isFingerprintCurrentlyAllowedOnBouncer
+                .collectLatest { newValue ->
+                    bouncerLogger.interestedStateChanged(
+                        "fingerprintCurrentlyAllowedOnBouncer",
+                        newValue
+                    )
+                }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
index 810b6d1..31479f1 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
@@ -30,8 +30,8 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.domain.interactor.BiometricMessageInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFingerprintAuthInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
 import com.android.systemui.deviceentry.shared.model.DeviceEntryRestrictionReason
 import com.android.systemui.deviceentry.shared.model.FaceFailureMessage
@@ -76,7 +76,7 @@
     private val biometricMessageInteractor: BiometricMessageInteractor,
     private val faceAuthInteractor: DeviceEntryFaceAuthInteractor,
     private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
-    private val fingerprintInteractor: DeviceEntryFingerprintAuthInteractor,
+    private val deviceEntryBiometricsAllowedInteractor: DeviceEntryBiometricsAllowedInteractor,
     flags: ComposeBouncerFlags,
 ) {
     /**
@@ -121,7 +121,8 @@
                         combine(
                             deviceUnlockedInteractor.deviceEntryRestrictionReason,
                             lockoutMessage,
-                            fingerprintInteractor.isFingerprintCurrentlyAllowedOnBouncer,
+                            deviceEntryBiometricsAllowedInteractor
+                                .isFingerprintCurrentlyAllowedOnBouncer,
                             resetToDefault,
                         ) { deviceEntryRestrictedReason, lockoutMsg, isFpAllowedInBouncer, _ ->
                             lockoutMsg
@@ -168,7 +169,7 @@
             biometricMessageInteractor.faceMessage
                 .sample(
                     authenticationInteractor.authenticationMethod,
-                    fingerprintInteractor.isFingerprintCurrentlyAllowedOnBouncer,
+                    deviceEntryBiometricsAllowedInteractor.isFingerprintCurrentlyAllowedOnBouncer,
                 )
                 .collectLatest { (faceMessage, authMethod, fingerprintAllowedOnBouncer) ->
                     val isFaceAuthStrong = faceAuthInteractor.isFaceAuthStrong()
@@ -223,7 +224,7 @@
             biometricMessageInteractor.fingerprintMessage
                 .sample(
                     authenticationInteractor.authenticationMethod,
-                    fingerprintInteractor.isFingerprintCurrentlyAllowedOnBouncer
+                    deviceEntryBiometricsAllowedInteractor.isFingerprintCurrentlyAllowedOnBouncer
                 )
                 .collectLatest { (fingerprintMessage, authMethod, isFingerprintAllowed) ->
                     val defaultPrimaryMessage =
@@ -261,7 +262,7 @@
             bouncerInteractor.onIncorrectBouncerInput
                 .sample(
                     authenticationInteractor.authenticationMethod,
-                    fingerprintInteractor.isFingerprintCurrentlyAllowedOnBouncer
+                    deviceEntryBiometricsAllowedInteractor.isFingerprintCurrentlyAllowedOnBouncer
                 )
                 .collectLatest { (_, authMethod, isFingerprintAllowed) ->
                     message.emit(
@@ -414,7 +415,7 @@
         biometricMessageInteractor: BiometricMessageInteractor,
         faceAuthInteractor: DeviceEntryFaceAuthInteractor,
         deviceUnlockedInteractor: DeviceUnlockedInteractor,
-        fingerprintInteractor: DeviceEntryFingerprintAuthInteractor,
+        deviceEntryBiometricsAllowedInteractor: DeviceEntryBiometricsAllowedInteractor,
         flags: ComposeBouncerFlags,
         userSwitcherViewModel: UserSwitcherViewModel,
     ): BouncerMessageViewModel {
@@ -428,7 +429,7 @@
             biometricMessageInteractor = biometricMessageInteractor,
             faceAuthInteractor = faceAuthInteractor,
             deviceUnlockedInteractor = deviceUnlockedInteractor,
-            fingerprintInteractor = fingerprintInteractor,
+            deviceEntryBiometricsAllowedInteractor = deviceEntryBiometricsAllowedInteractor,
             flags = flags,
             selectedUser = userSwitcherViewModel.selectedUser,
         )
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractor.kt
new file mode 100644
index 0000000..79b176c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractor.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.deviceentry.domain.interactor
+
+import com.android.systemui.biometrics.data.repository.FacePropertyRepository
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+/**
+ * Individual biometrics (ie: fingerprint or face) may not be allowed to be used based on the
+ * lockout states of biometrics of the same or higher sensor strength.
+ *
+ * This class coordinates the lockout states of each individual biometric based on the lockout
+ * states of other biometrics.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class DeviceEntryBiometricsAllowedInteractor
+@Inject
+constructor(
+    deviceEntryFingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor,
+    deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor,
+    biometricSettingsInteractor: DeviceEntryBiometricSettingsInteractor,
+    facePropertyRepository: FacePropertyRepository,
+) {
+
+    private val isStrongFaceAuth: Flow<Boolean> =
+        facePropertyRepository.sensorInfo.map { it?.strength == SensorStrength.STRONG }
+
+    private val isStrongFaceAuthLockedOut: Flow<Boolean> =
+        combine(isStrongFaceAuth, deviceEntryFaceAuthInteractor.isLockedOut) {
+            isStrongFaceAuth,
+            isFaceAuthLockedOut ->
+            isStrongFaceAuth && isFaceAuthLockedOut
+        }
+
+    /**
+     * Whether fingerprint authentication is currently allowed for the user. This is true if the
+     * user has fingerprint auth enabled, enrolled, it is not disabled by any security timeouts by
+     * [com.android.systemui.keyguard.shared.model.AuthenticationFlags], not locked out due to too
+     * many incorrect attempts, and other biometrics at a higher or equal strenght are not locking
+     * fingerprint out.
+     */
+    val isFingerprintAuthCurrentlyAllowed: Flow<Boolean> =
+        combine(
+            deviceEntryFingerprintAuthInteractor.isLockedOut,
+            biometricSettingsInteractor.fingerprintAuthCurrentlyAllowed,
+            isStrongFaceAuthLockedOut,
+        ) { fpLockedOut, fpAllowedBySettings, strongAuthFaceAuthLockedOut ->
+            !fpLockedOut && fpAllowedBySettings && !strongAuthFaceAuthLockedOut
+        }
+
+    /** Whether fingerprint authentication is currently allowed while on the bouncer. */
+    val isFingerprintCurrentlyAllowedOnBouncer =
+        deviceEntryFingerprintAuthInteractor.isSensorUnderDisplay.flatMapLatest { sensorBelowDisplay
+            ->
+            if (sensorBelowDisplay) {
+                flowOf(false)
+            } else {
+                isFingerprintAuthCurrentlyAllowed
+            }
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
index a5eafa9..969f53f 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
@@ -29,10 +29,7 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filterIsInstance
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -70,29 +67,9 @@
         repository.authenticationStatus.filterIsInstance<SuccessFingerprintAuthenticationStatus>()
 
     /**
-     * Whether fingerprint authentication is currently allowed for the user. This is true if the
-     * user has fingerprint auth enabled, enrolled, it is not disabled by any security timeouts by
-     * [com.android.systemui.keyguard.shared.model.AuthenticationFlags] and not locked out due to
-     * too many incorrect attempts.
-     */
-    val isFingerprintAuthCurrentlyAllowed: Flow<Boolean> =
-        combine(isLockedOut, biometricSettingsInteractor.fingerprintAuthCurrentlyAllowed, ::Pair)
-            .map { (lockedOut, currentlyAllowed) -> !lockedOut && currentlyAllowed }
-
-    /**
      * Whether the fingerprint sensor is present under the display as opposed to being on the power
      * button or behind/rear of the phone.
      */
     val isSensorUnderDisplay =
         fingerprintPropertyRepository.sensorType.map(FingerprintSensorType::isUdfps)
-
-    /** Whether fingerprint authentication is currently allowed while on the bouncer. */
-    val isFingerprintCurrentlyAllowedOnBouncer =
-        isSensorUnderDisplay.flatMapLatest { sensorBelowDisplay ->
-            if (sensorBelowDisplay) {
-                flowOf(false)
-            } else {
-                isFingerprintAuthCurrentlyAllowed
-            }
-        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractorTest.kt
new file mode 100644
index 0000000..295a626
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractorTest.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.deviceentry.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FaceSensorInfo
+import com.android.systemui.biometrics.data.repository.fakeFacePropertyRepository
+import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceEntryBiometricsAllowedInteractorTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val underTest = kosmos.deviceEntryBiometricsAllowedInteractor
+
+    @Test
+    fun isFingerprintAuthCurrentlyAllowed_true() =
+        testScope.runTest {
+            val fpAllowed by collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed)
+
+            // WHEN: not locked out, no face sensor, no strong auth requirements
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setLockedOut(false)
+            kosmos.fakeFacePropertyRepository.setSensorInfo(null)
+            kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+
+            // THEN fp is allowed
+            assertThat(fpAllowed).isTrue()
+        }
+
+    @Test
+    fun isFingerprintAuthCurrentlyAllowed_strongFaceLockedOut() =
+        testScope.runTest {
+            val fpAllowed by collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed)
+
+            // WHEN: not locked out, face is strong & locked out,  no strong auth requirements
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setLockedOut(false)
+            kosmos.fakeFacePropertyRepository.setSensorInfo(
+                FaceSensorInfo(
+                    id = 0,
+                    strength = SensorStrength.STRONG,
+                )
+            )
+            kosmos.fakeDeviceEntryFaceAuthRepository.setLockedOut(true)
+            kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+
+            // THEN fp is NOT allowed
+            assertThat(fpAllowed).isFalse()
+        }
+
+    @Test
+    fun isFingerprintAuthCurrentlyAllowed_convenienceFaceLockedOut() =
+        testScope.runTest {
+            val fpAllowed by collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed)
+
+            // WHEN: not locked out, face is convenience & locked out, no strong auth requirements
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setLockedOut(false)
+            kosmos.fakeFacePropertyRepository.setSensorInfo(
+                FaceSensorInfo(
+                    id = 0,
+                    strength = SensorStrength.CONVENIENCE,
+                )
+            )
+            kosmos.fakeDeviceEntryFaceAuthRepository.setLockedOut(true)
+            kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+
+            // THEN fp is allowed
+            assertThat(fpAllowed).isTrue()
+        }
+
+    @Test
+    fun isFingerprintAuthCurrentlyAllowed_primaryAuthRequired() =
+        testScope.runTest {
+            val fpAllowed by collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed)
+
+            // WHEN: not locked out, no face sensor,  no strong auth requirements
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setLockedOut(false)
+            kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(false)
+
+            // THEN fp is NOT allowed
+            assertThat(fpAllowed).isFalse()
+        }
+
+    @Test
+    fun isFingerprintAuthCurrentlyAllowedOnBouncer_sfps() =
+        testScope.runTest {
+            val fpAllowedOnBouncer by
+                collectLastValue(underTest.isFingerprintCurrentlyAllowedOnBouncer)
+
+            // GIVEN fingerprint is generally allowed
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setLockedOut(false)
+            kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+
+            // WHEN side fps
+            kosmos.fakeFingerprintPropertyRepository.supportsSideFps()
+
+            // THEN fp is allowed on the primary bouncer
+            assertThat(fpAllowedOnBouncer).isTrue()
+        }
+
+    @Test
+    fun isFingerprintAuthCurrentlyAllowedOnBouncer_rearFps() =
+        testScope.runTest {
+            val fpAllowedOnBouncer by
+                collectLastValue(underTest.isFingerprintCurrentlyAllowedOnBouncer)
+
+            // GIVEN fingerprint is generally allowed
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setLockedOut(false)
+            kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+
+            // WHEN rear fps
+            kosmos.fakeFingerprintPropertyRepository.supportsRearFps()
+
+            // THEN fp is allowed on the primary bouncer
+            assertThat(fpAllowedOnBouncer).isTrue()
+        }
+
+    @Test
+    fun isFingerprintAuthCurrentlyAllowedOnBouncer_udfps() =
+        testScope.runTest {
+            val fpAllowedOnBouncer by
+                collectLastValue(underTest.isFingerprintCurrentlyAllowedOnBouncer)
+
+            // GIVEN fp is generally allowed
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setLockedOut(false)
+            kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+
+            // WHEN UDFPS
+            kosmos.fakeFingerprintPropertyRepository.supportsUdfps()
+
+            // THEN fp is never allowed on the primary bouncer
+            assertThat(fpAllowedOnBouncer).isFalse()
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
index a8271c1..2a2a82d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
@@ -31,8 +31,8 @@
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFingerprintAuthInteractor
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
@@ -112,7 +112,7 @@
                 biometricSettingsRepository,
                 FakeSystemClock(),
                 keyguardUpdateMonitor,
-                { mock(DeviceEntryFingerprintAuthInteractor::class.java) },
+                { mock(DeviceEntryBiometricsAllowedInteractor::class.java) },
                 { mock(KeyguardInteractor::class.java) },
                 { mock(KeyguardTransitionInteractor::class.java) },
                 { kosmos.sceneInteractor },
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
index 9236bd2..63323b2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
@@ -19,9 +19,10 @@
 import com.android.keyguard.keyguardUpdateMonitor
 import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
 import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
-import com.android.systemui.deviceentry.domain.interactor.deviceEntryFingerprintAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryBiometricsAllowedInteractor
 import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.deviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
@@ -42,7 +43,7 @@
             biometricSettingsRepository = biometricSettingsRepository,
             systemClock = systemClock,
             keyguardUpdateMonitor = keyguardUpdateMonitor,
-            deviceEntryFingerprintAuthInteractor = { deviceEntryFingerprintAuthInteractor },
+            deviceEntryBiometricsAllowedInteractor = { deviceEntryBiometricsAllowedInteractor },
             keyguardInteractor = { keyguardInteractor },
             keyguardTransitionInteractor = { keyguardTransitionInteractor },
             scope = testScope.backgroundScope,
@@ -55,6 +56,7 @@
     this.keyguardBouncerRepository.setPrimaryShow(false)
     this.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
     this.biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+    this.deviceEntryFaceAuthRepository.setLockedOut(false)
     whenever(this.keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
     whenever(this.keyguardStateController.isUnlocked).thenReturn(false)
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt
index de5f0f3..e70631e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt
@@ -22,8 +22,8 @@
 import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
 import com.android.systemui.bouncer.shared.flag.composeBouncerFlags
 import com.android.systemui.deviceentry.domain.interactor.biometricMessageInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryBiometricsAllowedInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
-import com.android.systemui.deviceentry.domain.interactor.deviceEntryFingerprintAuthInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
@@ -45,7 +45,7 @@
             biometricMessageInteractor = biometricMessageInteractor,
             faceAuthInteractor = deviceEntryFaceAuthInteractor,
             deviceUnlockedInteractor = deviceUnlockedInteractor,
-            fingerprintInteractor = deviceEntryFingerprintAuthInteractor,
+            deviceEntryBiometricsAllowedInteractor = deviceEntryBiometricsAllowedInteractor,
             flags = composeBouncerFlags,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractorKosmos.kt
new file mode 100644
index 0000000..4357289
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractorKosmos.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.deviceentry.domain.interactor
+
+import com.android.systemui.biometrics.data.repository.facePropertyRepository
+import com.android.systemui.kosmos.Kosmos
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.deviceEntryBiometricsAllowedInteractor by
+    Kosmos.Fixture {
+        DeviceEntryBiometricsAllowedInteractor(
+            deviceEntryFingerprintAuthInteractor = deviceEntryFingerprintAuthInteractor,
+            deviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor,
+            biometricSettingsInteractor = deviceEntryBiometricSettingsInteractor,
+            facePropertyRepository = facePropertyRepository,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
index be8048e..9b7bca6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
@@ -27,8 +27,8 @@
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFingerprintAuthInteractor
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -87,7 +87,7 @@
                 FakeBiometricSettingsRepository(),
                 FakeSystemClock(),
                 keyguardUpdateMonitor,
-                { mock(DeviceEntryFingerprintAuthInteractor::class.java) },
+                { mock(DeviceEntryBiometricsAllowedInteractor::class.java) },
                 { mock(KeyguardInteractor::class.java) },
                 { mock(KeyguardTransitionInteractor::class.java) },
                 { mock(SceneInteractor::class.java) },