Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 19b88bbd authored by Beverly's avatar Beverly Committed by Beverly Tai
Browse files

Run face detect if bypass face auth is locked out

Previously we were only checking if strongAuth
was causing face to not be allowed. This
CL adds the check for face auth being locked out
as well.

Also renames KeyguardUpdateMonitor#isFaceDetectionRunning
to isFaceAuthOrDetectionRunning and updates
a test name in DeviceEntryFaceAuthInteractorTest
to better reflect the test.

Test: atest DeviceEntryFaceAuthRepositoryTest
Test: enroll face auth with skip lockscree enabled;
lockout face auth by failing face matching multiple
times; observe on lift on the lockscreen, face detect
will run and show the primary bouncer if face detected
Fixes: 422758277
Flag: EXEMPT bugfix

Change-Id: I891c086654594592f4e136b388382ff083ddf0ab
parent e5d353a3
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -115,7 +115,7 @@ class FaceScanningProviderFactoryTest : SysuiTestCase() {
    @Test
    fun shouldShowFaceScanningAnimationIfKeyguardFaceDetectionIsShowing() {
        whenever(keyguardUpdateMonitor.isFaceEnabledAndEnrolled).thenReturn(true)
        whenever(keyguardUpdateMonitor.isFaceDetectionRunning).thenReturn(true)
        whenever(keyguardUpdateMonitor.isFaceAuthOrDetectionRunning).thenReturn(true)

        assertThat(underTest.shouldShowFaceScanningAnim()).isTrue()
    }
+23 −2
Original line number Diff line number Diff line
@@ -737,7 +737,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
            biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(false)
            assertThat(canFaceAuthRun()).isFalse()
            underTest.requestAuthenticate(
                FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
                FaceAuthUiEvent.FACE_AUTH_TRIGGERED_PICK_UP_GESTURE_TRIGGERED,
                fallbackToDetection = true,
            )
            faceAuthenticateIsNotCalled()
@@ -758,7 +758,28 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
            keyguardRepository.setKeyguardDismissible(true)
            assertThat(canFaceAuthRun()).isFalse()
            underTest.requestAuthenticate(
                FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
                FaceAuthUiEvent.FACE_AUTH_TRIGGERED_PICK_UP_GESTURE_TRIGGERED,
                fallbackToDetection = true,
            )
            faceAuthenticateIsNotCalled()

            faceDetectIsCalled()
        }

    @Test
    fun authenticateFallbacksToDetectionWhenFaceIsLockedOut() =
        testScope.runTest {
            whenever(faceManager.sensorPropertiesInternal)
                .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = true)))
            whenever(bypassController.bypassEnabled).thenReturn(true)
            underTest = createDeviceEntryFaceAuthRepositoryImpl()
            initCollectors()
            allPreconditionsToRunFaceAuthAreTrue()

            underTest.setLockedOut(true)
            assertThat(canFaceAuthRun()).isFalse()
            underTest.requestAuthenticate(
                FaceAuthUiEvent.FACE_AUTH_TRIGGERED_PICK_UP_GESTURE_TRIGGERED,
                fallbackToDetection = true,
            )
            faceAuthenticateIsNotCalled()
