diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 7247ef77afb48..197739b6a067c 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -768,6 +768,20 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } } + /** + * Schedules a watchdog. + * + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public void scheduleWatchdog() { + try { + mService.scheduleWatchdog(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + private void cancelEnrollment(long requestId) { if (mService != null) { try { diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index 9b56f43a0f22a..2bf187ac9006e 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -172,4 +172,9 @@ interface IFaceService { // Registers BiometricStateListener. void registerBiometricStateListener(IBiometricStateListener listener); + + // Internal operation used to clear face biometric scheduler. + // Ensures that the scheduler is not stuck. + @EnforcePermission("USE_BIOMETRIC_INTERNAL") + void scheduleWatchdog(); } diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 0fd164de8ffb0..7b800d1ff3319 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -1124,6 +1124,20 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing return BIOMETRIC_LOCKOUT_NONE; } + /** + * Schedules a watchdog. + * + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public void scheduleWatchdog() { + try { + mService.scheduleWatchdog(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * @hide */ diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index 1ba9a0471c88a..051e3a4caa4eb 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -208,4 +208,9 @@ interface IFingerprintService { // Sends a power button pressed event to all listeners. @EnforcePermission("USE_BIOMETRIC_INTERNAL") oneway void onPowerPressed(); + + // Internal operation used to clear fingerprint biometric scheduler. + // Ensures that the scheduler is not stuck. + @EnforcePermission("USE_BIOMETRIC_INTERNAL") + void scheduleWatchdog(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index f5582761c0aea..8699e324adf9b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -106,7 +106,6 @@ import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; @@ -3796,4 +3795,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } mListenModels.print(pw); } + + /** + * Schedules a watchdog for the face and fingerprint BiometricScheduler. + * Cancels all operations in the scheduler if it is hung for 10 seconds. + */ + public void startBiometricWatchdog() { + if (mFaceManager != null) { + mFaceManager.scheduleWatchdog(); + } + if (mFpm != null) { + mFpm.scheduleWatchdog(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 84bd8cec51c87..ea26c4dc8a65c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -751,6 +751,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, if (DEBUG) Log.d(TAG, "keyguardGone"); mKeyguardViewControllerLazy.get().setKeyguardGoingAwayState(false); mKeyguardDisplayManager.hide(); + mUpdateMonitor.startBiometricWatchdog(); Trace.endSection(); } 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 1370fd83f6a8b..da7781add8c66 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java @@ -21,6 +21,7 @@ import static com.android.internal.annotations.VisibleForTesting.Visibility; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.hardware.biometrics.BiometricConstants; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; @@ -293,4 +294,30 @@ public abstract class BaseClientMonitor implements IBinder.DeathRecipient { + ", requestId=" + getRequestId() + ", userId=" + getTargetUserId() + "}"; } + + /** + * Cancels this ClientMonitor + */ + public void cancel() { + cancelWithoutStarting(mCallback); + } + + /** + * Cancels this ClientMonitor without starting + * @param callback + */ + public void cancelWithoutStarting(@NonNull ClientMonitorCallback callback) { + Slog.d(TAG, "cancelWithoutStarting: " + this); + + final int errorCode = BiometricConstants.BIOMETRIC_ERROR_CANCELED; + try { + ClientMonitorCallbackConverter listener = getListener(); + if (listener != null) { + listener.onError(getSensorId(), getCookie(), errorCode, 0 /* vendorCode */); + } + } catch (RemoteException e) { + Slog.w(TAG, "Failed to invoke sendError", e); + } + callback.onClientFinished(this, true /* success */); + } } 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 9317c4ec12b59..fb978b2ba4b9b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -543,4 +543,37 @@ public class BiometricScheduler { mPendingOperations.clear(); mCurrentOperation = null; } + + /** + * Marks all pending operations as canceling and cancels the current + * operation. + */ + private void clearScheduler() { + if (mCurrentOperation == null) { + return; + } + for (BiometricSchedulerOperation pendingOperation : mPendingOperations) { + Slog.d(getTag(), "[Watchdog cancelling pending] " + + pendingOperation.getClientMonitor()); + pendingOperation.markCanceling(); + } + Slog.d(getTag(), "[Watchdog cancelling current] " + + mCurrentOperation.getClientMonitor()); + mCurrentOperation.cancel(mHandler, getInternalCallback()); + } + + /** + * Start the timeout for the watchdog. + */ + public void startWatchdog() { + if (mCurrentOperation == null) { + return; + } + final BiometricSchedulerOperation mOperation = mCurrentOperation; + mHandler.postDelayed(() -> { + if (mOperation == mCurrentOperation) { + clearScheduler(); + } + }, 10000); + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java index ef2931ff58506..dacec38b0e7e7 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java @@ -267,7 +267,7 @@ public class BiometricSchedulerOperation { /** Flags this operation as canceled, if possible, but does not cancel it until started. */ public boolean markCanceling() { - if (mState == STATE_WAITING_IN_QUEUE && isInterruptable()) { + if (mState == STATE_WAITING_IN_QUEUE) { mState = STATE_WAITING_IN_QUEUE_CANCELING; return true; } @@ -287,10 +287,6 @@ public class BiometricSchedulerOperation { } final int currentState = mState; - if (!isInterruptable()) { - Slog.w(TAG, "Cannot cancel - operation not interruptable: " + this); - return; - } if (currentState == STATE_STARTED_CANCELING) { Slog.w(TAG, "Cannot cancel - already invoked for operation: " + this); return; @@ -301,10 +297,10 @@ public class BiometricSchedulerOperation { || currentState == STATE_WAITING_IN_QUEUE_CANCELING || currentState == STATE_WAITING_FOR_COOKIE) { Slog.d(TAG, "[Cancelling] Current client (without start): " + mClientMonitor); - ((Interruptable) mClientMonitor).cancelWithoutStarting(getWrappedCallback(callback)); + mClientMonitor.cancelWithoutStarting(getWrappedCallback(callback)); } else { Slog.d(TAG, "[Cancelling] Current client: " + mClientMonitor); - ((Interruptable) mClientMonitor).cancel(); + mClientMonitor.cancel(); } // forcibly finish this client if the HAL does not acknowledge within the timeout diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java index 271bce9890c63..2761ec04aa7ec 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java @@ -183,6 +183,18 @@ public class FaceService extends SystemService { receiver, opPackageName, disabledFeatures, previewSurface, debugConsent); } + @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) + @Override + public void scheduleWatchdog() { + final Pair provider = mRegistry.getSingleProvider(); + if (provider == null) { + Slog.w(TAG, "Null provider for scheduling watchdog"); + return; + } + + provider.second.scheduleWatchdog(provider.first); + } + @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_BIOMETRIC) @Override // Binder call public long enrollRemotely(int userId, final IBinder token, final byte[] hardwareAuthToken, diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java index 4efaedbd55301..85f95cec8377d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java @@ -128,4 +128,10 @@ public interface ServiceProvider extends BiometricServiceProvider provider = mRegistry.getSingleProvider(); + if (provider == null) { + Slog.w(TAG, "Null provider for scheduling watchdog"); + return; + } + + provider.second.scheduleWatchdog(provider.first); + } }; public FingerprintService(Context context) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java index 9075e7ec20801..0c29f5615b4c5 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java @@ -140,4 +140,10 @@ public interface ServiceProvider extends @NonNull ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback, @NonNull String opPackageName); + + /** + * Schedules watchdog for canceling hung operations + * @param sensorId sensor ID of the associated operation + */ + default void scheduleWatchdog(int sensorId) {} } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index 650894db431a9..17ba07f2c2bd9 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -59,6 +59,7 @@ import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.BaseClientMonitor; +import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.BiometricStateCallback; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; @@ -779,4 +780,14 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi } return null; } + + @Override + public void scheduleWatchdog(int sensorId) { + Slog.d(getTag(), "Starting watchdog for fingerprint"); + final BiometricScheduler biometricScheduler = mSensors.get(sensorId).getScheduler(); + if (biometricScheduler == null) { + return; + } + biometricScheduler.startWatchdog(); + } } 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 eb1314194aa37..ffacbf331d893 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 @@ -16,7 +16,7 @@ package com.android.server.biometrics.sensors; -import static android.testing.TestableLooper.RunWithLooper; +import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; @@ -24,8 +24,10 @@ import static junit.framework.Assert.fail; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -35,6 +37,7 @@ import static org.mockito.Mockito.when; import static org.mockito.Mockito.withSettings; import android.content.Context; +import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.IBiometricService; import android.os.Binder; @@ -63,27 +66,25 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; import java.util.function.Supplier; @Presubmit @SmallTest @RunWith(AndroidTestingRunner.class) -@RunWithLooper(setAsMainLooper = true) +@TestableLooper.RunWithLooper(setAsMainLooper = true) public class BiometricSchedulerTest { private static final String TAG = "BiometricSchedulerTest"; private static final int TEST_SENSOR_ID = 1; private static final int LOG_NUM_RECENT_OPERATIONS = 2; - - private BiometricScheduler mScheduler; - private IBinder mToken; - - @Mock - private IBiometricService mBiometricService; - @Rule public final TestableContext mContext = new TestableContext(InstrumentationRegistry.getContext(), null); + private BiometricScheduler mScheduler; + private IBinder mToken; + @Mock + private IBiometricService mBiometricService; @Before public void setUp() { @@ -323,7 +324,7 @@ public class BiometricSchedulerTest { client1.getCallback().onClientFinished(client1, true /* success */); waitForIdle(); verify(callback).onError(anyInt(), anyInt(), - eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED), + eq(BIOMETRIC_ERROR_CANCELED), eq(0) /* vendorCode */); assertNull(mScheduler.getCurrentClient()); assertTrue(client1.isAlreadyDone()); @@ -484,7 +485,7 @@ public class BiometricSchedulerTest { mScheduler.scheduleClientMonitor(interrupter); waitForIdle(); - verify((Interruptable) interruptableMonitor).cancel(); + verify(interruptableMonitor).cancel(); mScheduler.getInternalCallback().onClientFinished(interruptableMonitor, true /* success */); } @@ -500,7 +501,7 @@ public class BiometricSchedulerTest { mScheduler.scheduleClientMonitor(interrupter); waitForIdle(); - verify((Interruptable) interruptableMonitor, never()).cancel(); + verify(interruptableMonitor, never()).cancel(); } @Test @@ -514,21 +515,180 @@ public class BiometricSchedulerTest { assertTrue(client.mDestroyed); } + @Test + public void testClearBiometricQueue_clearsHungAuthOperation() { + // Creating a hung client + final TestableLooper looper = TestableLooper.get(this); + final Supplier lazyDaemon1 = () -> mock(Object.class); + final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext, + lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class), 0 /* cookie */); + final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class); + + mScheduler.scheduleClientMonitor(client1, callback1); + waitForIdle(); + + mScheduler.startWatchdog(); + waitForIdle(); + + //Checking client is hung + verify(callback1).onClientStarted(client1); + verify(callback1, never()).onClientFinished(any(), anyBoolean()); + assertNotNull(mScheduler.mCurrentOperation); + assertEquals(0, mScheduler.getCurrentPendingCount()); + + looper.moveTimeForward(10000); + waitForIdle(); + looper.moveTimeForward(3000); + waitForIdle(); + + // The hung client did not honor this operation, verify onError and authenticated + // were never called. + assertFalse(client1.mOnErrorCalled); + assertFalse(client1.mAuthenticateCalled); + verify(callback1).onClientFinished(client1, false /* success */); + assertNull(mScheduler.mCurrentOperation); + assertEquals(0, mScheduler.getCurrentPendingCount()); + } + + @Test + public void testAuthWorks_afterClearBiometricQueue() { + // Creating a hung client + final TestableLooper looper = TestableLooper.get(this); + final Supplier lazyDaemon1 = () -> mock(Object.class); + final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext, + lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class), 0 /* cookie */); + final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class); + + mScheduler.scheduleClientMonitor(client1, callback1); + + assertEquals(client1, mScheduler.mCurrentOperation.getClientMonitor()); + assertEquals(0, mScheduler.getCurrentPendingCount()); + + //Checking client is hung + waitForIdle(); + verify(callback1, never()).onClientFinished(any(), anyBoolean()); + + //Start watchdog + mScheduler.startWatchdog(); + waitForIdle(); + + // The watchdog should kick off the cancellation + looper.moveTimeForward(10000); + waitForIdle(); + // After 10 seconds the HAL has 3 seconds to respond to a cancel + looper.moveTimeForward(3000); + waitForIdle(); + + // The hung client did not honor this operation, verify onError and authenticated + // were never called. + assertFalse(client1.mOnErrorCalled); + assertFalse(client1.mAuthenticateCalled); + verify(callback1).onClientFinished(client1, false /* success */); + assertEquals(0, mScheduler.getCurrentPendingCount()); + assertNull(mScheduler.mCurrentOperation); + + + //Run additional auth client + final TestAuthenticationClient client2 = new TestAuthenticationClient(mContext, + lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class), 0 /* cookie */); + final ClientMonitorCallback callback2 = mock(ClientMonitorCallback.class); + + mScheduler.scheduleClientMonitor(client2, callback2); + + assertEquals(client2, mScheduler.mCurrentOperation.getClientMonitor()); + assertEquals(0, mScheduler.getCurrentPendingCount()); + + //Start watchdog + mScheduler.startWatchdog(); + waitForIdle(); + mScheduler.scheduleClientMonitor(mock(BaseClientMonitor.class), + mock(ClientMonitorCallback.class)); + waitForIdle(); + + //Ensure auth client passes + verify(callback2).onClientStarted(client2); + client2.getCallback().onClientFinished(client2, true); + waitForIdle(); + + looper.moveTimeForward(10000); + waitForIdle(); + // After 10 seconds the HAL has 3 seconds to respond to a cancel + looper.moveTimeForward(3000); + waitForIdle(); + + //Asserting auth client passes + assertTrue(client2.isAlreadyDone()); + assertNotNull(mScheduler.mCurrentOperation); + } + + @Test + public void testClearBiometricQueue_doesNotClearOperationsWhenQueueNotStuck() { + //Creating clients + final TestableLooper looper = TestableLooper.get(this); + final Supplier lazyDaemon1 = () -> mock(Object.class); + final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext, + lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class), 0 /* cookie */); + final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class); + + mScheduler.scheduleClientMonitor(client1, callback1); + //Start watchdog + mScheduler.startWatchdog(); + waitForIdle(); + mScheduler.scheduleClientMonitor(mock(BaseClientMonitor.class), + mock(ClientMonitorCallback.class)); + mScheduler.scheduleClientMonitor(mock(BaseClientMonitor.class), + mock(ClientMonitorCallback.class)); + waitForIdle(); + + assertEquals(client1, mScheduler.mCurrentOperation.getClientMonitor()); + assertEquals(2, mScheduler.getCurrentPendingCount()); + verify(callback1, never()).onClientFinished(any(), anyBoolean()); + verify(callback1).onClientStarted(client1); + + //Client finishes successfully + client1.getCallback().onClientFinished(client1, true); + waitForIdle(); + + // The watchdog should kick off the cancellation + looper.moveTimeForward(10000); + waitForIdle(); + // After 10 seconds the HAL has 3 seconds to respond to a cancel + looper.moveTimeForward(3000); + waitForIdle(); + + //Watchdog does not clear pending operations + assertEquals(1, mScheduler.getCurrentPendingCount()); + assertNotNull(mScheduler.mCurrentOperation); + + } + private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception { return BiometricSchedulerProto.parseFrom(mScheduler.dumpProtoState(clearSchedulerBuffer)); } + private void waitForIdle() { + TestableLooper.get(this).processAllMessages(); + } + private static class TestAuthenticationClient extends AuthenticationClient { boolean mStartedHal = false; boolean mStoppedHal = false; boolean mDestroyed = false; int mNumCancels = 0; + boolean mAuthenticateCalled = false; + boolean mOnErrorCalled = false; - public TestAuthenticationClient(@NonNull Context context, + TestAuthenticationClient(@NonNull Context context, @NonNull Supplier lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener) { + this(context, lazyDaemon, token, listener, 1 /* cookie */); + } + + TestAuthenticationClient(@NonNull Context context, + @NonNull Supplier lazyDaemon, @NonNull IBinder token, + @NonNull ClientMonitorCallbackConverter listener, int cookie) { super(context, lazyDaemon, token, listener, 0 /* targetUserId */, 0 /* operationId */, - false /* restricted */, TAG, 1 /* cookie */, false /* requireConfirmation */, + false /* restricted */, TAG, cookie, false /* requireConfirmation */, TEST_SENSOR_ID, mock(BiometricLogger.class), mock(BiometricContext.class), true /* isStrongBiometric */, null /* taskStackListener */, mock(LockoutTracker.class), false /* isKeyguard */, @@ -546,7 +706,19 @@ public class BiometricSchedulerTest { } @Override - protected void handleLifecycleAfterAuth(boolean authenticated) {} + protected void handleLifecycleAfterAuth(boolean authenticated) { + } + + @Override + public void onAuthenticated(BiometricAuthenticator.Identifier identifier, + boolean authenticated, ArrayList hardwareAuthToken) { + mAuthenticateCalled = true; + } + + @Override + protected void onErrorInternal(int errorCode, int vendorCode, boolean finish) { + mOnErrorCalled = true; + } @Override public boolean wasUserDetected() { @@ -651,8 +823,4 @@ public class BiometricSchedulerTest { mDestroyed = true; } } - - private void waitForIdle() { - TestableLooper.get(this).processAllMessages(); - } }