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

Commit af57f130 authored by Chandru's avatar Chandru Committed by Chandru S
Browse files

Always trigger face auth even when it is locked out.

1. This is the only way to show a message on keyguard that face unlock is not available in response to all face unlock triggers.
2. This was the behavior of face auth until recently (b/244402839#comment11)

Other options:
 - Add a new callback method to a KeyguardUpateMonitorCallback to indicate that face auth trigger occurred.
    - Adding a new method to a long list of methods in KeyguardUpateMonitorCallback is not a good idea
    - This would be a very specific API that no one else might even need.

Indication message shown on keyguard:
 1. first time face lockout.
    - Shows "Too many attempts. Face Unlock disabled." followed by "Use fingerprint instead" or "Swipe up to open"

 2. while the face is already locked out
    - when the device wakes up
      - "Face Unlock unavailable." followed by "Use fingerprint instead" or "Swipe up to open"

 3. Coex when both face and fingerprint are locked out
    - when the device wakes up
      - Existing behavior is retained, nothing is shown.

No difference in behavior between bypass vs non-bypass

Bug: 247688462
Test: atest KeyguardUpdateMonitorTest

Change-Id: I3c7e15818eedfef9b5af9dd0b0fc29b7fda231e1
parent 7fcef9e6
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -403,6 +403,8 @@
    <string name="keyguard_face_failed">Can\u2019t recognize face</string>
    <!-- Message shown to suggest using fingerprint sensor to authenticate after another biometric failed. [CHAR LIMIT=25] -->
    <string name="keyguard_suggest_fingerprint">Use fingerprint instead</string>
    <!-- Message shown to inform the user that face unlock is not available. [CHAR LIMIT=25] -->
    <string name="keyguard_face_unlock_unavailable">Face unlock unavailable.</string>

    <!-- Content description of the bluetooth icon when connected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
    <string name="accessibility_bluetooth_connected">Bluetooth connected.</string>
+7 −5
Original line number Diff line number Diff line
@@ -798,7 +798,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
        mFingerprintCancelSignal = null;
        updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
                FACE_AUTH_UPDATED_FP_AUTHENTICATED);
        mLogger.d("onFingerprintAuthenticated");
        mLogger.logFingerprintSuccess(userId, isStrongBiometric);
        for (int i = 0; i < mCallbacks.size(); i++) {
            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
@@ -2716,7 +2716,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
                        || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);

        // TODO: always disallow when fp is already locked out?
        final boolean fpLockedout = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
        final boolean fpLockedOut = mFingerprintLockedOut || mFingerprintLockedOutPermanent;

        final boolean canBypass = mKeyguardBypassController != null
                && mKeyguardBypassController.canBypass();
@@ -2745,7 +2745,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
        final boolean faceDisabledForUser = isFaceDisabled(user);
        final boolean biometricEnabledForUser = mBiometricEnabledForUser.get(user);
        final boolean shouldListenForFaceAssistant = shouldListenForFaceAssistant();
        final boolean fpOrFaceIsLockedOut = isFaceLockedOut() || fpLockedout;

        // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
        // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
