Enable cast via MediaSession2/MediaController2

Bug: 77659082
Test: ./gradlew :media:check :media:connectedCheck
Change-Id: Ia74e92b9dcab26f26b7e6e0bfef5718205bf3b78
diff --git a/media/src/androidTest/java/androidx/media/MediaController2Test.java b/media/src/androidTest/java/androidx/media/MediaController2Test.java
index 4cb51d3..a9be286 100644
--- a/media/src/androidTest/java/androidx/media/MediaController2Test.java
+++ b/media/src/androidTest/java/androidx/media/MediaController2Test.java
@@ -1092,6 +1092,62 @@
     }
 
     @Test
+    public void testSubscribeRouteInfo() throws InterruptedException {
+        prepareLooper();
+        final TestSessionCallback callback = new TestSessionCallback() {
+            @Override
+            public void onSubscribeRoutesInfo(@NonNull MediaSession2 session,
+                    @NonNull ControllerInfo controller) {
+                assertEquals(mContext.getPackageName(), controller.getPackageName());
+                mLatch.countDown();
+            }
+
+            @Override
+            public void onUnsubscribeRoutesInfo(@NonNull MediaSession2 session,
+                    @NonNull ControllerInfo controller) {
+                assertEquals(mContext.getPackageName(), controller.getPackageName());
+                mLatch.countDown();
+            }
+        };
+        mSession.close();
+        mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
+                .setSessionCallback(sHandlerExecutor, callback).setId(TAG).build();
+        final MediaController2 controller = createController(mSession.getToken());
+
+        callback.resetLatchCount(1);
+        controller.subscribeRoutesInfo();
+        assertTrue(callback.mLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+        callback.resetLatchCount(1);
+        controller.unsubscribeRoutesInfo();
+        assertTrue(callback.mLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testSelectRouteInfo() throws InterruptedException {
+        prepareLooper();
+        final Bundle testRoute = new Bundle();
+        testRoute.putString("id", "testRoute");
+        final TestSessionCallback callback = new TestSessionCallback() {
+            @Override
+            public void onSelectRoute(@NonNull MediaSession2 session,
+                    @NonNull ControllerInfo controller, @NonNull Bundle route) {
+                assertEquals(mContext.getPackageName(), controller.getPackageName());
+                assertTrue(TestUtils.equals(route, testRoute));
+                mLatch.countDown();
+            }
+        };
+        mSession.close();
+        mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
+                .setSessionCallback(sHandlerExecutor, callback).setId(TAG).build();
+        final MediaController2 controller = createController(mSession.getToken());
+
+        callback.resetLatchCount(1);
+        controller.selectRoute(testRoute);
+        assertTrue(callback.mLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
     public void testClose_beforeConnected() throws InterruptedException {
         prepareLooper();
         MediaController2 controller =
@@ -1222,4 +1278,12 @@
             mLatch.countDown();
         }
     }
+
+    class TestSessionCallback extends SessionCallback {
+        CountDownLatch mLatch;
+
+        void resetLatchCount(int count) {
+            mLatch = new CountDownLatch(count);
+        }
+    }
 }
diff --git a/media/src/androidTest/java/androidx/media/MediaSession2Test.java b/media/src/androidTest/java/androidx/media/MediaSession2Test.java
index 77dbf43..39764c6 100644
--- a/media/src/androidTest/java/androidx/media/MediaSession2Test.java
+++ b/media/src/androidTest/java/androidx/media/MediaSession2Test.java
@@ -38,6 +38,7 @@
 import android.support.test.runner.AndroidJUnit4;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.media.MediaController2.ControllerCallback;
 import androidx.media.MediaController2.PlaybackInfo;
 import androidx.media.MediaSession2.CommandButton;
@@ -827,6 +828,24 @@
         assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
     }
 
+    @Test
+    public void testNotifyRoutesInfoChanged() throws InterruptedException {
+        prepareLooper();
+        final CountDownLatch latch = new CountDownLatch(1);
+        final ControllerCallback callback = new ControllerCallback() {
+            @Override
+            public void onRoutesInfoChanged(@NonNull MediaController2 controller,
+                    @Nullable List<Bundle> routes) {
+                assertNull(routes);
+                latch.countDown();
+            }
+        };
+        final MediaController2 controller = createController(mSession.getToken(), true, callback);
+        ControllerInfo controllerInfo = getTestControllerInfo();
+        mSession.notifyRoutesInfoChanged(controllerInfo, null);
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
     private ControllerInfo getTestControllerInfo() {
         List<ControllerInfo> controllers = mSession.getConnectedControllers();
         assertNotNull(controllers);
diff --git a/media/src/androidTest/java/androidx/media/MediaSession2TestBase.java b/media/src/androidTest/java/androidx/media/MediaSession2TestBase.java
index bc9a31d..745ef3a 100644
--- a/media/src/androidTest/java/androidx/media/MediaSession2TestBase.java
+++ b/media/src/androidTest/java/androidx/media/MediaSession2TestBase.java
@@ -339,6 +339,12 @@
                 mOnCustomCommandRunnable = runnable;
             }
         }
+
+        @Override
+        public void onRoutesInfoChanged(@NonNull MediaController2 controller,
+                @Nullable List<Bundle> routes) {
+            mCallbackProxy.onRoutesInfoChanged(controller, routes);
+        }
     }
 
     public class TestMediaController extends MediaController2 implements TestControllerInterface {
diff --git a/media/src/main/java/androidx/media/MediaConstants2.java b/media/src/main/java/androidx/media/MediaConstants2.java
index b5af0a9..652776b 100644
--- a/media/src/main/java/androidx/media/MediaConstants2.java
+++ b/media/src/main/java/androidx/media/MediaConstants2.java
@@ -24,8 +24,9 @@
     // Event string used by IMediaControllerCallback.onEvent()
     static final String SESSION_EVENT_ON_PLAYER_STATE_CHANGED =
             "androidx.media.session.event.ON_PLAYER_STATE_CHANGED";
-    static final String SESSION_EVENT_ON_ERROR =
-            "androidx.media.session.event.ON_ERROR";
+    static final String SESSION_EVENT_ON_ERROR = "androidx.media.session.event.ON_ERROR";
+    static final String SESSION_EVENT_ON_ROUTES_INFO_CHANGED =
+            "androidx.media.session.event.ON_ROUTES_INFO_CHANGED";
     static final String SESSION_EVENT_ON_REPEAT_MODE_CHANGED =
             "androidx.media.session.event.ON_REPEAT_MODE_CHANGED";
     static final String SESSION_EVENT_ON_SHUFFLE_MODE_CHANGED =
@@ -76,6 +77,7 @@
     static final String ARGUMENT_ARGUMENTS = "androidx.media.argument.ARGUMENTS";
     static final String ARGUMENT_RESULT_RECEIVER = "androidx.media.argument.RESULT_RECEIVER";
     static final String ARGUMENT_COMMAND_BUTTONS = "androidx.media.argument.COMMAND_BUTTONS";
+    static final String ARGUMENT_ROUTE_BUNDLE = "androidx.media.argument.ROUTE_BUNDLE";
 
     static final String ARGUMENT_ICONTROLLER_CALLBACK =
             "androidx.media.argument.ICONTROLLER_CALLBACK";
diff --git a/media/src/main/java/androidx/media/MediaController2.java b/media/src/main/java/androidx/media/MediaController2.java
index f62616c..9822712 100644
--- a/media/src/main/java/androidx/media/MediaController2.java
+++ b/media/src/main/java/androidx/media/MediaController2.java
@@ -40,6 +40,7 @@
 import static androidx.media.MediaConstants2.ARGUMENT_RATING;
 import static androidx.media.MediaConstants2.ARGUMENT_REPEAT_MODE;
 import static androidx.media.MediaConstants2.ARGUMENT_RESULT_RECEIVER;
+import static androidx.media.MediaConstants2.ARGUMENT_ROUTE_BUNDLE;
 import static androidx.media.MediaConstants2.ARGUMENT_SEEK_POSITION;
 import static androidx.media.MediaConstants2.ARGUMENT_SHUFFLE_MODE;
 import static androidx.media.MediaConstants2.ARGUMENT_UID;
@@ -59,6 +60,7 @@
 import static androidx.media.MediaConstants2.SESSION_EVENT_ON_PLAYLIST_CHANGED;
 import static androidx.media.MediaConstants2.SESSION_EVENT_ON_PLAYLIST_METADATA_CHANGED;
 import static androidx.media.MediaConstants2.SESSION_EVENT_ON_REPEAT_MODE_CHANGED;
+import static androidx.media.MediaConstants2.SESSION_EVENT_ON_ROUTES_INFO_CHANGED;
 import static androidx.media.MediaConstants2.SESSION_EVENT_ON_SHUFFLE_MODE_CHANGED;
 import static androidx.media.MediaConstants2.SESSION_EVENT_SEND_CUSTOM_COMMAND;
 import static androidx.media.MediaConstants2.SESSION_EVENT_SET_CUSTOM_LAYOUT;
@@ -87,7 +89,10 @@
 import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH;
 import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_PREPARE_FROM_URI;
 import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_REWIND;
+import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_SELECT_ROUTE;
 import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_SET_RATING;
+import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_SUBSCRIBE_ROUTES_INFO;
+import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_UNSUBSCRIBE_ROUTES_INFO;
 import static androidx.media.SessionCommand2.COMMAND_CODE_VOLUME_ADJUST_VOLUME;
 import static androidx.media.SessionCommand2.COMMAND_CODE_VOLUME_SET_VOLUME;
 
@@ -331,6 +336,17 @@
          */
         public void onRepeatModeChanged(@NonNull MediaController2 controller,
                 @MediaPlaylistAgent.RepeatMode int repeatMode) { }
+
+        /**
+         * Called when a property of the indicated media route has changed.
+         *
+         * @param controller the controller for this event
+         * @param routes The list of Bundle from MediaRouteDescriptor.asBundle().
+         *              See MediaRouteDescriptor.fromBundle(Bundle bundle) to get
+         *              MediaRouteDescriptor object from the {@code routes}
+         */
+        public void onRoutesInfoChanged(@NonNull MediaController2 controller,
+                @Nullable List<Bundle> routes) { }
     }
 
     /**
@@ -526,6 +542,12 @@
                     mCallback.onError(MediaController2.this, errorCode, errorExtras);
                     break;
                 }
+                case SESSION_EVENT_ON_ROUTES_INFO_CHANGED: {
+                    List<Bundle> routes = MediaUtils2.toBundleList(
+                            extras.getParcelableArray(ARGUMENT_ROUTE_BUNDLE));
+                    mCallback.onRoutesInfoChanged(MediaController2.this, routes);
+                    break;
+                }
                 case SESSION_EVENT_ON_PLAYLIST_CHANGED: {
                     MediaMetadata2 playlistMetadata = MediaMetadata2.fromBundle(
                             extras.getBundle(ARGUMENT_PLAYLIST_METADATA));
@@ -1436,6 +1458,38 @@
         sendCommand(COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE, args);
     }
 
+    /**
+     * Queries for information about the routes currently known.
+     */
+    public void subscribeRoutesInfo() {
+        sendCommand(COMMAND_CODE_SESSION_SUBSCRIBE_ROUTES_INFO);
+    }
+
+    /**
+     * Unsubscribes for changes to the routes.
+     * <p>
+     * The {@link ControllerCallback#onRoutesInfoChanged callback} will no longer be invoked for
+     * the routes once this method returns.
+     * </p>
+     */
+    public void unsubscribeRoutesInfo() {
+        sendCommand(COMMAND_CODE_SESSION_UNSUBSCRIBE_ROUTES_INFO);
+    }
+
+    /**
+     * Selects the specified route.
+     *
+     * @param route The route to select.
+     */
+    public void selectRoute(@NonNull Bundle route) {
+        if (route == null) {
+            throw new IllegalArgumentException("route shouldn't be null");
+        }
+        Bundle args = new Bundle();
+        args.putBundle(ARGUMENT_ROUTE_BUNDLE, route);
+        sendCommand(COMMAND_CODE_SESSION_SELECT_ROUTE, args);
+    }
+
     // Should be used without a lock to prevent potential deadlock.
     void onConnectedNotLocked(Bundle data) {
         // TODO: Getting mPlaybackInfo via MediaControllerCompat.Callback.onAudioInfoChanged()
diff --git a/media/src/main/java/androidx/media/MediaSession2.java b/media/src/main/java/androidx/media/MediaSession2.java
index e11af45..0093991 100644
--- a/media/src/main/java/androidx/media/MediaSession2.java
+++ b/media/src/main/java/androidx/media/MediaSession2.java
@@ -431,6 +431,37 @@
         public void onRewind(@NonNull MediaSession2 session, ControllerInfo controller) { }
 
         /**
+         * Called when a controller called {@link MediaController2#subscribeRoutesInfo()}
+         * Session app should notify the routes information by calling
+         * {@link MediaSession2#notifyRoutesInfoChanged(ControllerInfo, List<Bundle>)}.
+         *
+         * @param session the session for this event
+         * @param controller controller information
+         * @see SessionCommand2#COMMAND_CODE_SESSION_SUBSCRIBE_ROUTES_INFO
+         */
+        public void onSubscribeRoutesInfo(@NonNull MediaSession2 session,
+                @NonNull ControllerInfo controller) { }
+
+        /**
+         * Called when a controller called {@link MediaController2#unsubscribeRoutesInfo()}
+         *
+         * @param session the session for this event
+         * @param controller controller information
+         * @see SessionCommand2#COMMAND_CODE_SESSION_UNSUBSCRIBE_ROUTES_INFO
+         */
+        public void onUnsubscribeRoutesInfo(@NonNull MediaSession2 session,
+                @NonNull ControllerInfo controller) { }
+
+        /**
+         * Called when a controller called {@link MediaController2#selectRoute(Bundle)}.
+         * @param session the session for this event
+         * @param controller controller information
+         * @param route The route bundle which may be from MediaRouteDescritor.asBundle().
+         * @see SessionCommand2#COMMAND_CODE_SESSION_SELECT_ROUTE
+         */
+        public void onSelectRoute(@NonNull MediaSession2 session,
+                @NonNull ControllerInfo controller, @NonNull Bundle route) { }
+        /**
          * Called when the player's current playing item is changed
          * <p>
          * When it's called, you should invalidate previous playback information and wait for later
@@ -1044,6 +1075,8 @@
         abstract void sendCustomCommand(@NonNull ControllerInfo controller,
                 @NonNull SessionCommand2 command, @Nullable Bundle args,
                 @Nullable ResultReceiver receiver);
+        abstract void notifyRoutesInfoChanged(@NonNull ControllerInfo controller,
+                @Nullable List<Bundle> routes);
 
         // Internally used methods
         abstract void setInstance(MediaSession2 session);
@@ -1293,6 +1326,18 @@
     }
 
     /**
+     * Notify routes information to a connected controller
+     *
+     * @param controller controller information
+     * @param routes The routes information. Each bundle should be from
+     *              MediaRouteDescritor.asBundle().
+     */
+    public void notifyRoutesInfoChanged(@NonNull ControllerInfo controller,
+            @Nullable List<Bundle> routes) {
+        mImpl.notifyRoutesInfoChanged(controller, routes);
+    }
+
+    /**
      * Gets the current player state.
      *
      * @return the current player state
diff --git a/media/src/main/java/androidx/media/MediaSession2ImplBase.java b/media/src/main/java/androidx/media/MediaSession2ImplBase.java
index b5a3350..eb23de3 100644
--- a/media/src/main/java/androidx/media/MediaSession2ImplBase.java
+++ b/media/src/main/java/androidx/media/MediaSession2ImplBase.java
@@ -240,7 +240,7 @@
         if (layout == null) {
             throw new IllegalArgumentException("layout shouldn't be null");
         }
-        mSession2Stub.notifyCustomLayoutNotLocked(controller, layout);
+        mSession2Stub.notifyCustomLayout(controller, layout);
     }
 
     @Override
@@ -357,6 +357,12 @@
     }
 
     @Override
+    public void notifyRoutesInfoChanged(@NonNull ControllerInfo controller,
+            @Nullable List<Bundle> routes) {
+        mSession2Stub.notifyRoutesInfoChanged(controller, routes);
+    }
+
+    @Override
     public @MediaPlayerBase.PlayerState int getPlayerState() {
         MediaPlayerBase player;
         synchronized (mLock) {
@@ -721,7 +727,7 @@
     @Override
     PlaybackStateCompat getPlaybackStateCompat() {
         synchronized (mLock) {
-            int state = MediaUtils2.toPlaybackStateCompatState(getPlayerState(),
+            int state = MediaUtils2.createPlaybackStateCompatState(getPlayerState(),
                     getBufferingState());
             // TODO: Consider following missing stuff
             //       - setCustomAction(): Fill custom layout
diff --git a/media/src/main/java/androidx/media/MediaSession2StubImplBase.java b/media/src/main/java/androidx/media/MediaSession2StubImplBase.java
index 6e0d375..b203255 100644
--- a/media/src/main/java/androidx/media/MediaSession2StubImplBase.java
+++ b/media/src/main/java/androidx/media/MediaSession2StubImplBase.java
@@ -37,6 +37,7 @@
 import static androidx.media.MediaConstants2.ARGUMENT_RATING;
 import static androidx.media.MediaConstants2.ARGUMENT_REPEAT_MODE;
 import static androidx.media.MediaConstants2.ARGUMENT_RESULT_RECEIVER;
+import static androidx.media.MediaConstants2.ARGUMENT_ROUTE_BUNDLE;
 import static androidx.media.MediaConstants2.ARGUMENT_SEEK_POSITION;
 import static androidx.media.MediaConstants2.ARGUMENT_SHUFFLE_MODE;
 import static androidx.media.MediaConstants2.ARGUMENT_UID;
@@ -56,6 +57,7 @@
 import static androidx.media.MediaConstants2.SESSION_EVENT_ON_PLAYLIST_CHANGED;
 import static androidx.media.MediaConstants2.SESSION_EVENT_ON_PLAYLIST_METADATA_CHANGED;
 import static androidx.media.MediaConstants2.SESSION_EVENT_ON_REPEAT_MODE_CHANGED;
+import static androidx.media.MediaConstants2.SESSION_EVENT_ON_ROUTES_INFO_CHANGED;
 import static androidx.media.MediaConstants2.SESSION_EVENT_ON_SHUFFLE_MODE_CHANGED;
 import static androidx.media.MediaConstants2.SESSION_EVENT_SEND_CUSTOM_COMMAND;
 import static androidx.media.MediaConstants2.SESSION_EVENT_SET_CUSTOM_LAYOUT;
@@ -82,7 +84,10 @@
 import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH;
 import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_PREPARE_FROM_URI;
 import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_REWIND;
+import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_SELECT_ROUTE;
 import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_SET_RATING;
+import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_SUBSCRIBE_ROUTES_INFO;
+import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_UNSUBSCRIBE_ROUTES_INFO;
 import static androidx.media.SessionCommand2.COMMAND_CODE_VOLUME_ADJUST_VOLUME;
 import static androidx.media.SessionCommand2.COMMAND_CODE_VOLUME_SET_VOLUME;
 
@@ -103,6 +108,7 @@
 
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.media.MediaSession2.CommandButton;
 import androidx.media.MediaSession2.ControllerInfo;
 
@@ -119,6 +125,7 @@
 
     private static final SparseArray<SessionCommand2> sCommandsForOnCommandRequest =
             new SparseArray<>();
+
     static {
         SessionCommandGroup2 group = new SessionCommandGroup2();
         group.addAllPlaybackCommands();
@@ -391,6 +398,21 @@
                                         mSession.getInstance(), controller, mediaId, rating);
                                 break;
                             }
+                            case COMMAND_CODE_SESSION_SUBSCRIBE_ROUTES_INFO: {
+                                mSession.getCallback().onSubscribeRoutesInfo(
+                                        mSession.getInstance(), controller);
+                                break;
+                            }
+                            case COMMAND_CODE_SESSION_UNSUBSCRIBE_ROUTES_INFO: {
+                                mSession.getCallback().onUnsubscribeRoutesInfo(
+                                        mSession.getInstance(), controller);
+                                break;
+                            }
+                            case COMMAND_CODE_SESSION_SELECT_ROUTE: {
+                                Bundle route = extras.getBundle(ARGUMENT_ROUTE_BUNDLE);
+                                mSession.getCallback().onSelectRoute(
+                                        mSession.getInstance(), controller, route);
+                            }
                         }
                     }
                 });
@@ -428,7 +450,7 @@
         return controllers;
     }
 
-    void notifyCustomLayoutNotLocked(ControllerInfo controller, final List<CommandButton> layout) {
+    void notifyCustomLayout(ControllerInfo controller, final List<CommandButton> layout) {
         notifyInternal(controller, new Session2Runnable() {
             @Override
             public void run(ControllerInfo controller) throws RemoteException {
@@ -516,6 +538,22 @@
         });
     }
 
+    void notifyRoutesInfoChanged(@NonNull final ControllerInfo controller,
+            @Nullable final List<Bundle> routes) {
+        notifyInternal(controller, new Session2Runnable() {
+            @Override
+            public void run(ControllerInfo controller) throws RemoteException {
+                Bundle bundle = null;
+                if (routes != null) {
+                    bundle = new Bundle();
+                    bundle.putParcelableArray(ARGUMENT_ROUTE_BUNDLE, routes.toArray(new Bundle[0]));
+                }
+                controller.getControllerBinder().onEvent(
+                        SESSION_EVENT_ON_ROUTES_INFO_CHANGED, bundle);
+            }
+        });
+    }
+
     void notifyPlaylistChanged(final List<MediaItem2> playlist,
             final MediaMetadata2 metadata) {
         notifyAll(SessionCommand2.COMMAND_CODE_PLAYLIST_GET_LIST, new Session2Runnable() {
diff --git a/media/src/main/java/androidx/media/MediaUtils2.java b/media/src/main/java/androidx/media/MediaUtils2.java
index b784599..1707f9c 100644
--- a/media/src/main/java/androidx/media/MediaUtils2.java
+++ b/media/src/main/java/androidx/media/MediaUtils2.java
@@ -371,7 +371,18 @@
                 .build();
     }
 
-    static int toPlaybackStateCompatState(int playerState, int bufferingState) {
+    static List<Bundle> toBundleList(Parcelable[] array) {
+        if (array == null) {
+            return null;
+        }
+        List<Bundle> bundleList = new ArrayList<>();
+        for (Parcelable p : array) {
+            bundleList.add((Bundle) p);
+        }
+        return bundleList;
+    }
+
+    static int createPlaybackStateCompatState(int playerState, int bufferingState) {
         switch (playerState) {
             case MediaPlayerBase.PLAYER_STATE_PLAYING:
                 switch (bufferingState) {
diff --git a/media/src/main/java/androidx/media/SessionCommand2.java b/media/src/main/java/androidx/media/SessionCommand2.java
index 32a9154..a07799b 100644
--- a/media/src/main/java/androidx/media/SessionCommand2.java
+++ b/media/src/main/java/androidx/media/SessionCommand2.java
@@ -265,6 +265,22 @@
     public static final int COMMAND_CODE_SESSION_SET_RATING = 28;
 
     /**
+     * Command code for {@link MediaController2#subscribeRoutesInfo()}
+     */
+    public static final int COMMAND_CODE_SESSION_SUBSCRIBE_ROUTES_INFO = 36;
+
+    /**
+     * Command code for {@link MediaController2#unsubscribeRoutesInfo()}
+     */
+    public static final int COMMAND_CODE_SESSION_UNSUBSCRIBE_ROUTES_INFO = 37;
+
+    /**
+     * Command code for {@link MediaController2#selectRoute(Bundle)}}
+     */
+    public static final int COMMAND_CODE_SESSION_SELECT_ROUTE = 38;
+
+
+    /**
      * Command code for {@link MediaBrowser2#getChildren(String, int, int, Bundle)}.
      */
     public static final int COMMAND_CODE_LIBRARY_GET_CHILDREN = 29;