Merge "Update FP failure bouncer & messaging logic" into tm-qpr-dev am: 5cdb41a8cb

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19681152

Change-Id: I34abd3ae720b26320ab0ac5abc82f89eecb216c3
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Beverly Tai
2022-08-29 14:25:27 +00:00
committed by Automerger Merge Worker
5 changed files with 73 additions and 26 deletions

View File

@@ -206,7 +206,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
*/ */
private static final int BIOMETRIC_STATE_CANCELLING_RESTARTING = 3; private static final int BIOMETRIC_STATE_CANCELLING_RESTARTING = 3;
private static final int BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED = -1; @VisibleForTesting
public static final int BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED = -1;
public static final int BIOMETRIC_HELP_FACE_NOT_RECOGNIZED = -2; public static final int BIOMETRIC_HELP_FACE_NOT_RECOGNIZED = -2;
/** /**

View File

@@ -122,6 +122,7 @@ public class KeyguardIndicationController {
private static final int MSG_HIDE_TRANSIENT = 1; private static final int MSG_HIDE_TRANSIENT = 1;
private static final int MSG_SHOW_ACTION_TO_UNLOCK = 2; private static final int MSG_SHOW_ACTION_TO_UNLOCK = 2;
private static final int MSG_HIDE_BIOMETRIC_MESSAGE = 3; private static final int MSG_HIDE_BIOMETRIC_MESSAGE = 3;
private static final int MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON = 4;
private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300; private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300;
public static final long DEFAULT_HIDE_DELAY_MS = public static final long DEFAULT_HIDE_DELAY_MS =
3500 + KeyguardIndicationTextView.Y_IN_DURATION; 3500 + KeyguardIndicationTextView.Y_IN_DURATION;
@@ -188,9 +189,11 @@ public class KeyguardIndicationController {
} }
}; };
private ScreenLifecycle mScreenLifecycle; private ScreenLifecycle mScreenLifecycle;
private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() { private final ScreenLifecycle.Observer mScreenObserver =
new ScreenLifecycle.Observer() {
@Override @Override
public void onScreenTurnedOn() { public void onScreenTurnedOn() {
mHandler.removeMessages(MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON);
if (mBiometricErrorMessageToShowOnScreenOn != null) { if (mBiometricErrorMessageToShowOnScreenOn != null) {
showBiometricMessage(mBiometricErrorMessageToShowOnScreenOn); showBiometricMessage(mBiometricErrorMessageToShowOnScreenOn);
// We want to keep this message around in case the screen was off // We want to keep this message around in case the screen was off
@@ -259,6 +262,8 @@ public class KeyguardIndicationController {
showActionToUnlock(); showActionToUnlock();
} else if (msg.what == MSG_HIDE_BIOMETRIC_MESSAGE) { } else if (msg.what == MSG_HIDE_BIOMETRIC_MESSAGE) {
hideBiometricMessage(); hideBiometricMessage();
} else if (msg.what == MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON) {
mBiometricErrorMessageToShowOnScreenOn = null;
} }
} }
}; };
@@ -1060,6 +1065,11 @@ public class KeyguardIndicationController {
} else if (showActionToUnlock) { } else if (showActionToUnlock) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_ACTION_TO_UNLOCK), mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_ACTION_TO_UNLOCK),
TRANSIENT_BIOMETRIC_ERROR_TIMEOUT); TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
} else {
mBiometricErrorMessageToShowOnScreenOn = helpString;
mHandler.sendMessageDelayed(
mHandler.obtainMessage(MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON),
1000);
} }
} }

View File

@@ -23,6 +23,7 @@ import android.content.res.Resources;
import android.hardware.biometrics.BiometricFaceConstants; import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricSourceType; import android.hardware.biometrics.BiometricSourceType;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintManager;
import android.metrics.LogMaker; import android.metrics.LogMaker;
import android.os.Handler; import android.os.Handler;
@@ -64,7 +65,6 @@ import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -87,7 +87,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000; 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 String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock:wakelock";
private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl(); private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl();
private static final int FP_ATTEMPTS_BEFORE_SHOW_BOUNCER = 2; private static final int UDFPS_ATTEMPTS_BEFORE_SHOW_BOUNCER = 3;
private static final VibrationEffect SUCCESS_VIBRATION_EFFECT = private static final VibrationEffect SUCCESS_VIBRATION_EFFECT =
VibrationEffect.get(VibrationEffect.EFFECT_CLICK); VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
private static final VibrationEffect ERROR_VIBRATION_EFFECT = private static final VibrationEffect ERROR_VIBRATION_EFFECT =
@@ -450,7 +450,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
// During wake and unlock, we need to draw black before waking up to avoid abrupt // During wake and unlock, we need to draw black before waking up to avoid abrupt
// brightness changes due to display state transitions. // brightness changes due to display state transitions.
Runnable wakeUp = ()-> { Runnable wakeUp = ()-> {
if (!wasDeviceInteractive) { if (!wasDeviceInteractive || mUpdateMonitor.isDreaming()) {
if (DEBUG_BIO_WAKELOCK) { if (DEBUG_BIO_WAKELOCK) {
Log.i(TAG, "bio wakelock: Authenticated, waking up..."); Log.i(TAG, "bio wakelock: Authenticated, waking up...");
} }
@@ -662,7 +662,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
if (!mVibratorHelper.hasVibrator() if (!mVibratorHelper.hasVibrator()
&& (!mUpdateMonitor.isDeviceInteractive() || mUpdateMonitor.isDreaming())) { && (!mUpdateMonitor.isDeviceInteractive() || mUpdateMonitor.isDreaming())) {
startWakeAndUnlock(MODE_SHOW_BOUNCER); startWakeAndUnlock(MODE_ONLY_WAKE);
} else if (biometricSourceType == BiometricSourceType.FINGERPRINT } else if (biometricSourceType == BiometricSourceType.FINGERPRINT
&& mUpdateMonitor.isUdfpsSupported()) { && mUpdateMonitor.isUdfpsSupported()) {
long currUptimeMillis = SystemClock.uptimeMillis(); long currUptimeMillis = SystemClock.uptimeMillis();
@@ -673,7 +673,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
} }
mLastFpFailureUptimeMillis = currUptimeMillis; mLastFpFailureUptimeMillis = currUptimeMillis;
if (mNumConsecutiveFpFailures >= FP_ATTEMPTS_BEFORE_SHOW_BOUNCER) { if (mNumConsecutiveFpFailures >= UDFPS_ATTEMPTS_BEFORE_SHOW_BOUNCER) {
startWakeAndUnlock(MODE_SHOW_BOUNCER); startWakeAndUnlock(MODE_SHOW_BOUNCER);
UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN, getSessionId()); UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN, getSessionId());
mNumConsecutiveFpFailures = 0; mNumConsecutiveFpFailures = 0;
@@ -699,13 +699,13 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
Optional.ofNullable(BiometricUiEvent.ERROR_EVENT_BY_SOURCE_TYPE.get(biometricSourceType)) Optional.ofNullable(BiometricUiEvent.ERROR_EVENT_BY_SOURCE_TYPE.get(biometricSourceType))
.ifPresent(event -> UI_EVENT_LOGGER.log(event, getSessionId())); .ifPresent(event -> UI_EVENT_LOGGER.log(event, getSessionId()));
// if we're on the shade and we're locked out, immediately show the bouncer final boolean fingerprintLockout = biometricSourceType == BiometricSourceType.FINGERPRINT
if (biometricSourceType == BiometricSourceType.FINGERPRINT
&& (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT && (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT
|| msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) || msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT);
&& mUpdateMonitor.isUdfpsSupported() final boolean faceLockout = biometricSourceType == BiometricSourceType.FACE
&& (mStatusBarStateController.getState() == StatusBarState.SHADE && (msgId == FaceManager.FACE_ERROR_LOCKOUT
|| mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED)) { || msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT);
if (fingerprintLockout || faceLockout) {
startWakeAndUnlock(MODE_SHOW_BOUNCER); startWakeAndUnlock(MODE_SHOW_BOUNCER);
UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN, getSessionId()); UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN, getSessionId());
} }

View File

@@ -20,6 +20,7 @@ import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE;
@@ -31,6 +32,7 @@ import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewCont
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRANSIENT; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRANSIENT;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_USER_LOCKED; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_USER_LOCKED;
import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_OFF;
import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON; import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
@@ -178,9 +180,12 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallbackCaptor; private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallbackCaptor;
@Captor @Captor
private ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateControllerCallbackCaptor; private ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateControllerCallbackCaptor;
@Captor
private ArgumentCaptor<ScreenLifecycle.Observer> mScreenObserverCaptor;
private KeyguardStateController.Callback mKeyguardStateControllerCallback; private KeyguardStateController.Callback mKeyguardStateControllerCallback;
private KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback; private KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback;
private StatusBarStateController.StateListener mStatusBarStateListener; private StatusBarStateController.StateListener mStatusBarStateListener;
private ScreenLifecycle.Observer mScreenObserver;
private BroadcastReceiver mBroadcastReceiver; private BroadcastReceiver mBroadcastReceiver;
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
private TestableLooper mTestableLooper; private TestableLooper mTestableLooper;
@@ -273,6 +278,9 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
mKeyguardUpdateMonitorCallbackCaptor.capture()); mKeyguardUpdateMonitorCallbackCaptor.capture());
mKeyguardUpdateMonitorCallback = mKeyguardUpdateMonitorCallbackCaptor.getValue(); mKeyguardUpdateMonitorCallback = mKeyguardUpdateMonitorCallbackCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
mScreenObserver = mScreenObserverCaptor.getValue();
mExecutor.runAllReady(); mExecutor.runAllReady();
reset(mRotateTextViewController); reset(mRotateTextViewController);
} }
@@ -536,6 +544,31 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
assertThat(mTextView.getText()).isNotEqualTo(message); assertThat(mTextView.getText()).isNotEqualTo(message);
} }
@Test
public void transientIndication_visibleWhenWokenUp() {
createController();
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
final String message = "helpMsg";
// GIVEN screen is off
when(mScreenLifecycle.getScreenState()).thenReturn(SCREEN_OFF);
// WHEN fingeprint help message received
mController.setVisible(true);
mController.getKeyguardCallback().onBiometricHelp(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
message, BiometricSourceType.FINGERPRINT);
// THEN message isn't shown right away
verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
// WHEN the screen turns on
mScreenObserver.onScreenTurnedOn();
mTestableLooper.processAllMessages();
// THEN the message is shown
verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, message);
}
@Test @Test
public void transientIndication_visibleWhenDozing_unlessSwipeUp_fromError() { public void transientIndication_visibleWhenDozing_unlessSwipeUp_fromError() {
createController(); createController();

View File

@@ -22,6 +22,8 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset; import static org.mockito.Mockito.reset;
@@ -395,15 +397,19 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
} }
@Test @Test
public void onUdfpsConsecutivelyFailedTwoTimes_showBouncer() { public void onUdfpsConsecutivelyFailedThreeTimes_showBouncer() {
// GIVEN UDFPS is supported // GIVEN UDFPS is supported
when(mUpdateMonitor.isUdfpsSupported()).thenReturn(true); when(mUpdateMonitor.isUdfpsSupported()).thenReturn(true);
// WHEN udfps fails once - then don't show the bouncer // WHEN udfps fails once - then don't show the bouncer yet
mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT); mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean()); verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
// WHEN udfps fails the second time // WHEN udfps fails the second time - then don't show the bouncer yet
mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
// WHEN udpfs fails the third time
mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT); mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
// THEN show the bouncer // THEN show the bouncer
@@ -427,8 +433,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
} }
@Test @Test
public void onFPFailureNoHaptics_notDeviceInteractive_showBouncer() { public void onFPFailureNoHaptics_notInteractive_showLockScreen() {
// GIVEN no vibrator and the screen is off // GIVEN no vibrator and device is dreaming
when(mVibratorHelper.hasVibrator()).thenReturn(false); when(mVibratorHelper.hasVibrator()).thenReturn(false);
when(mUpdateMonitor.isDeviceInteractive()).thenReturn(false); when(mUpdateMonitor.isDeviceInteractive()).thenReturn(false);
when(mUpdateMonitor.isDreaming()).thenReturn(false); when(mUpdateMonitor.isDreaming()).thenReturn(false);
@@ -436,15 +442,12 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
// WHEN FP fails // WHEN FP fails
mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT); mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
// after device is finished waking up // THEN wakeup the device
mBiometricUnlockController.mWakefulnessObserver.onFinishedWakingUp(); verify(mPowerManager).wakeUp(anyLong(), anyInt(), anyString());
// THEN show the bouncer
verify(mStatusBarKeyguardViewManager).showBouncer(true);
} }
@Test @Test
public void onFPFailureNoHaptics_dreaming_showBouncer() { public void onFPFailureNoHaptics_dreaming_showLockScreen() {
// GIVEN no vibrator and device is dreaming // GIVEN no vibrator and device is dreaming
when(mVibratorHelper.hasVibrator()).thenReturn(false); when(mVibratorHelper.hasVibrator()).thenReturn(false);
when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true); when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
@@ -453,7 +456,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
// WHEN FP fails // WHEN FP fails
mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT); mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
// THEN show the bouncer // THEN wakeup the device
verify(mStatusBarKeyguardViewManager).showBouncer(true); verify(mPowerManager).wakeUp(anyLong(), anyInt(), anyString());
} }
} }