Loading services/core/java/com/android/server/biometrics/AuthSession.java +34 −3 Original line number Diff line number Diff line Loading @@ -21,7 +21,10 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRIN import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR_BASE; import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE; import static com.android.server.biometrics.BiometricSensor.STATE_CANCELING; import static com.android.server.biometrics.BiometricSensor.STATE_UNKNOWN; import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTHENTICATED_PENDING_SYSUI; import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_CALLED; import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_IDLE; Loading Loading @@ -439,6 +442,13 @@ public final class AuthSession implements IBinder.DeathRecipient { return false; } final boolean errorLockout = error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT || error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT; if (errorLockout) { cancelAllSensors(sensor -> Utils.isAtLeastStrength(sensorIdToStrength(sensorId), sensor.getCurrentStrength())); } mErrorEscrow = error; mVendorCodeEscrow = vendorCode; Loading Loading @@ -477,8 +487,6 @@ public final class AuthSession implements IBinder.DeathRecipient { case STATE_AUTH_STARTED: case STATE_AUTH_STARTED_UI_SHOWING: { final boolean errorLockout = error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT || error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT; if (isAllowDeviceCredential() && errorLockout) { // SystemUI handles transition from biometric to device credential. mState = STATE_SHOWING_DEVICE_CREDENTIAL; Loading Loading @@ -675,7 +683,9 @@ public final class AuthSession implements IBinder.DeathRecipient { } private boolean pauseSensorIfSupported(int sensorId) { if (sensorIdToModality(sensorId) == TYPE_FACE) { boolean isSensorCancelling = sensorIdToState(sensorId) == STATE_CANCELING; // If the sensor is locked out, canceling sensors operation is handled in onErrorReceived() if (sensorIdToModality(sensorId) == TYPE_FACE && !isSensorCancelling) { cancelAllSensors(sensor -> sensor.id == sensorId); return true; } Loading Loading @@ -948,6 +958,27 @@ public final class AuthSession implements IBinder.DeathRecipient { return TYPE_NONE; } private @BiometricSensor.SensorState int sensorIdToState(int sensorId) { for (BiometricSensor sensor : mPreAuthInfo.eligibleSensors) { if (sensorId == sensor.id) { return sensor.getSensorState(); } } Slog.e(TAG, "Unknown sensor: " + sensorId); return STATE_UNKNOWN; } @BiometricManager.Authenticators.Types private int sensorIdToStrength(int sensorId) { for (BiometricSensor sensor : mPreAuthInfo.eligibleSensors) { if (sensorId == sensor.id) { return sensor.getCurrentStrength(); } } Slog.e(TAG, "Unknown sensor: " + sensorId); return BIOMETRIC_CONVENIENCE; } private String getAcquiredMessageForSensor(int sensorId, int acquiredInfo, int vendorCode) { final @Modality int modality = sensorIdToModality(sensorId); switch (modality) { Loading services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java +36 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_NEGAT import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_CALLED; import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_STARTED; import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_STARTED_UI_SHOWING; import static com.android.server.biometrics.BiometricServiceStateProto.STATE_ERROR_PENDING_SYSUI; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; Loading @@ -48,6 +49,7 @@ import android.app.admin.DevicePolicyManager; import android.app.trust.ITrustManager; import android.content.Context; import android.content.res.Resources; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricManager.Authenticators; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.ComponentInfoInternal; Loading Loading @@ -210,6 +212,40 @@ public class AuthSessionTest { } } @Test public void testOnErrorReceived_lockoutError() throws RemoteException { setupFingerprint(0 /* id */, FingerprintSensorProperties.TYPE_REAR); setupFace(1 /* id */, false /* confirmationAlwaysRequired */, mock(IBiometricAuthenticator.class)); final AuthSession session = createAuthSession(mSensors, false /* checkDevicePolicyManager */, Authenticators.BIOMETRIC_STRONG, TEST_REQUEST_ID, 0 /* operationId */, 0 /* userId */); session.goToInitialState(); for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) { assertEquals(BiometricSensor.STATE_WAITING_FOR_COOKIE, sensor.getSensorState()); session.onCookieReceived( session.mPreAuthInfo.eligibleSensors.get(sensor.id).getCookie()); } assertTrue(session.allCookiesReceived()); assertEquals(STATE_AUTH_STARTED, session.getState()); // Either of strong sensor's lockout should cancel both sensors. final int cookie1 = session.mPreAuthInfo.eligibleSensors.get(0).getCookie(); session.onErrorReceived(0, cookie1, BiometricConstants.BIOMETRIC_ERROR_LOCKOUT, 0); for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) { assertEquals(BiometricSensor.STATE_CANCELING, sensor.getSensorState()); } assertEquals(STATE_ERROR_PENDING_SYSUI, session.getState()); // If the sensor is STATE_CANCELING, delayed onAuthenticationRejected() shouldn't change the // session state to STATE_AUTH_PAUSED. session.onAuthenticationRejected(1); assertEquals(STATE_ERROR_PENDING_SYSUI, session.getState()); } @Test public void testCancelReducesAppetiteForCookies() throws Exception { setupFace(0 /* id */, false /* confirmationAlwaysRequired */, Loading Loading
services/core/java/com/android/server/biometrics/AuthSession.java +34 −3 Original line number Diff line number Diff line Loading @@ -21,7 +21,10 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRIN import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR_BASE; import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE; import static com.android.server.biometrics.BiometricSensor.STATE_CANCELING; import static com.android.server.biometrics.BiometricSensor.STATE_UNKNOWN; import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTHENTICATED_PENDING_SYSUI; import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_CALLED; import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_IDLE; Loading Loading @@ -439,6 +442,13 @@ public final class AuthSession implements IBinder.DeathRecipient { return false; } final boolean errorLockout = error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT || error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT; if (errorLockout) { cancelAllSensors(sensor -> Utils.isAtLeastStrength(sensorIdToStrength(sensorId), sensor.getCurrentStrength())); } mErrorEscrow = error; mVendorCodeEscrow = vendorCode; Loading Loading @@ -477,8 +487,6 @@ public final class AuthSession implements IBinder.DeathRecipient { case STATE_AUTH_STARTED: case STATE_AUTH_STARTED_UI_SHOWING: { final boolean errorLockout = error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT || error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT; if (isAllowDeviceCredential() && errorLockout) { // SystemUI handles transition from biometric to device credential. mState = STATE_SHOWING_DEVICE_CREDENTIAL; Loading Loading @@ -675,7 +683,9 @@ public final class AuthSession implements IBinder.DeathRecipient { } private boolean pauseSensorIfSupported(int sensorId) { if (sensorIdToModality(sensorId) == TYPE_FACE) { boolean isSensorCancelling = sensorIdToState(sensorId) == STATE_CANCELING; // If the sensor is locked out, canceling sensors operation is handled in onErrorReceived() if (sensorIdToModality(sensorId) == TYPE_FACE && !isSensorCancelling) { cancelAllSensors(sensor -> sensor.id == sensorId); return true; } Loading Loading @@ -948,6 +958,27 @@ public final class AuthSession implements IBinder.DeathRecipient { return TYPE_NONE; } private @BiometricSensor.SensorState int sensorIdToState(int sensorId) { for (BiometricSensor sensor : mPreAuthInfo.eligibleSensors) { if (sensorId == sensor.id) { return sensor.getSensorState(); } } Slog.e(TAG, "Unknown sensor: " + sensorId); return STATE_UNKNOWN; } @BiometricManager.Authenticators.Types private int sensorIdToStrength(int sensorId) { for (BiometricSensor sensor : mPreAuthInfo.eligibleSensors) { if (sensorId == sensor.id) { return sensor.getCurrentStrength(); } } Slog.e(TAG, "Unknown sensor: " + sensorId); return BIOMETRIC_CONVENIENCE; } private String getAcquiredMessageForSensor(int sensorId, int acquiredInfo, int vendorCode) { final @Modality int modality = sensorIdToModality(sensorId); switch (modality) { Loading
services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java +36 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_NEGAT import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_CALLED; import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_STARTED; import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_STARTED_UI_SHOWING; import static com.android.server.biometrics.BiometricServiceStateProto.STATE_ERROR_PENDING_SYSUI; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; Loading @@ -48,6 +49,7 @@ import android.app.admin.DevicePolicyManager; import android.app.trust.ITrustManager; import android.content.Context; import android.content.res.Resources; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricManager.Authenticators; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.ComponentInfoInternal; Loading Loading @@ -210,6 +212,40 @@ public class AuthSessionTest { } } @Test public void testOnErrorReceived_lockoutError() throws RemoteException { setupFingerprint(0 /* id */, FingerprintSensorProperties.TYPE_REAR); setupFace(1 /* id */, false /* confirmationAlwaysRequired */, mock(IBiometricAuthenticator.class)); final AuthSession session = createAuthSession(mSensors, false /* checkDevicePolicyManager */, Authenticators.BIOMETRIC_STRONG, TEST_REQUEST_ID, 0 /* operationId */, 0 /* userId */); session.goToInitialState(); for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) { assertEquals(BiometricSensor.STATE_WAITING_FOR_COOKIE, sensor.getSensorState()); session.onCookieReceived( session.mPreAuthInfo.eligibleSensors.get(sensor.id).getCookie()); } assertTrue(session.allCookiesReceived()); assertEquals(STATE_AUTH_STARTED, session.getState()); // Either of strong sensor's lockout should cancel both sensors. final int cookie1 = session.mPreAuthInfo.eligibleSensors.get(0).getCookie(); session.onErrorReceived(0, cookie1, BiometricConstants.BIOMETRIC_ERROR_LOCKOUT, 0); for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) { assertEquals(BiometricSensor.STATE_CANCELING, sensor.getSensorState()); } assertEquals(STATE_ERROR_PENDING_SYSUI, session.getState()); // If the sensor is STATE_CANCELING, delayed onAuthenticationRejected() shouldn't change the // session state to STATE_AUTH_PAUSED. session.onAuthenticationRejected(1); assertEquals(STATE_ERROR_PENDING_SYSUI, session.getState()); } @Test public void testCancelReducesAppetiteForCookies() throws Exception { setupFace(0 /* id */, false /* confirmationAlwaysRequired */, Loading