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
This commit is contained in:
Kevin Chyn
2021-01-26 14:50:39 -08:00
parent 77bb0b7291
commit 82b6469abf
2 changed files with 76 additions and 7 deletions

View File

@@ -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;
}
}
}
}
/**

View File

@@ -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;