Merge "MediaRouter: add session related apis into provider"
diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl
index a03aa86..abb2040 100644
--- a/media/java/android/media/IMediaRoute2Provider.aidl
+++ b/media/java/android/media/IMediaRoute2Provider.aidl
@@ -24,10 +24,14 @@
  */
 oneway interface IMediaRoute2Provider {
     void setClient(IMediaRoute2ProviderClient client);
-    void requestCreateSession(String packageName, String routeId, String controlCategory,
-            int requestId);
-    void requestSelectRoute(String packageName, String id, int seq);
-    void unselectRoute(String packageName, String id);
+    void requestCreateSession(String packageName, String routeId,
+            String controlCategory, int requestId);
+    void releaseSession(int sessionId);
+
+    void addRoute(int sessionId, String routeId);
+    void removeRoute(int sessionId, String routeId);
+    void transferRoute(int sessionId, String routeId);
+
     void notifyControlRequestSent(String id, in Intent request);
     void requestSetVolume(String id, int volume);
     void requestUpdateVolume(String id, int delta);
diff --git a/media/java/android/media/IMediaRoute2ProviderClient.aidl b/media/java/android/media/IMediaRoute2ProviderClient.aidl
index 657f703..ce5fa8c 100644
--- a/media/java/android/media/IMediaRoute2ProviderClient.aidl
+++ b/media/java/android/media/IMediaRoute2ProviderClient.aidl
@@ -26,7 +26,5 @@
  */
 oneway interface IMediaRoute2ProviderClient {
     void updateProviderInfo(in MediaRoute2ProviderInfo info);
-    void notifyRouteSelected(String packageName, String routeId, in @nullable Bundle controlHints,
-            int seq);
     void notifySessionCreated(in @nullable RouteSessionInfo sessionInfo, int requestId);
 }
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index faf2563..86fa20f 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -55,15 +55,9 @@
 
     void registerManager(IMediaRouter2Manager manager, String packageName);
     void unregisterManager(IMediaRouter2Manager manager);
-    /**
-     * Changes the selected route of an application.
-     *
-     * @param manager the manager that calls the method
-     * @param packageName the package name of the client that will change the selected route
-     * @param route the route to be selected
-     */
-    void selectClientRoute2(IMediaRouter2Manager manager, String packageName,
-            in @nullable MediaRoute2Info route);
+
+    void requestCreateClientSession(IMediaRouter2Manager manager, String packageName,
+        in @nullable MediaRoute2Info route, int requestId);
 
     void requestSetVolume2Manager(IMediaRouter2Manager manager,
             in MediaRoute2Info route, int volume);
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 387b042..fc741c6 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -22,10 +22,12 @@
 import android.annotation.Nullable;
 import android.app.Service;
 import android.content.Intent;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
+import android.os.Process;
 import android.os.RemoteException;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -47,7 +49,6 @@
     private final Handler mHandler;
     private final Object mSessionLock = new Object();
     private ProviderStub mStub;
-    // TODO: Rename this to mService (and accordingly IMediaRoute2ProviderClient to something else)
     private IMediaRoute2ProviderClient mClient;
     private MediaRoute2ProviderInfo mProviderInfo;
 
@@ -71,28 +72,6 @@
     }
 
     /**
-     * Called when selectRoute is called on a route of the provider.
-     * Once the route is ready to be used , call {@link #notifyRouteSelected(SelectToken, Bundle)}
-     * to notify that.
-     *
-     * @param packageName the package name of the application that selected the route
-     * @param routeId the id of the route being selected
-     * @param token token that contains select info
-     *
-     * @see #notifyRouteSelected
-     */
-    public abstract void onSelectRoute(@NonNull String packageName, @NonNull String routeId,
-            @NonNull SelectToken token);
-
-    /**
-     * Called when unselectRoute is called on a route of the provider.
-     *
-     * @param packageName the package name of the application that has selected the route.
-     * @param routeId the id of the route being unselected
-     */
-    public abstract void onUnselectRoute(@NonNull String packageName, @NonNull String routeId);
-
-    /**
      * Called when sendControlRequest is called on a route of the provider
      *
      * @param routeId the id of the target route
@@ -155,6 +134,8 @@
         Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
         int sessionId = sessionInfo.getSessionId();
 
+        //TODO: notify updated session info
+
         synchronized (mSessionLock) {
             if (mSessionInfo.containsKey(sessionId)) {
                 mSessionInfo.put(sessionId, sessionInfo);
@@ -162,6 +143,9 @@
                 Log.w(TAG, "Ignoring unknown session info.");
             }
         }
+        if (sessionInfo.getSelectedRoutes().isEmpty()) {
+            releaseSession(sessionId);
+        }
     }
 
     /**
@@ -169,15 +153,20 @@
      * controlled, pass a {@link Bundle} that contains how to control it.
      *
      * @param sessionInfo information of the new session.
-     *                    Pass {@code null} to reject the request or inform clients that
-     *                    session creation has failed.
+     *                    The {@link RouteSessionInfo#getSessionId() id} of the session must be
+     *                    unique. Pass {@code null} to reject the request or inform clients that
+     *                    session creation is failed.
      * @param requestId id of the previous request to create this session
      */
     //TODO: fail reason?
     public final void notifySessionCreated(@Nullable RouteSessionInfo sessionInfo, int requestId) {
-        //TODO: validate sessionInfo.getSessionId() (it must be in "waiting list")
         if (sessionInfo != null) {
+            int sessionId = sessionInfo.getSessionId();
             synchronized (mSessionLock) {
+                if (mSessionInfo.containsKey(sessionId)) {
+                    Log.w(TAG, "Ignoring duplicate session id.");
+                    return;
+                }
                 mSessionInfo.put(sessionInfo.getSessionId(), sessionInfo);
             }
         }
@@ -200,6 +189,7 @@
      * @see #onDestroySession(int, RouteSessionInfo)
      */
     public final void releaseSession(int sessionId) {
+        //TODO: notify media router service of release.
         RouteSessionInfo sessionInfo;
         synchronized (mSessionLock) {
             sessionInfo = mSessionInfo.put(sessionId, null);
@@ -283,29 +273,6 @@
         publishState();
     }
 
-    /**
-     * Notifies the client of that the selected route is ready for use. If the selected route can be
-     * controlled, pass a {@link Bundle} that contains how to control it.
-     *
-     * @param token token passed in {@link #onSelectRoute}
-     * @param controlHints a {@link Bundle} that contains how to control the given route.
-     * Pass {@code null} if the route is not available.
-     */
-    public final void notifyRouteSelected(@NonNull SelectToken token,
-            @Nullable Bundle controlHints) {
-        Objects.requireNonNull(token, "token must not be null");
-
-        if (mClient == null) {
-            return;
-        }
-        try {
-            mClient.notifyRouteSelected(token.mPackageName, token.mRouteId,
-                    controlHints, token.mSeq);
-        } catch (RemoteException ex) {
-            Log.w(TAG, "Failed to notify route selected");
-        }
-    }
-
     void setClient(IMediaRoute2ProviderClient client) {
         mClient = client;
         publishState();
@@ -323,68 +290,91 @@
         }
     }
 
-    /**
-     * Route selection information.
-     *
-     * @see #notifyRouteSelected
-     */
-    public final class SelectToken {
-        final String mPackageName;
-        final String mRouteId;
-        final int mSeq;
-
-        SelectToken(String packageName, String routeId, int seq) {
-            mPackageName = packageName;
-            mRouteId = routeId;
-            mSeq = seq;
-        }
-    }
-
     final class ProviderStub extends IMediaRoute2Provider.Stub {
         ProviderStub() { }
 
+        boolean checkCallerisSystem() {
+            return Binder.getCallingUid() == Process.SYSTEM_UID;
+        }
+
         @Override
         public void setClient(IMediaRoute2ProviderClient client) {
+            if (!checkCallerisSystem()) {
+                return;
+            }
             mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::setClient,
                     MediaRoute2ProviderService.this, client));
         }
 
         @Override
