diff options
10 files changed, 135 insertions, 7 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index e24f07c21076..b56d189d3ab3 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -801,13 +801,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mHandler.postDelayed(mRetryFingerprintAuthentication, HAL_ERROR_RETRY_TIMEOUT); } + boolean lockedOutStateChanged = false; if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) { + lockedOutStateChanged |= !mFingerprintLockedOutPermanent; mFingerprintLockedOutPermanent = true; requireStrongAuthIfAllLockedOut(); } if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT || msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) { + lockedOutStateChanged |= !mFingerprintLockedOut; mFingerprintLockedOut = true; if (isUdfpsEnrolled()) { updateFingerprintListeningState(); @@ -820,9 +823,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab cb.onBiometricError(msgId, errString, BiometricSourceType.FINGERPRINT); } } + + if (lockedOutStateChanged) { + notifyLockedOutStateChanged(BiometricSourceType.FINGERPRINT); + } } private void handleFingerprintLockoutReset() { + boolean changed = mFingerprintLockedOut || mFingerprintLockedOutPermanent; mFingerprintLockedOut = false; mFingerprintLockedOutPermanent = false; @@ -837,6 +845,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } else { updateFingerprintListeningState(); } + + if (changed) { + notifyLockedOutStateChanged(BiometricSourceType.FINGERPRINT); + } } private void setFingerprintRunningState(int fingerprintRunningState) { @@ -999,7 +1011,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } + boolean lockedOutStateChanged = false; if (msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) { + lockedOutStateChanged = !mFaceLockedOutPermanent; mFaceLockedOutPermanent = true; requireStrongAuthIfAllLockedOut(); } @@ -1011,11 +1025,21 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab BiometricSourceType.FACE); } } + + if (lockedOutStateChanged) { + notifyLockedOutStateChanged(BiometricSourceType.FACE); + } } private void handleFaceLockoutReset() { + boolean changed = mFaceLockedOutPermanent; mFaceLockedOutPermanent = false; + updateFaceListeningState(); + + if (changed) { + notifyLockedOutStateChanged(BiometricSourceType.FACE); + } } private void setFaceRunningState(int faceRunningState) { @@ -1237,6 +1261,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } + private void notifyLockedOutStateChanged(BiometricSourceType type) { + Assert.isMainThread(); + for (int i = 0; i < mCallbacks.size(); i++) { + KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); + if (cb != null) { + cb.onLockedOutStateChanged(type); + } + } + } + public boolean isScreenOn() { return mScreenOn; } @@ -2454,6 +2488,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } + public boolean isFingerprintLockedOut() { + return mFingerprintLockedOut || mFingerprintLockedOutPermanent; + } + /** * If biometrics hardware is available, not disabled, and user has enrolled templates. * This does NOT check if the device is encrypted or in lockdown. diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java index 12431984c9b9..8170a81a09e6 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java @@ -292,6 +292,11 @@ public class KeyguardUpdateMonitorCallback { public void onStrongAuthStateChanged(int userId) { } /** + * When the current user's locked out state changed. + */ + public void onLockedOutStateChanged(BiometricSourceType biometricSourceType) { } + + /** * Called when the dream's window state is changed. * @param dreaming true if the dream's window has been created and is visible */ diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java index 223eb78044c4..8f4d6f6aa973 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java @@ -255,7 +255,6 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud private void maybeShowInputBouncer() { if (mShowingUdfpsBouncer && hasUdfpsBouncerShownWithMinTime()) { mKeyguardViewManager.showBouncer(true); - mKeyguardViewManager.resetAlternateAuth(false); } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 7f9eae8bd55f..8d0733645117 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -793,7 +793,8 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, return KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN; } else if (trust && (strongAuth & SOME_AUTH_REQUIRED_AFTER_USER_REQUEST) != 0) { return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST; - } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0) { + } else if (any && ((strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0 + || mUpdateMonitor.isFingerprintLockedOut())) { return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT; } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE) != 0) { return KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index 67f51cb9ab43..aa3b3e12b8f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.content.Context; import android.content.res.Resources; import android.hardware.biometrics.BiometricSourceType; +import android.hardware.fingerprint.FingerprintManager; import android.metrics.LogMaker; import android.os.Handler; import android.os.PowerManager; @@ -46,9 +47,11 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationShadeWindowController; +import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.policy.KeyguardStateController; import java.io.FileDescriptor; @@ -71,6 +74,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000; private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock:wakelock"; private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl(); + private static final int FP_ATTEMPTS_BEFORE_SHOW_BOUNCER = 3; @IntDef(prefix = { "MODE_" }, value = { MODE_NONE, @@ -167,6 +171,10 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp private final MetricsLogger mMetricsLogger; private final AuthController mAuthController; + private final StatusBarStateController mStatusBarStateController; + + private long mLastFpFailureUptimeMillis; + private int mNumConsecutiveFpFailures; private static final class PendingAuthenticated { public final int userId; @@ -209,7 +217,10 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp BIOMETRIC_IRIS_FAILURE(403), @UiEvent(doc = "A biometric event of type iris errored.") - BIOMETRIC_IRIS_ERROR(404); + BIOMETRIC_IRIS_ERROR(404), + + @UiEvent(doc = "Bouncer was shown as a result of consecutive failed UDFPS attempts.") + BIOMETRIC_BOUNCER_SHOWN(916); private final int mId; @@ -257,7 +268,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp NotificationMediaManager notificationMediaManager, WakefulnessLifecycle wakefulnessLifecycle, ScreenLifecycle screenLifecycle, - AuthController authController) { + AuthController authController, + StatusBarStateController statusBarStateController) { mContext = context; mPowerManager = powerManager; mShadeController = shadeController; @@ -279,6 +291,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp mKeyguardBypassController.setUnlockController(this); mMetricsLogger = metricsLogger; mAuthController = authController; + mStatusBarStateController = statusBarStateController; dumpManager.registerDumpable(getClass().getName(), this); } @@ -620,6 +633,22 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp .setType(MetricsEvent.TYPE_FAILURE).setSubtype(toSubtype(biometricSourceType))); Optional.ofNullable(BiometricUiEvent.FAILURE_EVENT_BY_SOURCE_TYPE.get(biometricSourceType)) .ifPresent(UI_EVENT_LOGGER::log); + + long currUptimeMillis = SystemClock.uptimeMillis(); + if (currUptimeMillis - mLastFpFailureUptimeMillis < 2000) { // attempt within 2 seconds + mNumConsecutiveFpFailures += 1; + } else { + mNumConsecutiveFpFailures = 1; + } + mLastFpFailureUptimeMillis = currUptimeMillis; + + if (biometricSourceType.equals(BiometricSourceType.FINGERPRINT) + && mUpdateMonitor.isUdfpsSupported() + && mNumConsecutiveFpFailures >= FP_ATTEMPTS_BEFORE_SHOW_BOUNCER) { + mKeyguardViewController.showBouncer(true); + UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN); + mNumConsecutiveFpFailures = 0; + } cleanup(); } @@ -631,6 +660,16 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp .addTaggedData(MetricsEvent.FIELD_BIOMETRIC_AUTH_ERROR, msgId)); Optional.ofNullable(BiometricUiEvent.ERROR_EVENT_BY_SOURCE_TYPE.get(biometricSourceType)) .ifPresent(UI_EVENT_LOGGER::log); + + // if we're on the shade and we're locked out, immediately show the bouncer + if (biometricSourceType == BiometricSourceType.FINGERPRINT + && (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT + || msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) + && mUpdateMonitor.isUdfpsSupported() + && (mStatusBarStateController.getState() == StatusBarState.SHADE + || mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED)) { + mKeyguardViewController.showBouncer(true); + } cleanup(); } @@ -664,6 +703,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp mBiometricModeListener.onResetMode(); mBiometricModeListener.notifyBiometricAuthModeChanged(); } + mNumConsecutiveFpFailures = 0; + mLastFpFailureUptimeMillis = 0; } @VisibleForTesting 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 353868ba969f..9647486be992 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -21,6 +21,7 @@ import static com.android.systemui.plugins.ActivityStarter.OnDismissAction; import android.content.Context; import android.content.res.ColorStateList; +import android.hardware.biometrics.BiometricSourceType; import android.os.Handler; import android.os.UserHandle; import android.os.UserManager; @@ -81,6 +82,13 @@ public class KeyguardBouncer { public void onStrongAuthStateChanged(int userId) { mBouncerPromptReason = mCallback.getBouncerPromptReason(); } + + @Override + public void onLockedOutStateChanged(BiometricSourceType type) { + if (type == BiometricSourceType.FINGERPRINT) { + mBouncerPromptReason = mCallback.getBouncerPromptReason(); + } + } }; private final Runnable mRemoveViewRunnable = this::removeView; private final KeyguardBypassController mKeyguardBypassController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 7ab4a1ec237e..0d23d663c51c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -441,6 +441,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * dragging it and translation should be deferred {@see KeyguardBouncer#show(boolean, boolean)} */ public void showBouncer(boolean scrimmed) { + resetAlternateAuth(false); + if (mShowing && !mBouncer.isShowing()) { mBouncer.show(false /* resetSecuritySelection */, scrimmed); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java index 0e86964147d7..1cf21ac40e31 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java @@ -351,9 +351,8 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { mSystemClock.advanceTime(205); mController.onTouchOutsideView(); - // THEN show the bouncer and reset alt auth + // THEN show the bouncer verify(mStatusBarKeyguardViewManager).showBouncer(eq(true)); - verify(mStatusBarKeyguardViewManager).resetAlternateAuth(anyBoolean()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java index 25fd80133897..07debe68e224 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java @@ -45,6 +45,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -101,6 +102,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { private WakefulnessLifecycle mWakefulnessLifecycle; @Mock private ScreenLifecycle mScreenLifecycle; + @Mock + private StatusBarStateController mStatusBarStateController; private BiometricUnlockController mBiometricUnlockController; @Before @@ -123,7 +126,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { mUpdateMonitor, res.getResources(), mKeyguardBypassController, mDozeParameters, mMetricsLogger, mDumpManager, mPowerManager, mNotificationMediaManager, mWakefulnessLifecycle, mScreenLifecycle, - mAuthController); + mAuthController, mStatusBarStateController); mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager); mBiometricUnlockController.setBiometricModeListener(mBiometricModeListener); } @@ -378,6 +381,23 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { } @Test + public void onUdfpsConsecutivelyFailedThreeTimes_showBouncer() { + // GIVEN UDFPS is supported + when(mUpdateMonitor.isUdfpsSupported()).thenReturn(true); + + // WHEN udfps fails twice - then don't show the bouncer + mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT); + mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT); + verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean()); + + // WHEN udfps fails the third time + mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT); + + // THEN show the bouncer + verify(mStatusBarKeyguardViewManager).showBouncer(true); + } + + @Test public void onFinishedGoingToSleep_authenticatesWhenPending() { when(mUpdateMonitor.isGoingToSleep()).thenReturn(true); mBiometricUnlockController.onFinishedGoingToSleep(-1); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 6f174cbe0021..c5bdfed6082b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -374,6 +374,21 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test + public void testHideAltAuth_onShowBouncer() { + // GIVEN alt auth is showing + mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor); + when(mBouncer.isShowing()).thenReturn(false); + when(mAlternateAuthInterceptor.isShowingAlternateAuthBouncer()).thenReturn(true); + reset(mAlternateAuthInterceptor); + + // WHEN showBouncer is called + mStatusBarKeyguardViewManager.showBouncer(true); + + // THEN alt bouncer should be hidden + verify(mAlternateAuthInterceptor).hideAlternateAuthBouncer(); + } + + @Test public void testUpdateResources_delegatesToBouncer() { mStatusBarKeyguardViewManager.updateResources(); |