diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 947dc2de984170596feba5ebcf91f56d7f1bfbca..ccd0dccd13647e1bb058b5bf41b8002b09d2ffeb 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1970,8 +1970,8 @@ Face Unlock canceled by user Too many attempts. Try again later. - - Too many attempts. Face Unlock disabled. + + Too many attempts. Face Unlock unavailable. Too many attempts. Enter screen lock instead. diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt index 7f5a67f8c3275334e4c22c600f45b0e3986e6141..57ffd240065dd2faee94c7daf3dc095645519784 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt @@ -19,6 +19,8 @@ package com.android.systemui.biometrics import android.content.Context import android.hardware.biometrics.BiometricAuthenticator.Modality import android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE +import android.hardware.biometrics.BiometricConstants +import android.hardware.face.FaceManager import android.util.AttributeSet import com.android.systemui.R @@ -27,6 +29,7 @@ class AuthBiometricFingerprintAndFaceView( context: Context, attrs: AttributeSet? ) : AuthBiometricFingerprintView(context, attrs) { + var isFaceClass3 = false constructor (context: Context) : this(context, null) @@ -34,10 +37,22 @@ class AuthBiometricFingerprintAndFaceView( override fun forceRequireConfirmation(@Modality modality: Int) = modality == TYPE_FACE - override fun ignoreUnsuccessfulEventsFrom(@Modality modality: Int) = modality == TYPE_FACE + override fun ignoreUnsuccessfulEventsFrom(@Modality modality: Int, unsuccessfulReason: String) = + modality == TYPE_FACE && !(isFaceClass3 && isLockoutErrorString(unsuccessfulReason)) override fun onPointerDown(failedModalities: Set) = failedModalities.contains(TYPE_FACE) override fun createIconController(): AuthIconController = AuthBiometricFingerprintAndFaceIconController(mContext, mIconView, mIconViewOverlay) + + private fun isLockoutErrorString(unsuccessfulReason: String) = + unsuccessfulReason == FaceManager.getErrorString( + mContext, + BiometricConstants.BIOMETRIC_ERROR_LOCKOUT, + 0 /*vendorCode */ + ) || unsuccessfulReason == FaceManager.getErrorString( + mContext, + BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT, + 0 /*vendorCode */ + ) } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java index 13bb6d345dbd5dec28af2b1b924237fdd6cd3799..e04dd068006072d3225bd14ed90f3a07a50728b1 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java @@ -258,7 +258,8 @@ public abstract class AuthBiometricView extends LinearLayout { } /** Ignore all events from this (secondary) modality except successful authentication. */ - protected boolean ignoreUnsuccessfulEventsFrom(@Modality int modality) { + protected boolean ignoreUnsuccessfulEventsFrom(@Modality int modality, + String unsuccessfulReason) { return false; } @@ -541,7 +542,7 @@ public abstract class AuthBiometricView extends LinearLayout { */ public void onAuthenticationFailed( @Modality int modality, @Nullable String failureReason) { - if (ignoreUnsuccessfulEventsFrom(modality)) { + if (ignoreUnsuccessfulEventsFrom(modality, failureReason)) { return; } @@ -556,7 +557,7 @@ public abstract class AuthBiometricView extends LinearLayout { * @param error message */ public void onError(@Modality int modality, String error) { - if (ignoreUnsuccessfulEventsFrom(modality)) { + if (ignoreUnsuccessfulEventsFrom(modality, error)) { return; } @@ -586,7 +587,7 @@ public abstract class AuthBiometricView extends LinearLayout { * @param help message */ public void onHelp(@Modality int modality, String help) { - if (ignoreUnsuccessfulEventsFrom(modality)) { + if (ignoreUnsuccessfulEventsFrom(modality, help)) { return; } if (mSize != AuthDialog.SIZE_MEDIUM) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index 517f94fbe93892bce1b0f8292df6bb5d1b8b9a2c..f435b227542f74f655b2e16ceb93c08021197182 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -18,6 +18,7 @@ package com.android.systemui.biometrics; import static android.hardware.biometrics.BiometricManager.BIOMETRIC_MULTI_SENSOR_DEFAULT; import static android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode; +import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG; import static com.android.internal.jank.InteractionJankMonitor.CUJ_BIOMETRIC_PROMPT_TRANSITION; @@ -383,6 +384,8 @@ public class AuthContainerView extends LinearLayout fingerprintAndFaceView.setSensorProperties(fpProperties); fingerprintAndFaceView.setScaleFactorProvider(config.mScaleProvider); fingerprintAndFaceView.updateOverrideIconLayoutParamsSize(); + fingerprintAndFaceView.setFaceClass3( + faceProperties.sensorStrength == STRENGTH_STRONG); mBiometricView = fingerprintAndFaceView; } else if (fpProperties != null) { final AuthBiometricFingerprintView fpView = diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt index dd87f6e7f64b026fc29ddf08a646dd87c6d36157..f4dacabb18b1848f994765b8057a1c075e3fd423 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt @@ -18,6 +18,8 @@ package com.android.systemui.biometrics import android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE import android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT +import android.hardware.biometrics.BiometricConstants +import android.hardware.face.FaceManager import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper import android.view.View @@ -34,6 +36,7 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.never import org.mockito.Mockito.verify +import org.mockito.Mockito.times import org.mockito.junit.MockitoJUnit @@ -98,7 +101,7 @@ class AuthBiometricFingerprintAndFaceViewTest : SysuiTestCase() { } @Test - fun ignoresFaceErrors() { + fun ignoresFaceErrors_faceIsNotClass3_notLockoutError() { biometricView.onDialogAnimatedIn() biometricView.onError(TYPE_FACE, "not a face") waitForIdleSync() @@ -113,5 +116,47 @@ class AuthBiometricFingerprintAndFaceViewTest : SysuiTestCase() { verify(callback).onAction(AuthBiometricView.Callback.ACTION_ERROR) } + @Test + fun doNotIgnoresFaceErrors_faceIsClass3_notLockoutError() { + biometricView.isFaceClass3 = true + biometricView.onDialogAnimatedIn() + biometricView.onError(TYPE_FACE, "not a face") + waitForIdleSync() + + assertThat(biometricView.isAuthenticating).isTrue() + verify(callback, never()).onAction(AuthBiometricView.Callback.ACTION_ERROR) + + biometricView.onError(TYPE_FINGERPRINT, "that's a nope") + TestableLooper.get(this).moveTimeForward(1000) + waitForIdleSync() + + verify(callback).onAction(AuthBiometricView.Callback.ACTION_ERROR) + } + + @Test + fun doNotIgnoresFaceErrors_faceIsClass3_lockoutError() { + biometricView.isFaceClass3 = true + biometricView.onDialogAnimatedIn() + biometricView.onError( + TYPE_FACE, + FaceManager.getErrorString( + biometricView.context, + BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT, + 0 /*vendorCode */ + ) + ) + waitForIdleSync() + + assertThat(biometricView.isAuthenticating).isTrue() + verify(callback).onAction(AuthBiometricView.Callback.ACTION_ERROR) + + biometricView.onError(TYPE_FINGERPRINT, "that's a nope") + TestableLooper.get(this).moveTimeForward(1000) + waitForIdleSync() + + verify(callback, times(2)).onAction(AuthBiometricView.Callback.ACTION_ERROR) + } + + override fun waitForIdleSync() = TestableLooper.get(this).processAllMessages() }