diff options
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt | 34 | ||||
| -rw-r--r-- | packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt | 207 |
2 files changed, 139 insertions, 102 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index 3f22f18f6fa7..868ffcf54fe1 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -42,8 +42,8 @@ import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.statusbar.phone.BiometricUnlockController -import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_DISMISS_BOUNCER import com.android.systemui.statusbar.phone.CentralSurfaces +import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.KeyguardStateController @@ -61,9 +61,7 @@ import javax.inject.Provider * The ripple uses the accent color of the current theme. */ @CentralSurfacesScope -class AuthRippleController -@Inject -constructor( +class AuthRippleController @Inject constructor( private val centralSurfaces: CentralSurfaces, private val sysuiContext: Context, private val authController: AuthController, @@ -73,18 +71,18 @@ constructor( private val wakefulnessLifecycle: WakefulnessLifecycle, private val commandRegistry: CommandRegistry, private val notificationShadeWindowController: NotificationShadeWindowController, + private val bypassController: KeyguardBypassController, private val biometricUnlockController: BiometricUnlockController, private val udfpsControllerProvider: Provider<UdfpsController>, private val statusBarStateController: StatusBarStateController, private val featureFlags: FeatureFlags, private val logger: KeyguardLogger, - rippleView: AuthRippleView? -) : - ViewController<AuthRippleView>(rippleView), - KeyguardStateController.Callback, + rippleView: AuthRippleView? +) : ViewController<AuthRippleView>(rippleView), KeyguardStateController.Callback, WakefulnessLifecycle.Observer { - @VisibleForTesting internal var startLightRevealScrimOnKeyguardFadingAway = false + @VisibleForTesting + internal var startLightRevealScrimOnKeyguardFadingAway = false var lightRevealScrimAnimator: ValueAnimator? = null var fingerprintSensorLocation: Point? = null private var faceSensorLocation: Point? = null @@ -93,16 +91,6 @@ constructor( private var udfpsController: UdfpsController? = null private var udfpsRadius: Float = -1f - private val biometricModeListener = object : BiometricUnlockController.BiometricModeListener { - override fun onModeChanged(mode: Int) { - // isBiometricUnlock does not cover the scenario when biometrics unlocks - // the device while the bouncer is showing. - if (biometricUnlockController.isBiometricUnlock || mode == MODE_DISMISS_BOUNCER) { - showUnlockRipple(biometricUnlockController.biometricType) - } - } - } - @VisibleForTesting public override fun onViewAttached() { authController.addCallback(authControllerCallback) @@ -114,7 +102,6 @@ constructor( keyguardStateController.addCallback(this) wakefulnessLifecycle.addObserver(this) commandRegistry.registerCommand("auth-ripple") { AuthRippleCommand() } - biometricUnlockController.addBiometricModeListener(biometricModeListener) } @VisibleForTesting @@ -126,7 +113,6 @@ constructor( keyguardStateController.removeCallback(this) wakefulnessLifecycle.removeObserver(this) commandRegistry.unregisterCommand("auth-ripple") - biometricUnlockController.removeBiometricModeListener(biometricModeListener) notificationShadeWindowController.setForcePluginOpen(false, this) } @@ -157,7 +143,10 @@ constructor( showUnlockedRipple() } } else if (biometricSourceType == BiometricSourceType.FACE) { - faceSensorLocation?.let { + if (!bypassController.canBypass() && !authController.isUdfpsFingerDown) { + return + } + faceSensorLocation?.let { mView.setSensorLocation(it) circleReveal = CircleReveal( it.x, @@ -278,6 +267,7 @@ constructor( if (biometricSourceType == BiometricSourceType.FINGERPRINT) { mView.fadeDwellRipple() } + showUnlockRipple(biometricSourceType) } override fun onBiometricAuthFailed(biometricSourceType: BiometricSourceType) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt index 6e423593b379..a245c01d74de 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt @@ -17,14 +17,14 @@ package com.android.systemui.biometrics import android.graphics.Point -import android.hardware.biometrics.BiometricSourceType.FACE -import android.hardware.biometrics.BiometricSourceType.FINGERPRINT +import android.hardware.biometrics.BiometricSourceType import android.hardware.fingerprint.FingerprintSensorPropertiesInternal import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession import com.android.keyguard.KeyguardUpdateMonitor +import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.keyguard.logging.KeyguardLogger import com.android.systemui.SysuiTestCase import com.android.systemui.dump.logcatLogBuffer @@ -36,11 +36,11 @@ import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.phone.CentralSurfaces +import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.leak.RotationUtils import com.android.systemui.util.mockito.any -import javax.inject.Provider import org.junit.After import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -50,15 +50,15 @@ import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers import org.mockito.ArgumentMatchers.eq -import org.mockito.Captor import org.mockito.Mock +import org.mockito.Mockito.`when` import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import org.mockito.MockitoSession import org.mockito.quality.Strictness +import javax.inject.Provider @SmallTest @RunWith(AndroidTestingRunner::class) @@ -75,6 +75,7 @@ class AuthRippleControllerTest : SysuiTestCase() { @Mock private lateinit var keyguardStateController: KeyguardStateController @Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController + @Mock private lateinit var bypassController: KeyguardBypassController @Mock private lateinit var biometricUnlockController: BiometricUnlockController @Mock private lateinit var udfpsControllerProvider: Provider<UdfpsController> @Mock private lateinit var udfpsController: UdfpsController @@ -83,15 +84,10 @@ class AuthRippleControllerTest : SysuiTestCase() { @Mock private lateinit var lightRevealScrim: LightRevealScrim @Mock private lateinit var fpSensorProp: FingerprintSensorPropertiesInternal - @Captor - private lateinit var biometricModeListener: - ArgumentCaptor<BiometricUnlockController.BiometricModeListener> - @Before fun setUp() { MockitoAnnotations.initMocks(this) - staticMockSession = - mockitoSession() + staticMockSession = mockitoSession() .mockStatic(RotationUtils::class.java) .strictness(Strictness.LENIENT) .startMocking() @@ -100,24 +96,24 @@ class AuthRippleControllerTest : SysuiTestCase() { `when`(authController.udfpsProps).thenReturn(listOf(fpSensorProp)) `when`(udfpsControllerProvider.get()).thenReturn(udfpsController) - controller = - AuthRippleController( - mCentralSurfaces, - context, - authController, - configurationController, - keyguardUpdateMonitor, - keyguardStateController, - wakefulnessLifecycle, - commandRegistry, - notificationShadeWindowController, - biometricUnlockController, - udfpsControllerProvider, - statusBarStateController, - featureFlags, - KeyguardLogger(logcatLogBuffer(AuthRippleController.TAG)), - rippleView - ) + controller = AuthRippleController( + mCentralSurfaces, + context, + authController, + configurationController, + keyguardUpdateMonitor, + keyguardStateController, + wakefulnessLifecycle, + commandRegistry, + notificationShadeWindowController, + bypassController, + biometricUnlockController, + udfpsControllerProvider, + statusBarStateController, + featureFlags, + KeyguardLogger(logcatLogBuffer(AuthRippleController.TAG)), + rippleView + ) controller.init() `when`(mCentralSurfaces.lightRevealScrim).thenReturn(lightRevealScrim) } @@ -134,14 +130,16 @@ class AuthRippleControllerTest : SysuiTestCase() { `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation) controller.onViewAttached() `when`(keyguardStateController.isShowing).thenReturn(true) - `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(eq(FINGERPRINT))) - .thenReturn(true) - `when`(biometricUnlockController.isBiometricUnlock).thenReturn(true) - `when`(biometricUnlockController.biometricType).thenReturn(FINGERPRINT) + `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( + eq(BiometricSourceType.FINGERPRINT))).thenReturn(true) - // WHEN unlocked with fingerprint - verify(biometricUnlockController).addBiometricModeListener(biometricModeListener.capture()) - biometricModeListener.value.onModeChanged(/* mode= */ 0) + // WHEN fingerprint authenticated + val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) + verify(keyguardUpdateMonitor).registerCallback(captor.capture()) + captor.value.onBiometricAuthenticated( + 0 /* userId */, + BiometricSourceType.FINGERPRINT /* type */, + false /* isStrongBiometric */) // THEN update sensor location and show ripple verify(rippleView).setFingerprintSensorLocation(fpsLocation, 0f) @@ -154,15 +152,17 @@ class AuthRippleControllerTest : SysuiTestCase() { val fpsLocation = Point(5, 5) `when`(authController.udfpsLocation).thenReturn(fpsLocation) controller.onViewAttached() - `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(eq(FINGERPRINT))) - .thenReturn(true) - `when`(biometricUnlockController.isBiometricUnlock).thenReturn(true) - `when`(biometricUnlockController.biometricType).thenReturn(FINGERPRINT) + `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( + eq(BiometricSourceType.FINGERPRINT))).thenReturn(true) // WHEN keyguard is NOT showing & fingerprint authenticated `when`(keyguardStateController.isShowing).thenReturn(false) - verify(biometricUnlockController).addBiometricModeListener(biometricModeListener.capture()) - biometricModeListener.value.onModeChanged(/* mode= */ 0) + val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) + verify(keyguardUpdateMonitor).registerCallback(captor.capture()) + captor.value.onBiometricAuthenticated( + 0 /* userId */, + BiometricSourceType.FINGERPRINT /* type */, + false /* isStrongBiometric */) // THEN no ripple verify(rippleView, never()).startUnlockedRipple(any()) @@ -175,14 +175,61 @@ class AuthRippleControllerTest : SysuiTestCase() { `when`(authController.udfpsLocation).thenReturn(fpsLocation) controller.onViewAttached() `when`(keyguardStateController.isShowing).thenReturn(true) - `when`(biometricUnlockController.isBiometricUnlock).thenReturn(true) - `when`(biometricUnlockController.biometricType).thenReturn(FINGERPRINT) // WHEN unlocking with fingerprint is NOT allowed & fingerprint authenticated - `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(eq(FINGERPRINT))) - .thenReturn(false) - verify(biometricUnlockController).addBiometricModeListener(biometricModeListener.capture()) - biometricModeListener.value.onModeChanged(/* mode= */ 0) + `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( + eq(BiometricSourceType.FINGERPRINT))).thenReturn(false) + val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) + verify(keyguardUpdateMonitor).registerCallback(captor.capture()) + captor.value.onBiometricAuthenticated( + 0 /* userId */, + BiometricSourceType.FINGERPRINT /* type */, + false /* isStrongBiometric */) + + // THEN no ripple + verify(rippleView, never()).startUnlockedRipple(any()) + } + + @Test + fun testFaceTriggerBypassEnabled_Ripple() { + // GIVEN face auth sensor exists, keyguard is showing & unlocking with face is allowed + val faceLocation = Point(5, 5) + `when`(authController.faceSensorLocation).thenReturn(faceLocation) + controller.onViewAttached() + + `when`(keyguardStateController.isShowing).thenReturn(true) + `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( + BiometricSourceType.FACE)).thenReturn(true) + + // WHEN bypass is enabled & face authenticated + `when`(bypassController.canBypass()).thenReturn(true) + val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) + verify(keyguardUpdateMonitor).registerCallback(captor.capture()) + captor.value.onBiometricAuthenticated( + 0 /* userId */, + BiometricSourceType.FACE /* type */, + false /* isStrongBiometric */) + + // THEN show ripple + verify(rippleView).setSensorLocation(faceLocation) + verify(rippleView).startUnlockedRipple(any()) + } + + @Test + fun testFaceTriggerNonBypass_NoRipple() { + // GIVEN face auth sensor exists + val faceLocation = Point(5, 5) + `when`(authController.faceSensorLocation).thenReturn(faceLocation) + controller.onViewAttached() + + // WHEN bypass isn't enabled & face authenticated + `when`(bypassController.canBypass()).thenReturn(false) + val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) + verify(keyguardUpdateMonitor).registerCallback(captor.capture()) + captor.value.onBiometricAuthenticated( + 0 /* userId */, + BiometricSourceType.FACE /* type */, + false /* isStrongBiometric */) // THEN no ripple verify(rippleView, never()).startUnlockedRipple(any()) @@ -192,12 +239,14 @@ class AuthRippleControllerTest : SysuiTestCase() { fun testNullFaceSensorLocationDoesNothing() { `when`(authController.faceSensorLocation).thenReturn(null) controller.onViewAttached() - `when`(biometricUnlockController.biometricType).thenReturn(FACE) - `when`(biometricUnlockController.isBiometricUnlock).thenReturn(true) - verify(biometricUnlockController).addBiometricModeListener(biometricModeListener.capture()) - biometricModeListener.value.onModeChanged(/* mode= */ 0) + val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) + verify(keyguardUpdateMonitor).registerCallback(captor.capture()) + captor.value.onBiometricAuthenticated( + 0 /* userId */, + BiometricSourceType.FACE /* type */, + false /* isStrongBiometric */) verify(rippleView, never()).startUnlockedRipple(any()) } @@ -205,21 +254,25 @@ class AuthRippleControllerTest : SysuiTestCase() { fun testNullFingerprintSensorLocationDoesNothing() { `when`(authController.fingerprintSensorLocation).thenReturn(null) controller.onViewAttached() - `when`(biometricUnlockController.biometricType).thenReturn(FINGERPRINT) - `when`(biometricUnlockController.isBiometricUnlock).thenReturn(true) - verify(biometricUnlockController).addBiometricModeListener(biometricModeListener.capture()) - biometricModeListener.value.onModeChanged(/* mode= */ 0) + val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) + verify(keyguardUpdateMonitor).registerCallback(captor.capture()) + captor.value.onBiometricAuthenticated( + 0 /* userId */, + BiometricSourceType.FINGERPRINT /* type */, + false /* isStrongBiometric */) verify(rippleView, never()).startUnlockedRipple(any()) } @Test fun registersAndDeregisters() { controller.onViewAttached() - val captor = ArgumentCaptor.forClass(KeyguardStateController.Callback::class.java) + val captor = ArgumentCaptor + .forClass(KeyguardStateController.Callback::class.java) verify(keyguardStateController).addCallback(captor.capture()) - val captor2 = ArgumentCaptor.forClass(WakefulnessLifecycle.Observer::class.java) + val captor2 = ArgumentCaptor + .forClass(WakefulnessLifecycle.Observer::class.java) verify(wakefulnessLifecycle).addObserver(captor2.capture()) controller.onViewDetached() verify(keyguardStateController).removeCallback(any()) @@ -233,20 +286,17 @@ class AuthRippleControllerTest : SysuiTestCase() { `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation) controller.onViewAttached() `when`(keyguardStateController.isShowing).thenReturn(true) - `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(FINGERPRINT)).thenReturn(true) + `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( + BiometricSourceType.FINGERPRINT)).thenReturn(true) `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true) - controller.showUnlockRipple(FINGERPRINT) - assertTrue( - "reveal didn't start on keyguardFadingAway", - controller.startLightRevealScrimOnKeyguardFadingAway - ) + controller.showUnlockRipple(BiometricSourceType.FINGERPRINT) + assertTrue("reveal didn't start on keyguardFadingAway", + controller.startLightRevealScrimOnKeyguardFadingAway) `when`(keyguardStateController.isKeyguardFadingAway).thenReturn(true) controller.onKeyguardFadingAwayChanged() - assertFalse( - "reveal triggers multiple times", - controller.startLightRevealScrimOnKeyguardFadingAway - ) + assertFalse("reveal triggers multiple times", + controller.startLightRevealScrimOnKeyguardFadingAway) } @Test @@ -258,26 +308,23 @@ class AuthRippleControllerTest : SysuiTestCase() { `when`(keyguardStateController.isShowing).thenReturn(true) `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true) `when`(authController.isUdfpsFingerDown).thenReturn(true) - `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(eq(FACE))).thenReturn(true) + `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( + eq(BiometricSourceType.FACE))).thenReturn(true) - controller.showUnlockRipple(FACE) - assertTrue( - "reveal didn't start on keyguardFadingAway", - controller.startLightRevealScrimOnKeyguardFadingAway - ) + controller.showUnlockRipple(BiometricSourceType.FACE) + assertTrue("reveal didn't start on keyguardFadingAway", + controller.startLightRevealScrimOnKeyguardFadingAway) `when`(keyguardStateController.isKeyguardFadingAway).thenReturn(true) controller.onKeyguardFadingAwayChanged() - assertFalse( - "reveal triggers multiple times", - controller.startLightRevealScrimOnKeyguardFadingAway - ) + assertFalse("reveal triggers multiple times", + controller.startLightRevealScrimOnKeyguardFadingAway) } @Test fun testUpdateRippleColor() { controller.onViewAttached() - val captor = - ArgumentCaptor.forClass(ConfigurationController.ConfigurationListener::class.java) + val captor = ArgumentCaptor + .forClass(ConfigurationController.ConfigurationListener::class.java) verify(configurationController).addCallback(captor.capture()) reset(rippleView) |