Call InvalidationCallback#onCompleted when client is cancelled

Fixes: 366384601
Test: atest FingerprintInvalidationClientTest, atest
FaceInvalidationClientTest
Flag: EXEMPT bug fix
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:da582a35c9ce60fbdae6360cf042faef7d0bbe8d)
Merged-In: Id5ed4bc5f8e38052b8011528015945ed51639fe9
Change-Id: Id5ed4bc5f8e38052b8011528015945ed51639fe9
diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
index d5aa5e2..43414c0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
@@ -77,6 +77,16 @@
     }
 
     @Override
+    public void cancel() {
+        super.cancel();
+        try {
+            mInvalidationCallback.onCompleted();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to complete invalidation client due to exception: " + e);
+        }
+    }
+
+    @Override
     public int getProtoEnum() {
         return BiometricsProto.CM_INVALIDATE;
     }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClientTest.java
new file mode 100644
index 0000000..405fb44
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClientTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face.aidl;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.IInvalidationCallback;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.biometrics.face.V1_0.OptionalUint64;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.stubbing.Answer;
+
+import java.util.HashMap;
+
+@Presubmit
+@SmallTest
+public class FaceInvalidationClientTest {
+
+    private static final int SENSOR_ID = 4;
+    private static final int USER_ID = 0;
+
+
+    @Rule
+    public final TestableContext mContext = new TestableContext(
+            InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+    @Rule
+    public final MockitoRule mockito = MockitoJUnit.rule();
+
+    @Mock
+    private IBiometricsFace mFace;
+    @Mock
+    private AidlResponseHandler mAidlResponseHandler;
+    @Mock
+    private BiometricLogger mBiometricLogger;
+    @Mock
+    private BiometricContext mBiometricContext;
+    @Mock
+    private IInvalidationCallback mInvalidationCallback;
+    @Mock
+    private ClientMonitorCallback mClientMonitorCallback;
+
+    @Test
+    public void testStartInvalidationClient_whenHalIsHidl() throws RemoteException {
+        final OptionalUint64 halId = new OptionalUint64();
+
+        when(mFace.setCallback(any())).thenReturn(halId);
+
+        final AidlSession aidlSession = new AidlSession(mContext, () -> mFace, USER_ID,
+                mAidlResponseHandler);
+        final FaceInvalidationClient faceInvalidationClient =
+                new FaceInvalidationClient(mContext, () -> aidlSession, USER_ID,
+                        SENSOR_ID, mBiometricLogger, mBiometricContext, new HashMap<>(),
+                        mInvalidationCallback);
+
+        doAnswer((Answer<Void>) invocationOnMock -> {
+            faceInvalidationClient.cancel();
+            return null;
+        }).when(mAidlResponseHandler).onUnsupportedClientScheduled();
+
+        faceInvalidationClient.start(mClientMonitorCallback);
+
+        verify(mInvalidationCallback).onCompleted();
+    }
+}
+
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClientTest.java
new file mode 100644
index 0000000..1ee2fd1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClientTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.aidl;
+
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+
+import android.hardware.biometrics.IInvalidationCallback;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.stubbing.Answer;
+
+import java.util.HashMap;
+
+@Presubmit
+@SmallTest
+public class FingerprintInvalidationClientTest {
+
+    private static final int SENSOR_ID = 4;
+    private static final int USER_ID = 0;
+
+
+    @Rule
+    public final TestableContext mContext = new TestableContext(
+            InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+    @Rule
+    public final MockitoRule mockito = MockitoJUnit.rule();
+
+    @Mock
+    private IBiometricsFingerprint mFingerprint;
+    @Mock
+    private AidlResponseHandler mAidlResponseHandler;
+    @Mock
+    private BiometricLogger mBiometricLogger;
+    @Mock
+    private BiometricContext mBiometricContext;
+    @Mock
+    private IInvalidationCallback mInvalidationCallback;
+    @Mock
+    private ClientMonitorCallback mClientMonitorCallback;
+
+    @Test
+    public void testStartInvalidationClient_whenHalIsHidl() throws RemoteException {
+        final AidlSession aidlSession = new AidlSession(
+                () -> mFingerprint, USER_ID, mAidlResponseHandler);
+        final FingerprintInvalidationClient fingerprintInvalidationClient =
+                new FingerprintInvalidationClient(mContext, () -> aidlSession, USER_ID,
+                        SENSOR_ID, mBiometricLogger, mBiometricContext, new HashMap<>(),
+                        mInvalidationCallback);
+
+        doAnswer((Answer<Void>) invocationOnMock -> {
+            fingerprintInvalidationClient.cancel();
+            return null;
+        }).when(mAidlResponseHandler).onUnsupportedClientScheduled(
+                FingerprintInvalidationClient.class);
+
+        fingerprintInvalidationClient.start(mClientMonitorCallback);
+
+        verify(mInvalidationCallback).onCompleted();
+    }
+}