-        public void requestCreateSession(String packageName, String routeId, String controlCategory,
-                int requestId) {
+        public void requestCreateSession(String packageName, String routeId,
+                String controlCategory, int requestId) {
+            if (!checkCallerisSystem()) {
+                return;
+            }
             mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onCreateSession,
                     MediaRoute2ProviderService.this, packageName, routeId, controlCategory,
                     requestId));
         }
-
         @Override
-        public void requestSelectRoute(String packageName, String routeId, int seq) {
-            //TODO: call onCreateSession instead
-            mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSelectRoute,
-                    MediaRoute2ProviderService.this, packageName, routeId,
-                    new SelectToken(packageName, routeId, seq)));
+        public void releaseSession(int sessionId) {
+            if (!checkCallerisSystem()) {
+                return;
+            }
+            mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::releaseSession,
+                    MediaRoute2ProviderService.this, sessionId));
         }
 
         @Override
-        public void unselectRoute(String packageName, String routeId) {
-            mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onUnselectRoute,
-                    MediaRoute2ProviderService.this, packageName, routeId));
+        public void addRoute(int sessionId, String routeId) {
+            if (!checkCallerisSystem()) {
+                return;
+            }
+            mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onAddRoute,
+                    MediaRoute2ProviderService.this, sessionId, routeId));
+        }
+
+        @Override
+        public void removeRoute(int sessionId, String routeId) {
+            if (!checkCallerisSystem()) {
+                return;
+            }
+            mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onRemoveRoute,
+                    MediaRoute2ProviderService.this, sessionId, routeId));
+        }
+
+        @Override
+        public void transferRoute(int sessionId, String routeId) {
+            if (!checkCallerisSystem()) {
+                return;
+            }
+            mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onTransferRoute,
+                    MediaRoute2ProviderService.this, sessionId, routeId));
         }
 
         @Override
         public void notifyControlRequestSent(String routeId, Intent request) {
+            if (!checkCallerisSystem()) {
+                return;
+            }
             mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onControlRequest,
                     MediaRoute2ProviderService.this, routeId, request));
         }
 
         @Override
         public void requestSetVolume(String routeId, int volume) {
+            if (!checkCallerisSystem()) {
+                return;
+            }
             mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetVolume,
                     MediaRoute2ProviderService.this, routeId, volume));
         }
 
         @Override
         public void requestUpdateVolume(String routeId, int delta) {
+            if (!checkCallerisSystem()) {
+                return;
+            }
             mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onUpdateVolume,
                     MediaRoute2ProviderService.this, routeId, delta));
         }
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 5de3370..f677336 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -23,6 +23,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.os.Handler;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.text.TextUtils;
@@ -40,6 +41,7 @@
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * @hide
@@ -66,6 +68,8 @@
     @NonNull
     final ConcurrentMap<String, List<String>> mControlCategoryMap = new ConcurrentHashMap<>();
 
