MediaRouter: Release RoutingSession for died clients.

This CL makes MediaRouteProviderServiceImplApi30 override dispose
to release RoutingSessionInfo of died clients.

A new test that fails w/o this change is added to ensure the behavior
as well.

Bug: 168161584
Test: ./gradlew mediarouter:mediarouter:connectedCheck and
manually using GMSCore built with this.

Change-Id: I042dac2bb5954be1ca1e68ace0d778fae97720fd
diff --git a/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouter2Test.java b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouter2Test.java
index f64a65b..8747820 100644
--- a/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouter2Test.java
+++ b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouter2Test.java
@@ -25,6 +25,7 @@
 import android.media.MediaRoute2ProviderService;
 import android.media.RoutingSessionInfo;
 import android.os.Build;
+import android.os.Messenger;
 import android.support.mediacompat.testlib.util.PollingCheck;
 import android.text.TextUtils;
 
@@ -58,7 +59,9 @@
 
     Context mContext;
     MediaRouter mRouter;
+    StubMediaRouteProviderService mService;
     StubMediaRouteProviderService.StubMediaRouteProvider mProvider;
+    MediaRouteProviderService.MediaRouteProviderServiceImplApi30 mServiceImpl;
     MediaRoute2ProviderServiceAdapter mMr2ProviderServiceAdapter;
 
     List<MediaRouter.Callback> mCallbacks;
@@ -81,12 +84,13 @@
         new PollingCheck(TIMEOUT_MS) {
             @Override
             protected boolean check() {
-                StubMediaRouteProviderService.StubMediaRouteProvider provider =
-                        StubMediaRouteProviderService.getProvider();
-                if (provider != null) {
-                    mProvider = provider;
-                    mMr2ProviderServiceAdapter =
-                            StubMediaRouteProviderService.getMr2ProviderServiceAdapter();
+                mService = StubMediaRouteProviderService.getInstance();
+                if (mService != null && mService.getMediaRouteProvider() != null) {
+                    mProvider = (StubMediaRouteProviderService.StubMediaRouteProvider)
+                            mService.getMediaRouteProvider();
+                    mServiceImpl = (MediaRouteProviderService.MediaRouteProviderServiceImplApi30)
+                            mService.mImpl;
+                    mMr2ProviderServiceAdapter = mServiceImpl.mMR2ProviderServiceAdapter;
                     return true;
                 }
                 return false;
@@ -154,6 +158,42 @@
         assertTrue(onRouteUnselectedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
     }
 
+    @SmallTest
+    @Test
+    public void onBinderDied_releaseRoutingSessions() throws Exception {
+        String descriptorId = StubMediaRouteProviderService.ROUTE_ID1;
+
+        waitForRoutesAdded();
+        assertNotNull(mRoutes);
+
+        RouteInfo routeToSelect = mRoutes.get(descriptorId);
+        assertNotNull(routeToSelect);
+
+        getInstrumentation().runOnMainSync(() -> mRouter.selectRoute(routeToSelect));
+
+        // Wait for a session being created.
+        PollingCheck.waitFor(TIMEOUT_MS,
+                () -> !mMr2ProviderServiceAdapter.getAllSessionInfo().isEmpty());
+
+        try {
+            List<Messenger> messengers =
+                    mServiceImpl.mClients.stream().map(client -> client.mMessenger)
+                    .collect(Collectors.toList());
+            getInstrumentation().runOnMainSync(() ->
+                    messengers.forEach(mServiceImpl::onBinderDied));
+            // It should have no session info.
+            PollingCheck.waitFor(TIMEOUT_MS,
+                    () -> mMr2ProviderServiceAdapter.getAllSessionInfo().isEmpty());
+        } finally {
+            // Rebind for future tests
+            getInstrumentation().runOnMainSync(
+                    () -> {
+                        MediaRouter.sGlobal.mRegisteredProviderWatcher.stop();
+                        MediaRouter.sGlobal.mRegisteredProviderWatcher.start();
+                    });
+        }
+    }
+
     void addCallback(MediaRouter.Callback callback) {
         getInstrumentation().runOnMainSync(() -> {
             mRouter.addCallback(mSelector, callback, MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY
diff --git a/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/StubMediaRouteProviderService.java b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/StubMediaRouteProviderService.java
index f742900..540fee8 100644
--- a/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/StubMediaRouteProviderService.java
+++ b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/StubMediaRouteProviderService.java
@@ -51,26 +51,9 @@
         CONTROL_FILTERS_TEST.add(f1);
     }
 
-    public static StubMediaRouteProvider getProvider() {
+    public static StubMediaRouteProviderService getInstance() {
         synchronized (sLock) {
-            if (sInstance == null) {
-                return null;
-            }
-            MediaRouteProvider provider = sInstance.getMediaRouteProvider();
-            return (provider == null) ? null : (StubMediaRouteProvider) provider;
-        }
-    }
-
-    public static MediaRoute2ProviderServiceAdapter getMr2ProviderServiceAdapter() {
-        synchronized (sLock) {
-            if (sInstance == null) {
-                return null;
-            }
-            if (!(sInstance.mImpl instanceof MediaRouteProviderServiceImplApi30)) {
-                return null;
-            }
-            return ((MediaRouteProviderServiceImplApi30) sInstance.mImpl)
-                    .mMR2ProviderServiceAdapter;
+            return sInstance;
         }
     }
 
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProviderService.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProviderService.java
index 9ff3b77..0d8dac9 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProviderService.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProviderService.java
@@ -1185,6 +1185,17 @@
             }
 
             @Override
+            public void dispose() {
+                int count = mControllers.size();
+                for (int i = 0; i < count; i++) {
+                    int controllerId = mControllers.keyAt(i);
+                    mMR2ProviderServiceAdapter.notifyRouteControllerRemoved(controllerId);
+                }
+                mRouteIdToControllerMap.clear();
+                super.dispose();
+            }
+
+            @Override
             public boolean createRouteController(String routeId, String routeGroupId,
                     int controllerId) {
                 RouteController controller = mRouteIdToControllerMap.get(routeId);
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java
index b5dcd5d..bf82d6f 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java
@@ -44,6 +44,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
 import androidx.collection.ArrayMap;
 import androidx.core.app.ActivityManagerCompat;
 import androidx.core.content.ContextCompat;
@@ -2311,7 +2312,7 @@
      * state and the bulk of the media router implementation lives here.
      * </p>
      */
-    private static final class GlobalMediaRouter
+    static final class GlobalMediaRouter
             implements SystemMediaRouteProvider.SyncCallback,
             RegisteredMediaRouteProviderWatcher.Callback {
         final Context mApplicationContext;
@@ -2332,7 +2333,8 @@
         private final boolean mLowRam;
 
         private MediaRouterParams mRouterParams;
-        private RegisteredMediaRouteProviderWatcher mRegisteredProviderWatcher;
+        @VisibleForTesting
+        RegisteredMediaRouteProviderWatcher mRegisteredProviderWatcher;
         private RouteInfo mDefaultRoute;
         private RouteInfo mBluetoothRoute;
         RouteInfo mSelectedRoute;