@@ -2763,7 +2762,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
                && (!mSecureCameraLaunched || mOccludingAppRequestingFace)
                && !faceAuthenticated
                && !mGoingToSleep
                && !fpOrFaceIsLockedOut;
                // We only care about fp locked out state and not face because we still trigger
                // face auth even when face is locked out to show the user a message that face
                // unlock was supposed to run but didn't
                && !fpLockedOut;

        // Aggregate relevant fields for debug logging.
        maybeLogListenerModelData(
@@ -2778,7 +2780,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
                    faceAuthenticated,
                    faceDisabledForUser,
                    isFaceLockedOut(),
                    fpLockedout,
                    fpLockedOut,
                    mGoingToSleep,
                    awakeKeyguard,
                    mKeyguardGoingAway,
+7 −0
Original line number Diff line number Diff line
@@ -153,6 +153,13 @@ class KeyguardUpdateMonitorLogger @Inject constructor(
                { "fingerprintRunningState: $int1" })
    }

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

    fun logInvalidSubId(subId: Int) {
        logBuffer.log(TAG, INFO,
                { int1 = subId },
+45 −6
Original line number Diff line number Diff line
@@ -79,6 +79,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.R;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FaceHelpMessageDeferral;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
@@ -137,6 +138,7 @@ public class KeyguardIndicationController {
    private final KeyguardStateController mKeyguardStateController;
    protected final StatusBarStateController mStatusBarStateController;
    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
    private final AuthController mAuthController;
    private ViewGroup mIndicationArea;
    private KeyguardIndicationTextView mTopIndicationView;
    private KeyguardIndicationTextView mLockScreenIndicationView;
@@ -201,6 +203,7 @@ public class KeyguardIndicationController {
            }
        }
    };
    private boolean mFaceLockedOutThisAuthSession;

    /**
     * Creates a new KeyguardIndicationController and registers callbacks.
@@ -221,6 +224,7 @@ public class KeyguardIndicationController {
            @Main DelayableExecutor executor,
            @Background DelayableExecutor bgExecutor,
            FalsingManager falsingManager,
            AuthController authController,
            LockPatternUtils lockPatternUtils,
            ScreenLifecycle screenLifecycle,
            KeyguardBypassController keyguardBypassController,
@@ -240,6 +244,7 @@ public class KeyguardIndicationController {
        mExecutor = executor;
        mBackgroundExecutor = bgExecutor;
        mLockPatternUtils = lockPatternUtils;
        mAuthController = authController;
        mFalsingManager = falsingManager;
        mKeyguardBypassController = keyguardBypassController;
        mAccessibilityManager = accessibilityManager;
@@ -755,7 +760,7 @@ public class KeyguardIndicationController {
     * logic.
     */
    private void showBiometricMessage(CharSequence biometricMessage,
            CharSequence biometricMessageFollowUp) {
            @Nullable CharSequence biometricMessageFollowUp) {
        if (TextUtils.equals(biometricMessage, mBiometricMessage)) {
            return;
        }
@@ -1105,6 +1110,13 @@ public class KeyguardIndicationController {
            }
        }

        @Override
        public void onLockedOutStateChanged(BiometricSourceType biometricSourceType) {
            if (biometricSourceType == FACE && !mKeyguardUpdateMonitor.isFaceLockedOut()) {
                mFaceLockedOutThisAuthSession = false;
            }
        }

        @Override
        public void onBiometricError(int msgId, String errString,
                BiometricSourceType biometricSourceType) {
@@ -1124,8 +1136,10 @@ public class KeyguardIndicationController {
            }
            if (msgId == FaceManager.FACE_ERROR_TIMEOUT) {
                handleFaceAuthTimeoutError(deferredFaceMessage);
            } else if (isLockoutError(msgId)) {
                handleFaceLockoutError(errString);
            } else {
                handleGenericBiometricError(errString);
                showErrorMessageNowOrLater(errString, null);
            }
        }

@@ -1134,7 +1148,7 @@ public class KeyguardIndicationController {
                debugLog("suppressingFingerprintError msgId=" + msgId
                        + " errString= " + errString);
            } else {
                handleGenericBiometricError(errString);
                showErrorMessageNowOrLater(errString, null);
            }
        }

@@ -1145,7 +1159,7 @@ public class KeyguardIndicationController {
            // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
            // check of whether non-strong biometric is allowed
            return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)
                    && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
                    && !isLockoutError(msgId))
                    || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED
                    || msgId == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED
                    || msgId == FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED);
@@ -1234,6 +1248,31 @@ public class KeyguardIndicationController {
        }
    }

    private void handleFaceLockoutError(String errString) {
        int followupMsgId = canUnlockWithFingerprint() ? R.string.keyguard_suggest_fingerprint
                : R.string.keyguard_unlock;
        String followupMessage = mContext.getString(followupMsgId);
        // Lockout error can happen multiple times in a session because we trigger face auth
        // even when it is locked out so that the user is aware that face unlock would have
        // triggered but didn't because it is locked out.

        // On first lockout we show the error message from FaceManager, which tells the user they
        // had too many unsuccessful attempts.
        if (!mFaceLockedOutThisAuthSession) {
            mFaceLockedOutThisAuthSession = true;
            showErrorMessageNowOrLater(errString, followupMessage);
        } else if (!mAuthController.isUdfpsFingerDown()) {
            // On subsequent lockouts, we show a more generic locked out message.
            showBiometricMessage(mContext.getString(R.string.keyguard_face_unlock_unavailable),
                    followupMessage);
        }
    }

    private static boolean isLockoutError(int msgId) {
        return msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT
                || msgId == FaceManager.FACE_ERROR_LOCKOUT;
    }

    private void handleFaceAuthTimeoutError(@Nullable CharSequence deferredFaceMessage) {
        debugLog("showDeferredFaceMessage msgId=" + deferredFaceMessage);
        if (canUnlockWithFingerprint()) {
@@ -1274,11 +1313,11 @@ public class KeyguardIndicationController {
        }
    }

    private void handleGenericBiometricError(String errString) {
    private void showErrorMessageNowOrLater(String errString, @Nullable String followUpMsg) {
        if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
            mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState);
        } else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
            showBiometricMessage(errString);
            showBiometricMessage(errString, followUpMsg);
        } else {
            mBiometricErrorMessageToShowOnScreenOn = errString;
        }
+4 −2
Original line number Diff line number Diff line
@@ -1621,7 +1621,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
    }

    @Test
    public void testShouldListenForFace_whenFaceIsLockedOut_returnsFalse()
    public void testShouldListenForFace_whenFaceIsLockedOut_returnsTrue()
            throws RemoteException {
        // Preconditions for face auth to run
        keyguardNotGoingAway();
@@ -1638,7 +1638,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
        faceAuthLockedOut();
        mTestableLooper.processAllMessages();

        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
        // This is needed beccause we want to show face locked out error message whenever face auth
        // is supposed to run.
        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
    }

    @Test
Loading