diff options
| author | 2023-02-27 13:37:36 -0800 | |
|---|---|---|
| committer | 2023-03-10 17:07:16 +0000 | |
| commit | 553827df760944e299046515a77fb46f80c6f55c (patch) | |
| tree | 4e10a091428b88857f1e5daba6371102dbed8870 | |
| parent | f597a6c3ec6c3da35d197a8b6e9b35b049656591 (diff) | |
Use biometricUnlockController state change to show the unlock ripple.
Bug: 270989070
Test: atest AuthRippleControllerTest
Test: manually
1. Enroll side fps with touch to unlock anytime enabled
2. Press the power button to lock the device
3. Touch the power button quickly after that
4. Device should unlock and show launcher with unlock animation ripple
5. Repeat this 10 times as the time taken between onStartedGoingToSleep & onFinishedGoingToSleep is not always the same.
6. Also verify that the auth ripple is visible while using SFPS from AlternateBouncer (by tapping a notification on lockscreen) and by using SFPS from bouncer.
Change-Id: I1ad09b328b77d22646a5a74baaac9ec3b1da6a2e
| -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, 102 insertions, 139 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index 8f6b5c956853..e62f8aac6cc7 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -41,8 +41,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 @@ -60,7 +60,9 @@ 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, @@ -70,18 +72,18 @@ class AuthRippleController @Inject 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 @@ -90,6 +92,16 @@ class AuthRippleController @Inject 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) + } + } + } + override fun onInit() { mView.setAlphaInDuration(sysuiContext.resources.getInteger( R.integer.auth_ripple_alpha_in_duration).toLong()) @@ -106,6 +118,7 @@ class AuthRippleController @Inject constructor( keyguardStateController.addCallback(this) wakefulnessLifecycle.addObserver(this) commandRegistry.registerCommand("auth-ripple") { AuthRippleCommand() } + biometricUnlockController.addBiometricModeListener(biometricModeListener) } @VisibleForTesting @@ -117,6 +130,7 @@ class AuthRippleController @Inject constructor( keyguardStateController.removeCallback(this) wakefulnessLifecycle.removeObserver(this) commandRegistry.unregisterCommand("auth-ripple") + biometricUnlockController.removeBiometricModeListener(biometricModeListener) notificationShadeWindowController.setForcePluginOpen(false, this) } @@ -147,10 +161,7 @@ class AuthRippleController @Inject constructor( showUnlockedRipple() } } else if (biometricSourceType == BiometricSourceType.FACE) { - if (!bypassController.canBypass() && !authController.isUdfpsFingerDown) { - return - } - faceSensorLocation?.let { + faceSensorLocation?.let { mView.setSensorLocation(it) circleReveal = CircleReveal( it.x, @@ -271,7 +282,6 @@ class AuthRippleController @Inject 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 a245c01d74de..6e423593b379 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 +import android.hardware.biometrics.BiometricSourceType.FACE +import android.hardware.biometrics.BiometricSourceType.FINGERPRINT 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,7 +75,6 @@ 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 @@ -84,10 +83,15 @@ 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() @@ -96,24 +100,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, - bypassController, - biometricUnlockController, - udfpsControllerProvider, - statusBarStateController, - featureFlags, - KeyguardLogger(logcatLogBuffer(AuthRippleController.TAG)), - rippleView - ) + controller = + AuthRippleController( + mCentralSurfaces, + context, + authController, + configurationController, + keyguardUpdateMonitor, + keyguardStateController, + wakefulnessLifecycle, + commandRegistry, + notificationShadeWindowController, + biometricUnlockController, + udfpsControllerProvider, + statusBarStateController, + featureFlags, + KeyguardLogger(logcatLogBuffer(AuthRippleController.TAG)), + rippleView + ) controller.init() `when`(mCentralSurfaces.lightRevealScrim).thenReturn(lightRevealScrim) } @@ -130,16 +134,14 @@ class AuthRippleControllerTest : SysuiTestCase() { `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation) controller.onViewAttached() `when`(keyguardStateController.isShowing).thenReturn(true) - `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( - eq(BiometricSourceType.FINGERPRINT))).thenReturn(true) + `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(eq(FINGERPRINT))) + .thenReturn(true) + `when`(biometricUnlockController.isBiometricUnlock).thenReturn(true) + `when`(biometricUnlockController.biometricType).thenReturn(FINGERPRINT) - // 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 */) + // WHEN unlocked with fingerprint + verify(biometricUnlockController).addBiometricModeListener(biometricModeListener.capture()) + biometricModeListener.value.onModeChanged(/* mode= */ 0) // THEN update sensor location and show ripple verify(rippleView).setFingerprintSensorLocation(fpsLocation, 0f) @@ -152,17 +154,15 @@ class AuthRippleControllerTest : SysuiTestCase() { val fpsLocation = Point(5, 5) `when`(authController.udfpsLocation).thenReturn(fpsLocation) controller.onViewAttached() - `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( - eq(BiometricSourceType.FINGERPRINT))).thenReturn(true) + `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(eq(FINGERPRINT))) + .thenReturn(true) + `when`(biometricUnlockController.isBiometricUnlock).thenReturn(true) + `when`(biometricUnlockController.biometricType).thenReturn(FINGERPRINT) // WHEN keyguard is NOT showing & fingerprint authenticated `when`(keyguardStateController.isShowing).thenReturn(false) - val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) - verify(keyguardUpdateMonitor).registerCallback(captor.capture()) - captor.value.onBiometricAuthenticated( - 0 /* userId */, - BiometricSourceType.FINGERPRINT /* type */, - false /* isStrongBiometric */) + verify(biometricUnlockController).addBiometricModeListener(biometricModeListener.capture()) + biometricModeListener.value.onModeChanged(/* mode= */ 0) // THEN no ripple verify(rippleView, never()).startUnlockedRipple(any()) @@ -175,61 +175,14 @@ 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(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 */) + `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(eq(FINGERPRINT))) + .thenReturn(false) + verify(biometricUnlockController).addBiometricModeListener(biometricModeListener.capture()) + biometricModeListener.value.onModeChanged(/* mode= */ 0) // THEN no ripple verify(rippleView, never()).startUnlockedRipple(any()) @@ -239,14 +192,12 @@ class AuthRippleControllerTest : SysuiTestCase() { fun testNullFaceSensorLocationDoesNothing() { `when`(authController.faceSensorLocation).thenReturn(null) controller.onViewAttached() + `when`(biometricUnlockController.biometricType).thenReturn(FACE) + `when`(biometricUnlockController.isBiometricUnlock).thenReturn(true) - val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) - verify(keyguardUpdateMonitor).registerCallback(captor.capture()) + verify(biometricUnlockController).addBiometricModeListener(biometricModeListener.capture()) + biometricModeListener.value.onModeChanged(/* mode= */ 0) - captor.value.onBiometricAuthenticated( - 0 /* userId */, - BiometricSourceType.FACE /* type */, - false /* isStrongBiometric */) verify(rippleView, never()).startUnlockedRipple(any()) } @@ -254,25 +205,21 @@ class AuthRippleControllerTest : SysuiTestCase() { fun testNullFingerprintSensorLocationDoesNothing() { `when`(authController.fingerprintSensorLocation).thenReturn(null) controller.onViewAttached() + `when`(biometricUnlockController.biometricType).thenReturn(FINGERPRINT) + `when`(biometricUnlockController.isBiometricUnlock).thenReturn(true) - val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) - verify(keyguardUpdateMonitor).registerCallback(captor.capture()) + verify(biometricUnlockController).addBiometricModeListener(biometricModeListener.capture()) + biometricModeListener.value.onModeChanged(/* mode= */ 0) - 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()) @@ -286,17 +233,20 @@ class AuthRippleControllerTest : SysuiTestCase() { `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation) controller.onViewAttached() `when`(keyguardStateController.isShowing).thenReturn(true) - `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( - BiometricSourceType.FINGERPRINT)).thenReturn(true) + `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(FINGERPRINT)).thenReturn(true) `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true) - controller.showUnlockRipple(BiometricSourceType.FINGERPRINT) - assertTrue("reveal didn't start on keyguardFadingAway", - controller.startLightRevealScrimOnKeyguardFadingAway) + controller.showUnlockRipple(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 @@ -308,23 +258,26 @@ class AuthRippleControllerTest : SysuiTestCase() { `when`(keyguardStateController.isShowing).thenReturn(true) `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true) `when`(authController.isUdfpsFingerDown).thenReturn(true) - `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( - eq(BiometricSourceType.FACE))).thenReturn(true) + `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(eq(FACE))).thenReturn(true) - controller.showUnlockRipple(BiometricSourceType.FACE) - assertTrue("reveal didn't start on keyguardFadingAway", - controller.startLightRevealScrimOnKeyguardFadingAway) + controller.showUnlockRipple(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) |