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

Commit 8548493b authored by Beverly's avatar Beverly Committed by Beverly Tai
Browse files

Update vis of face acquisition + err messages

When fingerprint is also enrolled, don't surface
FACE_TIMEOUT errors or face acquisition messages.

This CL also adds the ability to override the face
acquisition messages that can show when fp is also
enrolled. This can be done via adb. For example,
the following command would enable
    FACE_ACQUIRED_MOUTH_COVERING_DETECTED and
    FACE_ACQUIRED_DARK_GLASSES_DETECTED
 adb shell settings put global coex_face_help_msgs "26\|25"
This override is for temporary teamfooding and should be removed
before release.

Test: manual
Test: atest KeyguardIndicationControllerTest
Bug: 231733975
Change-Id: I1fb569adcf91576f89aa1fb068db8bdd030ea645
parent a95c2647
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -611,6 +611,12 @@
         2 - Override the setting to never bypass keyguard -->
    <integer name="config_face_unlock_bypass_override">0</integer>

    <!-- Which face help messages to surface when fingerprint is also enrolled.
         Message ids correspond with the acquired ids in BiometricFaceConstants -->
    <integer-array name="config_face_help_msgs_when_fingerprint_enrolled">
        <!-- for example: <item>26</item> for FACE_ACQUIRED_MOUTH_COVERING_DETECTED -->
    </integer-array>

    <!-- Whether the communal service should be enabled -->
    <bool name="config_communalServiceEnabled">false</bool>

+0 −1
Original line number Diff line number Diff line
@@ -275,7 +275,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
    HashMap<Integer, SimData> mSimDatas = new HashMap<>();
    HashMap<Integer, ServiceState> mServiceStates = new HashMap<Integer, ServiceState>();

    private int mRingMode;
    private int mPhoneState;
    private boolean mKeyguardIsVisible;
    private boolean mCredentialAttempted;
+46 −7
Original line number Diff line number Diff line
@@ -49,12 +49,14 @@ import android.hardware.biometrics.BiometricSourceType;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.BatteryManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.util.Log;
@@ -92,6 +94,8 @@ import com.android.systemui.util.wakelock.WakeLock;

import java.io.PrintWriter;
import java.text.NumberFormat;
import java.util.HashSet;
import java.util.Set;

import javax.inject.Inject;

@@ -112,12 +116,12 @@ public class KeyguardIndicationController {

    private static final String TAG = "KeyguardIndication";
    private static final boolean DEBUG_CHARGING_SPEED = false;
    private static final boolean DEBUG = Build.IS_DEBUGGABLE;

    private static final int MSG_HIDE_TRANSIENT = 1;
    private static final int MSG_SHOW_ACTION_TO_UNLOCK = 2;
    private static final int MSG_HIDE_BIOMETRIC_MESSAGE = 3;
    private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300;
    private static final float BOUNCE_ANIMATION_FINAL_Y = 0f;

    private final Context mContext;
    private final BroadcastDispatcher mBroadcastDispatcher;
@@ -166,6 +170,7 @@ public class KeyguardIndicationController {
    private boolean mBatteryPresent = true;
    private long mChargingTimeRemaining;
    private String mMessageToShowOnScreenOn;
    private final Set<Integer> mCoExFaceHelpMsgIdsToShow;
    private boolean mInited;

    private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
@@ -234,6 +239,22 @@ public class KeyguardIndicationController {
        mScreenLifecycle = screenLifecycle;
        mScreenLifecycle.addObserver(mScreenObserver);

        mCoExFaceHelpMsgIdsToShow = new HashSet<>();
        final String msgsToShowOverride = Settings.Global.getString(mContext.getContentResolver(),
                "coex_face_help_msgs"); // TODO: remove after UX testing b/231733975
        if (msgsToShowOverride != null) {
            final String[] msgIds = msgsToShowOverride.split("\\|");
            for (String msgId : msgIds) {
                mCoExFaceHelpMsgIdsToShow.add(Integer.parseInt(msgId));
            }
        } else {
            int[] msgIds = context.getResources().getIntArray(
                    com.android.systemui.R.array.config_face_help_msgs_when_fingerprint_enrolled);
            for (int msgId : msgIds) {
                mCoExFaceHelpMsgIdsToShow.add(msgId);
            }
        }

        mHandler = new Handler(mainLooper) {
            @Override
            public void handleMessage(Message msg) {
@@ -924,6 +945,7 @@ public class KeyguardIndicationController {
                mTopIndicationView == null ? null : mTopIndicationView.getText()));
        pw.println("  computePowerIndication(): " + computePowerIndication());
        pw.println("  trustGrantedIndication: " + getTrustGrantedIndication());
        pw.println("    mCoExFaceHelpMsgIdsToShow=" + mCoExFaceHelpMsgIdsToShow);
        mRotateTextViewController.dump(pw, args);
    }

@@ -982,9 +1004,20 @@ public class KeyguardIndicationController {
                    .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)) {
                return;
            }

            boolean showActionToUnlock =
                    msgId == KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
            if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
            if (biometricSourceType == BiometricSourceType.FACE
                    && !showActionToUnlock
                    && mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
                            KeyguardUpdateMonitor.getCurrentUser())
                    && !mCoExFaceHelpMsgIdsToShow.contains(msgId)) {
                if (DEBUG) {
                    Log.d(TAG, "skip showing msgId=" + msgId + " helpString=" + helpString
                            + ", due to co-ex logic");
                }
                return;
            } else if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
                mStatusBarKeyguardViewManager.showBouncerMessage(helpString,
                        mInitialTextColorState);
            } else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
