Merge "Send cancel error when watchdog triggered" into 24D1-dev
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 0c3dfa7..eb78fe6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
@@ -23,6 +23,7 @@
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -135,6 +136,14 @@
         mCancelWatchdog = () -> {
             if (!isFinished()) {
                 Slog.e(TAG, "[Watchdog Triggered]: " + this);
+                try {
+                    mClientMonitor.getListener().onError(mClientMonitor.getSensorId(),
+                            mClientMonitor.getCookie(), BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+                            0 /* vendorCode */);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Remote exception when trying to send error in cancel "
+                            + "watchdog.");
+                }
                 getWrappedCallback(mOnStartCallback)
                         .onClientFinished(mClientMonitor, false /* success */);
             }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
index 527bc5b..f6da411 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
@@ -20,6 +20,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.eq;
@@ -29,7 +30,9 @@
 import static org.mockito.Mockito.when;
 import static org.testng.Assert.assertThrows;
 
+import android.hardware.biometrics.BiometricConstants;
 import android.os.Handler;
+import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -72,6 +75,8 @@
     @Mock
     private BaseClientMonitor mNonInterruptableClientMonitor;
     @Mock
+    private ClientMonitorCallbackConverter mListener;
+    @Mock
     private ClientMonitorCallback mClientCallback;
     @Mock
     private ClientMonitorCallback mOnStartCallback;
@@ -435,17 +440,18 @@
     }
 
     @Test
