diff options
author | 2021-02-10 09:40:21 +0000 | |
---|---|---|
committer | 2021-02-10 09:40:21 +0000 | |
commit | 8ce6b9fbd9084b0d837391462a136b127c61de9c (patch) | |
tree | 68338a7157927ec6b24dc418f43acc2b8cfc993d | |
parent | d438be4ba8d97233f04b15ca36b5027c853d4a16 (diff) | |
parent | 0c1be610a22585dfd191268c3d749d088db49108 (diff) |
Merge changes I96fe9587,Ie51b121b into sc-dev
* changes:
Request cancelAllSensors when STATE_AUTH_CALLED
Add BaseClientMonitor#interruptsPrecedingClients
9 files changed, 108 insertions, 25 deletions
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java index 14292d9c5f8d..c9560437799e 100644 --- a/services/core/java/com/android/server/biometrics/AuthSession.java +++ b/services/core/java/com/android/server/biometrics/AuthSession.java @@ -686,7 +686,8 @@ public final class AuthSession implements IBinder.DeathRecipient { * @return true if this AuthSession is finished, e.g. should be set to null */ boolean onCancelAuthSession(boolean force) { - final boolean authStarted = mState == STATE_AUTH_STARTED + final boolean authStarted = mState == STATE_AUTH_CALLED + || mState == STATE_AUTH_STARTED || mState == STATE_AUTH_STARTED_UI_SHOWING; if (authStarted && !force) { diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java index 0536e78e58f6..b31a54b8b15e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -311,4 +311,9 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> public int getProtoEnum() { return BiometricsProto.CM_AUTHENTICATE; } + + @Override + public boolean interruptsPrecedingClients() { + return true; + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java index 8fa3bbbf615a..81ce2d535237 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java @@ -82,12 +82,18 @@ public abstract class BaseClientMonitor extends LoggableMonitor @NonNull protected Callback mCallback; /** - * Returns a ClientMonitorEnum constant defined in biometrics.proto - * @return + * @return A ClientMonitorEnum constant defined in biometrics.proto */ public abstract int getProtoEnum(); /** + * @return True if the ClientMonitor should cancel any current and pending interruptable clients + */ + public boolean interruptsPrecedingClients() { + return false; + } + + /** * @param context system_server context * @param token a unique token for the client * @param listener recipient of related events (e.g. authentication) 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 c5237ab8c8e7..20c25c35535a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -475,14 +475,17 @@ public class BiometricScheduler { */ public void scheduleClientMonitor(@NonNull BaseClientMonitor clientMonitor, @Nullable BaseClientMonitor.Callback clientCallback) { - // Mark any interruptable pending clients as canceling. Once they reach the head of the - // queue, the scheduler will send ERROR_CANCELED and skip the operation. - for (Operation operation : mPendingOperations) { - if (operation.mClientMonitor instanceof Interruptable - && operation.mState != Operation.STATE_WAITING_IN_QUEUE_CANCELING) { - Slog.d(getTag(), "New client incoming, marking pending client as canceling: " - + operation.mClientMonitor); - operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING; + // If the incoming operation should interrupt preceding clients, mark any interruptable + // pending clients as canceling. Once they reach the head of the queue, the scheduler will + // send ERROR_CANCELED and skip the operation. + if (clientMonitor.interruptsPrecedingClients()) { + for (Operation operation : mPendingOperations) { + if (operation.mClientMonitor instanceof Interruptable + && operation.mState != Operation.STATE_WAITING_IN_QUEUE_CANCELING) { + Slog.d(getTag(), "New client incoming, marking pending client as canceling: " + + operation.mClientMonitor); + operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING; + } } } @@ -490,8 +493,11 @@ public class BiometricScheduler { Slog.d(getTag(), "[Added] " + clientMonitor + ", new queue size: " + mPendingOperations.size()); - // If the current operation is cancellable, start the cancellation process. - if (mCurrentOperation != null && mCurrentOperation.mClientMonitor instanceof Interruptable + // If the new operation should interrupt preceding clients, and if the current operation is + // cancellable, start the cancellation process. + if (clientMonitor.interruptsPrecedingClients() + && mCurrentOperation != null + && mCurrentOperation.mClientMonitor instanceof Interruptable && mCurrentOperation.mState == Operation.STATE_STARTED) { Slog.d(getTag(), "[Cancelling Interruptable]: " + mCurrentOperation); cancelInternal(mCurrentOperation); diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java index 8d81016dab59..e1320d8e1a4f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java @@ -113,4 +113,9 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> { public int getProtoEnum() { return BiometricsProto.CM_ENROLL; } + + @Override + public boolean interruptsPrecedingClients() { + return true; + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java index 9611192fa13e..bcd1b8bc9976 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java @@ -98,4 +98,9 @@ class FingerprintDetectClient extends AcquisitionClient<ISession> { public int getProtoEnum() { return BiometricsProto.CM_DETECT_INTERACTION; } + + @Override + public boolean interruptsPrecedingClients() { + return true; + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java index 7989dca3e06f..8acb284667c7 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java @@ -130,4 +130,9 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint> public int getProtoEnum() { return BiometricsProto.CM_DETECT_INTERACTION; } + + @Override + public boolean interruptsPrecedingClients() { + return true; + } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java index 8d890b9cd606..7a4b901bdc5e 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java @@ -103,7 +103,8 @@ public class AuthSessionTest { @Test public void testNewAuthSession_eligibleSensorsSetToStateUnknown() throws RemoteException { setupFingerprint(0 /* id */, FingerprintSensorProperties.TYPE_REAR); - setupFace(1 /* id */, false /* confirmationAlwaysRequired */); + setupFace(1 /* id */, false /* confirmationAlwaysRequired */, + mock(IBiometricAuthenticator.class)); final AuthSession session = createAuthSession(mSensors, false /* checkDevicePolicyManager */, @@ -118,7 +119,8 @@ public class AuthSessionTest { @Test public void testStartNewAuthSession() throws RemoteException { - setupFace(0 /* id */, false /* confirmationAlwaysRequired */); + setupFace(0 /* id */, false /* confirmationAlwaysRequired */, + mock(IBiometricAuthenticator.class)); setupFingerprint(1 /* id */, FingerprintSensorProperties.TYPE_REAR); final boolean requireConfirmation = true; @@ -181,9 +183,6 @@ public class AuthSessionTest { final long operationId = 123; final int userId = 10; - final int callingUid = 100; - final int callingPid = 1000; - final int callingUserId = 10000; final AuthSession session = createAuthSession(mSensors, false /* checkDevicePolicyManager */, @@ -220,7 +219,25 @@ public class AuthSessionTest { assertEquals(STATE_AUTH_STARTED_UI_SHOWING, session.getState()); assertEquals(BiometricSensor.STATE_AUTHENTICATING, session.mPreAuthInfo.eligibleSensors.get(0).getSensorState()); + } + + @Test + public void testCancelAuthentication_whenStateAuthCalled_invokesCancel() + throws RemoteException { + final IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class); + + setupFace(0 /* id */, false /* confirmationAlwaysRequired */, faceAuthenticator); + final AuthSession session = createAuthSession(mSensors, + false /* checkDevicePolicyManager */, + Authenticators.BIOMETRIC_STRONG, + 0 /* operationId */, + 0 /* userId */); + + session.goToInitialState(); + assertEquals(STATE_AUTH_CALLED, session.getState()); + session.onCancelAuthSession(false /* force */); + verify(faceAuthenticator).cancelAuthenticationFromService(eq(mToken), eq(TEST_PACKAGE)); } private PreAuthInfo createPreAuthInfo(List<BiometricSensor> sensors, int userId, @@ -282,14 +299,14 @@ public class AuthSessionTest { false /* resetLockoutRequiresHardwareAuthToken */)); } - private void setupFace(int id, boolean confirmationAlwaysRequired) throws RemoteException { - IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class); - when(faceAuthenticator.isHardwareDetected(any())).thenReturn(true); - when(faceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true); + private void setupFace(int id, boolean confirmationAlwaysRequired, + IBiometricAuthenticator authenticator) throws RemoteException { + when(authenticator.isHardwareDetected(any())).thenReturn(true); + when(authenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true); mSensors.add(new BiometricSensor(id, TYPE_FACE /* modality */, Authenticators.BIOMETRIC_STRONG /* strength */, - faceAuthenticator) { + authenticator) { @Override boolean confirmationAlwaysRequired(int userId) { return confirmationAlwaysRequired; 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 2d457260b8fe..7dd073499c73 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 @@ -27,6 +27,8 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; import android.content.Context; import android.hardware.biometrics.BiometricConstants; @@ -159,8 +161,8 @@ public class BiometricSchedulerTest { // Client 1 cleans up properly verify(listener1).onError(eq(TEST_SENSOR_ID), anyInt(), - eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED), eq(0)); - verify(callback1).onClientFinished(eq(client1), eq(true) /* success */); + eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE), eq(0)); + verify(callback1).onClientFinished(eq(client1), eq(false) /* success */); verify(callback1, never()).onClientStarted(any()); // Client 2 was able to start @@ -310,6 +312,37 @@ public class BiometricSchedulerTest { assertNull(mScheduler.getCurrentClient()); } + @Test + public void testInterruptPrecedingClients_whenExpected() { + final BaseClientMonitor interruptableMonitor = mock(BaseClientMonitor.class, + withSettings().extraInterfaces(Interruptable.class)); + + final BaseClientMonitor interrupter = mock(BaseClientMonitor.class); + when(interrupter.interruptsPrecedingClients()).thenReturn(true); + + mScheduler.scheduleClientMonitor(interruptableMonitor); + mScheduler.scheduleClientMonitor(interrupter); + waitForIdle(); + + verify((Interruptable) interruptableMonitor).cancel(); + mScheduler.getInternalCallback().onClientFinished(interruptableMonitor, true /* success */); + } + + @Test + public void testDoesNotInterruptPrecedingClients_whenNotExpected() { + final BaseClientMonitor interruptableMonitor = mock(BaseClientMonitor.class, + withSettings().extraInterfaces(Interruptable.class)); + + final BaseClientMonitor interrupter = mock(BaseClientMonitor.class); + when(interrupter.interruptsPrecedingClients()).thenReturn(false); + + mScheduler.scheduleClientMonitor(interruptableMonitor); + mScheduler.scheduleClientMonitor(interrupter); + waitForIdle(); + + verify((Interruptable) interruptableMonitor, never()).cancel(); + } + private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception { return BiometricSchedulerProto.parseFrom(mScheduler.dumpProtoState(clearSchedulerBuffer)); } |