Merge "Fix detect biometric logic." into udc-dev

This commit is contained in:
Beverly Tai
2023-02-28 16:00:36 +00:00
committed by Android (Google) Code Review
7 changed files with 197 additions and 87 deletions

View File

@@ -139,9 +139,9 @@ import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.logging.KeyguardUpdateMonitorLogger;
import com.android.settingslib.Utils;
import com.android.settingslib.WirelessUtils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.biometrics.AuthController;
@@ -823,6 +823,19 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
private void onBiometricDetected(int userId, BiometricSourceType biometricSourceType,
boolean isStrongBiometric) {
Assert.isMainThread();
Trace.beginSection("KeyGuardUpdateMonitor#onBiometricDetected");
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onBiometricDetected(userId, biometricSourceType, isStrongBiometric);
}
}
Trace.endSection();
}
@VisibleForTesting
protected void onFingerprintAuthenticated(int userId, boolean isStrongBiometric) {
Assert.isMainThread();
@@ -899,6 +912,20 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
private void handleBiometricDetected(int authUserId, BiometricSourceType biometricSourceType,
boolean isStrongBiometric) {
Trace.beginSection("KeyGuardUpdateMonitor#handlerBiometricDetected");
onBiometricDetected(authUserId, biometricSourceType, isStrongBiometric);
if (biometricSourceType == FINGERPRINT) {
mLogger.logFingerprintDetected(authUserId, isStrongBiometric);
} else if (biometricSourceType == FACE) {
mLogger.logFaceDetected(authUserId, isStrongBiometric);
setFaceRunningState(BIOMETRIC_STATE_STOPPED);
}
Trace.endSection();
}
private void handleFingerprintAuthenticated(int authUserId, boolean isStrongBiometric) {
Trace.beginSection("KeyGuardUpdateMonitor#handlerFingerPrintAuthenticated");
if (mHandler.hasCallbacks(mFpCancelNotReceived)) {
@@ -950,8 +977,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private void onFingerprintCancelNotReceived() {
mLogger.e("Fp cancellation not received, transitioning to STOPPED");
final boolean wasCancellingRestarting = mFingerprintRunningState
== BIOMETRIC_STATE_CANCELLING_RESTARTING;
mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_STOP);
if (wasCancellingRestarting) {
KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
} else {
KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_STOP);
}
}
private void handleFingerprintError(int msgId, String errString) {
@@ -1038,6 +1071,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
() -> updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE),
getBiometricLockoutDelay());
} else {
boolean temporaryLockoutReset = wasLockout && !mFingerprintLockedOut;
if (temporaryLockoutReset) {
mLogger.d("temporaryLockoutReset - stopListeningForFingerprint() to stop"
+ " detectFingerprint");
stopListeningForFingerprint();
}
updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
}
@@ -1747,10 +1786,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
};
private final FingerprintManager.FingerprintDetectionCallback mFingerprintDetectionCallback =
(sensorId, userId, isStrongBiometric) -> {
// Trigger the fingerprint detected path so the bouncer can be shown
handleBiometricDetected(userId, FINGERPRINT, isStrongBiometric);
};
private final FaceManager.FaceDetectionCallback mFaceDetectionCallback
= (sensorId, userId, isStrongBiometric) -> {
// Trigger the face success path so the bouncer can be shown
handleFaceAuthenticated(userId, isStrongBiometric);
// Trigger the face detected path so the bouncer can be shown
handleBiometricDetected(userId, FACE, isStrongBiometric);
};
@VisibleForTesting
@@ -2783,8 +2828,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
boolean shouldListen = shouldListenKeyguardState && shouldListenUserState
&& shouldListenBouncerState && shouldListenUdfpsState
&& shouldListenSideFpsState
&& !isFingerprintLockedOut();
&& shouldListenSideFpsState;
logListenerModelData(
new KeyguardFingerprintListenModel(
System.currentTimeMillis(),
@@ -2843,8 +2887,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
final boolean supportsDetect = !mFaceSensorProperties.isEmpty()
&& mFaceSensorProperties.get(0).supportsFaceDetection
&& canBypass && !mPrimaryBouncerIsOrWillBeShowing
&& !isUserInLockdown(user)
&& !isFingerprintLockedOut();
&& !isUserInLockdown(user);
final boolean faceAuthAllowedOrDetectionIsNeeded = faceAuthAllowed || supportsDetect;
// If the face or fp has recently been authenticated do not attempt to authenticate again.
@@ -2940,11 +2983,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mLogger.v("startListeningForFingerprint - detect");
mFpm.detectFingerprint(
mFingerprintCancelSignal,
(sensorId, user, isStrongBiometric) -> {
mLogger.d("fingerprint detected");
// Trigger the fingerprint success path so the bouncer can be shown
handleFingerprintAuthenticated(user, isStrongBiometric);
},
mFingerprintDetectionCallback,
new FingerprintAuthenticateOptions.Builder()
.setUserId(userId)
.build());

View File

@@ -214,13 +214,21 @@ public class KeyguardUpdateMonitorCallback {
public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) { }
/**
* Called when a biometric is recognized.
* Called when a biometric is authenticated.
* @param userId the user id for which the biometric sample was authenticated
* @param biometricSourceType
*/
public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType,
boolean isStrongBiometric) { }
/**
* Called when a biometric is detected but not successfully authenticated.
* @param userId the user id for which the biometric sample was detected
* @param biometricSourceType
*/
public void onBiometricDetected(int userId, BiometricSourceType biometricSourceType,
boolean isStrongBiometric) { }
/**
* Called when biometric authentication provides help string (e.g. "Try again")
* @param msgId

View File

@@ -167,6 +167,20 @@ class KeyguardUpdateMonitorLogger @Inject constructor(
}, {"Fingerprint auth successful: userId: $int1, isStrongBiometric: $bool1"})
}
fun logFaceDetected(userId: Int, isStrongBiometric: Boolean) {
logBuffer.log(TAG, DEBUG, {
int1 = userId
bool1 = isStrongBiometric
}, {"Face detected: userId: $int1, isStrongBiometric: $bool1"})
}
fun logFingerprintDetected(userId: Int, isStrongBiometric: Boolean) {
logBuffer.log(TAG, DEBUG, {
int1 = userId
bool1 = isStrongBiometric
}, {"Fingerprint detected: userId: $int1, isStrongBiometric: $bool1"})
}
fun logFingerprintError(msgId: Int, originalErrMsg: String) {
logBuffer.log(TAG, DEBUG, {
str1 = originalErrMsg

View File

@@ -373,6 +373,17 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
Trace.endSection();
}
@Override
public void onBiometricDetected(int userId, BiometricSourceType biometricSourceType,
boolean isStrongBiometric) {
Trace.beginSection("BiometricUnlockController#onBiometricDetected");
if (mUpdateMonitor.isGoingToSleep()) {
Trace.endSection();
return;
}
startWakeAndUnlock(MODE_SHOW_BOUNCER);
}
@Override
public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType,
boolean isStrongBiometric) {

View File

@@ -18,7 +18,6 @@ package com.android.keyguard;
import static android.app.StatusBarManager.SESSION_KEYGUARD;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_TIMED;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;
@@ -30,7 +29,6 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR
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;
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;
@@ -633,19 +631,47 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testOnlyDetectFingerprint_whenFingerprintUnlockNotAllowed() {
// Clear invocations, since previous setup (e.g. registering BiometricManager callbacks)
// will trigger updateBiometricListeningState();
clearInvocations(mFingerprintManager);
mKeyguardUpdateMonitor.resetBiometricListeningState();
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
mTestableLooper.processAllMessages();
givenDetectFingerprintWithClearingFingerprintManagerInvocations();
verifyFingerprintAuthenticateNeverCalled();
verifyFingerprintDetectCall();
}
@Test
public void whenDetectFingerprint_biometricDetectCallback() {
ArgumentCaptor<FingerprintManager.FingerprintDetectionCallback> fpDetectCallbackCaptor =
ArgumentCaptor.forClass(FingerprintManager.FingerprintDetectionCallback.class);
givenDetectFingerprintWithClearingFingerprintManagerInvocations();
verify(mFingerprintManager).detectFingerprint(
any(), fpDetectCallbackCaptor.capture(), any());
fpDetectCallbackCaptor.getValue().onFingerprintDetected(0, 0, true);
// THEN verify keyguardUpdateMonitorCallback receives a detect callback
// and NO authenticate callbacks
verify(mTestCallback).onBiometricDetected(
eq(0), eq(BiometricSourceType.FINGERPRINT), eq(true));
verify(mTestCallback, never()).onBiometricAuthenticated(
anyInt(), any(), anyBoolean());
}
@Test
public void whenDetectFace_biometricDetectCallback() {
ArgumentCaptor<FaceManager.FaceDetectionCallback> faceDetectCallbackCaptor =
ArgumentCaptor.forClass(FaceManager.FaceDetectionCallback.class);
givenDetectFace();
verify(mFaceManager).detectFace(any(), faceDetectCallbackCaptor.capture(), any());
faceDetectCallbackCaptor.getValue().onFaceDetected(0, 0, false);
// THEN verify keyguardUpdateMonitorCallback receives a detect callback
// and NO authenticate callbacks
verify(mTestCallback).onBiometricDetected(
eq(0), eq(BiometricSourceType.FACE), eq(false));
verify(mTestCallback, never()).onBiometricAuthenticated(
anyInt(), any(), anyBoolean());
}
@Test
public void testUnlockingWithFaceAllowed_strongAuthTrackerUnlockingWithBiometricAllowed() {
// GIVEN unlocking with biometric is allowed
@@ -674,7 +700,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
strongAuthNotRequired();
// WHEN fingerprint is locked out
fingerprintErrorLockedOut();
fingerprintErrorTemporaryLockedOut();
// THEN unlocking with face is not allowed
Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
@@ -697,7 +723,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
strongAuthNotRequired();
// WHEN fingerprint is locked out
fingerprintErrorLockedOut();
fingerprintErrorTemporaryLockedOut();
// THEN unlocking with fingerprint is not allowed
Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
@@ -721,7 +747,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser()));
// WHEN fingerprint is locked out
fingerprintErrorLockedOut();
fingerprintErrorTemporaryLockedOut();
// THEN user is NOT considered as "having trust" and bouncer cannot be skipped
Assert.assertFalse(mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser()));
@@ -781,11 +807,6 @@ 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();
@@ -804,22 +825,13 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
verifyFaceAuthenticateNeverCalled();
// THEN biometric help message sent to callback
verify(keyguardUpdateMonitorCallback).onBiometricHelp(
verify(mTestCallback).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);
mTestableLooper.processAllMessages();
givenDetectFace();
// FACE detect is triggered, not authenticate
verifyFaceDetectCall();
@@ -835,39 +847,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
verifyFaceDetectNeverCalled();
}
@Test
public void noFaceRun_whenFpLockout() {
// 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
// GIVEN fp is locked out
when(mFingerprintManager.getLockoutModeForUser(eq(FINGERPRINT_SENSOR_ID), anyInt()))
.thenReturn(BIOMETRIC_LOCKOUT_TIMED);
mKeyguardUpdateMonitor.handleUserSwitchComplete(0);
assertThat(mKeyguardUpdateMonitor.isFingerprintLockedOut()).isEqualTo(true);
// WHEN the device wakes up
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mTestableLooper.processAllMessages();
// FACE detect is NOT triggered and face authenticate is NOT triggered
verifyFaceDetectNeverCalled();
verifyFaceAuthenticateNeverCalled();
// WHEN bouncer becomes visible
setKeyguardBouncerVisibility(true);
clearInvocations(mFaceManager);
// THEN face scanning is not run
mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
verifyFaceAuthenticateNeverCalled();
verifyFaceDetectNeverCalled();
}
@Test
public void noFaceDetect_whenStrongAuthRequiredAndBypass_faceDetectionUnsupported() {
// GIVEN bypass is enabled, face detection is NOT supported and strong auth is required
@@ -1186,8 +1165,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// Fingerprint should be cancelled on lockout if going to lockout state, else
// restarted if it's not
assertThat(mKeyguardUpdateMonitor.mFingerprintRunningState)
.isEqualTo(fpLocked
? BIOMETRIC_STATE_CANCELLING : BIOMETRIC_STATE_CANCELLING_RESTARTING);
.isEqualTo(BIOMETRIC_STATE_CANCELLING_RESTARTING);
}
@Test
@@ -1646,7 +1624,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
// Fingerprint is locked out.
fingerprintErrorLockedOut();
fingerprintErrorTemporaryLockedOut();
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
}
@@ -2499,6 +2477,29 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
eq(false));
}
@Test
public void detectFingerprint_onTemporaryLockoutReset_authenticateFingerprint() {
ArgumentCaptor<FingerprintManager.LockoutResetCallback> fpLockoutResetCallbackCaptor =
ArgumentCaptor.forClass(FingerprintManager.LockoutResetCallback.class);
verify(mFingerprintManager).addLockoutResetCallback(fpLockoutResetCallbackCaptor.capture());
// GIVEN device is locked out
fingerprintErrorTemporaryLockedOut();
// GIVEN FP detection is running
givenDetectFingerprintWithClearingFingerprintManagerInvocations();
verifyFingerprintDetectCall();
verifyFingerprintAuthenticateNeverCalled();
// WHEN temporary lockout resets
fpLockoutResetCallbackCaptor.getValue().onLockoutReset(0);
mTestableLooper.processAllMessages();
// THEN fingerprint detect state should cancel & then restart (for authenticate call)
assertThat(mKeyguardUpdateMonitor.mFingerprintRunningState)
.isEqualTo(BIOMETRIC_STATE_CANCELLING_RESTARTING);
}
private void verifyFingerprintAuthenticateNeverCalled() {
verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
anyInt(), anyInt());
@@ -2602,7 +2603,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.setSwitchingUser(true);
}
private void fingerprintErrorLockedOut() {
private void fingerprintErrorTemporaryLockedOut() {
mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
.onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT, "Fingerprint locked out");
}
@@ -2741,6 +2742,30 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
receiver.setPendingResult(pendingResult);
}
private void givenDetectFingerprintWithClearingFingerprintManagerInvocations() {
// Clear invocations, since previous setup (e.g. registering BiometricManager callbacks)
// will trigger updateBiometricListeningState();
clearInvocations(mFingerprintManager);
mKeyguardUpdateMonitor.resetBiometricListeningState();
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
mTestableLooper.processAllMessages();
}
private void givenDetectFace() {
// 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);
mTestableLooper.processAllMessages();
}
private Intent putPhoneInfo(Intent intent, Bundle data, Boolean simInited) {
int subscription = simInited
? 1/* mock subid=1 */ : SubscriptionManager.PLACEHOLDER_SUBSCRIPTION_ID_BASE;

View File

@@ -517,6 +517,26 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
verify(mVibratorHelper).vibrateAuthError(anyString());
}
@Test
public void onFingerprintDetect_showBouncer() {
// WHEN fingerprint detect occurs
mBiometricUnlockController.onBiometricDetected(UserHandle.USER_CURRENT,
BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
// THEN shows primary bouncer
verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean());
}
@Test
public void onFaceDetect_showBouncer() {
// WHEN face detect occurs
mBiometricUnlockController.onBiometricDetected(UserHandle.USER_CURRENT,
BiometricSourceType.FACE, false /* isStrongBiometric */);
// THEN shows primary bouncer
verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean());
}
private void givenFingerprintModeUnlockCollapsing() {
when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);

View File

@@ -427,13 +427,6 @@ public class FingerprintService extends SystemService {
return -1;
}
if (!Utils.isUserEncryptedOrLockdown(mLockPatternUtils, options.getUserId())) {
// If this happens, something in KeyguardUpdateMonitor is wrong. This should only
// ever be invoked when the user is encrypted or lockdown.
Slog.e(TAG, "detectFingerprint invoked when user is not encrypted or lockdown");
return -1;
}
final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for detectFingerprint");