Minor cleanup: move logic that locks face whenever fingerprint is locked to the interactor

1. This is domain logic that belongs in the face auth interactor
2. When fingerprint state is moved out of KeyguardUpdateMonitor, fingperint interactor would depend on DeviceFaceAuthRepository.isLockedOut if face is class3

Bug: 275788040
Test: atest KeyguardFaceAuthInteractorTest
Test: atest DeviceEntryFaceAuthRepositoryTest
Change-Id: Iede9a41d0ee11fd0f419276efa992b05cba6d3ab
This commit is contained in:
Chandru S
2023-07-19 20:18:13 -07:00
parent eae7134dde
commit a381768b19
8 changed files with 94 additions and 19 deletions

View File

@@ -102,6 +102,9 @@ interface DeviceEntryFaceAuthRepository {
/** Whether bypass is currently enabled */
val isBypassEnabled: Flow<Boolean>
/** Set whether face authentication should be locked out or not */
fun lockoutFaceAuth()
/**
* Trigger face authentication.
*
@@ -199,6 +202,10 @@ constructor(
}
?: flowOf(false)
override fun lockoutFaceAuth() {
_isLockedOut.value = true
}
private val faceLockoutResetCallback =
object : FaceManager.LockoutResetCallback() {
override fun onLockoutReset(sensorId: Int) {
@@ -396,7 +403,7 @@ constructor(
private val faceAuthCallback =
object : FaceManager.AuthenticationCallback() {
override fun onAuthenticationFailed() {
_authenticationStatus.value = FailedFaceAuthenticationStatus
_authenticationStatus.value = FailedFaceAuthenticationStatus()
_isAuthenticated.value = false
faceAuthLogger.authenticationFailed()
onFaceAuthRequestCompleted()

View File

@@ -55,6 +55,8 @@ class NoopDeviceEntryFaceAuthRepository @Inject constructor() : DeviceEntryFaceA
override val isBypassEnabled: Flow<Boolean>
get() = emptyFlow()
override fun lockoutFaceAuth() = Unit
/**
* Trigger face authentication.
*

View File

@@ -30,6 +30,7 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -67,6 +68,7 @@ constructor(
private val featureFlags: FeatureFlags,
private val faceAuthenticationLogger: FaceAuthenticationLogger,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
) : CoreStartable, KeyguardFaceAuthInteractor {
private val listeners: MutableList<FaceAuthenticationListener> = mutableListOf()
@@ -117,6 +119,15 @@ constructor(
)
}
.launchIn(applicationScope)
deviceEntryFingerprintAuthRepository.isLockedOut
.onEach {
if (it) {
faceAuthenticationLogger.faceLockedOut("Fingerprint locked out")
repository.lockoutFaceAuth()
}
}
.launchIn(applicationScope)
}
override fun onSwipeUpOnBouncer() {

View File

@@ -23,29 +23,40 @@ import android.os.SystemClock.elapsedRealtime
* Authentication status provided by
* [com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository]
*/
sealed class FaceAuthenticationStatus(
// present to break equality check if the same error occurs repeatedly.
val createdAt: Long = elapsedRealtime()
)
sealed class FaceAuthenticationStatus
/** Success authentication status. */
data class SuccessFaceAuthenticationStatus(val successResult: FaceManager.AuthenticationResult) :
FaceAuthenticationStatus()
data class SuccessFaceAuthenticationStatus(
val successResult: FaceManager.AuthenticationResult,
// present to break equality check if the same error occurs repeatedly.
@JvmField val createdAt: Long = elapsedRealtime()
) : FaceAuthenticationStatus()
/** Face authentication help message. */
data class HelpFaceAuthenticationStatus(val msgId: Int, val msg: String?) :
FaceAuthenticationStatus()
data class HelpFaceAuthenticationStatus(
val msgId: Int,
val msg: String?, // present to break equality check if the same error occurs repeatedly.
@JvmField val createdAt: Long = elapsedRealtime()
) : FaceAuthenticationStatus()
/** Face acquired message. */
data class AcquiredFaceAuthenticationStatus(val acquiredInfo: Int) : FaceAuthenticationStatus()
data class AcquiredFaceAuthenticationStatus(
val acquiredInfo: Int, // present to break equality check if the same error occurs repeatedly.
@JvmField val createdAt: Long = elapsedRealtime()
) : FaceAuthenticationStatus()
/** Face authentication failed message. */
object FailedFaceAuthenticationStatus : FaceAuthenticationStatus()
data class FailedFaceAuthenticationStatus(
// present to break equality check if the same error occurs repeatedly.
@JvmField val createdAt: Long = elapsedRealtime()
) : FaceAuthenticationStatus()
/** Face authentication error message */
data class ErrorFaceAuthenticationStatus(
val msgId: Int,
val msg: String? = null,
// present to break equality check if the same error occurs repeatedly.
@JvmField val createdAt: Long = elapsedRealtime()
) : FaceAuthenticationStatus() {
/**
* Method that checks if [msgId] is a lockout error. A lockout error means that face
@@ -80,5 +91,5 @@ data class FaceDetectionStatus(
val userId: Int,
val isStrongBiometric: Boolean,
// present to break equality check if the same error occurs repeatedly.
val createdAt: Long = elapsedRealtime()
@JvmField val createdAt: Long = elapsedRealtime()
)

View File

@@ -8,6 +8,7 @@ import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.log.core.LogLevel.DEBUG
import com.android.systemui.log.dagger.FaceAuthLog
import com.google.errorprone.annotations.CompileTimeConstant
import javax.inject.Inject
private const val TAG = "DeviceEntryFaceAuthRepositoryLog"
@@ -264,4 +265,8 @@ constructor(
fun watchdogScheduled() {
logBuffer.log(TAG, DEBUG, "FaceManager Biometric watchdog scheduled.")
}
fun faceLockedOut(@CompileTimeConstant reason: String) {
logBuffer.log(TAG, DEBUG, "Face auth has been locked out: $reason")
}
}

View File

@@ -265,7 +265,8 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
val successResult = successResult()
authenticationCallback.value.onAuthenticationSucceeded(successResult)
assertThat(authStatus()).isEqualTo(SuccessFaceAuthenticationStatus(successResult))
val response = authStatus() as SuccessFaceAuthenticationStatus
assertThat(response.successResult).isEqualTo(successResult)
assertThat(authenticated()).isTrue()
assertThat(authRunning()).isFalse()
assertThat(canFaceAuthRun()).isFalse()
@@ -494,7 +495,9 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
authenticationCallback.value.onAuthenticationHelp(10, "Ignored help msg")
authenticationCallback.value.onAuthenticationHelp(11, "Ignored help msg")
assertThat(authStatus()).isEqualTo(HelpFaceAuthenticationStatus(9, "help msg"))
val response = authStatus() as HelpFaceAuthenticationStatus
assertThat(response.msg).isEqualTo("help msg")
assertThat(response.msgId).isEqualTo(response.msgId)
}
@Test
@@ -550,10 +553,8 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
}
@Test
fun authenticateDoesNotRunWhenFpIsLockedOut() =
testScope.runTest {
testGatingCheckForFaceAuth { deviceEntryFingerprintAuthRepository.setLockedOut(true) }
}
fun authenticateDoesNotRunWhenFaceIsDisabled() =
testScope.runTest { testGatingCheckForFaceAuth { underTest.lockoutFaceAuth() } }
@Test
fun authenticateDoesNotRunWhenUserIsCurrentlyTrusted() =
@@ -857,6 +858,19 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
}
}
@Test
fun disableFaceUnlockLocksOutFaceUnlock() =
testScope.runTest {
runCurrent()
initCollectors()
assertThat(underTest.isLockedOut.value).isFalse()
underTest.lockoutFaceAuth()
runCurrent()
assertThat(underTest.isLockedOut.value).isTrue()
}
@Test
fun detectDoesNotRunWhenFaceAuthNotSupportedInCurrentPosture() =
testScope.runTest {
@@ -1070,10 +1084,11 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
}
private suspend fun TestScope.allPreconditionsToRunFaceAuthAreTrue() {
verify(faceManager, atLeastOnce())
.addLockoutResetCallback(faceLockoutResetCallback.capture())
biometricSettingsRepository.setFaceEnrolled(true)
biometricSettingsRepository.setIsFaceAuthEnabled(true)
fakeUserRepository.setUserSwitching(false)
deviceEntryFingerprintAuthRepository.setLockedOut(false)
trustRepository.setCurrentUserTrusted(false)
keyguardRepository.setKeyguardGoingAway(false)
keyguardRepository.setWakefulnessModel(
@@ -1087,6 +1102,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
biometricSettingsRepository.setIsUserInLockdown(false)
fakeUserRepository.setSelectedUserInfo(primaryUser)
biometricSettingsRepository.setIsFaceAuthSupportedInCurrentPosture(true)
faceLockoutResetCallback.value.onLockoutReset(0)
bouncerRepository.setAlternateVisible(true)
keyguardRepository.setKeyguardShowing(true)
runCurrent()

View File

@@ -38,6 +38,7 @@ import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
@@ -73,6 +74,8 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() {
private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
private lateinit var faceAuthRepository: FakeDeviceEntryFaceAuthRepository
private lateinit var fakeDeviceEntryFingerprintAuthRepository:
FakeDeviceEntryFingerprintAuthRepository
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@@ -94,6 +97,7 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() {
)
.keyguardTransitionInteractor
fakeDeviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
underTest =
SystemUIKeyguardFaceAuthInteractor(
mContext,
@@ -127,6 +131,7 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() {
featureFlags,
FaceAuthenticationLogger(logcatLogBuffer("faceAuthBuffer")),
keyguardUpdateMonitor,
fakeDeviceEntryFingerprintAuthRepository
)
}
@@ -335,4 +340,14 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() {
assertThat(faceAuthRepository.runningAuthRequest.value)
.isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, false))
}
@Test
fun faceUnlockIsDisabledWhenFpIsLockedOut() = testScope.runTest {
underTest.start()
fakeDeviceEntryFingerprintAuthRepository.setLockedOut(true)
runCurrent()
assertThat(faceAuthRepository.wasDisabled).isTrue()
}
}

View File

@@ -27,6 +27,11 @@ import kotlinx.coroutines.flow.filterNotNull
class FakeDeviceEntryFaceAuthRepository : DeviceEntryFaceAuthRepository {
private var _wasDisabled: Boolean = false
val wasDisabled: Boolean
get() = _wasDisabled
override val isAuthenticated = MutableStateFlow(false)
override val canRunFaceAuth = MutableStateFlow(false)
private val _authenticationStatus = MutableStateFlow<FaceAuthenticationStatus?>(null)
@@ -52,6 +57,9 @@ class FakeDeviceEntryFaceAuthRepository : DeviceEntryFaceAuthRepository {
override val isAuthRunning: StateFlow<Boolean> = _isAuthRunning
override val isBypassEnabled = MutableStateFlow(false)
override fun lockoutFaceAuth() {
_wasDisabled = true
}
override suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
_runningAuthRequest.value = uiEvent to fallbackToDetection