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

Commit 8e989f9f authored by Beverly's avatar Beverly Committed by Beverly Tai
Browse files

Don't show face messages if fp messages are showing

Test: enroll face and fingeprint; wear a mask; intentionally
fail fingerprint, observe that the FP message always shows
instead of the face covering detected message (from face auth)
Test: atest KeyguardIndicationContrllerTest KeyguardMessageAreaControllerTest
Fixes: 302877651
Flag: None

Change-Id: I0df461f529b8853a3faf74017a704962eb56e1ef
parent be0aeae3
Loading
Loading
Loading
Loading
+45 −0
Original line number Diff line number Diff line
@@ -18,11 +18,16 @@ package com.android.keyguard;

import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.hardware.biometrics.BiometricSourceType;
import android.os.SystemClock;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.util.Pair;
import android.view.View;

import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -39,6 +44,16 @@ import javax.inject.Inject;
 */
public class KeyguardMessageAreaController<T extends KeyguardMessageArea>
        extends ViewController<T> {
    /**
     * Pair representing:
     *   first - BiometricSource the currently displayed message is associated with.
     *   second - Timestamp the biometric message came in uptimeMillis.
     * This Pair can be null if the message is not associated with a biometric.
     */
    @Nullable
    private Pair<BiometricSourceType, Long> mMessageBiometricSource = null;
    private static final Long SKIP_SHOWING_FACE_MESSAGE_AFTER_FP_MESSAGE_MS = 3500L;

    /**
     * Delay before speaking an accessibility announcement. Used to prevent
     * lift-to-type from interrupting itself.
@@ -149,12 +164,42 @@ public class KeyguardMessageAreaController<T extends KeyguardMessageArea>
     * Sets a message to the underlying text view.
     */
    public void setMessage(CharSequence s, boolean animate) {
        setMessage(s, animate, null);
    }

    /**
     * Sets a message to the underlying text view.
     */
    public void setMessage(CharSequence s, BiometricSourceType biometricSourceType) {
        setMessage(s, true, biometricSourceType);
    }

    private void setMessage(
            CharSequence s,
            boolean animate,
            BiometricSourceType biometricSourceType) {
        final long uptimeMillis = SystemClock.uptimeMillis();
        if (skipShowingFaceMessage(biometricSourceType, uptimeMillis)) {
            Log.d("KeyguardMessageAreaController", "Skip showing face message \"" + s + "\"");
            return;
        }
        mMessageBiometricSource =  new Pair<>(biometricSourceType, uptimeMillis);
        if (mView.isDisabled()) {
            return;
        }
        mView.setMessage(s, animate);
    }

    private boolean skipShowingFaceMessage(
            BiometricSourceType biometricSourceType, Long currentUptimeMillis
    ) {
        return mMessageBiometricSource != null
                && biometricSourceType == BiometricSourceType.FACE
                && mMessageBiometricSource.first == BiometricSourceType.FINGERPRINT
                && (currentUptimeMillis - mMessageBiometricSource.second)
                    < SKIP_SHOWING_FACE_MESSAGE_AFTER_FP_MESSAGE_MS;
    }

    public void setMessage(int resId) {
        String message = resId != 0 ? mView.getResources().getString(resId) : null;
        setMessage(message);
+21 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.keyguard.logging

import android.hardware.biometrics.BiometricSourceType
import com.android.systemui.biometrics.AuthRippleController
import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController
import com.android.systemui.log.LogBuffer
@@ -117,6 +118,26 @@ constructor(
        )
    }

    fun logDropNonFingerprintMessage(
        message: CharSequence,
        followUpMessage: CharSequence?,
        biometricSourceType: BiometricSourceType?,
    ) {
        buffer.log(
            KeyguardIndicationController.TAG,
            LogLevel.DEBUG,
            {
                str1 = message.toString()
                str2 = followUpMessage?.toString()
                str3 = biometricSourceType?.name
            },
            {
                "droppingNonFingerprintMessage message=$str1 " +
                    "followUpMessage:$str2 biometricSourceType:$str3"
            }
        )
    }

    fun logUpdateBatteryIndication(
        powerIndication: String,
        pluggedIn: Boolean,
+110 −41
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@ import android.os.UserManager;
import android.provider.DeviceConfig;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
@@ -187,6 +188,7 @@ public class KeyguardIndicationController {
    private CharSequence mTransientIndication;
    private CharSequence mBiometricMessage;
    private CharSequence mBiometricMessageFollowUp;
    private BiometricSourceType mBiometricMessageSource;
    protected ColorStateList mInitialTextColorState;
    private boolean mVisible;
    private boolean mOrganizationOwnedDevice;
@@ -206,7 +208,7 @@ public class KeyguardIndicationController {
    private int mBatteryLevel;
    private boolean mBatteryPresent = true;
    private long mChargingTimeRemaining;
    private String mBiometricErrorMessageToShowOnScreenOn;
    private Pair<String, BiometricSourceType> mBiometricErrorMessageToShowOnScreenOn;
    private final Set<Integer> mCoExFaceAcquisitionMsgIdsToShow;
    private final FaceHelpMessageDeferral mFaceAcquiredMessageDeferral;
    private boolean mInited;
@@ -225,15 +227,18 @@ public class KeyguardIndicationController {
                mIsActiveDreamLockscreenHosted = isLockscreenHosted;
                updateDeviceEntryIndication(false);
            };
    private final ScreenLifecycle.Observer mScreenObserver =
            new ScreenLifecycle.Observer() {
    private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
        @Override
        public void onScreenTurnedOn() {
            mHandler.removeMessages(MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON);
            if (mBiometricErrorMessageToShowOnScreenOn != null) {
                String followUpMessage = mFaceLockedOutThisAuthSession
                        ? faceLockedOutFollowupMessage() : null;
                showBiometricMessage(mBiometricErrorMessageToShowOnScreenOn, followUpMessage);
                showBiometricMessage(
                        mBiometricErrorMessageToShowOnScreenOn.first,
                        followUpMessage,
                        mBiometricErrorMessageToShowOnScreenOn.second
                );
                // We want to keep this message around in case the screen was off
                hideBiometricMessageDelayed(DEFAULT_HIDE_DELAY_MS);
                mBiometricErrorMessageToShowOnScreenOn = null;
@@ -879,8 +884,35 @@ public class KeyguardIndicationController {
        updateTransient();
    }

    private void showBiometricMessage(CharSequence biometricMessage) {
        showBiometricMessage(biometricMessage, null);
    private void showSuccessBiometricMessage(
            CharSequence biometricMessage,
            @Nullable CharSequence biometricMessageFollowUp,
            BiometricSourceType biometricSourceType
    ) {
        showBiometricMessage(biometricMessage, biometricMessageFollowUp, biometricSourceType, true);
    }

    private void showSuccessBiometricMessage(CharSequence biometricMessage,
            BiometricSourceType biometricSourceType) {
        showSuccessBiometricMessage(biometricMessage, null, biometricSourceType);
    }

    private void showBiometricMessage(CharSequence biometricMessage,
            BiometricSourceType biometricSourceType) {
        showBiometricMessage(biometricMessage, null, biometricSourceType, false);
    }

    private void showBiometricMessage(
            CharSequence biometricMessage,
            @Nullable CharSequence biometricMessageFollowUp,
            BiometricSourceType biometricSourceType
    ) {
        showBiometricMessage(
                biometricMessage,
                biometricMessageFollowUp,
                biometricSourceType,
                false
        );
    }

    /**
@@ -889,15 +921,33 @@ public class KeyguardIndicationController {
     * by {@link KeyguardIndicationRotateTextViewController}, see class for rotating message
     * logic.
     */
    private void showBiometricMessage(CharSequence biometricMessage,
            @Nullable CharSequence biometricMessageFollowUp) {
    private void showBiometricMessage(
            CharSequence biometricMessage,
            @Nullable CharSequence biometricMessageFollowUp,
            BiometricSourceType biometricSourceType,
            boolean isSuccessMessage
    ) {
        if (TextUtils.equals(biometricMessage, mBiometricMessage)
                && biometricSourceType == mBiometricMessageSource
                && TextUtils.equals(biometricMessageFollowUp, mBiometricMessageFollowUp)) {
            return;
        }

        if (!isSuccessMessage
                && mBiometricMessageSource == FINGERPRINT
                && biometricSourceType != FINGERPRINT) {
            // drop all non-fingerprint biometric messages if there's a fingerprint message showing
            mKeyguardLogger.logDropNonFingerprintMessage(
                    biometricMessage,
                    biometricMessageFollowUp,
                    biometricSourceType
            );
            return;
        }

        mBiometricMessage = biometricMessage;
        mBiometricMessageFollowUp = biometricMessageFollowUp;
        mBiometricMessageSource = biometricSourceType;

        mHandler.removeMessages(MSG_SHOW_ACTION_TO_UNLOCK);
        hideBiometricMessageDelayed(
@@ -914,6 +964,7 @@ public class KeyguardIndicationController {
        if (mBiometricMessage != null || mBiometricMessageFollowUp != null) {
            mBiometricMessage = null;
            mBiometricMessageFollowUp = null;
            mBiometricMessageSource = null;
            mHideBiometricMessageHandler.cancel();
            updateBiometricMessage();
        }
@@ -1085,7 +1136,8 @@ public class KeyguardIndicationController {
                } else {
                    message = mContext.getString(R.string.keyguard_retry);
                }
                mStatusBarKeyguardViewManager.setKeyguardMessage(message, mInitialTextColorState);
                mStatusBarKeyguardViewManager.setKeyguardMessage(message, mInitialTextColorState,
                        null);
            }
        } else {
            final boolean canSkipBouncer = mKeyguardUpdateMonitor.getUserCanSkipBouncer(
@@ -1097,34 +1149,40 @@ public class KeyguardIndicationController {
                        || mAccessibilityManager.isTouchExplorationEnabled();
                if (udfpsSupported && faceAuthenticated) { // co-ex
                    if (a11yEnabled) {
                        showBiometricMessage(
                        showSuccessBiometricMessage(
                                mContext.getString(R.string.keyguard_face_successful_unlock),
                                mContext.getString(R.string.keyguard_unlock)
                                mContext.getString(R.string.keyguard_unlock),
                                FACE
                        );
                    } else {
                        showBiometricMessage(
                        showSuccessBiometricMessage(
                                mContext.getString(R.string.keyguard_face_successful_unlock),
                                mContext.getString(R.string.keyguard_unlock_press)
                                mContext.getString(R.string.keyguard_unlock_press),
                                FACE
                        );
                    }
                } else if (faceAuthenticated) { // face-only
                    showBiometricMessage(
                    showSuccessBiometricMessage(
                            mContext.getString(R.string.keyguard_face_successful_unlock),
                            mContext.getString(R.string.keyguard_unlock)
                            mContext.getString(R.string.keyguard_unlock),
                            FACE
                    );
                } else if (udfpsSupported) { // udfps-only
                    if (a11yEnabled) {
                        showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
                        showSuccessBiometricMessage(
                                mContext.getString(R.string.keyguard_unlock),
                                null
                        );
                    } else {
                        showBiometricMessage(mContext.getString(
                                R.string.keyguard_unlock_press));
                        showSuccessBiometricMessage(mContext.getString(
                                R.string.keyguard_unlock_press), null);
                    }
                } else { // no security or unlocked by a trust agent
                    showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
                    showSuccessBiometricMessage(mContext.getString(R.string.keyguard_unlock), null);
                }
            } else {
                // suggest swiping up for the primary authentication bouncer
                showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
                showBiometricMessage(mContext.getString(R.string.keyguard_unlock), null);
            }
        }
    }
@@ -1245,49 +1303,55 @@ public class KeyguardIndicationController {
                    mBouncerMessageInteractor.setFaceAcquisitionMessage(helpString);
                }
                mStatusBarKeyguardViewManager.setKeyguardMessage(helpString,
                        mInitialTextColorState);
                        mInitialTextColorState, biometricSourceType);
            } else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
                if (isCoExFaceAcquisitionMessage && msgId == FACE_ACQUIRED_TOO_DARK) {
                    showBiometricMessage(
                            helpString,
                            mContext.getString(R.string.keyguard_suggest_fingerprint)
                            mContext.getString(R.string.keyguard_suggest_fingerprint),
                            biometricSourceType
                    );
                } else if (faceAuthFailed && isUnlockWithFingerprintPossible) {
                    showBiometricMessage(
                            mContext.getString(R.string.keyguard_face_failed),
                            mContext.getString(R.string.keyguard_suggest_fingerprint)
                            mContext.getString(R.string.keyguard_suggest_fingerprint),
                            biometricSourceType
                    );
                } else if (fpAuthFailed
                        && mKeyguardUpdateMonitor.isCurrentUserUnlockedWithFace()) {
                    // face had already previously unlocked the device, so instead of showing a
                    // fingerprint error, tell them they have already unlocked with face auth
                    // and how to enter their device
                    showBiometricMessage(
                    showSuccessBiometricMessage(
                            mContext.getString(R.string.keyguard_face_successful_unlock),
                            mContext.getString(R.string.keyguard_unlock)
                            mContext.getString(R.string.keyguard_unlock),
                            null
                    );
                } else if (fpAuthFailed
                        && mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser())) {
                    showBiometricMessage(
                    showSuccessBiometricMessage(
                            getTrustGrantedIndication(),
                            mContext.getString(R.string.keyguard_unlock)
                            mContext.getString(R.string.keyguard_unlock),
                            null
                    );
                } else if (faceAuthUnavailable) {
                    showBiometricMessage(
                            helpString,
                            isUnlockWithFingerprintPossible
                                    ? mContext.getString(R.string.keyguard_suggest_fingerprint)
                                    : mContext.getString(R.string.keyguard_unlock)
                                    : mContext.getString(R.string.keyguard_unlock),
                            biometricSourceType
                    );
                } else {
                    showBiometricMessage(helpString);
                    showBiometricMessage(helpString, biometricSourceType);
                }
            } else if (faceAuthFailed) {
                // show action to unlock
                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_ACTION_TO_UNLOCK),
                        TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
            } else {
                mBiometricErrorMessageToShowOnScreenOn = helpString;
                mBiometricErrorMessageToShowOnScreenOn =
                        new Pair<>(helpString, biometricSourceType);
                mHandler.sendMessageDelayed(
                        mHandler.obtainMessage(MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON),
                        1000);
@@ -1333,7 +1397,7 @@ public class KeyguardIndicationController {
            } else if (mIndicationHelper.isFaceLockoutErrorMsg(msgId)) {
                handleFaceLockoutError(errString);
            } else {
                showErrorMessageNowOrLater(errString, null);
                showErrorMessageNowOrLater(errString, null, FACE);
            }
        }

@@ -1343,7 +1407,7 @@ public class KeyguardIndicationController {
                        msgId,
                        errString);
            } else {
                showErrorMessageNowOrLater(errString, null);
                showErrorMessageNowOrLater(errString, null, FINGERPRINT);
            }
        }

@@ -1371,7 +1435,7 @@ public class KeyguardIndicationController {

        @Override
        public void onTrustAgentErrorMessage(CharSequence message) {
            showBiometricMessage(message);
            showBiometricMessage(message, null);
        }

        @Override
@@ -1459,12 +1523,13 @@ public class KeyguardIndicationController {
        // had too many unsuccessful attempts.
        if (!mFaceLockedOutThisAuthSession) {
            mFaceLockedOutThisAuthSession = true;
            showErrorMessageNowOrLater(errString, followupMessage);
            showErrorMessageNowOrLater(errString, followupMessage, FACE);
        } else if (!mAuthController.isUdfpsFingerDown()) {
            // On subsequent lockouts, we show a more generic locked out message.
            showErrorMessageNowOrLater(
                    mContext.getString(R.string.keyguard_face_unlock_unavailable),
                    followupMessage);
                    followupMessage,
                    FACE);
        }
    }

@@ -1484,7 +1549,8 @@ public class KeyguardIndicationController {
                    && !mStatusBarKeyguardViewManager.isBouncerShowing()) {
                showBiometricMessage(
                        deferredFaceMessage,
                        mContext.getString(R.string.keyguard_suggest_fingerprint)
                        mContext.getString(R.string.keyguard_suggest_fingerprint),
                        FACE
                );
            } else {
                // otherwise, don't show any message
@@ -1496,7 +1562,8 @@ public class KeyguardIndicationController {
            // user to manually retry.
            showBiometricMessage(
                    deferredFaceMessage,
                    mContext.getString(R.string.keyguard_unlock)
                    mContext.getString(R.string.keyguard_unlock),
                    FACE
            );
        } else {
            // Face-only
@@ -1510,13 +1577,15 @@ public class KeyguardIndicationController {
                getCurrentUser()) && mKeyguardUpdateMonitor.isUnlockingWithFingerprintAllowed();
    }

    private void showErrorMessageNowOrLater(String errString, @Nullable String followUpMsg) {
    private void showErrorMessageNowOrLater(String errString, @Nullable String followUpMsg,
            BiometricSourceType biometricSourceType) {
        if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
            mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState);
            mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState,
                    biometricSourceType);
        } else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
            showBiometricMessage(errString, followUpMsg);
            showBiometricMessage(errString, followUpMsg, biometricSourceType);
        } else {
            mBiometricErrorMessageToShowOnScreenOn = errString;
            mBiometricErrorMessageToShowOnScreenOn = new Pair<>(errString, biometricSourceType);
        }
    }

+4 −3
Original line number Diff line number Diff line
@@ -781,7 +781,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
                    }

                    updateAlternateBouncerShowing(mAlternateBouncerInteractor.show());
                    setKeyguardMessage(message, null);
                    setKeyguardMessage(message, null, null);
                    return;
                }

@@ -1444,11 +1444,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
    }

    /** Display security message to relevant KeyguardMessageArea. */
    public void setKeyguardMessage(String message, ColorStateList colorState) {
    public void setKeyguardMessage(String message, ColorStateList colorState,
            BiometricSourceType biometricSourceType) {
        if (mAlternateBouncerInteractor.isVisibleState()) {
            if (mKeyguardMessageAreaController != null) {
                DeviceEntryUdfpsRefactor.assertInLegacyMode();
                mKeyguardMessageAreaController.setMessage(message);
                mKeyguardMessageAreaController.setMessage(message, biometricSourceType);
            }
        } else {
            mPrimaryBouncerInteractor.showMessage(message, colorState);
+50 −0
Original line number Diff line number Diff line
@@ -19,11 +19,14 @@ package com.android.keyguard;
import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.hardware.biometrics.BiometricSourceType;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -119,4 +122,51 @@ public class KeyguardMessageAreaControllerTest extends SysuiTestCase {
        when(mKeyguardMessageArea.getText()).thenReturn(msg);
        assertThat(mMessageAreaController.getMessage()).isEqualTo(msg);
    }

    @Test
    public void testFingerprintMessageUpdate() {
        String msg = "fpMessage";
        mMessageAreaController.setMessage(
                msg, BiometricSourceType.FINGERPRINT
        );
        verify(mKeyguardMessageArea).setMessage(msg, /* animate= */ true);

        String msg2 = "fpMessage2";
        mMessageAreaController.setMessage(
                msg2, BiometricSourceType.FINGERPRINT
        );
        verify(mKeyguardMessageArea).setMessage(msg2, /* animate= */ true);
    }

    @Test
    public void testFaceMessageDroppedWhileFingerprintMessageShowing() {
        String fpMsg = "fpMessage";
        mMessageAreaController.setMessage(
                fpMsg, BiometricSourceType.FINGERPRINT
        );
        verify(mKeyguardMessageArea).setMessage(eq(fpMsg), /* animate= */ anyBoolean());

        String faceMessage = "faceMessage";
        mMessageAreaController.setMessage(
                faceMessage, BiometricSourceType.FACE
        );
        verify(mKeyguardMessageArea, never())
                .setMessage(eq(faceMessage), /* animate= */ anyBoolean());
    }

    @Test
    public void testGenericMessageShowsAfterFingerprintMessageShowing() {
        String fpMsg = "fpMessage";
        mMessageAreaController.setMessage(
                fpMsg, BiometricSourceType.FINGERPRINT
        );
        verify(mKeyguardMessageArea).setMessage(eq(fpMsg), /* animate= */ anyBoolean());

        String genericMessage = "genericMessage";
        mMessageAreaController.setMessage(
                genericMessage, null
        );
        verify(mKeyguardMessageArea)
                .setMessage(eq(genericMessage), /* animate= */ anyBoolean());
    }
}
Loading