+    private AtomicInteger mNextRequestId = new AtomicInteger(1);
+
     /**
      * Gets an instance of media router manager that controls media route of other applications.
      *
@@ -204,7 +208,7 @@
      * Selects media route for the specified package name.
      *
      * @param packageName the package name of the application that should change it's media route
-     * @param route the route to be selected
+     * @param route the route to be selected.
      */
     public void selectRoute(@NonNull String packageName, @NonNull MediaRoute2Info route) {
         Objects.requireNonNull(packageName, "packageName must not be null");
@@ -216,26 +220,10 @@
         }
         if (client != null) {
             try {
-                mMediaRouterService.selectClientRoute2(client, packageName, route);
-            } catch (RemoteException ex) {
-                Log.e(TAG, "Unable to select media route", ex);
-            }
-        }
-    }
-
-    /**
-     * Unselects media route for the specified package name.
-     *
-     * @param packageName the package name of the application that should stop routing
-     */
-    public void unselectRoute(@NonNull String packageName) {
-        Client client;
-        synchronized (sLock) {
-            client = mClient;
-        }
-        if (client != null) {
-            try {
-                mMediaRouterService.selectClientRoute2(client, packageName, null);
+                //TODO: make request id globally unique
+                int requestId = Process.myPid() * 10000 + mNextRequestId.getAndIncrement();
+                mMediaRouterService.requestCreateClientSession(
+                        client, packageName, route, requestId);
             } catch (RemoteException ex) {
                 Log.e(TAG, "Unable to select media route", ex);
             }
diff --git a/media/java/android/media/RouteSessionInfo.java b/media/java/android/media/RouteSessionInfo.java
index c22fc40..4fd27894 100644
--- a/media/java/android/media/RouteSessionInfo.java
+++ b/media/java/android/media/RouteSessionInfo.java
@@ -49,10 +49,13 @@
     final int mSessionId;
     final String mPackageName;
     final String mControlCategory;
+    @Nullable
+    final String mProviderId;
     final List<String> mSelectedRoutes;
     final List<String> mDeselectableRoutes;
     final List<String> mGroupableRoutes;
     final List<String> mTransferrableRoutes;
+    @Nullable
     final Bundle mControlHints;
 
     RouteSessionInfo(@NonNull Builder builder) {
@@ -61,6 +64,7 @@
         mSessionId = builder.mSessionId;
         mPackageName = builder.mPackageName;
         mControlCategory = builder.mControlCategory;
+        mProviderId = builder.mProviderId;
 
         mSelectedRoutes = Collections.unmodifiableList(builder.mSelectedRoutes);
         mDeselectableRoutes = Collections.unmodifiableList(builder.mDeselectableRoutes);
@@ -76,6 +80,7 @@
         mSessionId = src.readInt();
         mPackageName = ensureString(src.readString());
         mControlCategory = ensureString(src.readString());
+        mProviderId = src.readString();
 
         mSelectedRoutes = ensureList(src.createStringArrayList());
         mDeselectableRoutes = ensureList(src.createStringArrayList());
@@ -117,6 +122,14 @@
     }
 
     /**
+     * Gets the client package name of the session
+     */
+    @NonNull
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
      * Gets the control category of the session.
      * Routes that don't support the category can't be added to the session.
      */
@@ -126,6 +139,15 @@
     }
 
     /**
+     * Gets the provider id of the session.
+     * @hide
+     */
+    @Nullable
+    public String getProviderId() {
+        return mProviderId;
+    }
+
+    /**
      * Gets the list of ids of selected routes for the session. It shouldn't be empty.
      */
     @NonNull
@@ -175,6 +197,7 @@
         dest.writeInt(mSessionId);
         dest.writeString(mPackageName);
         dest.writeString(mControlCategory);
+        dest.writeString(mProviderId);
         dest.writeStringList(mSelectedRoutes);
         dest.writeStringList(mDeselectableRoutes);
         dest.writeStringList(mGroupableRoutes);
@@ -187,13 +210,20 @@
         StringBuilder result = new StringBuilder()
                 .append("RouteSessionInfo{ ")
                 .append("sessionId=").append(mSessionId)
-                .append(", selectedRoutes={");
-        for (int i = 0; i < mSelectedRoutes.size(); i++) {
-            if (i > 0) result.append(", ");
-            result.append(mSelectedRoutes.get(i));
-        }
-        result.append("}");
-        result.append(" }");
+                .append(", controlCategory=").append(mControlCategory)
+                .append(", selectedRoutes={")
+                .append(String.join(",", mSelectedRoutes))
+                .append("}")
+                .append(", deselectableRoutes={")
+                .append(String.join(",", mDeselectableRoutes))
+                .append("}")
+                .append(", groupableRoutes={")
+                .append(String.join(",", mGroupableRoutes))
+                .append("}")
+                .append(", transferrableRoutes={")
+                .append(String.join(",", mTransferrableRoutes))
+                .append("}")
+                .append(" }");
         return result.toString();
     }
 
@@ -204,6 +234,7 @@
         final String mPackageName;
         final int mSessionId;
         final String mControlCategory;
+        String mProviderId;
         final List<String> mSelectedRoutes;
         final List<String> mDeselectableRoutes;
         final List<String> mGroupableRoutes;
@@ -227,6 +258,7 @@
             mSessionId = sessionInfo.mSessionId;
             mPackageName = sessionInfo.mPackageName;
             mControlCategory = sessionInfo.mControlCategory;
+            mProviderId = sessionInfo.mProviderId;
 
             mSelectedRoutes = new ArrayList<>(sessionInfo.mSelectedRoutes);
             mDeselectableRoutes = new ArrayList<>(sessionInfo.mDeselectableRoutes);
@@ -237,6 +269,15 @@
         }
 
         /**
+         * Sets the provider id of the session.
+         */
+        @NonNull
+        public Builder setProviderId(String providerId) {
+            mProviderId = providerId;
+            return this;
+        }
+
+        /**
          * Clears the selected routes.
          */
         @NonNull
