diff options
| author | 2021-01-26 14:50:39 -0800 | |
|---|---|---|
| committer | 2021-01-27 05:19:41 +0000 | |
| commit | 82b6469abfe25c66dc01ef99e41b10b72ee36ca4 (patch) | |
| tree | 8d5147b5bd7f4677953dbdbf264e37a2621c87c0 | |
| parent | 77bb0b72919bb93d89095f2f78accac160633c02 (diff) | |
Check pending queue for cancelAuthentication
If cancelAuthentication is requested but actual authentication
has not started yet (e.g. a previous non-cancelable operation such
as updateActiveGroup or getAuthenticatorId is still running), look
through the pending queue and mark all matched authentication clients
as STATE_WAITING_IN_QUEUE_CANCELING.
Test: atest BiometricSchedulerTest
Fixes: 178105328
Change-Id: I755359adbdcda7f054135a190426b4a78a7a5125
2 files changed, 76 insertions, 7 deletions
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 c86bfcb4a88e..271537a9876c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -563,14 +563,26 @@ public class BiometricScheduler { final boolean isAuthenticating = mCurrentOperation.mClientMonitor instanceof AuthenticationConsumer; final boolean tokenMatches = mCurrentOperation.mClientMonitor.getToken() == token; - if (!isAuthenticating || !tokenMatches) { - Slog.w(getTag(), "Not cancelling authentication" - + ", current operation : " + mCurrentOperation - + ", tokenMatches: " + tokenMatches); - return; - } - cancelInternal(mCurrentOperation); + if (isAuthenticating && tokenMatches) { + Slog.d(getTag(), "Cancelling authentication: " + mCurrentOperation); + cancelInternal(mCurrentOperation); + } else if (!isAuthenticating) { + // Look through the current queue for all authentication clients for the specified + // token, and mark them as STATE_WAITING_IN_QUEUE_CANCELING. Note that we're marking + // all of them, instead of just the first one, since the API surface currently doesn't + // allow us to distinguish between multiple authentication requests from the same + // process. However, this generally does not happen anyway, and would be a class of + // bugs on its own. + for (Operation operation : mPendingOperations) { + if (operation.mClientMonitor instanceof AuthenticationConsumer + && operation.mClientMonitor.getToken() == token) { + Slog.d(getTag(), "Marking " + operation + + " as STATE_WAITING_IN_QUEUE_CANCELING"); + operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING; + } + } + } } /** diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java index 90ce6cbb7718..57b0d01b3025 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java @@ -33,6 +33,7 @@ import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.IBiometricService; import android.os.Binder; import android.os.IBinder; +import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import androidx.annotation.NonNull; @@ -267,6 +268,39 @@ public class BiometricSchedulerTest { assertEquals(0, bsp.recentOperations.length); } + @Test + public void testCancelPendingAuth() throws RemoteException { + final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class); + + final TestClientMonitor client1 = new TestClientMonitor(mContext, mToken, lazyDaemon); + final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class); + final TestAuthenticationClient client2 = new TestAuthenticationClient(mContext, lazyDaemon, + mToken, callback); + + // Add a non-cancellable client, then add the auth client + mScheduler.scheduleClientMonitor(client1); + mScheduler.scheduleClientMonitor(client2); + waitForIdle(); + + assertEquals(mScheduler.getCurrentClient(), client1); + assertEquals(Operation.STATE_WAITING_IN_QUEUE, + mScheduler.mPendingOperations.getFirst().mState); + + // Request cancel before the authentication client has started + mScheduler.cancelAuthentication(mToken); + waitForIdle(); + assertEquals(Operation.STATE_WAITING_IN_QUEUE_CANCELING, + mScheduler.mPendingOperations.getFirst().mState); + + // Finish the blocking client. The authentication client should send ERROR_CANCELED + client1.getCallback().onClientFinished(client1, true /* success */); + waitForIdle(); + verify(callback).onError(anyInt(), anyInt(), + eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED), + eq(0) /* vendorCode */); + assertNull(mScheduler.getCurrentClient()); + } + private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception { return BiometricSchedulerProto.parseFrom(mScheduler.dumpProtoState(clearSchedulerBuffer)); } @@ -293,6 +327,29 @@ public class BiometricSchedulerTest { } } + private static class TestAuthenticationClient extends AuthenticationClient<Object> { + + public TestAuthenticationClient(@NonNull Context context, + @NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token, + @NonNull ClientMonitorCallbackConverter listener) { + super(context, lazyDaemon, token, listener, 0 /* targetUserId */, 0 /* operationId */, + false /* restricted */, TAG, 1 /* cookie */, false /* requireConfirmation */, + TEST_SENSOR_ID, true /* isStrongBiometric */, 0 /* statsModality */, + 0 /* statsClient */, null /* taskStackListener */, mock(LockoutTracker.class), + false /* isKeyguard */); + } + + @Override + protected void stopHalOperation() { + + } + + @Override + protected void startHalOperation() { + + } + } + private static class TestClientMonitor2 extends TestClientMonitor { private final int mProtoEnum; |