Loading packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +22 −12 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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, Loading @@ -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, ) : 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 Loading @@ -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()) Loading @@ -106,6 +118,7 @@ class AuthRippleController @Inject constructor( keyguardStateController.addCallback(this) wakefulnessLifecycle.addObserver(this) commandRegistry.registerCommand("auth-ripple") { AuthRippleCommand() } biometricUnlockController.addBiometricModeListener(biometricModeListener) } @VisibleForTesting Loading @@ -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) } Loading Loading @@ -147,9 +161,6 @@ class AuthRippleController @Inject constructor( showUnlockedRipple() } } else if (biometricSourceType == BiometricSourceType.FACE) { if (!bypassController.canBypass() && !authController.isUdfpsFingerDown) { return } faceSensorLocation?.let { mView.setSensorLocation(it) circleReveal = CircleReveal( Loading Loading @@ -271,7 +282,6 @@ class AuthRippleController @Inject constructor( if (biometricSourceType == BiometricSourceType.FINGERPRINT) { mView.fadeDwellRipple() } showUnlockRipple(biometricSourceType) } override fun onBiometricAuthFailed(biometricSourceType: BiometricSourceType) { Loading packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt +80 −127 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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) Loading @@ -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 Loading @@ -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() Loading @@ -96,7 +100,8 @@ class AuthRippleControllerTest : SysuiTestCase() { `when`(authController.udfpsProps).thenReturn(listOf(fpSensorProp)) `when`(udfpsControllerProvider.get()).thenReturn(udfpsController) controller = AuthRippleController( controller = AuthRippleController( mCentralSurfaces, context, authController, Loading @@ -106,7 +111,6 @@ class AuthRippleControllerTest : SysuiTestCase() { wakefulnessLifecycle, commandRegistry, notificationShadeWindowController, bypassController, biometricUnlockController, udfpsControllerProvider, statusBarStateController, Loading @@ -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) Loading @@ -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()) Loading @@ -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()) Loading @@ -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()) } Loading @@ -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()) Loading @@ -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 Loading @@ -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) Loading Loading
packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +22 −12 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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, Loading @@ -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, ) : 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 Loading @@ -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()) Loading @@ -106,6 +118,7 @@ class AuthRippleController @Inject constructor( keyguardStateController.addCallback(this) wakefulnessLifecycle.addObserver(this) commandRegistry.registerCommand("auth-ripple") { AuthRippleCommand() } biometricUnlockController.addBiometricModeListener(biometricModeListener) } @VisibleForTesting Loading @@ -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) } Loading Loading @@ -147,9 +161,6 @@ class AuthRippleController @Inject constructor( showUnlockedRipple() } } else if (biometricSourceType == BiometricSourceType.FACE) { if (!bypassController.canBypass() && !authController.isUdfpsFingerDown) { return } faceSensorLocation?.let { mView.setSensorLocation(it) circleReveal = CircleReveal( Loading Loading @@ -271,7 +282,6 @@ class AuthRippleController @Inject constructor( if (biometricSourceType == BiometricSourceType.FINGERPRINT) { mView.fadeDwellRipple() } showUnlockRipple(biometricSourceType) } override fun onBiometricAuthFailed(biometricSourceType: BiometricSourceType) { Loading
packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt +80 −127 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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) Loading @@ -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 Loading @@ -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() Loading @@ -96,7 +100,8 @@ class AuthRippleControllerTest : SysuiTestCase() { `when`(authController.udfpsProps).thenReturn(listOf(fpSensorProp)) `when`(udfpsControllerProvider.get()).thenReturn(udfpsController) controller = AuthRippleController( controller = AuthRippleController( mCentralSurfaces, context, authController, Loading @@ -106,7 +111,6 @@ class AuthRippleControllerTest : SysuiTestCase() { wakefulnessLifecycle, commandRegistry, notificationShadeWindowController, bypassController, biometricUnlockController, udfpsControllerProvider, statusBarStateController, Loading @@ -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) Loading @@ -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()) Loading @@ -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()) Loading @@ -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()) } Loading @@ -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()) Loading @@ -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 Loading @@ -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) Loading