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:
@@ -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()
|
||||
|
||||
@@ -55,6 +55,8 @@ class NoopDeviceEntryFaceAuthRepository @Inject constructor() : DeviceEntryFaceA
|
||||
override val isBypassEnabled: Flow<Boolean>
|
||||
get() = emptyFlow()
|
||||
|
||||
override fun lockoutFaceAuth() = Unit
|
||||
|
||||
/**
|
||||
* Trigger face authentication.
|
||||
*
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user