Merge "Log biometric lockout events"
This commit is contained in:
@@ -294,6 +294,7 @@
|
||||
<string-array name="config_systemUIServiceComponents" translatable="false">
|
||||
<item>com.android.systemui.util.NotificationChannels</item>
|
||||
<item>com.android.systemui.keyguard.KeyguardViewMediator</item>
|
||||
<item>com.android.keyguard.KeyguardBiometricLockoutLogger</item>
|
||||
<item>com.android.systemui.recents.Recents</item>
|
||||
<item>com.android.systemui.volume.VolumeUI</item>
|
||||
<item>com.android.systemui.statusbar.phone.StatusBar</item>
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.keyguard
|
||||
|
||||
import android.content.Context
|
||||
import android.hardware.biometrics.BiometricSourceType
|
||||
import com.android.internal.annotations.VisibleForTesting
|
||||
import com.android.internal.logging.UiEvent
|
||||
import com.android.internal.logging.UiEventLogger
|
||||
import com.android.internal.widget.LockPatternUtils
|
||||
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT
|
||||
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
|
||||
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE
|
||||
import com.android.keyguard.KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
|
||||
import com.android.systemui.CoreStartable
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import java.io.FileDescriptor
|
||||
import java.io.PrintWriter
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Logs events when primary authentication requirements change. Primary authentication is considered
|
||||
* authentication using pin/pattern/password input.
|
||||
*
|
||||
* See [PrimaryAuthRequiredEvent] for all the events and their descriptions.
|
||||
*/
|
||||
@SysUISingleton
|
||||
class KeyguardBiometricLockoutLogger @Inject constructor(
|
||||
context: Context?,
|
||||
private val uiEventLogger: UiEventLogger,
|
||||
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
|
||||
private val dumpManager: DumpManager
|
||||
) : CoreStartable(context) {
|
||||
private var fingerprintLockedOut = false
|
||||
private var faceLockedOut = false
|
||||
private var encryptedOrLockdown = false
|
||||
private var unattendedUpdate = false
|
||||
private var timeout = false
|
||||
|
||||
override fun start() {
|
||||
dumpManager.registerDumpable(this)
|
||||
mKeyguardUpdateMonitorCallback.onStrongAuthStateChanged(
|
||||
KeyguardUpdateMonitor.getCurrentUser())
|
||||
keyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback)
|
||||
}
|
||||
|
||||
private val mKeyguardUpdateMonitorCallback: KeyguardUpdateMonitorCallback =
|
||||
object : KeyguardUpdateMonitorCallback() {
|
||||
override fun onLockedOutStateChanged(biometricSourceType: BiometricSourceType) {
|
||||
if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
|
||||
val lockedOut = keyguardUpdateMonitor.isFingerprintLockedOut
|
||||
if (lockedOut && !fingerprintLockedOut) {
|
||||
uiEventLogger.log(
|
||||
PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT)
|
||||
} else if (!lockedOut && fingerprintLockedOut) {
|
||||
uiEventLogger.log(
|
||||
PrimaryAuthRequiredEvent
|
||||
.PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET)
|
||||
}
|
||||
fingerprintLockedOut = lockedOut
|
||||
} else if (biometricSourceType == BiometricSourceType.FACE) {
|
||||
val lockedOut = keyguardUpdateMonitor.isFaceLockedOut
|
||||
if (lockedOut && !faceLockedOut) {
|
||||
uiEventLogger.log(
|
||||
PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT)
|
||||
} else if (!lockedOut && faceLockedOut) {
|
||||
uiEventLogger.log(
|
||||
PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET)
|
||||
}
|
||||
faceLockedOut = lockedOut
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStrongAuthStateChanged(userId: Int) {
|
||||
if (userId != KeyguardUpdateMonitor.getCurrentUser()) {
|
||||
return
|
||||
}
|
||||
val strongAuthFlags = keyguardUpdateMonitor.strongAuthTracker
|
||||
.getStrongAuthForUser(userId)
|
||||
|
||||
val newEncryptedOrLockdown = keyguardUpdateMonitor.isEncryptedOrLockdown(userId)
|
||||
if (newEncryptedOrLockdown && !encryptedOrLockdown) {
|
||||
uiEventLogger.log(
|
||||
PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN)
|
||||
}
|
||||
encryptedOrLockdown = newEncryptedOrLockdown
|
||||
|
||||
val newUnattendedUpdate = isUnattendedUpdate(strongAuthFlags)
|
||||
if (newUnattendedUpdate && !unattendedUpdate) {
|
||||
uiEventLogger.log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE)
|
||||
}
|
||||
unattendedUpdate = newUnattendedUpdate
|
||||
|
||||
val newTimeout = isStrongAuthTimeout(strongAuthFlags)
|
||||
if (newTimeout && !timeout) {
|
||||
uiEventLogger.log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_TIMEOUT)
|
||||
}
|
||||
timeout = newTimeout
|
||||
}
|
||||
}
|
||||
|
||||
private fun isUnattendedUpdate(
|
||||
@LockPatternUtils.StrongAuthTracker.StrongAuthFlags flags: Int
|
||||
) = containsFlag(flags, STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE)
|
||||
|
||||
private fun isStrongAuthTimeout(
|
||||
@LockPatternUtils.StrongAuthTracker.StrongAuthFlags flags: Int
|
||||
) = containsFlag(flags, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT) ||
|
||||
containsFlag(flags, STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT)
|
||||
|
||||
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
|
||||
pw.println(" mFingerprintLockedOut=$fingerprintLockedOut")
|
||||
pw.println(" mFaceLockedOut=$faceLockedOut")
|
||||
pw.println(" mIsEncryptedOrLockdown=$encryptedOrLockdown")
|
||||
pw.println(" mIsUnattendedUpdate=$unattendedUpdate")
|
||||
pw.println(" mIsTimeout=$timeout")
|
||||
}
|
||||
|
||||
/**
|
||||
* Events pertaining to whether primary authentication (pin/pattern/password input) is required
|
||||
* for device entry.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
enum class PrimaryAuthRequiredEvent(private val mId: Int) : UiEventLogger.UiEventEnum {
|
||||
@UiEvent(doc = "Fingerprint cannot be used to authenticate for device entry. This" +
|
||||
"can persist until the next primary auth or may timeout.")
|
||||
PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT(924),
|
||||
|
||||
@UiEvent(doc = "Fingerprint can be used to authenticate for device entry.")
|
||||
PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET(925),
|
||||
|
||||
@UiEvent(doc = "Face cannot be used to authenticate for device entry.")
|
||||
PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT(926),
|
||||
|
||||
@UiEvent(doc = "Face can be used to authenticate for device entry.")
|
||||
PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET(927),
|
||||
|
||||
@UiEvent(doc = "Device is encrypted (ie: after reboot) or device is locked down by DPM " +
|
||||
"or a manual user lockdown.")
|
||||
PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN(928),
|
||||
|
||||
@UiEvent(doc = "Primary authentication is required because it hasn't been used for a " +
|
||||
"time required by a device admin or because primary auth hasn't been used for a " +
|
||||
"time after a non-strong biometric (weak or convenience) is used to unlock the " +
|
||||
"device.")
|
||||
PRIMARY_AUTH_REQUIRED_TIMEOUT(929),
|
||||
|
||||
@UiEvent(doc = "Strong authentication is required to prepare for unattended upgrade.")
|
||||
PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE(931);
|
||||
|
||||
override fun getId(): Int {
|
||||
return mId
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun containsFlag(strongAuthFlags: Int, flagCheck: Int): Boolean {
|
||||
return strongAuthFlags and flagCheck != 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,7 +183,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|
||||
private static final int MSG_USER_STOPPED = 340;
|
||||
private static final int MSG_USER_REMOVED = 341;
|
||||
private static final int MSG_KEYGUARD_GOING_AWAY = 342;
|
||||
private static final int MSG_LOCK_SCREEN_MODE = 343;
|
||||
private static final int MSG_TIME_FORMAT_UPDATE = 344;
|
||||
private static final int MSG_REQUIRE_NFC_UNLOCK = 345;
|
||||
|
||||
@@ -221,7 +220,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|
||||
private static final int BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED = -1;
|
||||
public static final int BIOMETRIC_HELP_FACE_NOT_RECOGNIZED = -2;
|
||||
|
||||
private static final int DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT = 5000000;
|
||||
/**
|
||||
* If no cancel signal has been received after this amount of time, set the biometric running
|
||||
* state to stopped to allow Keyguard to retry authentication.
|
||||
@@ -231,7 +229,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|
||||
private static final ComponentName FALLBACK_HOME_COMPONENT = new ComponentName(
|
||||
"com.android.settings", "com.android.settings.FallbackHome");
|
||||
|
||||
|
||||
/**
|
||||
* If true, the system is in the half-boot-to-decryption-screen state.
|
||||
* Prudently disable lockscreen.
|
||||
@@ -1250,7 +1247,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|
||||
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
|
||||
}
|
||||
|
||||
private boolean isEncryptedOrLockdown(int userId) {
|
||||
/**
|
||||
* Returns true if primary authentication is required for the given user due to lockdown
|
||||
* or encryption after reboot.
|
||||
*/
|
||||
public boolean isEncryptedOrLockdown(int userId) {
|
||||
final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(userId);
|
||||
final boolean isLockDown =
|
||||
containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
|
||||
@@ -2514,6 +2515,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|
||||
return mFingerprintLockedOut || mFingerprintLockedOutPermanent;
|
||||
}
|
||||
|
||||
public boolean isFaceLockedOut() {
|
||||
return mFaceLockedOutPermanent;
|
||||
}
|
||||
|
||||
/**
|
||||
* If biometrics hardware is available, not disabled, and user has enrolled templates.
|
||||
* This does NOT check if the device is encrypted or in lockdown.
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.systemui.dagger;
|
||||
|
||||
import com.android.keyguard.KeyguardBiometricLockoutLogger;
|
||||
import com.android.systemui.CoreStartable;
|
||||
import com.android.systemui.LatencyTester;
|
||||
import com.android.systemui.ScreenDecorations;
|
||||
@@ -90,6 +91,13 @@ public abstract class SystemUIBinder {
|
||||
@ClassKey(KeyguardViewMediator.class)
|
||||
public abstract CoreStartable bindKeyguardViewMediator(KeyguardViewMediator sysui);
|
||||
|
||||
/** Inject into KeyguardBiometricLockoutLogger. */
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(KeyguardBiometricLockoutLogger.class)
|
||||
public abstract CoreStartable bindKeyguardBiometricLockoutLogger(
|
||||
KeyguardBiometricLockoutLogger sysui);
|
||||
|
||||
/** Inject into LatencyTests. */
|
||||
@Binds
|
||||
@IntoMap
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* 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
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.statusbar.phone;
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.keyguard
|
||||
|
||||
import android.hardware.biometrics.BiometricSourceType
|
||||
import org.mockito.Mockito.verify
|
||||
import android.testing.AndroidTestingRunner
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.internal.logging.UiEventLogger
|
||||
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
|
||||
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Captor
|
||||
import org.mockito.ArgumentMatchers.anyInt
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.verifyNoMoreInteractions
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@SmallTest
|
||||
class KeyguardBiometricLockoutLoggerTest : SysuiTestCase() {
|
||||
@Mock
|
||||
lateinit var uiEventLogger: UiEventLogger
|
||||
@Mock
|
||||
lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
|
||||
@Mock
|
||||
lateinit var dumpManager: DumpManager
|
||||
@Mock
|
||||
lateinit var strongAuthTracker: KeyguardUpdateMonitor.StrongAuthTracker
|
||||
|
||||
@Captor
|
||||
lateinit var updateMonitorCallbackCaptor: ArgumentCaptor<KeyguardUpdateMonitorCallback>
|
||||
lateinit var updateMonitorCallback: KeyguardUpdateMonitorCallback
|
||||
|
||||
lateinit var keyguardBiometricLockoutLogger: KeyguardBiometricLockoutLogger
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
whenever(keyguardUpdateMonitor.strongAuthTracker).thenReturn(strongAuthTracker)
|
||||
keyguardBiometricLockoutLogger = KeyguardBiometricLockoutLogger(
|
||||
mContext,
|
||||
uiEventLogger,
|
||||
keyguardUpdateMonitor,
|
||||
dumpManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_logsOnStart() {
|
||||
// GIVEN is encrypted / lockdown before start
|
||||
whenever(keyguardUpdateMonitor.isEncryptedOrLockdown(anyInt()))
|
||||
.thenReturn(true)
|
||||
|
||||
// WHEN start
|
||||
keyguardBiometricLockoutLogger.start()
|
||||
|
||||
// THEN encrypted / lockdown state is logged
|
||||
verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
|
||||
.PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_logTimeoutChange() {
|
||||
keyguardBiometricLockoutLogger.start()
|
||||
captureUpdateMonitorCallback()
|
||||
|
||||
// GIVEN primary auth required b/c timeout
|
||||
whenever(strongAuthTracker.getStrongAuthForUser(anyInt()))
|
||||
.thenReturn(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT)
|
||||
|
||||
// WHEN primary auth requirement changes
|
||||
updateMonitorCallback.onStrongAuthStateChanged(0)
|
||||
|
||||
// THEN primary auth required state is logged
|
||||
verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
|
||||
.PRIMARY_AUTH_REQUIRED_TIMEOUT)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_logUnattendedUpdate() {
|
||||
keyguardBiometricLockoutLogger.start()
|
||||
captureUpdateMonitorCallback()
|
||||
|
||||
// GIVEN primary auth required b/c unattended update
|
||||
whenever(strongAuthTracker.getStrongAuthForUser(anyInt()))
|
||||
.thenReturn(STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE)
|
||||
|
||||
// WHEN primary auth requirement changes
|
||||
updateMonitorCallback.onStrongAuthStateChanged(0)
|
||||
|
||||
// THEN primary auth required state is logged
|
||||
verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
|
||||
.PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_logMultipleChanges() {
|
||||
keyguardBiometricLockoutLogger.start()
|
||||
captureUpdateMonitorCallback()
|
||||
|
||||
// GIVEN primary auth required b/c timeout
|
||||
whenever(strongAuthTracker.getStrongAuthForUser(anyInt()))
|
||||
.thenReturn(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
|
||||
or STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE)
|
||||
|
||||
// WHEN primary auth requirement changes
|
||||
updateMonitorCallback.onStrongAuthStateChanged(0)
|
||||
|
||||
// THEN primary auth required state is logged with all the reasons
|
||||
verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
|
||||
.PRIMARY_AUTH_REQUIRED_TIMEOUT)
|
||||
verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
|
||||
.PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE)
|
||||
|
||||
// WHEN onStrongAuthStateChanged is called again
|
||||
updateMonitorCallback.onStrongAuthStateChanged(0)
|
||||
|
||||
// THEN no more events are sent since there haven't been any changes
|
||||
verifyNoMoreInteractions(uiEventLogger)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_logFaceLockout() {
|
||||
keyguardBiometricLockoutLogger.start()
|
||||
captureUpdateMonitorCallback()
|
||||
|
||||
// GIVEN primary auth required b/c face lock
|
||||
whenever(keyguardUpdateMonitor.isFaceLockedOut).thenReturn(true)
|
||||
|
||||
// WHEN lockout state changes
|
||||
updateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FACE)
|
||||
|
||||
// THEN primary auth required state is logged
|
||||
verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
|
||||
.PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT)
|
||||
|
||||
// WHEN face lockout is reset
|
||||
whenever(keyguardUpdateMonitor.isFaceLockedOut).thenReturn(false)
|
||||
updateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FACE)
|
||||
|
||||
// THEN primary auth required state is logged
|
||||
verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
|
||||
.PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_logFingerprintLockout() {
|
||||
keyguardBiometricLockoutLogger.start()
|
||||
captureUpdateMonitorCallback()
|
||||
|
||||
// GIVEN primary auth required b/c fingerprint lock
|
||||
whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(true)
|
||||
|
||||
// WHEN lockout state changes
|
||||
updateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FINGERPRINT)
|
||||
|
||||
// THEN primary auth required state is logged
|
||||
verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
|
||||
.PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT)
|
||||
|
||||
// WHEN fingerprint lockout is reset
|
||||
whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
|
||||
updateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FINGERPRINT)
|
||||
|
||||
// THEN primary auth required state is logged
|
||||
verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
|
||||
.PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET)
|
||||
}
|
||||
|
||||
fun captureUpdateMonitorCallback() {
|
||||
verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallbackCaptor.capture())
|
||||
updateMonitorCallback = updateMonitorCallbackCaptor.value
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user