diff --git a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
index d04c9b2..36a02c8 100644
--- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
@@ -24,7 +24,6 @@
 import android.media.MediaRoute2ProviderInfo;
 import android.media.MediaRoute2ProviderService;
 import android.media.RouteSessionInfo;
-import android.os.Bundle;
 import android.os.IBinder;
 import android.text.TextUtils;
 
@@ -62,6 +61,7 @@
     public static final int SESSION_ID_1 = 1000;
 
     Map<String, MediaRoute2Info> mRoutes = new HashMap<>();
+    Map<String, Integer> mRouteSessionMap = new HashMap<>();
 
     private void initializeRoutes() {
         MediaRoute2Info route1 = new MediaRoute2Info.Builder(ROUTE_ID1, ROUTE_NAME1)
@@ -113,31 +113,6 @@
     }
 
     @Override
-    public void onSelectRoute(String packageName, String routeId, SelectToken token) {
-        MediaRoute2Info route = mRoutes.get(routeId);
-        if (route == null) {
-            return;
-        }
-        mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
-                .setClientPackageName(packageName)
-                .build());
-        publishRoutes();
-        notifyRouteSelected(token, Bundle.EMPTY);
-    }
-
-    @Override
-    public void onUnselectRoute(String packageName, String routeId) {
-        MediaRoute2Info route = mRoutes.get(routeId);
-        if (route == null) {
-            return;
-        }
-        mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
-                .setClientPackageName(null)
-                .build());
-        publishRoutes();
-    }
-
-    @Override
     public void onControlRequest(String routeId, Intent request) {
         String action = request.getAction();
         if (ACTION_REMOVE_ROUTE.equals(action)) {
@@ -180,26 +155,56 @@
     @Override
     public void onCreateSession(String packageName, String routeId, String controlCategory,
             int requestId) {
-        if (TextUtils.equals(ROUTE_ID3_SESSION_CREATION_FAILED, routeId)) {
+        MediaRoute2Info route = mRoutes.get(routeId);
+        if (route == null || TextUtils.equals(ROUTE_ID3_SESSION_CREATION_FAILED, routeId)) {
             // Tell the router that session cannot be created by passing null as sessionInfo.
             notifySessionCreated(/* sessionInfo= */ null, requestId);
             return;
         }
+        maybeRemoveRoute(routeId);
+
+        int sessionId = SESSION_ID_1;
+
+        mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
+                .setClientPackageName(packageName)
+                .build());
+        mRouteSessionMap.put(routeId, sessionId);
 
         RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder(
-                SESSION_ID_1, packageName, controlCategory)
+                sessionId, packageName, controlCategory)
                 .addSelectedRoute(routeId)
                 .build();
-        notifySessionCreated(sessionInfo,  requestId);
+        notifySessionCreated(sessionInfo, requestId);
+        publishRoutes();
     }
 
     @Override
-    public void onDestroySession(int sessionId, RouteSessionInfo lastSessionInfo) {}
+    public void onDestroySession(int sessionId, RouteSessionInfo lastSessionInfo) {
+        for (String routeId : lastSessionInfo.getSelectedRoutes()) {
+            mRouteSessionMap.remove(routeId);
+            MediaRoute2Info route = mRoutes.get(routeId);
+            if (route != null) {
+                mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
+                        .setClientPackageName(null)
+                        .build());
+            }
+        }
+    }
 
     @Override
     public void onAddRoute(int sessionId, String routeId) {
         RouteSessionInfo sessionInfo = getSessionInfo(sessionId);
-        //TODO: we may want to remove route if it belongs to another session
+        MediaRoute2Info route = mRoutes.get(routeId);
+        if (route == null || sessionInfo == null) {
+            return;
+        }
+        maybeRemoveRoute(routeId);
+
+        mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
+                .setClientPackageName(sessionInfo.getPackageName())
+                .build());
+        mRouteSessionMap.put(routeId, sessionId);
+
         RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo)
                 .addSelectedRoute(routeId)
                 .build();
@@ -210,6 +215,16 @@
     @Override
     public void onRemoveRoute(int sessionId, String routeId) {
         RouteSessionInfo sessionInfo = getSessionInfo(sessionId);
+        MediaRoute2Info route = mRoutes.get(routeId);
+
+        mRouteSessionMap.remove(routeId);
+        if (sessionInfo == null || route == null) {
+            return;
+        }
+        mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
+                .setClientPackageName(null)
+                .build());
+
         RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo)
                 .removeSelectedRoute(routeId)
                 .build();
@@ -228,6 +243,15 @@
         publishRoutes();
     }
 
+    void maybeRemoveRoute(String routeId) {
+        if (!mRouteSessionMap.containsKey(routeId)) {
+            return;
+        }
+
+        int sessionId = mRouteSessionMap.get(routeId);
+        onRemoveRoute(sessionId, routeId);
+    }
+
     void publishRoutes() {
         MediaRoute2ProviderInfo info = new MediaRoute2ProviderInfo.Builder()
                 .addRoutes(mRoutes.values())
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index 9761e6d..728745a 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -226,7 +226,8 @@
             mManager.selectRoute(mPackageName, routeToSelect);
             assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
         } finally {
-            mManager.unselectRoute(mPackageName);
+            //TODO: release the session
+            //mManager.selectRoute(mPackageName, null);
         }
     }
 
@@ -234,7 +235,7 @@
      * Tests if MR2Manager.Callback.onRouteSelected is called
      * when a route is selected by MR2Manager.
      */