+1 −2
Original line number Diff line number Diff line
@@ -190,10 +190,9 @@ class DeviceEntryFaceAuthInteractorTest : SysuiTestCase() {
        }

    @Test
    fun whenFaceIsLockedOutAndBypass_DetectRuns() =
    fun whenFaceIsLockedOutAndBypass_runningAuthRequestNotNull() =
        kosmos.runTest {
            underTest.start()
            val authenticationStatus = collectLastValue(underTest.authenticationStatus)
            faceAuthRepository.setLockedOut(true)
            fakeDeviceEntryFaceAuthRepository.isBypassEnabled.value = true

+2 −2
Original line number Diff line number Diff line
@@ -1294,7 +1294,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, CoreSt
        for (int i = 0; i < mCallbacks.size(); i++) {
            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onBiometricRunningStateChanged(isFaceDetectionRunning(),
                cb.onBiometricRunningStateChanged(isFaceAuthOrDetectionRunning(),
                        FACE);
            }
        }
@@ -1308,7 +1308,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, CoreSt
     * @deprecated This is being migrated to use modern architecture.
     */
    @Deprecated
    public boolean isFaceDetectionRunning() {
    public boolean isFaceAuthOrDetectionRunning() {
        return getFaceAuthInteractor() != null
                && (getFaceAuthInteractor().isAuthRunning()
                || getFaceAuthInteractor().isDetectRunning());
+114 −98
Original line number Diff line number Diff line
@@ -87,7 +87,7 @@ class FaceScanningOverlay(

    override fun enableShowProtection(isCameraActive: Boolean) {
        val scanningAnimationRequiredWhenCameraActive =
                keyguardUpdateMonitor.isFaceDetectionRunning || authController.isShowing || mDebug
            keyguardUpdateMonitor.isFaceAuthOrDetectionRunning || authController.isShowing || mDebug
        val faceAuthSucceeded = keyguardUpdateMonitor.isFaceAuthenticated
        val showScanningAnimationNow = scanningAnimationRequiredWhenCameraActive && isCameraActive
        if (showScanningAnimationNow == showScanningAnim) {
@@ -95,11 +95,12 @@ class FaceScanningOverlay(
        }
        logger.cameraProtectionShownOrHidden(
            showScanningAnimationNow,
                keyguardUpdateMonitor.isFaceDetectionRunning,
            keyguardUpdateMonitor.isFaceAuthOrDetectionRunning,
            authController.isShowing,
            faceAuthSucceeded,
            isCameraActive,
                showScanningAnim)
            showScanningAnim,
        )
        showScanningAnim = showScanningAnimationNow
        updateProtectionBoundingPath()
        // Delay the relayout until the end of the animation when hiding,
@@ -115,7 +116,8 @@ class FaceScanningOverlay(
        }

        rimAnimator?.cancel()
        rimAnimator = faceScanningRimAnimator(
        rimAnimator =
            faceScanningRimAnimator(
                faceAuthSucceeded,
                if (Flags.faceScanningAnimationNpeFix()) {
                    cameraProtectionAnimator(faceAuthSucceeded)
@@ -128,23 +130,28 @@ class FaceScanningOverlay(

    private fun faceScanningRimAnimator(
        faceAuthSucceeded: Boolean,
        cameraProtectAnimator: ValueAnimator?
        cameraProtectAnimator: ValueAnimator?,
    ): AnimatorSet {
        return if (showScanningAnim) {
            createFaceScanningRimAnimator(cameraProtectAnimator)
        } else if (faceAuthSucceeded) {
        } else {
            if (faceAuthSucceeded) {
                    createFaceSuccessRimAnimator(cameraProtectAnimator)
                } else {
                    createFaceNotSuccessRimAnimator(cameraProtectAnimator)
        }.apply {
            addListener(object : AnimatorListenerAdapter() {
                }
                .apply {
                    addListener(
                        object : AnimatorListenerAdapter() {
                            override fun onAnimationEnd(animation: Animator) {
                                rimAnimator = null
                                if (!showScanningAnim) {
                                    requestLayout()
                                }
                            }
            })
                        }
                    )
                }
        }
    }

@@ -152,8 +159,9 @@ class FaceScanningOverlay(
        return ValueAnimator.ofFloat(
                cameraProtectionProgress,
                if (showScanningAnim) SHOW_CAMERA_PROTECTION_SCALE
            else HIDDEN_CAMERA_PROTECTION_SCALE
        ).apply {
                else HIDDEN_CAMERA_PROTECTION_SCALE,
            )
            .apply {
                startDelay =
                    if (showScanningAnim) 0
                    else if (faceAuthSucceeded) PULSE_SUCCESS_DISAPPEAR_DURATION
@@ -167,7 +175,8 @@ class FaceScanningOverlay(
                    else if (faceAuthSucceeded) Interpolators.STANDARD
                    else Interpolators.STANDARD_DECELERATE
                addUpdateListener(this@FaceScanningOverlay::updateCameraProtectionProgress)
            addListener(object : AnimatorListenerAdapter() {
                addListener(
                    object : AnimatorListenerAdapter() {
                        override fun onAnimationEnd(animation: Animator) {
                            if (!Flags.faceScanningAnimationNpeFix()) {
                                cameraProtectionAnimator = null
@@ -176,7 +185,8 @@ class FaceScanningOverlay(
                                hide()
                            }
                        }
            })
                    }
                )
            }
    }

@@ -202,21 +212,25 @@ class FaceScanningOverlay(
                rimRect.left.toInt(),
                rimRect.top.toInt(),
                rimRect.right.toInt(),
                rimRect.bottom.toInt())
                rimRect.bottom.toInt(),
            )
            val measuredWidth = resolveSizeAndState(mTotalBounds.width(), widthMeasureSpec, 0)
            val measuredHeight = resolveSizeAndState(mTotalBounds.height(), heightMeasureSpec, 0)
            logger.boundingRect(rimRect, "onMeasure: Face scanning animation")
            logger.boundingRect(mBoundingRect, "onMeasure: Display cutout view bounding rect")
            logger.boundingRect(mTotalBounds, "onMeasure: TotalBounds")
            logger.onMeasureDimensions(widthMeasureSpec,
            logger.onMeasureDimensions(
                widthMeasureSpec,
                heightMeasureSpec,
                measuredWidth,
                    measuredHeight)
                measuredHeight,
            )
            setMeasuredDimension(measuredWidth, measuredHeight)
        } else {
            setMeasuredDimension(
                resolveSizeAndState(mBoundingRect.width(), widthMeasureSpec, 0),
                resolveSizeAndState(mBoundingRect.height(), heightMeasureSpec, 0))
                resolveSizeAndState(mBoundingRect.height(), heightMeasureSpec, 0),
            )
        }
    }

@@ -225,10 +239,11 @@ class FaceScanningOverlay(
        scalePath(rimPath, rimProgress)
        rimPaint.style = Paint.Style.FILL
        val rimPaintAlpha = rimPaint.alpha
        rimPaint.color = ColorUtils.blendARGB(
        rimPaint.color =
            ColorUtils.blendARGB(
                faceScanningAnimColor,
                Color.WHITE,
            statusBarStateController.dozeAmount
                statusBarStateController.dozeAmount,
            )
        rimPaint.alpha = rimPaintAlpha
        canvas.drawPath(rimPath, rimPaint)
@@ -248,22 +263,22 @@ class FaceScanningOverlay(
            createRimDisappearAnimator(
                PULSE_RADIUS_SUCCESS,
                PULSE_SUCCESS_DISAPPEAR_DURATION,
                Interpolators.STANDARD_DECELERATE
                Interpolators.STANDARD_DECELERATE,
            ),
            createSuccessOpacityAnimator(),
        )
        return AnimatorSet().apply {
            playTogether(rimSuccessAnimator, cameraProtectAnimator)
        }
        return AnimatorSet().apply { playTogether(rimSuccessAnimator, cameraProtectAnimator) }
    }

    private fun createFaceNotSuccessRimAnimator(cameraProtectAnimator: ValueAnimator?): AnimatorSet {
    private fun createFaceNotSuccessRimAnimator(
        cameraProtectAnimator: ValueAnimator?
    ): AnimatorSet {
        return AnimatorSet().apply {
            playTogether(
                createRimDisappearAnimator(
                    SHOW_CAMERA_PROTECTION_SCALE,
                    PULSE_ERROR_DISAPPEAR_DURATION,
                    Interpolators.STANDARD
                    Interpolators.STANDARD,
                ),
                cameraProtectAnimator,
            )
@@ -273,18 +288,20 @@ class FaceScanningOverlay(
    private fun createRimDisappearAnimator(
        endValue: Float,
        animDuration: Long,
        timeInterpolator: TimeInterpolator
        timeInterpolator: TimeInterpolator,
    ): ValueAnimator {
        return ValueAnimator.ofFloat(rimProgress, endValue).apply {
            duration = animDuration
            interpolator = timeInterpolator
            addUpdateListener(this@FaceScanningOverlay::updateRimProgress)
            addListener(object : AnimatorListenerAdapter() {
            addListener(
                object : AnimatorListenerAdapter() {
                    override fun onAnimationEnd(animation: Animator) {
                        rimProgress = HIDDEN_RIM_SCALE
                        invalidate()
                    }
            })
                }
            )
        }
    }

@@ -293,29 +310,25 @@ class FaceScanningOverlay(
            duration = PULSE_SUCCESS_DISAPPEAR_DURATION
            interpolator = Interpolators.LINEAR
            addUpdateListener(this@FaceScanningOverlay::updateRimAlpha)
            addListener(object : AnimatorListenerAdapter() {
            addListener(
                object : AnimatorListenerAdapter() {
                    override fun onAnimationEnd(animation: Animator) {
                        rimPaint.alpha = 255
                        invalidate()
                    }
            })
                }
            )
        }
    }

    private fun createFaceScanningRimAnimator(cameraProtectAnimator: ValueAnimator?): AnimatorSet {
        return AnimatorSet().apply {
            playSequentially(
                cameraProtectAnimator,
                createRimAppearAnimator(),
            )
            playSequentially(cameraProtectAnimator, createRimAppearAnimator())
        }
    }

    private fun createRimAppearAnimator(): ValueAnimator {
        return ValueAnimator.ofFloat(
            SHOW_CAMERA_PROTECTION_SCALE,
            PULSE_RADIUS_OUT
        ).apply {
        return ValueAnimator.ofFloat(SHOW_CAMERA_PROTECTION_SCALE, PULSE_RADIUS_OUT).apply {
            duration = PULSE_APPEAR_DURATION
            interpolator = Interpolators.STANDARD_DECELERATE
            addUpdateListener(this@FaceScanningOverlay::updateRimProgress)
@@ -361,12 +374,15 @@ class FaceScanningOverlay(
        private const val CAMERA_PROTECTION_ERROR_DISAPPEAR_DURATION = 300L // without start delay

        private fun scalePath(path: Path, scalingFactor: Float) {
            val scaleMatrix = Matrix().apply {
            val scaleMatrix =
                Matrix().apply {
                    val boundingRectangle = RectF()
                    path.computeBounds(boundingRectangle, true)
                    setScale(
                    scalingFactor, scalingFactor,
                    boundingRectangle.centerX(), boundingRectangle.centerY()
                        scalingFactor,
                        scalingFactor,
                        boundingRectangle.centerX(),
                        boundingRectangle.centerY(),
                    )
                }
            path.transform(scaleMatrix)
Loading