Loading packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +51 −12 Original line number Diff line number Diff line Loading @@ -814,6 +814,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(); Loading Loading @@ -890,6 +903,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)) { Loading Loading @@ -941,9 +968,15 @@ 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; if (wasCancellingRestarting) { KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); } else { KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_STOP); } } private void handleFingerprintError(int msgId, String errString) { Assert.isMainThread(); Loading Loading @@ -1029,6 +1062,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); } Loading Loading @@ -1728,10 +1767,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 Loading Loading @@ -2759,8 +2804,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab boolean shouldListen = shouldListenKeyguardState && shouldListenUserState && shouldListenBouncerState && shouldListenUdfpsState && shouldListenSideFpsState && !isFingerprintLockedOut(); && shouldListenSideFpsState; logListenerModelData( new KeyguardFingerprintListenModel( System.currentTimeMillis(), Loading Loading @@ -2819,8 +2863,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. Loading Loading @@ -2916,11 +2959,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, userId); } else { mLogger.v("startListeningForFingerprint - authenticate"); Loading packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java +9 −1 Original line number Diff line number Diff line Loading @@ -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 Loading packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt +14 −0 Original line number Diff line number Diff line Loading @@ -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 Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +11 −0 Original line number Diff line number Diff line Loading @@ -382,6 +382,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) { Loading packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +91 −65 Original line number Diff line number Diff line Loading @@ -29,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; Loading Loading @@ -614,19 +613,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(), anyInt()); 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(), anyInt()); 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 Loading Loading @@ -655,7 +682,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( Loading @@ -678,7 +705,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( Loading @@ -702,7 +729,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())); Loading Loading @@ -764,11 +791,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(); Loading @@ -787,22 +809,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(); Loading @@ -818,39 +831,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 Loading Loading @@ -1159,8 +1139,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 Loading Loading @@ -1619,7 +1598,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); // Fingerprint is locked out. fingerprintErrorLockedOut(); fingerprintErrorTemporaryLockedOut(); assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); } Loading Loading @@ -2428,6 +2407,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()); Loading Loading @@ -2532,7 +2534,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mKeyguardUpdateMonitor.setSwitchingUser(true); } private void fingerprintErrorLockedOut() { private void fingerprintErrorTemporaryLockedOut() { mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback .onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT, "Fingerprint locked out"); } Loading Loading @@ -2672,6 +2674,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; Loading Loading
packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +51 −12 Original line number Diff line number Diff line Loading @@ -814,6 +814,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(); Loading Loading @@ -890,6 +903,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)) { Loading Loading @@ -941,9 +968,15 @@ 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; if (wasCancellingRestarting) { KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); } else { KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_STOP); } } private void handleFingerprintError(int msgId, String errString) { Assert.isMainThread(); Loading Loading @@ -1029,6 +1062,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); } Loading Loading @@ -1728,10 +1767,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 Loading Loading @@ -2759,8 +2804,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab boolean shouldListen = shouldListenKeyguardState && shouldListenUserState && shouldListenBouncerState && shouldListenUdfpsState && shouldListenSideFpsState && !isFingerprintLockedOut(); && shouldListenSideFpsState; logListenerModelData( new KeyguardFingerprintListenModel( System.currentTimeMillis(), Loading Loading @@ -2819,8 +2863,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. Loading Loading @@ -2916,11 +2959,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, userId); } else { mLogger.v("startListeningForFingerprint - authenticate"); Loading
packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java +9 −1 Original line number Diff line number Diff line Loading @@ -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 Loading
packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt +14 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +11 −0 Original line number Diff line number Diff line Loading @@ -382,6 +382,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) { Loading
packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +91 −65 Original line number Diff line number Diff line Loading @@ -29,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; Loading Loading @@ -614,19 +613,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(), anyInt()); 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(), anyInt()); 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 Loading Loading @@ -655,7 +682,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( Loading @@ -678,7 +705,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( Loading @@ -702,7 +729,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())); Loading Loading @@ -764,11 +791,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(); Loading @@ -787,22 +809,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(); Loading @@ -818,39 +831,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 Loading Loading @@ -1159,8 +1139,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 Loading Loading @@ -1619,7 +1598,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); // Fingerprint is locked out. fingerprintErrorLockedOut(); fingerprintErrorTemporaryLockedOut(); assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); } Loading Loading @@ -2428,6 +2407,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()); Loading Loading @@ -2532,7 +2534,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mKeyguardUpdateMonitor.setSwitchingUser(true); } private void fingerprintErrorLockedOut() { private void fingerprintErrorTemporaryLockedOut() { mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback .onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT, "Fingerprint locked out"); } Loading Loading @@ -2672,6 +2674,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; Loading