-    @Test
+    //TODO: test session created callback instead of onRouteSelected
     public void testManagerOnRouteSelected() throws Exception {
         CountDownLatch latch = new CountDownLatch(1);
         Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
@@ -257,14 +258,14 @@
             mManager.selectRoute(mPackageName, routeToSelect);
             assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
         } finally {
-            mManager.unselectRoute(mPackageName);
+            //TODO: release the session
+            //mManager.selectRoute(mPackageName, null);
         }
     }
 
-    @Test
+    //TODO: enable this when "releasing session" is implemented
     public void testGetActiveRoutes() throws Exception {
         CountDownLatch latch = new CountDownLatch(1);
-        CountDownLatch latch2 = new CountDownLatch(1);
 
         Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
         addRouterCallback(new RouteCallback());
@@ -278,6 +279,7 @@
             }
         });
 
+        //TODO: it fails due to not releasing session
         assertEquals(0, mManager.getActiveRoutes().size());
 
         mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1));
@@ -285,17 +287,20 @@
 
         assertEquals(1, mManager.getActiveRoutes().size());
 
+        //TODO: release the session
+        /*
         awaitOnRouteChangedManager(
-                () -> mManager.unselectRoute(mPackageName),
+                () -> mManager.selectRoute(mPackageName, null),
                 ROUTE_ID1,
                 route -> TextUtils.equals(route.getClientPackageName(), null));
         assertEquals(0, mManager.getActiveRoutes().size());
+        */
     }
 
     /**
      * Tests selecting and unselecting routes of a single provider.
      */
-    @Test
+    //TODO: @Test when session is released
     public void testSingleProviderSelect() throws Exception {
         Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
         addRouterCallback(new RouteCallback());
@@ -310,10 +315,14 @@
                 ROUTE_ID2,
                 route -> TextUtils.equals(route.getClientPackageName(), mPackageName));
 
+        //TODO: release the session
+        /*
         awaitOnRouteChangedManager(
-                () -> mManager.unselectRoute(mPackageName),
+                () -> mManager.selectRoute(mPackageName, null),
                 ROUTE_ID2,
                 route -> TextUtils.equals(route.getClientPackageName(), null));
+
+        */
     }
 
     @Test
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 0483431..31c649e 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -23,7 +23,6 @@
 import android.media.MediaRoute2Info;
 import android.media.MediaRoute2ProviderInfo;
 import android.media.RouteSessionInfo;
-import android.os.Bundle;
 
 import java.util.Objects;
 
@@ -45,8 +44,12 @@
 
     public abstract void requestCreateSession(String packageName, String routeId,
             String controlCategory, int requestId);
-    public abstract void requestSelectRoute(String packageName, String routeId, int seq);
-    public abstract void unselectRoute(String packageName, String routeId);
+    public abstract void releaseSession(int sessionId);
+
+    public abstract void addRoute(int sessionId, MediaRoute2Info route);
+    public abstract void removeRoute(int sessionId, MediaRoute2Info route);
+    public abstract void transferRoute(int sessionId, MediaRoute2Info route);
+
     public abstract void sendControlRequest(MediaRoute2Info route, Intent request);
     public abstract void requestSetVolume(MediaRoute2Info route, int volume);
     public abstract void requestUpdateVolume(MediaRoute2Info route, int delta);
@@ -82,9 +85,6 @@
 
     public interface Callback {
         void onProviderStateChanged(@Nullable MediaRoute2Provider provider);
-        void onRouteSelected(@NonNull MediaRoute2ProviderProxy provider,
-                @NonNull String clientPackageName, @NonNull MediaRoute2Info route,
-                @Nullable Bundle controlHints, int seq);
         void onSessionCreated(@NonNull MediaRoute2Provider provider,
                 @Nullable RouteSessionInfo sessionInfo, int requestId);
     }
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index 893747b..902a257 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -28,7 +28,6 @@
 import android.media.MediaRoute2ProviderInfo;
 import android.media.MediaRoute2ProviderService;
 import android.media.RouteSessionInfo;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
@@ -86,18 +85,31 @@
     }
 
     @Override
