[nearby] Unregister broadcast listener in death

Fix: 280377682
Test: unit test
Ignore-AOSP-First: nearby_not_in_aosp_yet
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:691adc8cdd1cca9077740ce06246ce347867d757)
Merged-In: I1d632cb150ae30808cbf3343869534111e3dde12
Change-Id: I1d632cb150ae30808cbf3343869534111e3dde12
diff --git a/nearby/service/java/com/android/server/nearby/managers/BroadcastProviderManager.java b/nearby/service/java/com/android/server/nearby/managers/BroadcastProviderManager.java
index 024bff8..d95032c 100644
--- a/nearby/service/java/com/android/server/nearby/managers/BroadcastProviderManager.java
+++ b/nearby/service/java/com/android/server/nearby/managers/BroadcastProviderManager.java
@@ -22,6 +22,7 @@
 import android.nearby.BroadcastRequest;
 import android.nearby.IBroadcastListener;
 import android.nearby.PresenceBroadcastRequest;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -49,6 +50,10 @@
     private final NearbyConfiguration mNearbyConfiguration;
 
     private IBroadcastListener mBroadcastListener;
+    // Used with mBroadcastListener. Now we only support single client, for multi clients, a map
+    // between live binder to the information over the binder is needed.
+    // TODO: Finish multi-client logic for broadcast.
+    private BroadcastListenerDeathRecipient mDeathRecipient;
 
     public BroadcastProviderManager(Context context, Injector injector) {
         this(ForegroundThread.getExecutor(),
@@ -62,6 +67,7 @@
         mLock = new Object();
         mNearbyConfiguration = new NearbyConfiguration();
         mBroadcastListener = null;
+        mDeathRecipient = null;
     }
 
     /**
@@ -70,6 +76,9 @@
     public void startBroadcast(BroadcastRequest broadcastRequest, IBroadcastListener listener) {
         synchronized (mLock) {
             mExecutor.execute(() -> {
+                if (listener == null) {
+                    return;
+                }
                 if (!mNearbyConfiguration.isTestAppSupported()) {
                     NearbyConfiguration configuration = new NearbyConfiguration();
                     if (!configuration.isPresenceBroadcastLegacyEnabled()) {
@@ -89,7 +98,17 @@
                     reportBroadcastStatus(listener, BroadcastCallback.STATUS_FAILURE);
                     return;
                 }
+                BroadcastListenerDeathRecipient deathRecipient =
+                        new BroadcastListenerDeathRecipient(listener);
+                try {
+                    listener.asBinder().linkToDeath(deathRecipient, 0);
+                } catch (RemoteException e) {
+                    // This binder has already died, so call the DeathRecipient as if we had
+                    // called linkToDeath in time.
+                    deathRecipient.binderDied();
+                }
                 mBroadcastListener = listener;
+                mDeathRecipient = deathRecipient;
                 mBleBroadcastProvider.start(presenceBroadcastRequest.getVersion(),
                         advertisement.toBytes(), this);
             });
@@ -113,13 +132,19 @@
      */
     public void stopBroadcast(IBroadcastListener listener) {
         synchronized (mLock) {
-            if (!mNearbyConfiguration.isTestAppSupported()
-                    && !mNearbyConfiguration.isPresenceBroadcastLegacyEnabled()) {
-                reportBroadcastStatus(listener, BroadcastCallback.STATUS_FAILURE);
-                return;
+            if (listener != null) {
+                if (!mNearbyConfiguration.isTestAppSupported()
+                        && !mNearbyConfiguration.isPresenceBroadcastLegacyEnabled()) {
+                    reportBroadcastStatus(listener, BroadcastCallback.STATUS_FAILURE);
+                    return;
+                }
+                if (mDeathRecipient != null) {
+                    listener.asBinder().unlinkToDeath(mDeathRecipient, 0);
+                }
             }
             mBroadcastListener = null;
-            mExecutor.execute(() -> mBleBroadcastProvider.stop());
+            mDeathRecipient = null;
+            mExecutor.execute(mBleBroadcastProvider::stop);
         }
     }
 
@@ -142,4 +167,21 @@
             Log.e(TAG, "remote exception when reporting status");
         }
     }
+
+    /**
+     * Class to make listener unregister after the binder is dead.
+     */
+    public class BroadcastListenerDeathRecipient implements IBinder.DeathRecipient {
+        public IBroadcastListener mListener;
+
+        BroadcastListenerDeathRecipient(IBroadcastListener listener) {
+            mListener = listener;
+        }
+
+        @Override
+        public void binderDied() {
+            Log.d(TAG, "Binder is dead - stop broadcast listener");
+            stopBroadcast(mListener);
+        }
+    }
 }
diff --git a/nearby/tests/unit/src/com/android/server/nearby/managers/BroadcastProviderManagerTest.java b/nearby/tests/unit/src/com/android/server/nearby/managers/BroadcastProviderManagerTest.java
index bc38210..47133f4 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/managers/BroadcastProviderManagerTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/managers/BroadcastProviderManagerTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.app.UiAutomation;
 import android.content.Context;
@@ -34,6 +35,7 @@
 import android.nearby.PresenceBroadcastRequest;
 import android.nearby.PresenceCredential;
 import android.nearby.PrivateCredential;
+import android.os.IBinder;
 import android.provider.DeviceConfig;
 
 import androidx.test.core.app.ApplicationProvider;
@@ -74,6 +76,8 @@
     IBroadcastListener mBroadcastListener;
     @Mock
     BleBroadcastProvider mBleBroadcastProvider;
+    @Mock
+    IBinder mBinder;
     private Context mContext;
     private BroadcastProviderManager mBroadcastProviderManager;
     private BroadcastRequest mBroadcastRequest;
@@ -82,9 +86,12 @@
 
     @Before
     public void setUp() {
+        when(mBroadcastListener.asBinder()).thenReturn(mBinder);
         mUiAutomation.adoptShellPermissionIdentity(WRITE_DEVICE_CONFIG, READ_DEVICE_CONFIG);
         DeviceConfig.setProperty(
                 NAMESPACE, NEARBY_ENABLE_PRESENCE_BROADCAST_LEGACY, "true", false);
+        DeviceConfig.setProperty(
+                NAMESPACE, NEARBY_SUPPORT_TEST_APP, "true", false);
 
         mContext = ApplicationProvider.getApplicationContext();
         mBroadcastProviderManager = new BroadcastProviderManager(
@@ -104,15 +111,18 @@
     }
 
     @Test
-    public void testStartAdvertising() {
+    public void testStartAdvertising() throws Exception {
         mBroadcastProviderManager.startBroadcast(mBroadcastRequest, mBroadcastListener);
         verify(mBleBroadcastProvider).start(eq(BroadcastRequest.PRESENCE_VERSION_V0),
                 any(byte[].class), any(BleBroadcastProvider.BroadcastListener.class));
+        verify(mBinder).linkToDeath(any(), eq(0));
     }
 
     @Test
     public void testStopAdvertising() {
+        mBroadcastProviderManager.startBroadcast(mBroadcastRequest, mBroadcastListener);
         mBroadcastProviderManager.stopBroadcast(mBroadcastListener);
+        verify(mBinder).unlinkToDeath(any(), eq(0));
     }
 
     @Test