Always trigger face auth even when it is locked out.
1. This is the only way to show a message on keyguard that face unlock is not available in response to all face unlock triggers.
2. This was the behavior of face auth until recently (b/244402839#comment11)
Other options:
- Add a new callback method to a KeyguardUpateMonitorCallback to indicate that face auth trigger occurred.
- Adding a new method to a long list of methods in KeyguardUpateMonitorCallback is not a good idea
- This would be a very specific API that no one else might even need.
Indication message shown on keyguard:
1. first time face lockout.
- Shows "Too many attempts. Face Unlock disabled." followed by "Use fingerprint instead" or "Swipe up to open"
2. while the face is already locked out
- when the device wakes up
- "Face Unlock unavailable." followed by "Use fingerprint instead" or "Swipe up to open"
3. Coex when both face and fingerprint are locked out
- when the device wakes up
- Existing behavior is retained, nothing is shown.
No difference in behavior between bypass vs non-bypass
Bug: 247688462
Test: atest KeyguardUpdateMonitorTest
Change-Id: I3c7e15818eedfef9b5af9dd0b0fc29b7fda231e1
This commit is contained in:
@@ -403,6 +403,8 @@
|
|||||||
<string name="keyguard_face_failed">Can\u2019t recognize face</string>
|
<string name="keyguard_face_failed">Can\u2019t recognize face</string>
|
||||||
<!-- Message shown to suggest using fingerprint sensor to authenticate after another biometric failed. [CHAR LIMIT=25] -->
|
<!-- Message shown to suggest using fingerprint sensor to authenticate after another biometric failed. [CHAR LIMIT=25] -->
|
||||||
<string name="keyguard_suggest_fingerprint">Use fingerprint instead</string>
|
<string name="keyguard_suggest_fingerprint">Use fingerprint instead</string>
|
||||||
|
<!-- Message shown to inform the user that face unlock is not available. [CHAR LIMIT=25] -->
|
||||||
|
<string name="keyguard_face_unlock_unavailable">Face unlock unavailable.</string>
|
||||||
|
|
||||||
<!-- Content description of the bluetooth icon when connected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
|
<!-- Content description of the bluetooth icon when connected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
|
||||||
<string name="accessibility_bluetooth_connected">Bluetooth connected.</string>
|
<string name="accessibility_bluetooth_connected">Bluetooth connected.</string>
|
||||||
|
|||||||
@@ -798,7 +798,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|
|||||||
mFingerprintCancelSignal = null;
|
mFingerprintCancelSignal = null;
|
||||||
updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
|
updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
|
||||||
FACE_AUTH_UPDATED_FP_AUTHENTICATED);
|
FACE_AUTH_UPDATED_FP_AUTHENTICATED);
|
||||||
mLogger.d("onFingerprintAuthenticated");
|
mLogger.logFingerprintSuccess(userId, isStrongBiometric);
|
||||||
for (int i = 0; i < mCallbacks.size(); i++) {
|
for (int i = 0; i < mCallbacks.size(); i++) {
|
||||||
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
|
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
|
||||||
if (cb != null) {
|
if (cb != null) {
|
||||||
@@ -2716,7 +2716,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|
|||||||
|| containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
|
|| containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
|
||||||
|
|
||||||
// TODO: always disallow when fp is already locked out?
|
// TODO: always disallow when fp is already locked out?
|
||||||
final boolean fpLockedout = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
|
final boolean fpLockedOut = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
|
||||||
|
|
||||||
final boolean canBypass = mKeyguardBypassController != null
|
final boolean canBypass = mKeyguardBypassController != null
|
||||||
&& mKeyguardBypassController.canBypass();
|
&& mKeyguardBypassController.canBypass();
|
||||||
@@ -2745,7 +2745,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|
|||||||
final boolean faceDisabledForUser = isFaceDisabled(user);
|
final boolean faceDisabledForUser = isFaceDisabled(user);
|
||||||
final boolean biometricEnabledForUser = mBiometricEnabledForUser.get(user);
|
final boolean biometricEnabledForUser = mBiometricEnabledForUser.get(user);
|
||||||
final boolean shouldListenForFaceAssistant = shouldListenForFaceAssistant();
|
final boolean shouldListenForFaceAssistant = shouldListenForFaceAssistant();
|
||||||
final boolean fpOrFaceIsLockedOut = isFaceLockedOut() || fpLockedout;
|
|
||||||
|
|
||||||
// Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
|
// Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
|
||||||
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
|
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
|
||||||
@@ -2763,7 +2762,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|
|||||||
&& (!mSecureCameraLaunched || mOccludingAppRequestingFace)
|
&& (!mSecureCameraLaunched || mOccludingAppRequestingFace)
|
||||||
&& !faceAuthenticated
|
&& !faceAuthenticated
|
||||||
&& !mGoingToSleep
|
&& !mGoingToSleep
|
||||||
&& !fpOrFaceIsLockedOut;
|
// We only care about fp locked out state and not face because we still trigger
|
||||||
|
// face auth even when face is locked out to show the user a message that face
|
||||||
|
// unlock was supposed to run but didn't
|
||||||
|
&& !fpLockedOut;
|
||||||
|
|
||||||
// Aggregate relevant fields for debug logging.
|
// Aggregate relevant fields for debug logging.
|
||||||
maybeLogListenerModelData(
|
maybeLogListenerModelData(
|
||||||
@@ -2778,7 +2780,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|
|||||||
faceAuthenticated,
|
faceAuthenticated,
|
||||||
faceDisabledForUser,
|
faceDisabledForUser,
|
||||||
isFaceLockedOut(),
|
isFaceLockedOut(),
|
||||||
fpLockedout,
|
fpLockedOut,
|
||||||
mGoingToSleep,
|
mGoingToSleep,
|
||||||
awakeKeyguard,
|
awakeKeyguard,
|
||||||
mKeyguardGoingAway,
|
mKeyguardGoingAway,
|
||||||
|
|||||||
@@ -153,6 +153,13 @@ class KeyguardUpdateMonitorLogger @Inject constructor(
|
|||||||
{ "fingerprintRunningState: $int1" })
|
{ "fingerprintRunningState: $int1" })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun logFingerprintSuccess(userId: Int, isStrongBiometric: Boolean) {
|
||||||
|
logBuffer.log(TAG, DEBUG, {
|
||||||
|
int1 = userId
|
||||||
|
bool1 = isStrongBiometric
|
||||||
|
}, {"Fingerprint auth successful: userId: $int1, isStrongBiometric: $bool1"})
|
||||||
|
}
|
||||||
|
|
||||||
fun logInvalidSubId(subId: Int) {
|
fun logInvalidSubId(subId: Int) {
|
||||||
logBuffer.log(TAG, INFO,
|
logBuffer.log(TAG, INFO,
|
||||||
{ int1 = subId },
|
{ int1 = subId },
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
|
|||||||
import com.android.settingslib.Utils;
|
import com.android.settingslib.Utils;
|
||||||
import com.android.settingslib.fuelgauge.BatteryStatus;
|
import com.android.settingslib.fuelgauge.BatteryStatus;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
|
import com.android.systemui.biometrics.AuthController;
|
||||||
import com.android.systemui.biometrics.FaceHelpMessageDeferral;
|
import com.android.systemui.biometrics.FaceHelpMessageDeferral;
|
||||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||||
import com.android.systemui.dagger.SysUISingleton;
|
import com.android.systemui.dagger.SysUISingleton;
|
||||||
@@ -137,6 +138,7 @@ public class KeyguardIndicationController {
|
|||||||
private final KeyguardStateController mKeyguardStateController;
|
private final KeyguardStateController mKeyguardStateController;
|
||||||
protected final StatusBarStateController mStatusBarStateController;
|
protected final StatusBarStateController mStatusBarStateController;
|
||||||
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
|
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
|
||||||
|
private final AuthController mAuthController;
|
||||||
private ViewGroup mIndicationArea;
|
private ViewGroup mIndicationArea;
|
||||||
private KeyguardIndicationTextView mTopIndicationView;
|
private KeyguardIndicationTextView mTopIndicationView;
|
||||||
private KeyguardIndicationTextView mLockScreenIndicationView;
|
private KeyguardIndicationTextView mLockScreenIndicationView;
|
||||||
@@ -201,6 +203,7 @@ public class KeyguardIndicationController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
private boolean mFaceLockedOutThisAuthSession;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new KeyguardIndicationController and registers callbacks.
|
* Creates a new KeyguardIndicationController and registers callbacks.
|
||||||
@@ -221,6 +224,7 @@ public class KeyguardIndicationController {
|
|||||||
@Main DelayableExecutor executor,
|
@Main DelayableExecutor executor,
|
||||||
@Background DelayableExecutor bgExecutor,
|
@Background DelayableExecutor bgExecutor,
|
||||||
FalsingManager falsingManager,
|
FalsingManager falsingManager,
|
||||||
|
AuthController authController,
|
||||||
LockPatternUtils lockPatternUtils,
|
LockPatternUtils lockPatternUtils,
|
||||||
ScreenLifecycle screenLifecycle,
|
ScreenLifecycle screenLifecycle,
|
||||||
KeyguardBypassController keyguardBypassController,
|
KeyguardBypassController keyguardBypassController,
|
||||||
@@ -240,6 +244,7 @@ public class KeyguardIndicationController {
|
|||||||
mExecutor = executor;
|
mExecutor = executor;
|
||||||
mBackgroundExecutor = bgExecutor;
|
mBackgroundExecutor = bgExecutor;
|
||||||
mLockPatternUtils = lockPatternUtils;
|
mLockPatternUtils = lockPatternUtils;
|
||||||
|
mAuthController = authController;
|
||||||
mFalsingManager = falsingManager;
|
mFalsingManager = falsingManager;
|
||||||
mKeyguardBypassController = keyguardBypassController;
|
mKeyguardBypassController = keyguardBypassController;
|
||||||
mAccessibilityManager = accessibilityManager;
|
mAccessibilityManager = accessibilityManager;
|
||||||
@@ -755,7 +760,7 @@ public class KeyguardIndicationController {
|
|||||||
* logic.
|
* logic.
|
||||||
*/
|
*/
|
||||||
private void showBiometricMessage(CharSequence biometricMessage,
|
private void showBiometricMessage(CharSequence biometricMessage,
|
||||||
CharSequence biometricMessageFollowUp) {
|
@Nullable CharSequence biometricMessageFollowUp) {
|
||||||
if (TextUtils.equals(biometricMessage, mBiometricMessage)) {
|
if (TextUtils.equals(biometricMessage, mBiometricMessage)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1105,6 +1110,13 @@ public class KeyguardIndicationController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLockedOutStateChanged(BiometricSourceType biometricSourceType) {
|
||||||
|
if (biometricSourceType == FACE && !mKeyguardUpdateMonitor.isFaceLockedOut()) {
|
||||||
|
mFaceLockedOutThisAuthSession = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBiometricError(int msgId, String errString,
|
public void onBiometricError(int msgId, String errString,
|
||||||
BiometricSourceType biometricSourceType) {
|
BiometricSourceType biometricSourceType) {
|
||||||
@@ -1124,8 +1136,10 @@ public class KeyguardIndicationController {
|
|||||||
}
|
}
|
||||||
if (msgId == FaceManager.FACE_ERROR_TIMEOUT) {
|
if (msgId == FaceManager.FACE_ERROR_TIMEOUT) {
|
||||||
handleFaceAuthTimeoutError(deferredFaceMessage);
|
handleFaceAuthTimeoutError(deferredFaceMessage);
|
||||||
|
} else if (isLockoutError(msgId)) {
|
||||||
|
handleFaceLockoutError(errString);
|
||||||
} else {
|
} else {
|
||||||
handleGenericBiometricError(errString);
|
showErrorMessageNowOrLater(errString, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1134,7 +1148,7 @@ public class KeyguardIndicationController {
|
|||||||
debugLog("suppressingFingerprintError msgId=" + msgId
|
debugLog("suppressingFingerprintError msgId=" + msgId
|
||||||
+ " errString= " + errString);
|
+ " errString= " + errString);
|
||||||
} else {
|
} else {
|
||||||
handleGenericBiometricError(errString);
|
showErrorMessageNowOrLater(errString, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1145,7 +1159,7 @@ public class KeyguardIndicationController {
|
|||||||
// pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
|
// pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
|
||||||
// check of whether non-strong biometric is allowed
|
// check of whether non-strong biometric is allowed
|
||||||
return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)
|
return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)
|
||||||
&& msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
|
&& !isLockoutError(msgId))
|
||||||
|| msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED
|
|| msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED
|
||||||
|| msgId == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED
|
|| msgId == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED
|
||||||
|| msgId == FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED);
|
|| msgId == FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED);
|
||||||
@@ -1234,6 +1248,31 @@ public class KeyguardIndicationController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleFaceLockoutError(String errString) {
|
||||||
|
int followupMsgId = canUnlockWithFingerprint() ? R.string.keyguard_suggest_fingerprint
|
||||||
|
: R.string.keyguard_unlock;
|
||||||
|
String followupMessage = mContext.getString(followupMsgId);
|
||||||
|
// Lockout error can happen multiple times in a session because we trigger face auth
|
||||||
|
// even when it is locked out so that the user is aware that face unlock would have
|
||||||
|
// triggered but didn't because it is locked out.
|
||||||
|
|
||||||
|
// On first lockout we show the error message from FaceManager, which tells the user they
|
||||||
|
// had too many unsuccessful attempts.
|
||||||
|
if (!mFaceLockedOutThisAuthSession) {
|
||||||
|
mFaceLockedOutThisAuthSession = true;
|
||||||
|
showErrorMessageNowOrLater(errString, followupMessage);
|
||||||
|
} else if (!mAuthController.isUdfpsFingerDown()) {
|
||||||
|
// On subsequent lockouts, we show a more generic locked out message.
|
||||||
|
showBiometricMessage(mContext.getString(R.string.keyguard_face_unlock_unavailable),
|
||||||
|
followupMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isLockoutError(int msgId) {
|
||||||
|
return msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT
|
||||||
|
|| msgId == FaceManager.FACE_ERROR_LOCKOUT;
|
||||||
|
}
|
||||||
|
|
||||||
private void handleFaceAuthTimeoutError(@Nullable CharSequence deferredFaceMessage) {
|
private void handleFaceAuthTimeoutError(@Nullable CharSequence deferredFaceMessage) {
|
||||||
debugLog("showDeferredFaceMessage msgId=" + deferredFaceMessage);
|
debugLog("showDeferredFaceMessage msgId=" + deferredFaceMessage);
|
||||||
if (canUnlockWithFingerprint()) {
|
if (canUnlockWithFingerprint()) {
|
||||||
@@ -1274,11 +1313,11 @@ public class KeyguardIndicationController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleGenericBiometricError(String errString) {
|
private void showErrorMessageNowOrLater(String errString, @Nullable String followUpMsg) {
|
||||||
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
|
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
|
||||||
mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState);
|
mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState);
|
||||||
} else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
|
} else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
|
||||||
showBiometricMessage(errString);
|
showBiometricMessage(errString, followUpMsg);
|
||||||
} else {
|
} else {
|
||||||
mBiometricErrorMessageToShowOnScreenOn = errString;
|
mBiometricErrorMessageToShowOnScreenOn = errString;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1621,7 +1621,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testShouldListenForFace_whenFaceIsLockedOut_returnsFalse()
|
public void testShouldListenForFace_whenFaceIsLockedOut_returnsTrue()
|
||||||
throws RemoteException {
|
throws RemoteException {
|
||||||
// Preconditions for face auth to run
|
// Preconditions for face auth to run
|
||||||
keyguardNotGoingAway();
|
keyguardNotGoingAway();
|
||||||
@@ -1638,7 +1638,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
|
|||||||
faceAuthLockedOut();
|
faceAuthLockedOut();
|
||||||
mTestableLooper.processAllMessages();
|
mTestableLooper.processAllMessages();
|
||||||
|
|
||||||
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
|
// This is needed beccause we want to show face locked out error message whenever face auth
|
||||||
|
// is supposed to run.
|
||||||
|
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -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 android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK;
|
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK;
|
||||||
|
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT;
|
||||||
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT;
|
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT;
|
||||||
|
|
||||||
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
|
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
|
||||||
@@ -54,6 +55,7 @@ import static org.mockito.Mockito.never;
|
|||||||
import static org.mockito.Mockito.reset;
|
import static org.mockito.Mockito.reset;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.app.Instrumentation;
|
import android.app.Instrumentation;
|
||||||
@@ -88,6 +90,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
|
|||||||
import com.android.settingslib.fuelgauge.BatteryStatus;
|
import com.android.settingslib.fuelgauge.BatteryStatus;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.SysuiTestCase;
|
import com.android.systemui.SysuiTestCase;
|
||||||
|
import com.android.systemui.biometrics.AuthController;
|
||||||
import com.android.systemui.biometrics.FaceHelpMessageDeferral;
|
import com.android.systemui.biometrics.FaceHelpMessageDeferral;
|
||||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||||
import com.android.systemui.dock.DockManager;
|
import com.android.systemui.dock.DockManager;
|
||||||
@@ -173,6 +176,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
|
|||||||
private FaceHelpMessageDeferral mFaceHelpMessageDeferral;
|
private FaceHelpMessageDeferral mFaceHelpMessageDeferral;
|
||||||
@Mock
|
@Mock
|
||||||
private ScreenLifecycle mScreenLifecycle;
|
private ScreenLifecycle mScreenLifecycle;
|
||||||
|
@Mock
|
||||||
|
private AuthController mAuthController;
|
||||||
@Captor
|
@Captor
|
||||||
private ArgumentCaptor<DockManager.AlignmentStateListener> mAlignmentListener;
|
private ArgumentCaptor<DockManager.AlignmentStateListener> mAlignmentListener;
|
||||||
@Captor
|
@Captor
|
||||||
@@ -263,8 +268,9 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
|
|||||||
mWakeLockBuilder,
|
mWakeLockBuilder,
|
||||||
mKeyguardStateController, mStatusBarStateController, mKeyguardUpdateMonitor,
|
mKeyguardStateController, mStatusBarStateController, mKeyguardUpdateMonitor,
|
||||||
mDockManager, mBroadcastDispatcher, mDevicePolicyManager, mIBatteryStats,
|
mDockManager, mBroadcastDispatcher, mDevicePolicyManager, mIBatteryStats,
|
||||||
mUserManager, mExecutor, mExecutor, mFalsingManager, mLockPatternUtils,
|
mUserManager, mExecutor, mExecutor, mFalsingManager,
|
||||||
mScreenLifecycle, mKeyguardBypassController, mAccessibilityManager,
|
mAuthController, mLockPatternUtils, mScreenLifecycle,
|
||||||
|
mKeyguardBypassController, mAccessibilityManager,
|
||||||
mFaceHelpMessageDeferral);
|
mFaceHelpMessageDeferral);
|
||||||
mController.init();
|
mController.init();
|
||||||
mController.setIndicationArea(mIndicationArea);
|
mController.setIndicationArea(mIndicationArea);
|
||||||
@@ -1371,6 +1377,110 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBiometricError_faceLockedOutFirstTime_showsThePassedInMessage() {
|
||||||
|
createController();
|
||||||
|
onFaceLockoutError("first lockout");
|
||||||
|
|
||||||
|
verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE, "first lockout");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBiometricError_faceLockedOutFirstTimeAndFpAllowed_showsTheFpFollowupMessage() {
|
||||||
|
createController();
|
||||||
|
fingerprintUnlockIsPossible();
|
||||||
|
onFaceLockoutError("first lockout");
|
||||||
|
|
||||||
|
verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
|
||||||
|
mContext.getString(R.string.keyguard_suggest_fingerprint));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBiometricError_faceLockedOutFirstTimeAndFpNotAllowed_showsDefaultFollowup() {
|
||||||
|
createController();
|
||||||
|
fingerprintUnlockIsNotPossible();
|
||||||
|
onFaceLockoutError("first lockout");
|
||||||
|
|
||||||
|
verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
|
||||||
|
mContext.getString(R.string.keyguard_unlock));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBiometricError_faceLockedOutSecondTimeInSession_showsUnavailableMessage() {
|
||||||
|
createController();
|
||||||
|
onFaceLockoutError("first lockout");
|
||||||
|
clearInvocations(mRotateTextViewController);
|
||||||
|
|
||||||
|
onFaceLockoutError("second lockout");
|
||||||
|
|
||||||
|
verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE,
|
||||||
|
mContext.getString(R.string.keyguard_face_unlock_unavailable));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBiometricError_faceLockedOutSecondTimeButUdfpsActive_showsNoMessage() {
|
||||||
|
createController();
|
||||||
|
onFaceLockoutError("first lockout");
|
||||||
|
clearInvocations(mRotateTextViewController);
|
||||||
|
|
||||||
|
when(mAuthController.isUdfpsFingerDown()).thenReturn(true);
|
||||||
|
onFaceLockoutError("second lockout");
|
||||||
|
|
||||||
|
verifyNoMoreInteractions(mRotateTextViewController);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBiometricError_faceLockedOutAgainAndFpAllowed_showsTheFpFollowupMessage() {
|
||||||
|
createController();
|
||||||
|
fingerprintUnlockIsPossible();
|
||||||
|
onFaceLockoutError("first lockout");
|
||||||
|
clearInvocations(mRotateTextViewController);
|
||||||
|
|
||||||
|
onFaceLockoutError("second lockout");
|
||||||
|
|
||||||
|
verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
|
||||||
|
mContext.getString(R.string.keyguard_suggest_fingerprint));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBiometricError_faceLockedOutAgainAndFpNotAllowed_showsDefaultFollowup() {
|
||||||
|
createController();
|
||||||
|
fingerprintUnlockIsNotPossible();
|
||||||
|
onFaceLockoutError("first lockout");
|
||||||
|
clearInvocations(mRotateTextViewController);
|
||||||
|
|
||||||
|
onFaceLockoutError("second lockout");
|
||||||
|
|
||||||
|
verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
|
||||||
|
mContext.getString(R.string.keyguard_unlock));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBiometricError_whenFaceLockoutReset_onLockOutError_showsPassedInMessage() {
|
||||||
|
createController();
|
||||||
|
onFaceLockoutError("first lockout");
|
||||||
|
clearInvocations(mRotateTextViewController);
|
||||||
|
when(mKeyguardUpdateMonitor.isFaceLockedOut()).thenReturn(false);
|
||||||
|
mKeyguardUpdateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FACE);
|
||||||
|
|
||||||
|
onFaceLockoutError("second lockout");
|
||||||
|
|
||||||
|
verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE, "second lockout");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBiometricError_whenFaceIsLocked_onMultipleLockOutErrors_showUnavailableMessage() {
|
||||||
|
createController();
|
||||||
|
onFaceLockoutError("first lockout");
|
||||||
|
clearInvocations(mRotateTextViewController);
|
||||||
|
when(mKeyguardUpdateMonitor.isFaceLockedOut()).thenReturn(true);
|
||||||
|
mKeyguardUpdateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FACE);
|
||||||
|
|
||||||
|
onFaceLockoutError("second lockout");
|
||||||
|
|
||||||
|
verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE,
|
||||||
|
mContext.getString(R.string.keyguard_face_unlock_unavailable));
|
||||||
|
}
|
||||||
|
|
||||||
private void sendUpdateDisclosureBroadcast() {
|
private void sendUpdateDisclosureBroadcast() {
|
||||||
mBroadcastReceiver.onReceive(mContext, new Intent());
|
mBroadcastReceiver.onReceive(mContext, new Intent());
|
||||||
@@ -1419,4 +1529,33 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
|
|||||||
anyObject(), anyBoolean());
|
anyObject(), anyBoolean());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void verifyIndicationShown(int indicationType, String message) {
|
||||||
|
verify(mRotateTextViewController)
|
||||||
|
.updateIndication(eq(indicationType),
|
||||||
|
mKeyguardIndicationCaptor.capture(),
|
||||||
|
eq(true));
|
||||||
|
assertThat(mKeyguardIndicationCaptor.getValue().getMessage().toString())
|
||||||
|
.isEqualTo(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fingerprintUnlockIsNotPossible() {
|
||||||
|
setupFingerprintUnlockPossible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fingerprintUnlockIsPossible() {
|
||||||
|
setupFingerprintUnlockPossible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupFingerprintUnlockPossible(boolean possible) {
|
||||||
|
when(mKeyguardUpdateMonitor
|
||||||
|
.getCachedIsUnlockWithFingerprintPossible(KeyguardUpdateMonitor.getCurrentUser()))
|
||||||
|
.thenReturn(possible);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onFaceLockoutError(String errMsg) {
|
||||||
|
mKeyguardUpdateMonitorCallback.onBiometricError(FACE_ERROR_LOCKOUT_PERMANENT,
|
||||||
|
errMsg,
|
||||||
|
BiometricSourceType.FACE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user