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

Commit b628a2bc authored by Beverly Tai's avatar Beverly Tai Committed by Android (Google) Code Review
Browse files

Merge "RESTRICT AUTOMERGE Fix detect biometric logic." into tm-qpr-dev

parents 44a6d7fb 5cfa9271
Loading
Loading
Loading
Loading
+51 −12
Original line number Diff line number Diff line
@@ -814,6 +814,19 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
        }
    }

    private void onBiometricDetected(int userId, BiometricSourceType biometricSourceType,
            boolean isStrongBiometric) {
        Assert.isMainThread();
        Trace.beginSection("KeyGuardUpdateMonitor#onBiometricDetected");
        for (int i = 0; i < mCallbacks.size(); i++) {
            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onBiometricDetected(userId, biometricSourceType, isStrongBiometric);
            }
        }
        Trace.endSection();
    }

    @VisibleForTesting
    protected void onFingerprintAuthenticated(int userId, boolean isStrongBiometric) {
        Assert.isMainThread();
@@ -890,6 +903,20 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
        }
    }

    private void handleBiometricDetected(int authUserId, BiometricSourceType biometricSourceType,
            boolean isStrongBiometric) {
        Trace.beginSection("KeyGuardUpdateMonitor#handlerBiometricDetected");
        onBiometricDetected(authUserId, biometricSourceType, isStrongBiometric);
        if (biometricSourceType == FINGERPRINT) {
            mLogger.logFingerprintDetected(authUserId, isStrongBiometric);
        } else if (biometricSourceType == FACE) {
            mLogger.logFaceDetected(authUserId, isStrongBiometric);
            setFaceRunningState(BIOMETRIC_STATE_STOPPED);
        }

        Trace.endSection();
    }

    private void handleFingerprintAuthenticated(int authUserId, boolean isStrongBiometric) {
        Trace.beginSection("KeyGuardUpdateMonitor#handlerFingerPrintAuthenticated");
        if (mHandler.hasCallbacks(mFpCancelNotReceived)) {
@@ -941,9 +968,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab

    private void onFingerprintCancelNotReceived() {
        mLogger.e("Fp cancellation not received, transitioning to STOPPED");
        final boolean wasCancellingRestarting = mFingerprintRunningState
                == BIOMETRIC_STATE_CANCELLING_RESTARTING;
        mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
        if (wasCancellingRestarting) {
            KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
        } else {
            KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_STOP);
        }
    }

    private void handleFingerprintError(int msgId, String errString) {
        Assert.isMainThread();
@@ -1029,6 +1062,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
                    () -> updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE),
                    getBiometricLockoutDelay());
        } else {
            boolean temporaryLockoutReset = wasLockout && !mFingerprintLockedOut;
            if (temporaryLockoutReset) {
                mLogger.d("temporaryLockoutReset - stopListeningForFingerprint() to stop"
                        + " detectFingerprint");
                stopListeningForFingerprint();
            }
            updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
        }

@@ -1728,10 +1767,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
                }
            };

    private final FingerprintManager.FingerprintDetectionCallback mFingerprintDetectionCallback =
            (sensorId, userId, isStrongBiometric) -> {
                // Trigger the fingerprint detected path so the bouncer can be shown
                handleBiometricDetected(userId, FINGERPRINT, isStrongBiometric);
            };

    private final FaceManager.FaceDetectionCallback mFaceDetectionCallback
            = (sensorId, userId, isStrongBiometric) -> {
                // Trigger the face success path so the bouncer can be shown
                handleFaceAuthenticated(userId, isStrongBiometric);
                // Trigger the face detected path so the bouncer can be shown
                handleBiometricDetected(userId, FACE, isStrongBiometric);
            };

    @VisibleForTesting
