diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java index bfd4d6e8fe5da..1ac91672c7dd0 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -355,7 +355,9 @@ public class BiometricScheduler { /** * Creates a new scheduler. + * @param context system_server context. * @param tag for the specific instance of the scheduler. Should be unique. + * @param sensorType the sensorType that this scheduler is handling. * @param gestureAvailabilityDispatcher may be null if the sensor does not support gestures * (such as fingerprint swipe). */ diff --git a/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java b/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java index 18e9cb16e2660..7638a51a3710f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java +++ b/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java @@ -141,6 +141,7 @@ public class CoexCoordinator { } else { // Multi sensor authentication AuthenticationClient udfps = mClientMap.getOrDefault(SENSOR_TYPE_UDFPS, null); + AuthenticationClient face = mClientMap.getOrDefault(SENSOR_TYPE_FACE, null); if (isCurrentFaceAuth(client)) { if (isPointerDown(udfps)) { // Face auth success while UDFPS pointer down. No callback, no haptic. @@ -149,6 +150,14 @@ public class CoexCoordinator { callback.sendHapticFeedback(); callback.sendAuthenticationResult(true /* addAuthTokenIfStrong */); } + } else if (isCurrentUdfps(client)) { + if (isFaceScanning()) { + // UDFPS succeeds while face is still scanning + // Cancel face auth and/or prevent it from invoking haptics/callbacks after + face.cancel(); + } + callback.sendHapticFeedback(); + callback.sendAuthenticationResult(true /* addAuthTokenIfStrong */); } } } else { @@ -175,7 +184,15 @@ public class CoexCoordinator { return client == mClientMap.getOrDefault(SENSOR_TYPE_FACE, null); } - private boolean isPointerDown(@Nullable AuthenticationClient client) { + private boolean isCurrentUdfps(@NonNull AuthenticationClient client) { + return client == mClientMap.getOrDefault(SENSOR_TYPE_UDFPS, null); + } + + private boolean isFaceScanning() { + return mClientMap.containsKey(SENSOR_TYPE_FACE); + } + + private static boolean isPointerDown(@Nullable AuthenticationClient client) { if (client instanceof Udfps) { return ((Udfps) client).isPointerDown(); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java index 46a4e91b6a52d..9c42f45584508 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java @@ -154,4 +154,25 @@ public class CoexCoordinatorTest { verify(mCallback, never()).sendHapticFeedback(); verify(mCallback, never()).sendAuthenticationResult(anyBoolean()); } + + @Test + public void testKeyguard_udfpsAuthSuccess_whileFaceScanning() { + mCoexCoordinator.reset(); + + AuthenticationClient faceClient = mock(AuthenticationClient.class); + when(faceClient.isKeyguard()).thenReturn(true); + + AuthenticationClient udfpsClient = mock(AuthenticationClient.class, + withSettings().extraInterfaces(Udfps.class)); + when(udfpsClient.isKeyguard()).thenReturn(true); + when(((Udfps) udfpsClient).isPointerDown()).thenReturn(true); + + mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient); + mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient); + + mCoexCoordinator.onAuthenticationSucceeded(udfpsClient, mCallback); + verify(mCallback).sendHapticFeedback(); + verify(mCallback).sendAuthenticationResult(eq(true)); + verify(faceClient).cancel(); + } }