@@ -1001,14 +1034,20 @@ public class KeyguardIndicationController {
            if (shouldSuppressBiometricError(msgId, biometricSourceType, mKeyguardUpdateMonitor)) {
                return;
            }

            if (msgId == FaceManager.FACE_ERROR_TIMEOUT) {
                if (mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
                        KeyguardUpdateMonitor.getCurrentUser())) {
                    // no message if fingerprint is also enrolled
                    if (DEBUG) {
                        Log.d(TAG, "skip showing FACE_ERROR_TIMEOUT due to co-ex logic");
                    }
                    return;
                }

                // The face timeout message is not very actionable, let's ask the user to
                // manually retry.
                if (!mStatusBarKeyguardViewManager.isBouncerShowing()
                        && mKeyguardUpdateMonitor.isUdfpsEnrolled()
                        && mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
                    showFaceFailedTryFingerprintMsg(msgId, errString);
                } else if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
                if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
                    mStatusBarKeyguardViewManager.showBouncerMessage(
                            mContext.getResources().getString(R.string.keyguard_try_fingerprint),
                            mInitialTextColorState
+92 −0
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@@ -59,6 +60,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.UserInfo;
import android.graphics.Color;
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
@@ -106,6 +108,9 @@ import org.mockito.MockitoAnnotations;

import java.text.NumberFormat;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -577,6 +582,80 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
        verify(mStatusBarKeyguardViewManager).showBouncerMessage(eq(message), any());
    }

    @Test
    public void faceErrorTimeout_whenFingerprintEnrolled_doesNotShowMessage() {
        createController();
        when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
                0)).thenReturn(true);
        String message = "A message";

        mController.setVisible(true);
        mController.getKeyguardCallback().onBiometricError(
                FaceManager.FACE_ERROR_TIMEOUT, message, BiometricSourceType.FACE);
        verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
    }

    @Test
    public void doNotSendFaceHelpMessages_fingerprintEnrolled() {
        createController();

        // GIVEN fingerprint enrolled
        when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
                0)).thenReturn(true);

        // WHEN help messages received
        final String helpString = "helpString";
        final int[] msgIds = new int[]{
                BiometricFaceConstants.FACE_ACQUIRED_FACE_OBSCURED,
                BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED,
                BiometricFaceConstants.FACE_ACQUIRED_TOO_RIGHT,
                BiometricFaceConstants.FACE_ACQUIRED_TOO_LEFT,
                BiometricFaceConstants.FACE_ACQUIRED_TOO_HIGH,
                BiometricFaceConstants.FACE_ACQUIRED_TOO_LOW,
                BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT,
                BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK
        };
        for (int msgId : msgIds) {
            mKeyguardUpdateMonitorCallback.onBiometricHelp(
                    msgId,  helpString + msgId, BiometricSourceType.FACE);
        }

        // THEN no messages shown
        verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
    }

    @Test
    public void sendAllFaceHelpMessages_fingerprintNotEnrolled() {
        createController();

        // GIVEN fingerprint NOT enrolled
        when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
                0)).thenReturn(false);

        // WHEN help messages received
        final Set<CharSequence> helpStrings = new HashSet<>();
        final String helpString = "helpString";
        final int[] msgIds = new int[]{
                BiometricFaceConstants.FACE_ACQUIRED_FACE_OBSCURED,
                BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED,
                BiometricFaceConstants.FACE_ACQUIRED_TOO_RIGHT,
                BiometricFaceConstants.FACE_ACQUIRED_TOO_LEFT,
                BiometricFaceConstants.FACE_ACQUIRED_TOO_HIGH,
                BiometricFaceConstants.FACE_ACQUIRED_TOO_LOW,
                BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT,
                BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK
        };
        for (int msgId : msgIds) {
            final String numberedHelpString = helpString + msgId;
            mKeyguardUpdateMonitorCallback.onBiometricHelp(
                    msgId,  numberedHelpString, BiometricSourceType.FACE);
            helpStrings.add(numberedHelpString);
        }

        // THEN message shown for each call
        verifyIndicationMessages(INDICATION_TYPE_BIOMETRIC_MESSAGE, helpStrings);
    }

    @Test
    public void updateMonitor_listenerUpdatesIndication() {
        createController();
@@ -850,6 +929,19 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
        mBroadcastReceiver.onReceive(mContext, new Intent());
    }

    private void verifyIndicationMessages(int type, Set<CharSequence> messages) {
        verify(mRotateTextViewController, times(messages.size())).updateIndication(eq(type),
                mKeyguardIndicationCaptor.capture(), anyBoolean());
        List<KeyguardIndication> kis = mKeyguardIndicationCaptor.getAllValues();

        for (KeyguardIndication ki : kis) {
            final CharSequence msg = ki.getMessage();
            assertTrue(messages.contains(msg)); // check message is shown
            messages.remove(msg);
        }
        assertThat(messages.size()).isEqualTo(0); // check that all messages accounted for (removed)
    }

    private void verifyIndicationMessage(int type, String message) {
        verify(mRotateTextViewController).updateIndication(eq(type),
                mKeyguardIndicationCaptor.capture(), anyBoolean());