@@ -2759,8 +2804,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab

        boolean shouldListen = shouldListenKeyguardState && shouldListenUserState
                && shouldListenBouncerState && shouldListenUdfpsState
                && shouldListenSideFpsState
                && !isFingerprintLockedOut();
                && shouldListenSideFpsState;
        logListenerModelData(
                new KeyguardFingerprintListenModel(
                    System.currentTimeMillis(),
@@ -2819,8 +2863,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
        final boolean supportsDetect = !mFaceSensorProperties.isEmpty()
                && mFaceSensorProperties.get(0).supportsFaceDetection
                && canBypass && !mPrimaryBouncerIsOrWillBeShowing
                && !isUserInLockdown(user)
                && !isFingerprintLockedOut();
                && !isUserInLockdown(user);
        final boolean faceAuthAllowedOrDetectionIsNeeded = faceAuthAllowed || supportsDetect;

        // If the face or fp has recently been authenticated do not attempt to authenticate again.
@@ -2916,11 +2959,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
                mLogger.v("startListeningForFingerprint - detect");
                mFpm.detectFingerprint(
                        mFingerprintCancelSignal,
                        (sensorId, user, isStrongBiometric) -> {
                            mLogger.d("fingerprint detected");
                            // Trigger the fingerprint success path so the bouncer can be shown
                            handleFingerprintAuthenticated(user, isStrongBiometric);
                        },
                        mFingerprintDetectionCallback,
                        userId);
            } else {
                mLogger.v("startListeningForFingerprint - authenticate");
+9 −1
Original line number Diff line number Diff line
@@ -214,13 +214,21 @@ public class KeyguardUpdateMonitorCallback {
    public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) { }

    /**
     * Called when a biometric is recognized.
     * Called when a biometric is authenticated.
     * @param userId the user id for which the biometric sample was authenticated
     * @param biometricSourceType
     */
    public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType,
            boolean isStrongBiometric) { }

    /**
     * Called when a biometric is detected but not successfully authenticated.
     * @param userId the user id for which the biometric sample was detected
     * @param biometricSourceType
     */
    public void onBiometricDetected(int userId, BiometricSourceType biometricSourceType,
            boolean isStrongBiometric) { }

    /**
     * Called when biometric authentication provides help string (e.g. "Try again")
     * @param msgId
+14 −0
Original line number Diff line number Diff line
@@ -167,6 +167,20 @@ class KeyguardUpdateMonitorLogger @Inject constructor(
        }, {"Fingerprint auth successful: userId: $int1, isStrongBiometric: $bool1"})
    }

    fun logFaceDetected(userId: Int, isStrongBiometric: Boolean) {
        logBuffer.log(TAG, DEBUG, {
            int1 = userId
            bool1 = isStrongBiometric
        }, {"Face detected: userId: $int1, isStrongBiometric: $bool1"})
    }

    fun logFingerprintDetected(userId: Int, isStrongBiometric: Boolean) {
        logBuffer.log(TAG, DEBUG, {
            int1 = userId
            bool1 = isStrongBiometric
        }, {"Fingerprint detected: userId: $int1, isStrongBiometric: $bool1"})
    }

    fun logFingerprintError(msgId: Int, originalErrMsg: String) {
        logBuffer.log(TAG, DEBUG, {
            str1 = originalErrMsg
+11 −0
Original line number Diff line number Diff line
@@ -382,6 +382,17 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
        Trace.endSection();
    }

    @Override
    public void onBiometricDetected(int userId, BiometricSourceType biometricSourceType,
            boolean isStrongBiometric) {
        Trace.beginSection("BiometricUnlockController#onBiometricDetected");
        if (mUpdateMonitor.isGoingToSleep()) {
            Trace.endSection();
            return;
        }
        startWakeAndUnlock(MODE_SHOW_BOUNCER);
    }

    @Override
    public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType,
            boolean isStrongBiometric) {
+91 −65
Original line number Diff line number Diff line
@@ -29,7 +29,6 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import static com.android.keyguard.FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED;
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_AVAILABLE;
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELLING;
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELLING_RESTARTING;
import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
import static com.android.keyguard.KeyguardUpdateMonitor.HAL_POWER_PRESS_TIMEOUT;
@@ -614,19 +613,47 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {

    @Test
    public void testOnlyDetectFingerprint_whenFingerprintUnlockNotAllowed() {
        // Clear invocations, since previous setup (e.g. registering BiometricManager callbacks)
        // will trigger updateBiometricListeningState();
        clearInvocations(mFingerprintManager);
        mKeyguardUpdateMonitor.resetBiometricListeningState();

        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
        mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
        mTestableLooper.processAllMessages();
        givenDetectFingerprintWithClearingFingerprintManagerInvocations();

        verifyFingerprintAuthenticateNeverCalled();
        verifyFingerprintDetectCall();
    }

    @Test
    public void whenDetectFingerprint_biometricDetectCallback() {
        ArgumentCaptor<FingerprintManager.FingerprintDetectionCallback> fpDetectCallbackCaptor =
                ArgumentCaptor.forClass(FingerprintManager.FingerprintDetectionCallback.class);

        givenDetectFingerprintWithClearingFingerprintManagerInvocations();
        verify(mFingerprintManager).detectFingerprint(
                any(), fpDetectCallbackCaptor.capture(), anyInt());
        fpDetectCallbackCaptor.getValue().onFingerprintDetected(0, 0, true);

        // THEN verify keyguardUpdateMonitorCallback receives a detect callback
        // and NO authenticate callbacks
        verify(mTestCallback).onBiometricDetected(
                eq(0), eq(BiometricSourceType.FINGERPRINT), eq(true));
        verify(mTestCallback, never()).onBiometricAuthenticated(
                anyInt(), any(), anyBoolean());
    }

    @Test
    public void whenDetectFace_biometricDetectCallback() {
        ArgumentCaptor<FaceManager.FaceDetectionCallback> faceDetectCallbackCaptor =
                ArgumentCaptor.forClass(FaceManager.FaceDetectionCallback.class);

        givenDetectFace();
        verify(mFaceManager).detectFace(any(), faceDetectCallbackCaptor.capture(), anyInt());
        faceDetectCallbackCaptor.getValue().onFaceDetected(0, 0, false);

        // THEN verify keyguardUpdateMonitorCallback receives a detect callback
        // and NO authenticate callbacks
        verify(mTestCallback).onBiometricDetected(
                eq(0), eq(BiometricSourceType.FACE), eq(false));
        verify(mTestCallback, never()).onBiometricAuthenticated(
                anyInt(), any(), anyBoolean());
    }

    @Test
    public void testUnlockingWithFaceAllowed_strongAuthTrackerUnlockingWithBiometricAllowed() {
        // GIVEN unlocking with biometric is allowed
@@ -655,7 +682,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
        strongAuthNotRequired();

        // WHEN fingerprint is locked out
        fingerprintErrorLockedOut();
        fingerprintErrorTemporaryLockedOut();

        // THEN unlocking with face is not allowed
        Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
@@ -678,7 +705,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
        strongAuthNotRequired();

        // WHEN fingerprint is locked out
        fingerprintErrorLockedOut();
        fingerprintErrorTemporaryLockedOut();

        // THEN unlocking with fingerprint is not allowed
        Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
@@ -702,7 +729,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
        Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser()));

        // WHEN fingerprint is locked out
        fingerprintErrorLockedOut();
        fingerprintErrorTemporaryLockedOut();

        // THEN user is NOT considered as "having trust" and bouncer cannot be skipped
        Assert.assertFalse(mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser()));
@@ -764,11 +791,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {

    @Test
    public void nofaceDetect_whenStrongAuthRequiredAndBypassUdfpsSupportedAndFpRunning() {
        // GIVEN mocked keyguardUpdateMonitorCallback
        KeyguardUpdateMonitorCallback keyguardUpdateMonitorCallback =
                mock(KeyguardUpdateMonitorCallback.class);
        mKeyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback);

        // GIVEN bypass is enabled, face detection is supported
        lockscreenBypassIsAllowed();
        supportsFaceDetection();
@@ -787,22 +809,13 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
        verifyFaceAuthenticateNeverCalled();

        // THEN biometric help message sent to callback
        verify(keyguardUpdateMonitorCallback).onBiometricHelp(
        verify(mTestCallback).onBiometricHelp(
                eq(BIOMETRIC_HELP_FACE_NOT_AVAILABLE), anyString(), eq(BiometricSourceType.FACE));
    }

    @Test
    public void faceDetect_whenStrongAuthRequiredAndBypass() {
        // GIVEN bypass is enabled, face detection is supported and strong auth is required
        lockscreenBypassIsAllowed();
        supportsFaceDetection();
        strongAuthRequiredEncrypted();
        keyguardIsVisible();
        // fingerprint is NOT running, UDFPS is NOT supported

        // WHEN the device wakes up
        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
        mTestableLooper.processAllMessages();
        givenDetectFace();

        // FACE detect is triggered, not authenticate
        verifyFaceDetectCall();
@@ -818,39 +831,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
        verifyFaceDetectNeverCalled();
    }

    @Test
    public void noFaceRun_whenFpLockout() {
        // GIVEN bypass is enabled, face detection is supported and strong auth is required
        lockscreenBypassIsAllowed();
        supportsFaceDetection();
        strongAuthRequiredEncrypted();
        keyguardIsVisible();
        // fingerprint is NOT running, UDFPS is NOT supported

        // GIVEN fp is locked out
        when(mFingerprintManager.getLockoutModeForUser(eq(FINGERPRINT_SENSOR_ID), anyInt()))
                .thenReturn(BIOMETRIC_LOCKOUT_TIMED);
        mKeyguardUpdateMonitor.handleUserSwitchComplete(0);
        assertThat(mKeyguardUpdateMonitor.isFingerprintLockedOut()).isEqualTo(true);

        // WHEN the device wakes up
        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
        mTestableLooper.processAllMessages();

        // FACE detect is NOT triggered and face authenticate is NOT triggered
        verifyFaceDetectNeverCalled();
        verifyFaceAuthenticateNeverCalled();

        // WHEN bouncer becomes visible
        setKeyguardBouncerVisibility(true);
        clearInvocations(mFaceManager);

        // THEN face scanning is not run
        mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
        verifyFaceAuthenticateNeverCalled();
        verifyFaceDetectNeverCalled();
    }

    @Test
    public void noFaceDetect_whenStrongAuthRequiredAndBypass_faceDetectionUnsupported() {
        // GIVEN bypass is enabled, face detection is NOT supported and strong auth is required
@@ -1159,8 +1139,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
        // Fingerprint should be cancelled on lockout if going to lockout state, else
        // restarted if it's not
        assertThat(mKeyguardUpdateMonitor.mFingerprintRunningState)
                .isEqualTo(fpLocked
                        ? BIOMETRIC_STATE_CANCELLING : BIOMETRIC_STATE_CANCELLING_RESTARTING);
                .isEqualTo(BIOMETRIC_STATE_CANCELLING_RESTARTING);
    }

    @Test
@@ -1619,7 +1598,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();

        // Fingerprint is locked out.
        fingerprintErrorLockedOut();
        fingerprintErrorTemporaryLockedOut();

        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
    }
@@ -2428,6 +2407,29 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
                eq(false));
    }

    @Test
    public void detectFingerprint_onTemporaryLockoutReset_authenticateFingerprint() {
        ArgumentCaptor<FingerprintManager.LockoutResetCallback> fpLockoutResetCallbackCaptor =
                ArgumentCaptor.forClass(FingerprintManager.LockoutResetCallback.class);
        verify(mFingerprintManager).addLockoutResetCallback(fpLockoutResetCallbackCaptor.capture());

        // GIVEN device is locked out
        fingerprintErrorTemporaryLockedOut();

        // GIVEN FP detection is running
        givenDetectFingerprintWithClearingFingerprintManagerInvocations();
        verifyFingerprintDetectCall();
        verifyFingerprintAuthenticateNeverCalled();

        // WHEN temporary lockout resets
        fpLockoutResetCallbackCaptor.getValue().onLockoutReset(0);
        mTestableLooper.processAllMessages();

        // THEN fingerprint detect state should cancel & then restart (for authenticate call)
        assertThat(mKeyguardUpdateMonitor.mFingerprintRunningState)
                .isEqualTo(BIOMETRIC_STATE_CANCELLING_RESTARTING);
    }

    private void verifyFingerprintAuthenticateNeverCalled() {
        verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
                anyInt(), anyInt());
@@ -2532,7 +2534,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
        mKeyguardUpdateMonitor.setSwitchingUser(true);
    }

    private void fingerprintErrorLockedOut() {
    private void fingerprintErrorTemporaryLockedOut() {
        mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
                .onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT, "Fingerprint locked out");
    }
@@ -2672,6 +2674,30 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
        receiver.setPendingResult(pendingResult);
    }

    private void givenDetectFingerprintWithClearingFingerprintManagerInvocations() {
        // Clear invocations, since previous setup (e.g. registering BiometricManager callbacks)
        // will trigger updateBiometricListeningState();
        clearInvocations(mFingerprintManager);
        mKeyguardUpdateMonitor.resetBiometricListeningState();

        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
        mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
        mTestableLooper.processAllMessages();
    }

    private void givenDetectFace() {
        // GIVEN bypass is enabled, face detection is supported and strong auth is required
        lockscreenBypassIsAllowed();
        supportsFaceDetection();
        strongAuthRequiredEncrypted();
        keyguardIsVisible();
        // fingerprint is NOT running, UDFPS is NOT supported

        // WHEN the device wakes up
        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
        mTestableLooper.processAllMessages();
    }

    private Intent putPhoneInfo(Intent intent, Bundle data, Boolean simInited) {
        int subscription = simInited
                ? 1/* mock subid=1 */ : SubscriptionManager.PLACEHOLDER_SUBSCRIPTION_ID_BASE;
Loading