Add unlinking from binder death

- When a binder dies or when CarWatchdogService unregisters itself from
car watchdog daemon, binder death is no longer of interest.

Bug: 150728793
Test: atest CarWatchdogServiceTest
Change-Id: I4fd2fd744664342e911775b9fb177cac014e3095
diff --git a/service/src/com/android/car/watchdog/CarWatchdogService.java b/service/src/com/android/car/watchdog/CarWatchdogService.java
index 348522d..b15ede5 100644
--- a/service/src/com/android/car/watchdog/CarWatchdogService.java
+++ b/service/src/com/android/car/watchdog/CarWatchdogService.java
@@ -59,6 +59,20 @@
     @GuardedBy("mLock")
     private @Nullable ICarWatchdog mCarWatchdogDaemon;
 
+    private final DeathRecipient mDeathRecipient = new DeathRecipient() {
+        @Override
+        public void binderDied() {
+            Log.w(TAG_WATCHDOG, "Car watchdog daemon died: reconnecting");
+            unlinkToDeath();
+            synchronized (mLock) {
+                mCarWatchdogDaemon = null;
+            }
+            mMainHandler.postDelayed(() -> {
+                connectToDaemon(CAR_WATCHDOG_DAEMON_BIND_MAX_RETRY);
+            }, CAR_WATCHDOG_DAEMON_BIND_RETRY_INTERVAL_MS);
+        }
+    };
+
     public CarWatchdogService(Context context) {
         // Car watchdog daemon is found at init().
         this(context, /* daemon= */ null);
@@ -81,12 +95,14 @@
         if (daemon == null) {
             connectToDaemon(CAR_WATCHDOG_DAEMON_BIND_MAX_RETRY);
         } else {
+            linkToDeath();
             registerToDaemon(daemon);
         }
     }
 
     @Override
     public void release() {
+        unlinkToDeath();
         ICarWatchdog daemon;
         synchronized (mLock) {
             daemon = mCarWatchdogDaemon;
@@ -149,23 +165,6 @@
             Log.wtf(TAG_WATCHDOG,
                     "Finding car watchdog daemon took too long(" + elapsedTimeMs + "ms)");
         }
-        try {
-            binder.linkToDeath(new DeathRecipient() {
-                @Override
-                public void binderDied() {
-                    Log.w(TAG_WATCHDOG, "Car watchdog daemon died: reconnecting");
-                    synchronized (mLock) {
-                        mCarWatchdogDaemon = null;
-                    }
-                    mMainHandler.postDelayed(() -> {
-                        connectToDaemon(CAR_WATCHDOG_DAEMON_BIND_MAX_RETRY);
-                    }, CAR_WATCHDOG_DAEMON_BIND_RETRY_INTERVAL_MS);
-                }
-            }, 0);
-        } catch (RemoteException e) {
-            Log.w(TAG_WATCHDOG, "Linking to binder death recipient failed: " + e);
-            return false;
-        }
 
         ICarWatchdog daemon = ICarWatchdog.Stub.asInterface(binder);
         if (daemon == null) {
@@ -175,6 +174,7 @@
         synchronized (mLock) {
             mCarWatchdogDaemon = daemon;
         }
+        linkToDeath();
         registerToDaemon(daemon);
         return true;
     }
@@ -211,6 +211,32 @@
         });
     }
 
+    private void linkToDeath() {
+        IBinder binder;
+        synchronized (mLock) {
+            if (mCarWatchdogDaemon == null) {
+                return;
+            }
+            binder = mCarWatchdogDaemon.asBinder();
+        }
+        try {
+            binder.linkToDeath(mDeathRecipient, 0);
+        } catch (RemoteException e) {
+            Log.w(TAG_WATCHDOG, "Linking to binder death recipient failed: " + e);
+        }
+    }
+
+    private void unlinkToDeath() {
+        IBinder binder;
+        synchronized (mLock) {
+            if (mCarWatchdogDaemon == null) {
+                return;
+            }
+            binder = mCarWatchdogDaemon.asBinder();
+        }
+        binder.unlinkToDeath(mDeathRecipient, 0);
+    }
+
     private static final class ICarWatchdogClientImpl extends ICarWatchdogClient.Stub {
         private final WeakReference<CarWatchdogService> mService;
 
diff --git a/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java b/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java
index dcc6f81..3ab03e5 100644
--- a/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java
@@ -18,11 +18,16 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.verify;
+
 import android.automotive.watchdog.ICarWatchdog;
 import android.automotive.watchdog.ICarWatchdogClient;
 import android.automotive.watchdog.TimeoutLength;
 import android.content.Context;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Log;
@@ -47,6 +52,7 @@
     private static final String TAG = CarWatchdogServiceTest.class.getSimpleName();
 
     @Mock private Context mMockContext;
+    @Mock private IBinder mBinder;
 
     private CarWatchdogService mCarWatchdogService;
     private FakeCarWatchdog mFakeCarWatchdog;
@@ -71,6 +77,18 @@
         assertThat(mFakeCarWatchdog.gotResponse()).isTrue();
     }
 
+    @Test
+    public void testLinkUnlinkDeathRecipient() {
+        mCarWatchdogService.init();
+        try {
+            verify(mBinder).linkToDeath(any(), anyInt());
+        } catch (RemoteException e) {
+            // Do nothing
+        }
+        mCarWatchdogService.release();
+        verify(mBinder).unlinkToDeath(any(), anyInt());
+    }
+
     // FakeCarWatchdog mimics ICarWatchdog daemon in local process.
     final class FakeCarWatchdog extends ICarWatchdog.Default {
 
@@ -98,6 +116,11 @@
         }
 
         @Override
+        public IBinder asBinder() {
+            return mBinder;
+        }
+
+        @Override
         public void registerMediator(ICarWatchdogClient mediator) throws RemoteException {
             mClients.add(mediator);
             mMainHandler.post(() -> {