-    public void requestSelectRoute(String packageName, String routeId, int seq) {
+    public void releaseSession(int sessionId) {
         if (mConnectionReady) {
-            mActiveConnection.requestSelectRoute(packageName, routeId, seq);
+            mActiveConnection.releaseSession(sessionId);
             updateBinding();
         }
     }
 
     @Override
-    public void unselectRoute(String packageName, String routeId) {
+    public void addRoute(int sessionId, MediaRoute2Info route) {
         if (mConnectionReady) {
-            mActiveConnection.unselectRoute(packageName, routeId);
-            updateBinding();
+            mActiveConnection.addRoute(sessionId, route.getId());
+        }
+    }
+
+    @Override
+    public void removeRoute(int sessionId, MediaRoute2Info route) {
+        if (mConnectionReady) {
+            mActiveConnection.removeRoute(sessionId, route.getId());
+        }
+    }
+
+    @Override
+    public void transferRoute(int sessionId, MediaRoute2Info route) {
+        if (mConnectionReady) {
+            mActiveConnection.transferRoute(sessionId, route.getId());
         }
     }
 
@@ -266,20 +278,6 @@
         setAndNotifyProviderInfo(info);
     }
 
-    private void onRouteSelected(Connection connection,
-            String packageName, String routeId, Bundle controlHints, int seq) {
-        if (mActiveConnection != connection) {
-            return;
-        }
-        MediaRoute2ProviderInfo providerInfo = getProviderInfo();
-        MediaRoute2Info route = (providerInfo == null) ? null : providerInfo.getRoute(routeId);
-        if (route == null) {
-            Slog.w(TAG, this + ": Unknown route " + routeId + " is selected from remove provider");
-            return;
-        }
-        mCallback.onRouteSelected(this, packageName, route, controlHints, seq);
-    }
-
     private void onSessionCreated(Connection connection, @Nullable RouteSessionInfo sessionInfo,
             int requestId) {
         if (mActiveConnection != connection) {
@@ -331,25 +329,42 @@
         public void requestCreateSession(String packageName, String routeId, String controlCategory,
                 int requestId) {
             try {
-                mProvider.requestCreateSession(packageName, routeId, controlCategory, requestId);
+                mProvider.requestCreateSession(packageName, routeId,
+                        controlCategory, requestId);
             } catch (RemoteException ex) {
                 Slog.e(TAG, "Failed to deliver request to create a session.", ex);
             }
         }
 
-        public void requestSelectRoute(String packageName, String routeId, int seq) {
+        public void releaseSession(int sessionId) {
             try {
-                mProvider.requestSelectRoute(packageName, routeId, seq);
+                mProvider.releaseSession(sessionId);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "Failed to deliver request to set discovery mode.", ex);
+                Slog.e(TAG, "Failed to deliver request to release a session.", ex);
             }
         }
 
-        public void unselectRoute(String packageName, String routeId) {
+        public void addRoute(int sessionId, String routeId) {
             try {
-                mProvider.unselectRoute(packageName, routeId);
+                mProvider.addRoute(sessionId, routeId);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "Failed to deliver request to set discovery mode.", ex);
+                Slog.e(TAG, "Failed to deliver request to add a route to a session.", ex);
+            }
+        }
+
+        public void removeRoute(int sessionId, String routeId) {
+            try {
+                mProvider.removeRoute(sessionId, routeId);
+            } catch (RemoteException ex) {
+                Slog.e(TAG, "Failed to deliver request to remove a route from a session.", ex);
+            }
+        }
+
+        public void transferRoute(int sessionId, String routeId) {
+            try {
+                mProvider.transferRoute(sessionId, routeId);
+            } catch (RemoteException ex) {
+                Slog.e(TAG, "Failed to deliver request to transfer a session to a route.", ex);
             }
         }
 
@@ -386,11 +401,6 @@
             mHandler.post(() -> onProviderInfoUpdated(Connection.this, info));
         }
 
-        void postRouteSelected(String packageName, String routeId, Bundle controlHints, int seq) {
-            mHandler.post(() -> onRouteSelected(Connection.this,
-                    packageName, routeId, controlHints, seq));
-        }
-
         void postSessionCreated(@Nullable RouteSessionInfo sessionInfo, int requestId) {
             mHandler.post(() -> onSessionCreated(Connection.this, sessionInfo,
                     requestId));
@@ -417,15 +427,6 @@
         }
 
         @Override
-        public void notifyRouteSelected(String packageName, String routeId,
-                Bundle controlHints, int seq) {
-            Connection connection = mConnectionRef.get();
-            if (connection != null) {
-                connection.postRouteSelected(packageName, routeId, controlHints, seq);
-            }
-        }
-
-        @Override
         public void notifySessionCreated(@Nullable RouteSessionInfo sessionInfo, int requestId) {
             Connection connection = mConnectionRef.get();
             if (connection != null) {
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index a711863..a1ef850 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -28,14 +28,12 @@
 import android.media.IMediaRouter2Manager;
 import android.media.MediaRoute2Info;
 import android.media.MediaRoute2ProviderInfo;
-import android.media.MediaRouter2;
 import android.media.RouteSessionInfo;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
-import android.os.Message;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.text.TextUtils;
@@ -77,8 +75,6 @@
     private final ArrayMap<IBinder, ManagerRecord> mAllManagerRecords = new ArrayMap<>();
     @GuardedBy("mLock")
     private int mCurrentUserId = -1;
-    @GuardedBy("mLock")
-    private int mSelectRouteRequestSequenceNumber = 1;
 
     MediaRouter2ServiceImpl(Context context) {
         mContext = context;
@@ -246,12 +242,12 @@
         }
     }
 
-    public void selectClientRoute2(@NonNull IMediaRouter2Manager manager,
-            String packageName, @Nullable MediaRoute2Info route) {
+    public void requestCreateClientSession(IMediaRouter2Manager manager, String packageName,
+            MediaRoute2Info route, int requestId) {
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
-                selectClientRoute2Locked(manager, packageName, route);
+                requestClientCreateSessionLocked(manager, packageName, route, requestId);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -374,42 +370,6 @@
         }
     }
 
-    private void requestSelectRoute2Locked(Client2Record clientRecord, boolean selectedByManager,
-            MediaRoute2Info route) {
-        if (clientRecord != null) {
-            MediaRoute2Info oldRoute = clientRecord.mSelectedRoute;
-            clientRecord.mSelectingRoute = route;
-            clientRecord.mIsManagerSelecting = selectedByManager;
-
-            UserHandler handler = clientRecord.mUserRecord.mHandler;
-            //TODO: Handle transfer instead of unselect and select
-            if (oldRoute != null) {
-                handler.sendMessage(obtainMessage(
-                        UserHandler::unselectRoute, handler, clientRecord.mPackageName, oldRoute));
-            }
-            if (route != null) {
-                final int seq = mSelectRouteRequestSequenceNumber;
-                mSelectRouteRequestSequenceNumber++;
-
-                handler.sendMessage(obtainMessage(
-                        UserHandler::requestSelectRoute, handler, clientRecord.mPackageName,
-                        route, seq));
-                // Remove all previous timeout messages
-                for (int previousSeq : clientRecord.mSelectRouteSequenceNumbers) {
-                    clientRecord.mUserRecord.mHandler.removeMessages(previousSeq);
-                }
-                clientRecord.mSelectRouteSequenceNumbers.clear();
-
-                // When the request is not handled in timeout, set the client's route to default.
-                Message timeoutMsg = obtainMessage(UserHandler::handleRouteSelectionTimeout,
-                        handler, clientRecord.mPackageName, route);
-                timeoutMsg.what = seq; // Make the message cancelable.
-                handler.sendMessageDelayed(timeoutMsg, ROUTE_SELECTION_REQUEST_TIMEOUT_MS);
-                clientRecord.mSelectRouteSequenceNumbers.add(seq);
-            }
-        }
-    }
-
     private void setControlCategoriesLocked(Client2Record clientRecord, List<String> categories) {
         if (clientRecord != null) {
             if (clientRecord.mControlCategories.equals(categories)) {
@@ -512,17 +472,19 @@
         }
     }
 
-    private void selectClientRoute2Locked(IMediaRouter2Manager manager,
-            String packageName, MediaRoute2Info route) {
+    private void requestClientCreateSessionLocked(IMediaRouter2Manager manager,
+            String packageName, MediaRoute2Info route, int requestId) {
         ManagerRecord managerRecord = mAllManagerRecords.get(manager.asBinder());
         if (managerRecord != null) {
             Client2Record clientRecord =
                     managerRecord.mUserRecord.findClientRecordLocked(packageName);
             if (clientRecord == null) {
-                Slog.w(TAG, "Ignoring route selection for unknown client.");
+                Slog.w(TAG, "Ignoring session creation for unknown client.");
             }
             if (clientRecord != null && managerRecord.mTrusted) {
-                requestSelectRoute2Locked(clientRecord, true, route);
+                //TODO: select category properly
+                requestCreateSessionLocked(clientRecord.mClient, route,
+                        route.getSupportedCategories().get(0), requestId);
             }
         }
     }
@@ -551,7 +513,6 @@
         }
     }
 
-
     private void initializeUserLocked(UserRecord userRecord) {
         if (DEBUG) {
             Slog.d(TAG, userRecord + ": Initialized");
@@ -738,14 +699,6 @@
         }
 
         @Override
-        public void onRouteSelected(@NonNull MediaRoute2ProviderProxy provider,
-                String clientPackageName, MediaRoute2Info route, Bundle controlHints, int seq) {
-            sendMessage(PooledLambda.obtainMessage(
-                    UserHandler::updateSelectedRoute, this, provider, clientPackageName, route,
-                    controlHints, seq));
-        }
-
-        @Override
         public void onSessionCreated(@NonNull MediaRoute2Provider provider,
                 @Nullable RouteSessionInfo sessionInfo, int requestId) {
             sendMessage(PooledLambda.obtainMessage(UserHandler::handleCreateSessionResultOnHandler,
@@ -865,6 +818,7 @@
                 @NonNull MediaRoute2Provider provider, @Nullable RouteSessionInfo sessionInfo,
                 int requestId) {
             SessionCreationRequest matchingRequest = null;
+
             for (SessionCreationRequest request : mSessionCreationRequests) {
                 if (request.mRequestId == requestId
                         && TextUtils.equals(
@@ -880,26 +834,34 @@
                 return;
             }
 
+            mSessionCreationRequests.remove(matchingRequest);
+
             if (sessionInfo == null) {
                 // Failed
                 notifySessionCreationFailed(matchingRequest.mClientRecord, requestId);
                 return;
             }
 
+            RouteSessionInfo sessionInfoWithProviderId = new RouteSessionInfo.Builder(sessionInfo)
+                    .setProviderId(provider.getUniqueId())
+                    .build();
+
             String originalRouteId = matchingRequest.mRoute.getId();
             String originalCategory = matchingRequest.mControlCategory;
-            if (!sessionInfo.getSelectedRoutes().contains(originalRouteId)
-                    || !TextUtils.equals(originalCategory, sessionInfo.getControlCategory())) {
+            if (!sessionInfoWithProviderId.getSelectedRoutes().contains(originalRouteId)
+                    || !TextUtils.equals(originalCategory,
+                        sessionInfoWithProviderId.getControlCategory())) {
                 Slog.w(TAG, "Created session doesn't match the original request."
                         + " originalRouteId=" + originalRouteId
-                        + ", originalCategory=" + originalCategory
-                        + ", requestId=" + requestId + ", sessionInfo=" + sessionInfo);
+                        + ", originalCategory=" + originalCategory + ", requestId=" + requestId
+                        + ", sessionInfo=" + sessionInfoWithProviderId);
                 notifySessionCreationFailed(matchingRequest.mClientRecord, requestId);
                 return;
             }
 
             // Succeeded
-            notifySessionCreated(matchingRequest.mClientRecord, sessionInfo, requestId);
+            notifySessionCreated(matchingRequest.mClientRecord,
+                    sessionInfoWithProviderId, requestId);
             // TODO: Tell managers for the session creation
         }
 
@@ -922,109 +884,6 @@
             }
         }
 
-        private void updateSelectedRoute(MediaRoute2ProviderProxy provider,
-                String clientPackageName, MediaRoute2Info selectedRoute, Bundle controlHints,
-                int seq) {
-            if (selectedRoute == null
-                    || !TextUtils.equals(clientPackageName, selectedRoute.getClientPackageName())) {
-                Log.w(TAG, "Ignoring route selection which has non-matching clientPackageName.");
-                return;
-            }
-
-            MediaRouter2ServiceImpl service = mServiceRef.get();
-            if (service == null) {
-                return;
-            }
-
-            Client2Record clientRecord;
-            synchronized (service.mLock) {
-                clientRecord = mUserRecord.findClientRecordLocked(clientPackageName);
-            }
-
-            //TODO: handle a case such that controlHints is null. (How should we notify MR2?)
-
-            if (clientRecord.mSelectingRoute == null || !TextUtils.equals(
-                    clientRecord.mSelectingRoute.getUniqueId(), selectedRoute.getUniqueId())) {
-                Log.w(TAG, "Ignoring invalid updateSelectedRoute call. selectingRoute="
-                        + clientRecord.mSelectingRoute + " route=" + selectedRoute);
-                unselectRoute(clientPackageName, selectedRoute);
-                return;
-            }
-            clientRecord.mSelectingRoute = null;
-            clientRecord.mSelectedRoute = selectedRoute;
-
-            notifyRouteSelectedToClient(clientRecord.mClient,
-                    selectedRoute,
-                    clientRecord.mIsManagerSelecting
-                            ? MediaRouter2.SELECT_REASON_SYSTEM_SELECTED :
-                            MediaRouter2.SELECT_REASON_USER_SELECTED,
-                    controlHints);
-            updateClientUsage(clientRecord);
-
-            // Remove the fallback route selection message.
-            removeMessages(seq);
-        }
-
-        private void handleRouteSelectionTimeout(String clientPackageName,
-                MediaRoute2Info selectingRoute) {
-            MediaRouter2ServiceImpl service = mServiceRef.get();
-            if (service == null) {
-                return;
-            }
-
-            Client2Record clientRecord;
-            synchronized (service.mLock) {
-                clientRecord = mUserRecord.findClientRecordLocked(clientPackageName);
-            }
-
-            if (clientRecord == null) {
-                Log.w(TAG, "The client has gone. packageName=" + clientPackageName
-                        + " selectingRoute=" + selectingRoute);
-                return;
-            }
-
-            if (clientRecord.mSelectingRoute == null || !TextUtils.equals(
-                    clientRecord.mSelectingRoute.getUniqueId(), selectingRoute.getUniqueId())) {
-                Log.w(TAG, "Ignoring invalid selectFallbackRoute call. "
-                        + "Current selectingRoute=" + clientRecord.mSelectingRoute
-                        + " , original selectingRoute=" + selectingRoute);
-                return;
-            }
-
-            clientRecord.mSelectingRoute = null;
-            // TODO: When the default route is introduced, make mSelectedRoute always non-null.
-            MediaRoute2Info fallbackRoute = null;
-            clientRecord.mSelectedRoute = fallbackRoute;
-
-            notifyRouteSelectedToClient(clientRecord.mClient,
-                    fallbackRoute,
-                    MediaRouter2.SELECT_REASON_FALLBACK,
-                    Bundle.EMPTY /* controlHints */);
-            updateClientUsage(clientRecord);
-        }
-
-        private void requestSelectRoute(String clientPackageName, MediaRoute2Info route, int seq) {
-            if (route != null) {
-                MediaRoute2Provider provider = findProvider(route.getProviderId());
-                if (provider == null) {
-                    Slog.w(TAG, "Ignoring to select route of unknown provider " + route);
-                } else {
-                    provider.requestSelectRoute(clientPackageName, route.getId(), seq);
-                }
-            }
-        }
-
-        private void unselectRoute(String clientPackageName, MediaRoute2Info route) {
-            if (route != null) {
-                MediaRoute2Provider provider = findProvider(route.getProviderId());
-                if (provider == null) {
-                    Slog.w(TAG, "Ignoring to unselect route of unknown provider " + route);
-                } else {
-                    provider.unselectRoute(clientPackageName, route.getId());
-                }
-            }
-        }
-
         private void sendControlRequest(MediaRoute2Info route, Intent request) {
             final MediaRoute2Provider provider = findProvider(route.getProviderId());
             if (provider != null) {
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index a280f91..a51158b 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -487,11 +487,10 @@
 
     // Binder call
     @Override
-    public void selectClientRoute2(IMediaRouter2Manager manager,
-            String packageName, MediaRoute2Info route) {
-        mService2.selectClientRoute2(manager, packageName, route);
+    public void requestCreateClientSession(IMediaRouter2Manager manager, String packageName,
+            MediaRoute2Info route, int requestId) {
+        mService2.requestCreateClientSession(manager, packageName, route, requestId);
     }
-
     // Binder call
     @Override
     public void setControlCategories(IMediaRouter2Client client, List<String> categories) {
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 6c4c8d5..23c67e2 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -94,21 +94,24 @@
         // Do nothing
     }
 
-    //TODO: implement method
     @Override
-    public void requestSelectRoute(@NonNull String packageName, @NonNull String routeId, int seq) {
-        try {
-            mAudioService.setBluetoothA2dpOn(
-                    !TextUtils.equals(routeId, mDefaultRoute.getId()));
-        } catch (RemoteException ex) {
-            Log.e(TAG, "Error changing Bluetooth A2DP route");
-        }
+    public void releaseSession(int sessionId) {
+        // Do nothing
     }
 
-    //TODO: implement method
     @Override
-    public void unselectRoute(@NonNull String packageName, @NonNull String routeId) {
-        // does nothing..?
+    public void addRoute(int sessionId, MediaRoute2Info route) {
+        //TODO: implement method
+    }
+
+    @Override
+    public void removeRoute(int sessionId, MediaRoute2Info route) {
+        //TODO: implement method
+    }
+
+    @Override
+    public void transferRoute(int sessionId, MediaRoute2Info route) {
+        //TODO: implement method
     }
 
     //TODO: implement method