diff options
| author | 2023-02-02 20:01:13 +0000 | |
|---|---|---|
| committer | 2023-02-06 16:51:52 +0000 | |
| commit | e8da39df1ba53961f1e470cedb09a0f84a936c09 (patch) | |
| tree | 974ac843e90d398ecb571f1595bd664c5b199dd1 | |
| parent | a858b620bdc2863633c82dc4d1baab0846072a79 (diff) | |
Update face strong auth strongAuth message
If bypass is enabled & UDFPS fingerprint is enrolled,
if the user cannot user face auth for device entry,
immediately (when face auth would normally trigger)
inform the user that face unlock is unavailable
and that they can use fingerprint to unlock instead.
On devices with face bypass & a non-UDFPS
enrolled, faceDetect will run and, if a face is detected,
the user will be sent to the bouncer. This CUJ doesn't work for
UDFPS because on the bouncer, UDFPS cannot be used to authenticate.
Test: manual
Test: atest KeyguardIndicationControllerTest KeyguardUpdateMonitorTest
Fixes: 231029398
Change-Id: Ibbe30a4708a92750ba54fb3f7e164f1ab72e7a3a
4 files changed, 114 insertions, 10 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index cd5b7c18bb69..5b5d79d28f2d 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -260,6 +260,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @VisibleForTesting public static final int BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED = -1; public static final int BIOMETRIC_HELP_FACE_NOT_RECOGNIZED = -2; + public static final int BIOMETRIC_HELP_FACE_NOT_AVAILABLE = -3; /** * If no cancel signal has been received after this amount of time, set the biometric running @@ -2948,9 +2949,26 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // This would need to be updated for multi-sensor devices final boolean supportsFaceDetection = !mFaceSensorProperties.isEmpty() && mFaceSensorProperties.get(0).supportsFaceDetection; - if (!isUnlockingWithBiometricAllowed(FACE) && supportsFaceDetection) { - mLogger.v("startListeningForFace - detect"); - mFaceManager.detectFace(mFaceCancelSignal, mFaceDetectionCallback, userId); + if (!isUnlockingWithBiometricAllowed(FACE)) { + final boolean udfpsFingerprintAuthRunning = isUdfpsSupported() + && isFingerprintDetectionRunning(); + if (supportsFaceDetection && !udfpsFingerprintAuthRunning) { + // Run face detection. (If a face is detected, show the bouncer.) + mLogger.v("startListeningForFace - detect"); + mFaceManager.detectFace(mFaceCancelSignal, mFaceDetectionCallback, userId); + } else { + // Don't run face detection. Instead, inform the user + // face auth is unavailable and how to proceed. + // (ie: "Use fingerprint instead" or "Swipe up to open") + mLogger.v("Ignoring \"startListeningForFace - detect\". " + + "Informing user face isn't available."); + mFaceAuthenticationCallback.onAuthenticationHelp( + BIOMETRIC_HELP_FACE_NOT_AVAILABLE, + mContext.getResources().getString( + R.string.keyguard_face_unlock_unavailable) + ); + return; + } } else { mLogger.v("startListeningForFace - authenticate"); final boolean isBypassEnabled = mKeyguardBypassController != null diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 4bf84f76d224..7a29fe114107 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -25,6 +25,7 @@ import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT; import static android.view.View.GONE; import static android.view.View.VISIBLE; +import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_AVAILABLE; import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED; import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED; import static com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser; @@ -1080,18 +1081,23 @@ public class KeyguardIndicationController { } } + final boolean faceAuthUnavailable = biometricSourceType == FACE + && msgId == BIOMETRIC_HELP_FACE_NOT_AVAILABLE; + // TODO(b/141025588): refactor to reduce repetition of code/comments // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong // as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the // check of whether non-strong biometric is allowed if (!mKeyguardUpdateMonitor - .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)) { + .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */) + && !faceAuthUnavailable) { return; } final boolean faceAuthSoftError = biometricSourceType == FACE - && msgId != BIOMETRIC_HELP_FACE_NOT_RECOGNIZED; + && msgId != BIOMETRIC_HELP_FACE_NOT_RECOGNIZED + && msgId != BIOMETRIC_HELP_FACE_NOT_AVAILABLE; final boolean faceAuthFailed = biometricSourceType == FACE && msgId == BIOMETRIC_HELP_FACE_NOT_RECOGNIZED; // ran through matcher & failed final boolean fpAuthFailed = biometricSourceType == FINGERPRINT @@ -1134,6 +1140,13 @@ public class KeyguardIndicationController { getTrustGrantedIndication(), mContext.getString(R.string.keyguard_unlock) ); + } else if (faceAuthUnavailable) { + showBiometricMessage( + helpString, + isUnlockWithFingerprintPossible + ? mContext.getString(R.string.keyguard_suggest_fingerprint) + : mContext.getString(R.string.keyguard_unlock) + ); } else { showBiometricMessage(helpString); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index cd8857add5b1..9211ff704e13 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -27,6 +27,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOM import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT; 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_RESTARTING; import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT; import static com.android.keyguard.KeyguardUpdateMonitor.HAL_POWER_PRESS_TIMEOUT; @@ -240,7 +241,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Mock private FingerprintInteractiveToAuthProvider mInteractiveToAuthProvider; - + private List<FingerprintSensorPropertiesInternal> mFingerprintSensorProperties; private final int mCurrentUserId = 100; private final UserInfo mCurrentUserInfo = new UserInfo(mCurrentUserId, "Test user", 0); @@ -280,9 +281,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { when(mFaceSensorProperties.get(anyInt())).thenReturn( createFaceSensorProperties(/* supportsFaceDetection = */ false)); - when(mFingerprintManager.isHardwareDetected()).thenReturn(true); - when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true); - when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(List.of( + mFingerprintSensorProperties = List.of( new FingerprintSensorPropertiesInternal(1 /* sensorId */, FingerprintSensorProperties.STRENGTH_STRONG, 1 /* maxEnrollmentsPerUser */, @@ -291,7 +290,11 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { "1.01" /* firmwareVersion */, "00000001" /* serialNumber */, "" /* softwareVersion */)), FingerprintSensorProperties.TYPE_UDFPS_OPTICAL, - false /* resetLockoutRequiresHAT */))); + false /* resetLockoutRequiresHAT */)); + when(mFingerprintManager.isHardwareDetected()).thenReturn(true); + when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true); + when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn( + mFingerprintSensorProperties); when(mUserManager.isUserUnlocked(anyInt())).thenReturn(true); when(mUserManager.isPrimaryUser()).thenReturn(true); when(mStrongAuthTracker.getStub()).thenReturn(mock(IStrongAuthTracker.Stub.class)); @@ -770,12 +773,43 @@ 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(); + keyguardIsVisible(); + + // GIVEN udfps is supported and strong auth required for weak biometrics (face) only + givenUdfpsSupported(); + strongAuthRequiredForWeakBiometricOnly(); // this allows fingerprint to run but not face + + // WHEN the device wakes up + mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); + mTestableLooper.processAllMessages(); + + // THEN face detect and authenticate are NOT triggered + verify(mFaceManager, never()).detectFace(any(), any(), anyInt()); + verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(), + anyBoolean()); + + // THEN biometric help message sent to callback + verify(keyguardUpdateMonitorCallback).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); @@ -2466,6 +2500,11 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false); } + private void strongAuthRequiredForWeakBiometricOnly() { + when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(eq(true))).thenReturn(true); + when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(eq(false))).thenReturn(false); + } + private void strongAuthNotRequired() { when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser())) .thenReturn(0); @@ -2520,6 +2559,12 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } + private void givenUdfpsSupported() { + Assert.assertFalse(mFingerprintSensorProperties.isEmpty()); + when(mAuthController.getUdfpsProps()).thenReturn(mFingerprintSensorProperties); + Assert.assertTrue(mKeyguardUpdateMonitor.isUdfpsSupported()); + } + private void setBroadcastReceiverPendingResult(BroadcastReceiver receiver) { BroadcastReceiver.PendingResult pendingResult = new BroadcastReceiver.PendingResult(Activity.RESULT_OK, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index f9f2c45190bc..406826b860d4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -23,6 +23,7 @@ import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_T import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT; import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT; +import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_AVAILABLE; import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED; import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT; @@ -620,6 +621,33 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { } @Test + public void onBiometricHelp_coEx_faceUnavailable() { + createController(); + + // GIVEN unlocking with fingerprint is possible + when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(anyInt())) + .thenReturn(true); + + String message = "A message"; + mController.setVisible(true); + + // WHEN there's a face unavailable message + mController.getKeyguardCallback().onBiometricHelp( + BIOMETRIC_HELP_FACE_NOT_AVAILABLE, + message, + BiometricSourceType.FACE); + + // THEN show sequential messages such as: 'face unlock unavailable' and + // 'try fingerprint instead' + verifyIndicationMessage( + INDICATION_TYPE_BIOMETRIC_MESSAGE, + message); + verifyIndicationMessage( + INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP, + mContext.getString(R.string.keyguard_suggest_fingerprint)); + } + + @Test public void onBiometricHelp_coEx_fpFailure_faceAlreadyUnlocked() { createController(); |