Loading services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java +12 −5 Original line number Diff line number Diff line Loading @@ -268,11 +268,18 @@ public class CoexCoordinator { AuthenticationClient<?> udfps = mClientMap.getOrDefault(SENSOR_TYPE_UDFPS, null); AuthenticationClient<?> face = mClientMap.getOrDefault(SENSOR_TYPE_FACE, null); if (isCurrentFaceAuth(client)) { if (isUdfpsActivelyAuthing(udfps)) { // UDFPS should still be running in this case, do not vibrate. However, we // should notify the callback and finish the client, so that Keyguard and // BiometricScheduler do not get stuck. Slog.d(TAG, "Face rejected in multi-sensor auth, udfps: " + udfps); callback.handleLifecycleAfterAuth(); } else { // UDFPS is not actively authenticating (finger not touching, already // rejected, etc). callback.sendHapticFeedback(); callback.handleLifecycleAfterAuth(); } } else if (isCurrentUdfps(client)) { // Face should either be running, or have already finished SuccessfulAuth auth = popSuccessfulFaceAuthIfExists(currentTimeMillis); Loading services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java +68 −0 Original line number Diff line number Diff line Loading @@ -258,6 +258,74 @@ public class CoexCoordinatorTest { verify(mCallback).handleLifecycleAfterAuth(); } @Test public void testKeyguard_faceRejectedWhenUdfpsTouching_thenUdfpsRejected() { mCoexCoordinator.reset(); AuthenticationClient<?> faceClient = mock(AuthenticationClient.class); when(faceClient.isKeyguard()).thenReturn(true); when(faceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED); AuthenticationClient<?> udfpsClient = mock(AuthenticationClient.class, withSettings().extraInterfaces(Udfps.class)); when(udfpsClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED); when(udfpsClient.isKeyguard()).thenReturn(true); when(((Udfps) udfpsClient).isPointerDown()).thenReturn(true); mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient); mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient); mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, faceClient, LockoutTracker.LOCKOUT_NONE, mCallback); verify(mCallback, never()).sendHapticFeedback(); verify(mCallback).handleLifecycleAfterAuth(); // BiometricScheduler removes the face authentication client after rejection mCoexCoordinator.removeAuthenticationClient(SENSOR_TYPE_FACE, faceClient); // Then UDFPS rejected CoexCoordinator.Callback udfpsCallback = mock(CoexCoordinator.Callback.class); mCoexCoordinator.onAuthenticationRejected(1 /* currentTimeMillis */, udfpsClient, LockoutTracker.LOCKOUT_NONE, udfpsCallback); verify(udfpsCallback).sendHapticFeedback(); verify(udfpsCallback).sendAuthenticationResult(eq(false) /* addAuthTokenIfStrong */); verify(mCallback, never()).sendHapticFeedback(); } @Test public void testKeyguard_udfpsRejected_thenFaceRejected() { mCoexCoordinator.reset(); AuthenticationClient<?> faceClient = mock(AuthenticationClient.class); when(faceClient.isKeyguard()).thenReturn(true); when(faceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED); AuthenticationClient<?> udfpsClient = mock(AuthenticationClient.class, withSettings().extraInterfaces(Udfps.class)); when(udfpsClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED); when(udfpsClient.isKeyguard()).thenReturn(true); when(((Udfps) udfpsClient).isPointerDown()).thenReturn(true); mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient); mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient); mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, udfpsClient, LockoutTracker.LOCKOUT_NONE, mCallback); // Client becomes paused, but finger does not necessarily lift, since we suppress the haptic when(udfpsClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED_PAUSED); verify(mCallback, never()).sendHapticFeedback(); verify(mCallback).handleLifecycleAfterAuth(); // Then face rejected. Note that scheduler leaves UDFPS in the CoexCoordinator since // unlike face, its lifecycle becomes "paused" instead of "finished". CoexCoordinator.Callback faceCallback = mock(CoexCoordinator.Callback.class); mCoexCoordinator.onAuthenticationRejected(1 /* currentTimeMillis */, faceClient, LockoutTracker.LOCKOUT_NONE, faceCallback); verify(faceCallback).sendHapticFeedback(); verify(faceCallback).sendAuthenticationResult(eq(false) /* addAuthTokenIfStrong */); verify(mCallback, never()).sendHapticFeedback(); } @Test public void testNonKeyguard_rejectAndNotLockedOut() { mCoexCoordinator.reset(); Loading Loading
services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java +12 −5 Original line number Diff line number Diff line Loading @@ -268,11 +268,18 @@ public class CoexCoordinator { AuthenticationClient<?> udfps = mClientMap.getOrDefault(SENSOR_TYPE_UDFPS, null); AuthenticationClient<?> face = mClientMap.getOrDefault(SENSOR_TYPE_FACE, null); if (isCurrentFaceAuth(client)) { if (isUdfpsActivelyAuthing(udfps)) { // UDFPS should still be running in this case, do not vibrate. However, we // should notify the callback and finish the client, so that Keyguard and // BiometricScheduler do not get stuck. Slog.d(TAG, "Face rejected in multi-sensor auth, udfps: " + udfps); callback.handleLifecycleAfterAuth(); } else { // UDFPS is not actively authenticating (finger not touching, already // rejected, etc). callback.sendHapticFeedback(); callback.handleLifecycleAfterAuth(); } } else if (isCurrentUdfps(client)) { // Face should either be running, or have already finished SuccessfulAuth auth = popSuccessfulFaceAuthIfExists(currentTimeMillis); Loading
services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java +68 −0 Original line number Diff line number Diff line Loading @@ -258,6 +258,74 @@ public class CoexCoordinatorTest { verify(mCallback).handleLifecycleAfterAuth(); } @Test public void testKeyguard_faceRejectedWhenUdfpsTouching_thenUdfpsRejected() { mCoexCoordinator.reset(); AuthenticationClient<?> faceClient = mock(AuthenticationClient.class); when(faceClient.isKeyguard()).thenReturn(true); when(faceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED); AuthenticationClient<?> udfpsClient = mock(AuthenticationClient.class, withSettings().extraInterfaces(Udfps.class)); when(udfpsClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED); when(udfpsClient.isKeyguard()).thenReturn(true); when(((Udfps) udfpsClient).isPointerDown()).thenReturn(true); mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient); mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient); mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, faceClient, LockoutTracker.LOCKOUT_NONE, mCallback); verify(mCallback, never()).sendHapticFeedback(); verify(mCallback).handleLifecycleAfterAuth(); // BiometricScheduler removes the face authentication client after rejection mCoexCoordinator.removeAuthenticationClient(SENSOR_TYPE_FACE, faceClient); // Then UDFPS rejected CoexCoordinator.Callback udfpsCallback = mock(CoexCoordinator.Callback.class); mCoexCoordinator.onAuthenticationRejected(1 /* currentTimeMillis */, udfpsClient, LockoutTracker.LOCKOUT_NONE, udfpsCallback); verify(udfpsCallback).sendHapticFeedback(); verify(udfpsCallback).sendAuthenticationResult(eq(false) /* addAuthTokenIfStrong */); verify(mCallback, never()).sendHapticFeedback(); } @Test public void testKeyguard_udfpsRejected_thenFaceRejected() { mCoexCoordinator.reset(); AuthenticationClient<?> faceClient = mock(AuthenticationClient.class); when(faceClient.isKeyguard()).thenReturn(true); when(faceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED); AuthenticationClient<?> udfpsClient = mock(AuthenticationClient.class, withSettings().extraInterfaces(Udfps.class)); when(udfpsClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED); when(udfpsClient.isKeyguard()).thenReturn(true); when(((Udfps) udfpsClient).isPointerDown()).thenReturn(true); mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient); mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient); mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, udfpsClient, LockoutTracker.LOCKOUT_NONE, mCallback); // Client becomes paused, but finger does not necessarily lift, since we suppress the haptic when(udfpsClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED_PAUSED); verify(mCallback, never()).sendHapticFeedback(); verify(mCallback).handleLifecycleAfterAuth(); // Then face rejected. Note that scheduler leaves UDFPS in the CoexCoordinator since // unlike face, its lifecycle becomes "paused" instead of "finished". CoexCoordinator.Callback faceCallback = mock(CoexCoordinator.Callback.class); mCoexCoordinator.onAuthenticationRejected(1 /* currentTimeMillis */, faceClient, LockoutTracker.LOCKOUT_NONE, faceCallback); verify(faceCallback).sendHapticFeedback(); verify(faceCallback).sendAuthenticationResult(eq(false) /* addAuthTokenIfStrong */); verify(mCallback, never()).sendHapticFeedback(); } @Test public void testNonKeyguard_rejectAndNotLockedOut() { mCoexCoordinator.reset(); Loading