Merge "Added subscription to binder death which removes dead callbacks"
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java b/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java
index f854316..2263e80 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java
@@ -28,6 +28,7 @@
 import android.hardware.biometrics.BiometricStateListener;
 import android.hardware.biometrics.IBiometricStateListener;
 import android.hardware.biometrics.SensorPropertiesInternal;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserManager;
 import android.util.Slog;
@@ -45,7 +46,8 @@
  * @param <P> internal property type
  */
 public class BiometricStateCallback<T extends BiometricServiceProvider<P>,
-        P extends SensorPropertiesInternal> implements ClientMonitorCallback {
+        P extends SensorPropertiesInternal>
+        implements ClientMonitorCallback, IBinder.DeathRecipient {
 
     private static final String TAG = "BiometricStateCallback";
 
@@ -161,6 +163,11 @@
             @NonNull IBiometricStateListener listener) {
         mBiometricStateListeners.add(listener);
         broadcastCurrentEnrollmentState(listener);
+        try {
+            listener.asBinder().linkToDeath(this, 0 /* flags */);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to link to death", e);
+        }
     }
 
     private synchronized void broadcastCurrentEnrollmentState(
@@ -196,4 +203,19 @@
             Slog.e(TAG, "Remote exception", e);
         }
     }
-}
+
+    @Override
+    public void binderDied() {
+        // Do nothing, handled below
+    }
+
+    @Override
+    public void binderDied(IBinder who) {
+        Slog.w(TAG, "Callback binder died: " + who);
+        if (mBiometricStateListeners.removeIf(listener -> listener.asBinder().equals(who))) {
+            Slog.w(TAG, "Removed dead listener for " + who);
+        } else {
+            Slog.w(TAG, "No dead listeners found");
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java
index 0e30782..3b66eab 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java
@@ -80,6 +80,8 @@
         when(mFakeProvider.hasEnrollments(eq(SENSOR_ID), eq(USER_ID))).thenReturn(true);
         when(mUserManager.getAliveUsers()).thenReturn(
                 List.of(new UserInfo(USER_ID, "name", 0)));
+        when(mBiometricStateListener.asBinder()).thenReturn(mBiometricStateListener);
+
 
         mCallback = new BiometricStateCallback<>(mUserManager);
         mCallback.registerBiometricStateListener(mBiometricStateListener);
@@ -110,6 +112,14 @@
                 false /* expectCallback */, false /* expectedCallbackValue */);
     }
 
+    @Test
+    public void testBinderDeath() {
+        mCallback.binderDied(mBiometricStateListener.asBinder());
+
+        testEnrollmentCallback(true /* changed */, false /* isNowEnrolled */,
+                false /* expectCallback */, false /* expectedCallbackValue */);
+    }
+
     private void testEnrollmentCallback(boolean changed, boolean isNowEnrolled,
             boolean expectCallback, boolean expectedCallbackValue) {
         EnrollClient<?> client = mock(EnrollClient.class);