Loading core/res/res/values/strings.xml +2 −2 Original line number Diff line number Diff line Loading @@ -1970,8 +1970,8 @@ <string name="face_error_user_canceled">Face Unlock canceled by user</string> <!-- Generic error message shown when the face operation fails because too many attempts have been made. [CHAR LIMIT=50] --> <string name="face_error_lockout">Too many attempts. Try again later.</string> <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=90] --> <string name="face_error_lockout_permanent">Too many attempts. Face Unlock disabled.</string> <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=100] --> <string name="face_error_lockout_permanent">Too many attempts. Face Unlock unavailable.</string> <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=90] --> <string name="face_error_lockout_screen_lock">Too many attempts. Enter screen lock instead.</string> <!-- Generic error message shown when the face hardware can't recognize the face. [CHAR LIMIT=50] --> Loading packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt +16 −1 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -27,6 +29,7 @@ class AuthBiometricFingerprintAndFaceView( context: Context, attrs: AttributeSet? ) : AuthBiometricFingerprintView(context, attrs) { var isFaceClass3 = false constructor (context: Context) : this(context, null) Loading @@ -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<Int>) = 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 */ ) } packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java +5 −4 Original line number Diff line number Diff line Loading @@ -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; } Loading Loading @@ -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; } Loading @@ -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; } Loading Loading @@ -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) { Loading packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +3 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 = Loading packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt +46 −1 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -98,7 +101,7 @@ class AuthBiometricFingerprintAndFaceViewTest : SysuiTestCase() { } @Test fun ignoresFaceErrors() { fun ignoresFaceErrors_faceIsNotClass3_notLockoutError() { biometricView.onDialogAnimatedIn() biometricView.onError(TYPE_FACE, "not a face") waitForIdleSync() Loading @@ -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() } Loading
core/res/res/values/strings.xml +2 −2 Original line number Diff line number Diff line Loading @@ -1970,8 +1970,8 @@ <string name="face_error_user_canceled">Face Unlock canceled by user</string> <!-- Generic error message shown when the face operation fails because too many attempts have been made. [CHAR LIMIT=50] --> <string name="face_error_lockout">Too many attempts. Try again later.</string> <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=90] --> <string name="face_error_lockout_permanent">Too many attempts. Face Unlock disabled.</string> <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=100] --> <string name="face_error_lockout_permanent">Too many attempts. Face Unlock unavailable.</string> <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=90] --> <string name="face_error_lockout_screen_lock">Too many attempts. Enter screen lock instead.</string> <!-- Generic error message shown when the face hardware can't recognize the face. [CHAR LIMIT=50] --> Loading
packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt +16 −1 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -27,6 +29,7 @@ class AuthBiometricFingerprintAndFaceView( context: Context, attrs: AttributeSet? ) : AuthBiometricFingerprintView(context, attrs) { var isFaceClass3 = false constructor (context: Context) : this(context, null) Loading @@ -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<Int>) = 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 */ ) }
packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java +5 −4 Original line number Diff line number Diff line Loading @@ -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; } Loading Loading @@ -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; } Loading @@ -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; } Loading Loading @@ -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) { Loading
packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +3 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 = Loading
packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt +46 −1 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -98,7 +101,7 @@ class AuthBiometricFingerprintAndFaceViewTest : SysuiTestCase() { } @Test fun ignoresFaceErrors() { fun ignoresFaceErrors_faceIsNotClass3_notLockoutError() { biometricView.onDialogAnimatedIn() biometricView.onError(TYPE_FACE, "not a face") waitForIdleSync() Loading @@ -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() }