Merge "Show bouncer after failed UDFPS attempts" into sc-v2-dev
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -291,6 +291,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
|
||||
|
||||
@@ -255,7 +255,6 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
|
||||
private void maybeShowInputBouncer() {
|
||||
if (mShowingUdfpsBouncer && hasUdfpsBouncerShownWithMinTime()) {
|
||||
mKeyguardViewManager.showBouncer(true);
|
||||
mKeyguardViewManager.resetAlternateAuth(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -377,6 +380,23 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
|
||||
.isEqualTo(BiometricUnlockController.MODE_ONLY_WAKE);
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
@@ -373,6 +373,21 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
|
||||
mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing());
|
||||
}
|
||||
|
||||
@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();
|
||||
|
||||
Reference in New Issue
Block a user