-    public void cancelWatchdogWhenStarted() {
+    public void cancelWatchdogWhenStarted() throws RemoteException {
         cancelWatchdog(true);
     }
 
     @Test
-    public void cancelWatchdogWithoutStarting() {
+    public void cancelWatchdogWithoutStarting() throws RemoteException {
         cancelWatchdog(false);
     }
 
-    private void cancelWatchdog(boolean start) {
+    private void cancelWatchdog(boolean start) throws RemoteException {
         when(mInterruptableClientMonitor.getFreshDaemon()).thenReturn(mHal);
+        when(mInterruptableClientMonitor.getListener()).thenReturn(mListener);
 
         mInterruptableOperation.start(mOnStartCallback);
         if (start) {
@@ -461,6 +467,8 @@
 
         assertThat(mInterruptableOperation.isFinished()).isTrue();
         assertThat(mInterruptableOperation.isCanceling()).isFalse();
+        verify(mInterruptableClientMonitor.getListener()).onError(anyInt(), anyInt(), eq(
+                BiometricConstants.BIOMETRIC_ERROR_CANCELED), eq(0));
         verify(mOnStartCallback).onClientFinished(eq(mInterruptableClientMonitor), eq(false));
         verify(mInterruptableClientMonitor).destroy();
     }
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 981eba5..971323a 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
@@ -135,6 +135,8 @@
     private ISession mSession;
     @Mock
     private IFingerprint mFingerprint;
+    @Mock
+    private ClientMonitorCallbackConverter mListener;
 
     @Before
     public void setUp() {
@@ -206,7 +208,7 @@
         // Pretend the scheduler is busy so the first operation doesn't start right away. We want
         // to pretend like there are two operations in the queue before kicking things off
         mScheduler.mCurrentOperation = new BiometricSchedulerOperation(
-                mock(BaseClientMonitor.class), mock(ClientMonitorCallback.class));
+                createBaseClientMonitor(), mock(ClientMonitorCallback.class));
 
         mScheduler.scheduleClientMonitor(client1, callback1);
         assertEquals(1, mScheduler.mPendingOperations.size());
@@ -244,7 +246,7 @@
         // Pretend the scheduler is busy so the first operation doesn't start right away. We want
         // to pretend like there are two operations in the queue before kicking things off
         mScheduler.mCurrentOperation = new BiometricSchedulerOperation(
-                mock(BaseClientMonitor.class), mock(ClientMonitorCallback.class));
+                createBaseClientMonitor(), mock(ClientMonitorCallback.class));
 
         mScheduler.scheduleClientMonitor(client1, callback1);
         assertEquals(1, mScheduler.mPendingOperations.size());
@@ -612,10 +614,10 @@
 
     @Test
     public void testInterruptPrecedingClients_whenExpected() {
-        final BaseClientMonitor interruptableMonitor = mock(BaseClientMonitor.class);
+        final BaseClientMonitor interruptableMonitor = createBaseClientMonitor();
         when(interruptableMonitor.isInterruptable()).thenReturn(true);
 
-        final BaseClientMonitor interrupter = mock(BaseClientMonitor.class);
+        final BaseClientMonitor interrupter = createBaseClientMonitor();
         when(interrupter.interruptsPrecedingClients()).thenReturn(true);
 
         mScheduler.scheduleClientMonitor(interruptableMonitor);
@@ -628,10 +630,10 @@
 
     @Test
     public void testDoesNotInterruptPrecedingClients_whenNotExpected() {
-        final BaseClientMonitor interruptableMonitor = mock(BaseClientMonitor.class);
+        final BaseClientMonitor interruptableMonitor = createBaseClientMonitor();
         when(interruptableMonitor.isInterruptable()).thenReturn(true);
 
-        final BaseClientMonitor interrupter = mock(BaseClientMonitor.class);
+        final BaseClientMonitor interrupter = createBaseClientMonitor();
         when(interrupter.interruptsPrecedingClients()).thenReturn(false);
 
         mScheduler.scheduleClientMonitor(interruptableMonitor);
@@ -741,7 +743,7 @@
         //Start watchdog
         mScheduler.startWatchdog();
         waitForIdle();
-        mScheduler.scheduleClientMonitor(mock(BaseClientMonitor.class),
+        mScheduler.scheduleClientMonitor(createBaseClientMonitor(),
                 mock(ClientMonitorCallback.class));
         waitForIdle();
 
@@ -775,9 +777,9 @@
         //Start watchdog
         mScheduler.startWatchdog();
         waitForIdle();
-        mScheduler.scheduleClientMonitor(mock(BaseClientMonitor.class),
+        mScheduler.scheduleClientMonitor(createBaseClientMonitor(),
                 mock(ClientMonitorCallback.class));
-        mScheduler.scheduleClientMonitor(mock(BaseClientMonitor.class),
+        mScheduler.scheduleClientMonitor(createBaseClientMonitor(),
                 mock(ClientMonitorCallback.class));
         waitForIdle();
 
@@ -857,7 +859,7 @@
     public void testScheduleOperation_whenNoUser() {
         mCurrentUserId = UserHandle.USER_NULL;
 
-        final BaseClientMonitor nextClient = mock(BaseClientMonitor.class);
+        final BaseClientMonitor nextClient = createBaseClientMonitor();
         when(nextClient.getTargetUserId()).thenReturn(0);
 
         mScheduler.scheduleClientMonitor(nextClient);
@@ -875,9 +877,9 @@
         mStartOperationsFinish = false;
 
         final BaseClientMonitor[] nextClients = new BaseClientMonitor[]{
-                mock(BaseClientMonitor.class),
-                mock(BaseClientMonitor.class),
-                mock(BaseClientMonitor.class)
+                createBaseClientMonitor(),
+                createBaseClientMonitor(),
+                createBaseClientMonitor()
         };
         for (BaseClientMonitor client : nextClients) {
             when(client.getTargetUserId()).thenReturn(5);
@@ -899,7 +901,7 @@
         mCurrentUserId = UserHandle.USER_NULL;
         mStartOperationsFinish = false;
 
-        final BaseClientMonitor client = mock(BaseClientMonitor.class);
+        final BaseClientMonitor client = createBaseClientMonitor();
 
         when(client.getTargetUserId()).thenReturn(5);
 
@@ -913,7 +915,7 @@
         assertThat(mScheduler.mCurrentOperation).isNull();
 
         final BiometricSchedulerOperation fakeOperation = new BiometricSchedulerOperation(
-                mock(BaseClientMonitor.class), new ClientMonitorCallback() {});
+                createBaseClientMonitor(), new ClientMonitorCallback() {});
         mScheduler.mCurrentOperation = fakeOperation;
         startUserClient.mCallback.onClientFinished(startUserClient, true);
 
@@ -925,7 +927,7 @@
     public void testScheduleOperation_whenSameUser() {
         mCurrentUserId = 10;
 
-        BaseClientMonitor nextClient = mock(BaseClientMonitor.class);
+        BaseClientMonitor nextClient = createBaseClientMonitor();
         when(nextClient.getTargetUserId()).thenReturn(mCurrentUserId);
 
         mScheduler.scheduleClientMonitor(nextClient);
@@ -943,7 +945,7 @@
         mCurrentUserId = 10;
 
         final int nextUserId = 11;
-        BaseClientMonitor nextClient = mock(BaseClientMonitor.class);
+        BaseClientMonitor nextClient = createBaseClientMonitor();
         when(nextClient.getTargetUserId()).thenReturn(nextUserId);
 
         mScheduler.scheduleClientMonitor(nextClient);
@@ -963,7 +965,7 @@
     public void testStartUser_alwaysStartsNextOperation() {
         mCurrentUserId = UserHandle.USER_NULL;
 
-        BaseClientMonitor nextClient = mock(BaseClientMonitor.class);
+        BaseClientMonitor nextClient = createBaseClientMonitor();
         when(nextClient.getTargetUserId()).thenReturn(10);
 
         mScheduler.scheduleClientMonitor(nextClient);
@@ -977,7 +979,7 @@
 
         // schedule second operation but swap out the current operation
         // before it runs so that it's not current when it's completion callback runs
-        nextClient = mock(BaseClientMonitor.class);
+        nextClient = createBaseClientMonitor();
         when(nextClient.getTargetUserId()).thenReturn(11);
         mScheduler.scheduleClientMonitor(nextClient);
 
@@ -994,7 +996,7 @@
 
         // When a stop user client fails, check that mStopUserClient
         // is set to null to prevent the scheduler from getting stuck.
-        BaseClientMonitor nextClient = mock(BaseClientMonitor.class);
+        BaseClientMonitor nextClient = createBaseClientMonitor();
         when(nextClient.getTargetUserId()).thenReturn(10);
 
         mScheduler.scheduleClientMonitor(nextClient);
@@ -1008,7 +1010,7 @@
 
         // schedule second operation but swap out the current operation
         // before it runs so that it's not current when it's completion callback runs
-        nextClient = mock(BaseClientMonitor.class);
+        nextClient = createBaseClientMonitor();
         when(nextClient.getTargetUserId()).thenReturn(11);
         mShouldFailStopUser = true;
         mScheduler.scheduleClientMonitor(nextClient);
@@ -1023,6 +1025,13 @@
         return BiometricSchedulerProto.parseFrom(mScheduler.dumpProtoState(clearSchedulerBuffer));
     }
 
+    private BaseClientMonitor createBaseClientMonitor() {
+        BaseClientMonitor client = mock(BaseClientMonitor.class);
+        when(client.getListener()).thenReturn(mListener);
+
+        return client;
+    }
+
     private void waitForIdle() {
         TestableLooper.get(this).processAllMessages();
     }