Always trigger face auth even when it is locked out. am: af57f130e9 am: 416aa19b03

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

Change-Id: I420107d736db701a120792eaecf8b5224096feab
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Chandru
2022-11-05 18:33:37 +00:00
committed by Automerger Merge Worker
6 changed files with 206 additions and 15 deletions

View File

@@ -403,6 +403,8 @@
<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] -->
<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] -->
<string name="accessibility_bluetooth_connected">Bluetooth connected.</string>

View File

@@ -782,7 +782,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mFingerprintCancelSignal = null;
updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
FACE_AUTH_UPDATED_FP_AUTHENTICATED);
mLogger.d("onFingerprintAuthenticated");
mLogger.logFingerprintSuccess(userId, isStrongBiometric);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -2701,7 +2701,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|| containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
// TODO: always disallow when fp is already locked out?
final boolean fpLockedout = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
final boolean fpLockedOut = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
final boolean canBypass = mKeyguardBypassController != null
&& mKeyguardBypassController.canBypass();
@@ -2730,7 +2730,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
final boolean faceDisabledForUser = isFaceDisabled(user);
final boolean biometricEnabledForUser = mBiometricEnabledForUser.get(user);
final boolean shouldListenForFaceAssistant = shouldListenForFaceAssistant();
final boolean fpOrFaceIsLockedOut = isFaceLockedOut() || fpLockedout;
// Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
@@ -2748,7 +2747,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
&& (!mSecureCameraLaunched || mOccludingAppRequestingFace)
&& !faceAuthenticated
&& !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.
maybeLogListenerModelData(
@@ -2763,7 +2765,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
faceAuthenticated,
faceDisabledForUser,
isFaceLockedOut(),
fpLockedout,
fpLockedOut,
mGoingToSleep,
awakeKeyguard,
mKeyguardGoingAway,

View File

@@ -153,6 +153,13 @@ class KeyguardUpdateMonitorLogger @Inject constructor(
{ "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) {
logBuffer.log(TAG, INFO,
{ int1 = subId },

View File

@@ -79,6 +79,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.R;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FaceHelpMessageDeferral;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
@@ -137,6 +138,7 @@ public class KeyguardIndicationController {
private final KeyguardStateController mKeyguardStateController;
protected final StatusBarStateController mStatusBarStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final AuthController mAuthController;
private ViewGroup mIndicationArea;
private KeyguardIndicationTextView mTopIndicationView;
private KeyguardIndicationTextView mLockScreenIndicationView;
@@ -201,6 +203,7 @@ public class KeyguardIndicationController {
}
}
};
private boolean mFaceLockedOutThisAuthSession;
/**
* Creates a new KeyguardIndicationController and registers callbacks.
@@ -221,6 +224,7 @@ public class KeyguardIndicationController {
@Main DelayableExecutor executor,
@Background DelayableExecutor bgExecutor,
FalsingManager falsingManager,
AuthController authController,
LockPatternUtils lockPatternUtils,
ScreenLifecycle screenLifecycle,
KeyguardBypassController keyguardBypassController,
@@ -240,6 +244,7 @@ public class KeyguardIndicationController {
mExecutor = executor;
mBackgroundExecutor = bgExecutor;
mLockPatternUtils = lockPatternUtils;
mAuthController = authController;
mFalsingManager = falsingManager;
mKeyguardBypassController = keyguardBypassController;
mAccessibilityManager = accessibilityManager;
@@ -755,7 +760,7 @@ public class KeyguardIndicationController {
* logic.
*/
private void showBiometricMessage(CharSequence biometricMessage,
CharSequence biometricMessageFollowUp) {
@Nullable CharSequence biometricMessageFollowUp) {
if (TextUtils.equals(biometricMessage, mBiometricMessage)) {
return;
}
@@ -1105,6 +1110,13 @@ public class KeyguardIndicationController {
}
}
@Override
public void onLockedOutStateChanged(BiometricSourceType biometricSourceType) {
if (biometricSourceType == FACE && !mKeyguardUpdateMonitor.isFaceLockedOut()) {
mFaceLockedOutThisAuthSession = false;
}
}
@Override
public void onBiometricError(int msgId, String errString,
BiometricSourceType biometricSourceType) {
@@ -1124,8 +1136,10 @@ public class KeyguardIndicationController {
}
if (msgId == FaceManager.FACE_ERROR_TIMEOUT) {
handleFaceAuthTimeoutError(deferredFaceMessage);
} else if (isLockoutError(msgId)) {
handleFaceLockoutError(errString);
} else {
handleGenericBiometricError(errString);
showErrorMessageNowOrLater(errString, null);
}
}
@@ -1134,7 +1148,7 @@ public class KeyguardIndicationController {
debugLog("suppressingFingerprintError msgId=" + msgId
+ " errString= " + errString);
} else {
handleGenericBiometricError(errString);
showErrorMessageNowOrLater(errString, null);
}
}
@@ -1145,7 +1159,7 @@ public class KeyguardIndicationController {
// pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
// check of whether non-strong biometric is allowed
return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)
&& msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
&& !isLockoutError(msgId))
|| msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED
|| msgId == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED
|| 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) {
debugLog("showDeferredFaceMessage msgId=" + deferredFaceMessage);
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()) {
mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState);
} else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
showBiometricMessage(errString);
showBiometricMessage(errString, followUpMsg);
} else {
mBiometricErrorMessageToShowOnScreenOn = errString;
}

View File

@@ -1620,7 +1620,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
public void testShouldListenForFace_whenFaceIsLockedOut_returnsFalse()
public void testShouldListenForFace_whenFaceIsLockedOut_returnsTrue()
throws RemoteException {
// Preconditions for face auth to run
keyguardNotGoingAway();
@@ -1637,7 +1637,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
faceAuthLockedOut();
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

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.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
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 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.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.Instrumentation;
@@ -88,6 +90,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FaceHelpMessageDeferral;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager;
@@ -173,6 +176,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
private FaceHelpMessageDeferral mFaceHelpMessageDeferral;
@Mock
private ScreenLifecycle mScreenLifecycle;
@Mock
private AuthController mAuthController;
@Captor
private ArgumentCaptor<DockManager.AlignmentStateListener> mAlignmentListener;
@Captor
@@ -263,8 +268,9 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
mWakeLockBuilder,
mKeyguardStateController, mStatusBarStateController, mKeyguardUpdateMonitor,
mDockManager, mBroadcastDispatcher, mDevicePolicyManager, mIBatteryStats,
mUserManager, mExecutor, mExecutor, mFalsingManager, mLockPatternUtils,
mScreenLifecycle, mKeyguardBypassController, mAccessibilityManager,
mUserManager, mExecutor, mExecutor, mFalsingManager,
mAuthController, mLockPatternUtils, mScreenLifecycle,
mKeyguardBypassController, mAccessibilityManager,
mFaceHelpMessageDeferral);
mController.init();
mController.setIndicationArea(mIndicationArea);
@@ -1374,6 +1380,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() {
mBroadcastReceiver.onReceive(mContext, new Intent());
@@ -1422,4 +1532,33 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
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);
}
}