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
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
@@ -769,6 +772,36 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
|
||||
anyBoolean());
|
||||
}
|
||||
|
||||
@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
|
||||
@@ -776,6 +809,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
|
||||
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,
|
||||
|
||||
@@ -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;
|
||||
@@ -619,6 +620,33 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
|
||||
mContext.getString(R.string.keyguard_suggest_fingerprint));
|
||||
}
|
||||
|
||||
@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();
|
||||
|
||||
Reference in New Issue
Block a user