For non-udfps, detectFP if primaryAuth required
If the user attempts to FP auth, we will listen for FP
detection (as opposed to authentication or not at all). If the
user places their finger on the FP sensor, without running the
FP matcher, we can show the bouncer.
Additionally, update the logic when switching users. Immediately
after a user switch, cancel the FP listening state and then
restart based on the new state (because it may change to/from
detect or authenticate).
Repro steps:
1. Enable extra logging:
adb shell settings put global systemui/buffer/KeyguardUpdateMonitorLog v
2. Setup side fps
3. Fail side fps 5 times to lock out fp
4. See logs that SysUI is running detectFP ("startListeningForFingerprint - detect")
5. Attempt FP => bouncer appears
Test: atest KeyguardUpdateMonitorTest
Test: atest SystemUITests
Bug: 245778799
Change-Id: Ic1d4984c7514fc96b6552267128dee814730bb30
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
index 8197685..e6283b8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
@@ -26,7 +26,6 @@
val credentialAttempted: Boolean,
val deviceInteractive: Boolean,
val dreaming: Boolean,
- val encryptedOrLockdown: Boolean,
val fingerprintDisabled: Boolean,
val fingerprintLockedOut: Boolean,
val goingToSleep: Boolean,
@@ -37,6 +36,7 @@
val primaryUser: Boolean,
val shouldListenSfpsState: Boolean,
val shouldListenForFingerprintAssistant: Boolean,
+ val strongerAuthRequired: Boolean,
val switchingUser: Boolean,
val udfps: Boolean,
val userDoesNotHaveTrust: Boolean
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 01be33e..4d0a273 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -363,16 +363,18 @@
final boolean sfpsEnabled = getResources().getBoolean(
R.bool.config_show_sidefps_hint_on_bouncer);
final boolean fpsDetectionRunning = mUpdateMonitor.isFingerprintDetectionRunning();
- final boolean needsStrongAuth = mUpdateMonitor.userNeedsStrongAuth();
+ final boolean isUnlockingWithFpAllowed =
+ mUpdateMonitor.isUnlockingWithFingerprintAllowed();
- boolean toShow = mBouncerVisible && sfpsEnabled && fpsDetectionRunning && !needsStrongAuth;
+ boolean toShow = mBouncerVisible && sfpsEnabled && fpsDetectionRunning
+ && isUnlockingWithFpAllowed;
if (DEBUG) {
Log.d(TAG, "sideFpsToShow=" + toShow + ", "
+ "mBouncerVisible=" + mBouncerVisible + ", "
+ "configEnabled=" + sfpsEnabled + ", "
+ "fpsDetectionRunning=" + fpsDetectionRunning + ", "
- + "needsStrongAuth=" + needsStrongAuth);
+ + "isUnlockingWithFpAllowed=" + isUnlockingWithFpAllowed);
}
if (toShow) {
mSideFpsController.get().show(SideFpsUiRequestSource.PRIMARY_BOUNCER);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 39ade34..993d80f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -27,6 +27,8 @@
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_TIMED;
import static android.hardware.biometrics.BiometricConstants.LockoutMode;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
+import static android.hardware.biometrics.BiometricSourceType.FACE;
+import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
@@ -228,7 +230,15 @@
* Biometric authentication: Cancelling and waiting for the relevant biometric service to
* send us the confirmation that cancellation has happened.
*/
- private static final int BIOMETRIC_STATE_CANCELLING = 2;
+ @VisibleForTesting
+ protected static final int BIOMETRIC_STATE_CANCELLING = 2;
+
+ /**
+ * Biometric state: During cancelling we got another request to start listening, so when we
+ * receive the cancellation done signal, we should start listening again.
+ */
+ @VisibleForTesting
+ protected static final int BIOMETRIC_STATE_CANCELLING_RESTARTING = 3;
/**
* Action indicating keyguard *can* start biometric authentiation.
@@ -243,12 +253,6 @@
*/
private static final int BIOMETRIC_ACTION_UPDATE = 2;
- /**
- * Biometric state: During cancelling we got another request to start listening, so when we
- * receive the cancellation done signal, we should start listening again.
- */
- private static final int BIOMETRIC_STATE_CANCELLING_RESTARTING = 3;
-
@VisibleForTesting
public static final int BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED = -1;
public static final int BIOMETRIC_HELP_FACE_NOT_RECOGNIZED = -2;
@@ -356,7 +360,8 @@
private KeyguardBypassController mKeyguardBypassController;
private List<SubscriptionInfo> mSubscriptionInfo;
- private int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
+ @VisibleForTesting
+ protected int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
private int mFaceRunningState = BIOMETRIC_STATE_STOPPED;
private boolean mIsDreaming;
private boolean mLogoutEnabled;
@@ -790,7 +795,7 @@
new BiometricAuthenticated(true, isStrongBiometric));
// Update/refresh trust state only if user can skip bouncer
if (getUserCanSkipBouncer(userId)) {
- mTrustManager.unlockedByBiometricForUser(userId, BiometricSourceType.FINGERPRINT);
+ mTrustManager.unlockedByBiometricForUser(userId, FINGERPRINT);
}
// Don't send cancel if authentication succeeds
mFingerprintCancelSignal = null;
@@ -800,7 +805,7 @@
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onBiometricAuthenticated(userId, BiometricSourceType.FINGERPRINT,
+ cb.onBiometricAuthenticated(userId, FINGERPRINT,
isStrongBiometric);
}
}
@@ -833,7 +838,7 @@
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
+ cb.onBiometricAuthFailed(FINGERPRINT);
}
}
if (isUdfpsSupported()) {
@@ -858,7 +863,7 @@
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onBiometricAcquired(BiometricSourceType.FINGERPRINT, acquireInfo);
+ cb.onBiometricAcquired(FINGERPRINT, acquireInfo);
}
}
}
@@ -892,7 +897,7 @@
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onBiometricHelp(msgId, helpString, BiometricSourceType.FINGERPRINT);
+ cb.onBiometricHelp(msgId, helpString, FINGERPRINT);
}
}
}
@@ -944,7 +949,7 @@
if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
lockedOutStateChanged = !mFingerprintLockedOutPermanent;
mFingerprintLockedOutPermanent = true;
- mLogger.d("Fingerprint locked out - requiring strong auth");
+ mLogger.d("Fingerprint permanently locked out - requiring stronger auth");
mLockPatternUtils.requireStrongAuth(
STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, getCurrentUser());
}
@@ -953,6 +958,7 @@
|| msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
lockedOutStateChanged |= !mFingerprintLockedOut;
mFingerprintLockedOut = true;
+ mLogger.d("Fingerprint temporarily locked out - requiring stronger auth");
if (isUdfpsEnrolled()) {
updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
}
@@ -963,12 +969,12 @@
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onBiometricError(msgId, errString, BiometricSourceType.FINGERPRINT);
+ cb.onBiometricError(msgId, errString, FINGERPRINT);
}
}
if (lockedOutStateChanged) {
- notifyLockedOutStateChanged(BiometricSourceType.FINGERPRINT);
+ notifyLockedOutStateChanged(FINGERPRINT);
}
}
@@ -996,7 +1002,7 @@
}
if (changed) {
- notifyLockedOutStateChanged(BiometricSourceType.FINGERPRINT);
+ notifyLockedOutStateChanged(FINGERPRINT);
}
}
@@ -1019,7 +1025,7 @@
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onBiometricRunningStateChanged(isFingerprintDetectionRunning(),
- BiometricSourceType.FINGERPRINT);
+ FINGERPRINT);
}
}
}
@@ -1032,7 +1038,7 @@
new BiometricAuthenticated(true, isStrongBiometric));
// Update/refresh trust state only if user can skip bouncer
if (getUserCanSkipBouncer(userId)) {
- mTrustManager.unlockedByBiometricForUser(userId, BiometricSourceType.FACE);
+ mTrustManager.unlockedByBiometricForUser(userId, FACE);
}
// Don't send cancel if authentication succeeds
mFaceCancelSignal = null;
@@ -1043,7 +1049,7 @@
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onBiometricAuthenticated(userId,
- BiometricSourceType.FACE,
+ FACE,
isStrongBiometric);
}
}
@@ -1065,7 +1071,7 @@
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onBiometricAuthFailed(BiometricSourceType.FACE);
+ cb.onBiometricAuthFailed(FACE);
}
}
handleFaceHelp(BIOMETRIC_HELP_FACE_NOT_RECOGNIZED,
@@ -1078,7 +1084,7 @@
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onBiometricAcquired(BiometricSourceType.FACE, acquireInfo);
+ cb.onBiometricAcquired(FACE, acquireInfo);
}
}
}
@@ -1113,7 +1119,7 @@
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onBiometricHelp(msgId, helpString, BiometricSourceType.FACE);
+ cb.onBiometricHelp(msgId, helpString, FACE);
}
}
}
@@ -1181,12 +1187,12 @@
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onBiometricError(msgId, errString,
- BiometricSourceType.FACE);
+ FACE);
}
}
if (lockedOutStateChanged) {
- notifyLockedOutStateChanged(BiometricSourceType.FACE);
+ notifyLockedOutStateChanged(FACE);
}
}
@@ -1200,7 +1206,7 @@
FACE_AUTH_TRIGGERED_FACE_LOCKOUT_RESET), getBiometricLockoutDelay());
if (changed) {
- notifyLockedOutStateChanged(BiometricSourceType.FACE);
+ notifyLockedOutStateChanged(FACE);
}
}
@@ -1223,7 +1229,7 @@
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onBiometricRunningStateChanged(isFaceDetectionRunning(),
- BiometricSourceType.FACE);
+ FACE);
}
}
}
@@ -1364,7 +1370,39 @@
}
public boolean isUnlockingWithBiometricAllowed(boolean isStrongBiometric) {
- return mStrongAuthTracker.isUnlockingWithBiometricAllowed(isStrongBiometric);
+ // StrongAuthTracker#isUnlockingWithBiometricAllowed includes
+ // STRONG_AUTH_REQUIRED_AFTER_LOCKOUT which is the same as mFingerprintLockedOutPermanent;
+ // however the strong auth tracker does not include the temporary lockout
+ // mFingerprintLockedOut.
+ return mStrongAuthTracker.isUnlockingWithBiometricAllowed(isStrongBiometric)
+ && !mFingerprintLockedOut;
+ }
+
+ private boolean isUnlockingWithFaceAllowed() {
+ return mStrongAuthTracker.isUnlockingWithBiometricAllowed(false);
+ }
+
+ /**
+ * Whether fingerprint is allowed ot be used for unlocking based on the strongAuthTracker
+ * and temporary lockout state (tracked by FingerprintManager via error codes).
+ */
+ public boolean isUnlockingWithFingerprintAllowed() {
+ return isUnlockingWithBiometricAllowed(true);
+ }
+
+ /**
+ * Whether the given biometric is allowed based on strongAuth & lockout states.
+ */
+ public boolean isUnlockingWithBiometricAllowed(
+ @NonNull BiometricSourceType biometricSourceType) {
+ switch (biometricSourceType) {
+ case FINGERPRINT:
+ return isUnlockingWithFingerprintAllowed();
+ case FACE:
+ return isUnlockingWithFaceAllowed();
+ default:
+ return false;
+ }
}
public boolean isUserInLockdown(int userId) {
@@ -1386,11 +1424,6 @@
return isEncrypted || isLockDown;
}
- public boolean userNeedsStrongAuth() {
- return mStrongAuthTracker.getStrongAuthForUser(getCurrentUser())
- != LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
- }
-
private boolean containsFlag(int haystack, int needle) {
return (haystack & needle) != 0;
}
@@ -1560,12 +1593,6 @@
}
};
- private final FingerprintManager.FingerprintDetectionCallback mFingerprintDetectionCallback
- = (sensorId, userId, isStrongBiometric) -> {
- // Trigger the fingerprint success path so the bouncer can be shown
- handleFingerprintAuthenticated(userId, isStrongBiometric);
- };
-
/**
* Propagates a pointer down event to keyguard.
*/
@@ -2636,27 +2663,25 @@
&& (!mKeyguardGoingAway || !mDeviceInteractive)
&& mIsPrimaryUser
&& biometricEnabledForUser;
-
- final boolean shouldListenBouncerState = !(mFingerprintLockedOut
- && mPrimaryBouncerIsOrWillBeShowing && mCredentialAttempted);
-
- final boolean isEncryptedOrLockdownForUser = isEncryptedOrLockdown(user);
+ final boolean strongerAuthRequired = !isUnlockingWithFingerprintAllowed();
+ final boolean isSideFps = isSfpsSupported() && isSfpsEnrolled();
+ final boolean shouldListenBouncerState =
+ !strongerAuthRequired || !mPrimaryBouncerIsOrWillBeShowing;
final boolean shouldListenUdfpsState = !isUdfps
|| (!userCanSkipBouncer
- && !isEncryptedOrLockdownForUser
+ && !strongerAuthRequired
&& userDoesNotHaveTrust);
boolean shouldListenSideFpsState = true;
- if (isSfpsSupported() && isSfpsEnrolled()) {
+ if (isSideFps) {
shouldListenSideFpsState =
mSfpsRequireScreenOnToAuthPrefEnabled ? isDeviceInteractive() : true;
}
boolean shouldListen = shouldListenKeyguardState && shouldListenUserState
- && shouldListenBouncerState && shouldListenUdfpsState && !isFingerprintLockedOut()
+ && shouldListenBouncerState && shouldListenUdfpsState
&& shouldListenSideFpsState;
-
maybeLogListenerModelData(
new KeyguardFingerprintListenModel(
System.currentTimeMillis(),
@@ -2668,7 +2693,6 @@
mCredentialAttempted,
mDeviceInteractive,
mIsDreaming,
- isEncryptedOrLockdownForUser,
fingerprintDisabledForUser,
mFingerprintLockedOut,
mGoingToSleep,
@@ -2679,6 +2703,7 @@
mIsPrimaryUser,
shouldListenSideFpsState,
shouldListenForFingerprintAssistant,
+ strongerAuthRequired,
mSwitchingUser,
isUdfps,
userDoesNotHaveTrust));
@@ -2706,10 +2731,7 @@
final boolean isEncryptedOrTimedOut =
containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT)
|| containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
-
- // TODO: always disallow when fp is already locked out?
- final boolean fpLockedOut = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
-
+ final boolean fpLockedOut = isFingerprintLockedOut();
final boolean canBypass = mKeyguardBypassController != null
&& mKeyguardBypassController.canBypass();
// There's no reason to ask the HAL for authentication when the user can dismiss the
@@ -2831,15 +2853,22 @@
// Waiting for restart via handleFingerprintError().
return;
}
- mLogger.v("startListeningForFingerprint()");
if (unlockPossible) {
mFingerprintCancelSignal = new CancellationSignal();
- if (isEncryptedOrLockdown(userId)) {
- mFpm.detectFingerprint(mFingerprintCancelSignal, mFingerprintDetectionCallback,
+ if (!isUnlockingWithFingerprintAllowed()) {
+ 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);
+ },
userId);
} else {
+ mLogger.v("startListeningForFingerprint - authenticate");
mFpm.authenticate(null /* crypto */, mFingerprintCancelSignal,
mFingerprintAuthenticationCallback, null /* handler */,
FingerprintManager.SENSOR_ID_ANY, userId, 0 /* flags */);
@@ -3056,11 +3085,15 @@
}
}
+ // Immediately stop previous biometric listening states.
+ // Resetting lockout states updates the biometric listening states.
if (mFaceManager != null && !mFaceSensorProperties.isEmpty()) {
+ stopListeningForFace(FACE_AUTH_UPDATED_USER_SWITCHING);
handleFaceLockoutReset(mFaceManager.getLockoutModeForUser(
mFaceSensorProperties.get(0).sensorId, userId));
}
if (mFpm != null && !mFingerprintSensorProperties.isEmpty()) {
+ stopListeningForFingerprint();
handleFingerprintLockoutReset(mFpm.getLockoutModeForUser(
mFingerprintSensorProperties.get(0).sensorId, userId));
}
@@ -3467,7 +3500,7 @@
@AnyThread
public void setSwitchingUser(boolean switching) {
mSwitchingUser = switching;
- // Since this comes in on a binder thread, we need to post if first
+ // Since this comes in on a binder thread, we need to post it first
mHandler.post(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
FACE_AUTH_UPDATED_USER_SWITCHING));
}
@@ -3559,8 +3592,8 @@
Assert.isMainThread();
mUserFingerprintAuthenticated.clear();
mUserFaceAuthenticated.clear();
- mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FINGERPRINT, unlockedUser);
- mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FACE, unlockedUser);
+ mTrustManager.clearAllBiometricRecognized(FINGERPRINT, unlockedUser);
+ mTrustManager.clearAllBiometricRecognized(FACE, unlockedUser);
mLogger.d("clearBiometricRecognized");
for (int i = 0; i < mCallbacks.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index fc5f447..6ac54fe 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -116,9 +116,9 @@
notificationShadeWindowController.setForcePluginOpen(false, this)
}
- fun showUnlockRipple(biometricSourceType: BiometricSourceType?) {
+ fun showUnlockRipple(biometricSourceType: BiometricSourceType) {
if (!keyguardStateController.isShowing ||
- keyguardUpdateMonitor.userNeedsStrongAuth()) {
+ !keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(biometricSourceType)) {
return
}
@@ -246,7 +246,7 @@
object : KeyguardUpdateMonitorCallback() {
override fun onBiometricAuthenticated(
userId: Int,
- biometricSourceType: BiometricSourceType?,
+ biometricSourceType: BiometricSourceType,
isStrongBiometric: Boolean
) {
if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
@@ -255,14 +255,14 @@
showUnlockRipple(biometricSourceType)
}
- override fun onBiometricAuthFailed(biometricSourceType: BiometricSourceType?) {
+ override fun onBiometricAuthFailed(biometricSourceType: BiometricSourceType) {
if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
mView.retractDwellRipple()
}
}
override fun onBiometricAcquired(
- biometricSourceType: BiometricSourceType?,
+ biometricSourceType: BiometricSourceType,
acquireInfo: Int
) {
if (biometricSourceType == BiometricSourceType.FINGERPRINT &&
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
index 84a8074..2cf5fb9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import android.content.res.ColorStateList
+import android.hardware.biometrics.BiometricSourceType
import android.os.Handler
import android.os.Trace
import android.os.UserHandle
@@ -71,7 +72,7 @@
KeyguardUpdateMonitor.getCurrentUser()
) &&
!needsFullscreenBouncer() &&
- !keyguardUpdateMonitor.userNeedsStrongAuth() &&
+ keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE) &&
!keyguardBypassController.bypassEnabled
/** Runnable to show the primary bouncer. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index aa0757e..000fe14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -240,8 +240,8 @@
&& !mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
KeyguardUpdateMonitor.getCurrentUser())
&& !needsFullscreenBouncer()
- && !mKeyguardUpdateMonitor.isFaceLockedOut()
- && !mKeyguardUpdateMonitor.userNeedsStrongAuth()
+ && mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ BiometricSourceType.FACE)
&& !mKeyguardBypassController.getBypassEnabled()) {
mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY);
} else {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
index 8839662..afd582a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
@@ -63,7 +63,6 @@
credentialAttempted = false,
deviceInteractive = false,
dreaming = false,
- encryptedOrLockdown = false,
fingerprintDisabled = false,
fingerprintLockedOut = false,
goingToSleep = false,
@@ -74,6 +73,7 @@
primaryUser = false,
shouldListenSfpsState = false,
shouldListenForFingerprintAssistant = false,
+ strongerAuthRequired = false,
switchingUser = false,
udfps = false,
userDoesNotHaveTrust = false
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index 4d58b09..e39b9b5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -379,9 +379,9 @@
}
@Test
- public void onBouncerVisibilityChanged_needsStrongAuth_sideFpsHintHidden() {
+ public void onBouncerVisibilityChanged_unlockingWithFingerprintNotAllowed_sideFpsHintHidden() {
setupConditionsToEnableSideFpsHint();
- setNeedsStrongAuth(true);
+ setUnlockingWithFingerprintAllowed(false);
reset(mSideFpsController);
mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
@@ -574,7 +574,7 @@
attachView();
setSideFpsHintEnabledFromResources(true);
setFingerprintDetectionRunning(true);
- setNeedsStrongAuth(false);
+ setUnlockingWithFingerprintAllowed(true);
}
private void attachView() {
@@ -593,9 +593,8 @@
enabled);
}
- private void setNeedsStrongAuth(boolean needed) {
- when(mKeyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(needed);
- mKeyguardUpdateMonitorCallback.getValue().onStrongAuthStateChanged(/* userId= */ 0);
+ private void setUnlockingWithFingerprintAllowed(boolean allowed) {
+ when(mKeyguardUpdateMonitor.isUnlockingWithFingerprintAllowed()).thenReturn(allowed);
}
private void setupGetSecurityView() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 7231b34..63e1603 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -28,6 +28,7 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
import static com.android.keyguard.FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED;
+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.getCurrentUser;
@@ -281,7 +282,6 @@
componentInfo, FaceSensorProperties.TYPE_UNKNOWN,
false /* supportsFaceDetection */, true /* supportsSelfIllumination */,
false /* resetLockoutRequiresChallenge */));
-
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(List.of(
@@ -594,30 +594,13 @@
}
@Test
- public void testFingerprintDoesNotAuth_whenEncrypted() {
- testFingerprintWhenStrongAuth(
- STRONG_AUTH_REQUIRED_AFTER_BOOT);
- }
-
- @Test
- public void testFingerprintDoesNotAuth_whenDpmLocked() {
- testFingerprintWhenStrongAuth(
- KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW);
- }
-
- @Test
- public void testFingerprintDoesNotAuth_whenUserLockdown() {
- testFingerprintWhenStrongAuth(
- KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
- }
-
- private void testFingerprintWhenStrongAuth(int strongAuth) {
+ public void testOnlyDetectFingerprint_whenFingerprintUnlockNotAllowed() {
// Clear invocations, since previous setup (e.g. registering BiometricManager callbacks)
// will trigger updateBiometricListeningState();
clearInvocations(mFingerprintManager);
mKeyguardUpdateMonitor.resetBiometricListeningState();
- when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(strongAuth);
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
mTestableLooper.processAllMessages();
@@ -928,10 +911,6 @@
faceLockoutMode != BiometricConstants.BIOMETRIC_LOCKOUT_NONE;
final boolean fpLocked =
fingerprintLockoutMode != BiometricConstants.BIOMETRIC_LOCKOUT_NONE;
- when(mFingerprintManager.getLockoutModeForUser(eq(FINGERPRINT_SENSOR_ID), eq(newUser)))
- .thenReturn(fingerprintLockoutMode);
- when(mFaceManager.getLockoutModeForUser(eq(FACE_SENSOR_ID), eq(newUser)))
- .thenReturn(faceLockoutMode);
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mTestableLooper.processAllMessages();
@@ -940,7 +919,13 @@
verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
anyInt());
+// resetFaceManager();
+// resetFingerprintManager();
+ when(mFingerprintManager.getLockoutModeForUser(eq(FINGERPRINT_SENSOR_ID), eq(newUser)))
+ .thenReturn(fingerprintLockoutMode);
+ when(mFaceManager.getLockoutModeForUser(eq(FACE_SENSOR_ID), eq(newUser)))
+ .thenReturn(faceLockoutMode);
final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
final CancellationSignal fpCancel = spy(mKeyguardUpdateMonitor.mFingerprintCancelSignal);
mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
@@ -951,14 +936,22 @@
mKeyguardUpdateMonitor.handleUserSwitchComplete(newUser);
mTestableLooper.processAllMessages();
- verify(faceCancel, faceLocked ? times(1) : never()).cancel();
- verify(fpCancel, fpLocked ? times(1) : never()).cancel();
- verify(callback, faceLocked ? times(1) : never()).onBiometricRunningStateChanged(
+ // THEN face and fingerprint listening are always cancelled immediately
+ verify(faceCancel).cancel();
+ verify(callback).onBiometricRunningStateChanged(
eq(false), eq(BiometricSourceType.FACE));
- verify(callback, fpLocked ? times(1) : never()).onBiometricRunningStateChanged(
+ verify(fpCancel).cancel();
+ verify(callback).onBiometricRunningStateChanged(
eq(false), eq(BiometricSourceType.FINGERPRINT));
+
+ // THEN locked out states are updated
assertThat(mKeyguardUpdateMonitor.isFingerprintLockedOut()).isEqualTo(fpLocked);
assertThat(mKeyguardUpdateMonitor.isFaceLockedOut()).isEqualTo(faceLocked);
+
+ // Fingerprint should be restarted once its cancelled bc on lockout, the device
+ // can still detectFingerprint (and if it's not locked out, fingerprint can listen)
+ assertThat(mKeyguardUpdateMonitor.mFingerprintRunningState)
+ .isEqualTo(BIOMETRIC_STATE_CANCELLING_RESTARTING);
}
@Test
@@ -1144,9 +1137,8 @@
// GIVEN status bar state is on the keyguard
mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD);
- // WHEN user hasn't authenticated since last boot
- when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
- .thenReturn(STRONG_AUTH_REQUIRED_AFTER_BOOT);
+ // WHEN user hasn't authenticated since last boot, cannot unlock with FP
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
// THEN we shouldn't listen for udfps
assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(false);
@@ -1258,8 +1250,7 @@
when(mStrongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true);
// WHEN device in lock down
- when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(
- KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
// THEN we shouldn't listen for udfps
assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index 0b528a5..eb8c823 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -37,7 +37,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.leak.RotationUtils
-import javax.inject.Provider
+import com.android.systemui.util.mockito.any
import org.junit.After
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -46,15 +46,16 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
-import org.mockito.Mockito.any
+import org.mockito.Mockito.`when`
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
+import javax.inject.Provider
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -118,12 +119,13 @@
@Test
fun testFingerprintTrigger_KeyguardShowing_Ripple() {
- // GIVEN fp exists, keyguard is showing, user doesn't need strong auth
+ // GIVEN fp exists, keyguard is showing, unlocking with fp allowed
val fpsLocation = Point(5, 5)
`when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
controller.onViewAttached()
`when`(keyguardStateController.isShowing).thenReturn(true)
- `when`(keyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(false)
+ `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ eq(BiometricSourceType.FINGERPRINT))).thenReturn(true)
// WHEN fingerprint authenticated
val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
@@ -140,11 +142,12 @@
@Test
fun testFingerprintTrigger_KeyguardNotShowing_NoRipple() {
- // GIVEN fp exists & user doesn't need strong auth
+ // GIVEN fp exists & unlocking with fp allowed
val fpsLocation = Point(5, 5)
`when`(authController.udfpsLocation).thenReturn(fpsLocation)
controller.onViewAttached()
- `when`(keyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(false)
+ `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ eq(BiometricSourceType.FINGERPRINT))).thenReturn(true)
// WHEN keyguard is NOT showing & fingerprint authenticated
`when`(keyguardStateController.isShowing).thenReturn(false)
@@ -160,15 +163,16 @@
}
@Test
- fun testFingerprintTrigger_StrongAuthRequired_NoRipple() {
+ fun testFingerprintTrigger_biometricUnlockNotAllowed_NoRipple() {
// GIVEN fp exists & keyguard is showing
val fpsLocation = Point(5, 5)
`when`(authController.udfpsLocation).thenReturn(fpsLocation)
controller.onViewAttached()
`when`(keyguardStateController.isShowing).thenReturn(true)
- // WHEN user needs strong auth & fingerprint authenticated
- `when`(keyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(true)
+ // WHEN unlocking with fingerprint is NOT allowed & fingerprint authenticated
+ `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ eq(BiometricSourceType.FINGERPRINT))).thenReturn(false)
val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
verify(keyguardUpdateMonitor).registerCallback(captor.capture())
captor.value.onBiometricAuthenticated(
@@ -182,13 +186,14 @@
@Test
fun testFaceTriggerBypassEnabled_Ripple() {
- // GIVEN face auth sensor exists, keyguard is showing & strong auth isn't required
+ // GIVEN face auth sensor exists, keyguard is showing & unlocking with face is allowed
val faceLocation = Point(5, 5)
`when`(authController.faceSensorLocation).thenReturn(faceLocation)
controller.onViewAttached()
`when`(keyguardStateController.isShowing).thenReturn(true)
- `when`(keyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(false)
+ `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ BiometricSourceType.FACE)).thenReturn(true)
// WHEN bypass is enabled & face authenticated
`when`(bypassController.canBypass()).thenReturn(true)
@@ -275,6 +280,8 @@
`when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
controller.onViewAttached()
`when`(keyguardStateController.isShowing).thenReturn(true)
+ `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ BiometricSourceType.FINGERPRINT)).thenReturn(true)
`when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
controller.showUnlockRipple(BiometricSourceType.FINGERPRINT)
@@ -295,6 +302,8 @@
`when`(keyguardStateController.isShowing).thenReturn(true)
`when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
`when`(authController.isUdfpsFingerDown).thenReturn(true)
+ `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ eq(BiometricSourceType.FACE))).thenReturn(true)
controller.showUnlockRipple(BiometricSourceType.FACE)
assertTrue("reveal didn't start on keyguardFadingAway",
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index d3b5418..df7ee43 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -39,6 +39,7 @@
import android.content.res.ColorStateList;
import android.graphics.Color;
+import android.hardware.biometrics.BiometricSourceType;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -398,6 +399,8 @@
@Test
public void testShow_delaysIfFaceAuthIsRunning() {
+ when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE))
+ .thenReturn(true);
when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
mBouncer.show(true /* reset */);
@@ -410,9 +413,10 @@
}
@Test
- public void testShow_doesNotDelaysIfFaceAuthIsLockedOut() {
+ public void testShow_doesNotDelaysIfFaceAuthIsNotAllowed() {
when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
- when(mKeyguardUpdateMonitor.isFaceLockedOut()).thenReturn(true);
+ when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE))
+ .thenReturn(false);
mBouncer.show(true /* reset */);
verify(mHandler, never()).postDelayed(any(), anyLong());