Merge "MediaRouter: Introduce discovery request"
diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl
index 02a3816..51fa4ee 100644
--- a/media/java/android/media/IMediaRoute2Provider.aidl
+++ b/media/java/android/media/IMediaRoute2Provider.aidl
@@ -25,7 +25,7 @@
 oneway interface IMediaRoute2Provider {
     void setClient(IMediaRoute2ProviderClient client);
     void requestCreateSession(String packageName, String routeId,
-            String controlCategory, long requestId);
+            String routeType, long requestId);
     void releaseSession(int sessionId);
 
     void selectRoute(int sessionId, String routeId);
diff --git a/media/java/android/media/IMediaRouter2Manager.aidl b/media/java/android/media/IMediaRouter2Manager.aidl
index b7cb705..e8af21e 100644
--- a/media/java/android/media/IMediaRouter2Manager.aidl
+++ b/media/java/android/media/IMediaRouter2Manager.aidl
@@ -24,7 +24,7 @@
  */
 oneway interface IMediaRouter2Manager {
     void notifyRouteSelected(String packageName, in MediaRoute2Info route);
-    void notifyControlCategoriesChanged(String packageName, in List<String> categories);
+    void notifyRouteTypesChanged(String packageName, in List<String> routeTypes);
     void notifyRoutesAdded(in List<MediaRoute2Info> routes);
     void notifyRoutesRemoved(in List<MediaRoute2Info> routes);
     void notifyRoutesChanged(in List<MediaRoute2Info> routes);
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index e5b62ff..b573f64 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -22,6 +22,7 @@
 import android.media.IMediaRouterClient;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouterClientState;
+import android.media.RouteDiscoveryRequest;
 import android.media.RouteSessionInfo;
 
 /**
@@ -51,8 +52,8 @@
     void requestUpdateVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int direction);
 
     void requestCreateSession(IMediaRouter2Client client, in MediaRoute2Info route,
-            String controlCategory, int requestId);
-    void setControlCategories(IMediaRouter2Client client, in List<String> categories);
+            String routeType, int requestId);
+    void setDiscoveryRequest2(IMediaRouter2Client client, in RouteDiscoveryRequest request);
     void selectRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route);
     void deselectRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route);
     void transferToRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route);
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 4e6b4af..13640a4 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -83,12 +83,14 @@
      * controlled from this object. An example of fixed playback volume is a remote player,
      * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
      * than attenuate at the source.
+     *
      * @see #getVolumeHandling()
      */
     public static final int PLAYBACK_VOLUME_FIXED = 0;
     /**
      * Playback information indicating the playback volume is variable and can be controlled
      * from this object.
+     *
      * @see #getVolumeHandling()
      */
     public static final int PLAYBACK_VOLUME_VARIABLE = 1;
@@ -148,7 +150,7 @@
     @Nullable
     final String mClientPackageName;
     @NonNull
-    final List<String> mSupportedCategories;
+    final List<String> mRouteTypes;
     final int mVolume;
     final int mVolumeMax;
     final int mVolumeHandling;
@@ -164,7 +166,7 @@
         mConnectionState = builder.mConnectionState;
         mIconUri = builder.mIconUri;
         mClientPackageName = builder.mClientPackageName;
-        mSupportedCategories = builder.mSupportedCategories;
+        mRouteTypes = builder.mRouteTypes;
         mVolume = builder.mVolume;
         mVolumeMax = builder.mVolumeMax;
         mVolumeHandling = builder.mVolumeHandling;
@@ -180,7 +182,7 @@
         mConnectionState = in.readInt();
         mIconUri = in.readParcelable(null);
         mClientPackageName = in.readString();
-        mSupportedCategories = in.createStringArrayList();
+        mRouteTypes = in.createStringArrayList();
         mVolume = in.readInt();
         mVolumeMax = in.readInt();
         mVolumeHandling = in.readInt();
@@ -226,7 +228,7 @@
                 && (mConnectionState == other.mConnectionState)
                 && Objects.equals(mIconUri, other.mIconUri)
                 && Objects.equals(mClientPackageName, other.mClientPackageName)
-                && Objects.equals(mSupportedCategories, other.mSupportedCategories)
+                && Objects.equals(mRouteTypes, other.mRouteTypes)
                 && (mVolume == other.mVolume)
                 && (mVolumeMax == other.mVolumeMax)
                 && (mVolumeHandling == other.mVolumeHandling)
@@ -238,7 +240,7 @@
     @Override
     public int hashCode() {
         return Objects.hash(mId, mName, mDescription, mConnectionState, mIconUri,
-                mSupportedCategories, mVolume, mVolumeMax, mVolumeHandling, mDeviceType);
+                mRouteTypes, mVolume, mVolumeMax, mVolumeHandling, mDeviceType);
     }
 
     /**
@@ -327,8 +329,8 @@
      * Gets the supported categories of the route.
      */
     @NonNull
-    public List<String> getSupportedCategories() {
-        return mSupportedCategories;
+    public List<String> getRouteTypes() {
+        return mRouteTypes;
     }
 
     //TODO: once device types are confirmed, reflect those into the comment.
@@ -372,32 +374,15 @@
     }
 
     /**
-     * Returns if the route supports the specified control category
+     * Returns if the route contains at least one of the specified route types.
      *
-     * @param controlCategory control category to consider
-     * @return true if the route supports at the category
+     * @param routeTypes the list of route types to consider
+     * @return true if the route contains at least one type in the list
      */
-    public boolean supportsControlCategory(@NonNull String controlCategory) {
-        Objects.requireNonNull(controlCategory, "control category must not be null");
-        for (String supportedCategory : getSupportedCategories()) {
-            if (TextUtils.equals(controlCategory, supportedCategory)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    //TODO: Move this if we re-define control category / selector things.
-    /**
-     * Returns if the route supports at least one of the specified control categories
-     *
-     * @param controlCategories the list of control categories to consider
-     * @return true if the route supports at least one category
-     */
-    public boolean supportsControlCategories(@NonNull Collection<String> controlCategories) {
-        Objects.requireNonNull(controlCategories, "control categories must not be null");
-        for (String controlCategory : controlCategories) {
-            if (supportsControlCategory(controlCategory)) {
+    public boolean containsRouteTypes(@NonNull Collection<String> routeTypes) {
+        Objects.requireNonNull(routeTypes, "routeTypes must not be null");
+        for (String routeType : routeTypes) {
+            if (getRouteTypes().contains(routeType)) {
                 return true;
             }
         }
@@ -418,7 +403,7 @@
         dest.writeInt(mConnectionState);
         dest.writeParcelable(mIconUri, flags);
         dest.writeString(mClientPackageName);
-        dest.writeStringList(mSupportedCategories);
+        dest.writeStringList(mRouteTypes);
         dest.writeInt(mVolume);
         dest.writeInt(mVolumeMax);
         dest.writeInt(mVolumeHandling);
@@ -456,7 +441,7 @@
         int mConnectionState;
         Uri mIconUri;
         String mClientPackageName;
-        List<String> mSupportedCategories;
+        List<String> mRouteTypes;
         int mVolume;
         int mVolumeMax;
         int mVolumeHandling = PLAYBACK_VOLUME_FIXED;
@@ -467,7 +452,7 @@
         public Builder(@NonNull String id, @NonNull CharSequence name) {
             setId(id);
             setName(name);
-            mSupportedCategories = new ArrayList<>();
+            mRouteTypes = new ArrayList<>();
         }
 
         public Builder(@NonNull MediaRoute2Info routeInfo) {
@@ -484,7 +469,7 @@
             mConnectionState = routeInfo.mConnectionState;
             mIconUri = routeInfo.mIconUri;
             setClientPackageName(routeInfo.mClientPackageName);
-            setSupportedCategories(routeInfo.mSupportedCategories);
+            setRouteTypes(routeInfo.mRouteTypes);
             setVolume(routeInfo.mVolume);
             setVolumeMax(routeInfo.mVolumeMax);
             setVolumeHandling(routeInfo.mVolumeHandling);
@@ -589,35 +574,35 @@
         }
 
         /**
-         * Sets the supported categories of the route.
+         * Sets the types of the route.
          */
         @NonNull
-        public Builder setSupportedCategories(@NonNull Collection<String> categories) {
-            mSupportedCategories = new ArrayList<>();
-            return addSupportedCategories(categories);
+        public Builder setRouteTypes(@NonNull Collection<String> routeTypes) {
+            mRouteTypes = new ArrayList<>();
+            return addRouteTypes(routeTypes);
         }
 
         /**
-         * Adds supported categories for the route.
+         * Adds types for the route.
          */
         @NonNull
-        public Builder addSupportedCategories(@NonNull Collection<String> categories) {
-            Objects.requireNonNull(categories, "categories must not be null");
-            for (String category: categories) {
-                addSupportedCategory(category);
+        public Builder addRouteTypes(@NonNull Collection<String> routeTypes) {
+            Objects.requireNonNull(routeTypes, "routeTypes must not be null");
+            for (String routeType: routeTypes) {
+                addRouteType(routeType);
             }
             return this;
         }
 
         /**
-         * Add a supported category for the route.
+         * Add a type for the route.
          */
         @NonNull
-        public Builder addSupportedCategory(@NonNull String category) {
-            if (TextUtils.isEmpty(category)) {
-                throw new IllegalArgumentException("category must not be null or empty");
+        public Builder addRouteType(@NonNull String routeType) {
+            if (TextUtils.isEmpty(routeType)) {
+                throw new IllegalArgumentException("routeType must not be null or empty");
             }
-            mSupportedCategories.add(category);
+            mRouteTypes.add(routeType);
             return this;
         }
 
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 99bd1dc..91cc448 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -246,11 +246,11 @@
      *
      * @param packageName the package name of the application that selected the route
      * @param routeId the id of the route initially being connected
-     * @param controlCategory the control category of the new session
+     * @param routeType the route type of the new session
      * @param requestId the id of this session creation request
      */
     public abstract void onCreateSession(@NonNull String packageName, @NonNull String routeId,
-            @NonNull String controlCategory, long requestId);
+            @NonNull String routeType, long requestId);
 
     /**
      * Called when a session is about to be destroyed.
@@ -301,6 +301,25 @@
     public abstract void onTransferToRoute(int sessionId, @NonNull String routeId);
 
     /**
+     * Called when the {@link RouteDiscoveryRequest discovery request} has changed.
+     * <p>
+     * Whenever an application registers a {@link MediaRouter2.RouteCallback callback},
+     * it also provides a discovery request to specify types of routes that it is interested in.
+     * The media router combines all of these discovery request into a single discovery request
+     * and notifies each provider.
+     * </p><p>
+     * The provider should examine {@link RouteDiscoveryRequest#getRouteTypes() route types}
+     * in the discovery request to determine what kind of routes it should try to discover
+     * and whether it should perform active or passive scans. In many cases, the provider may be
+     * able to save power by not performing any scans when the request doesn't have any matching
+     * route types.
+     * </p>
+     *
+     * @param request the new discovery request
+     */
+    public void onDiscoveryRequestChanged(@NonNull RouteDiscoveryRequest request) {}
+
+    /**
      * Updates provider info and publishes routes and session info.
      */
     public final void updateProviderInfo(@NonNull MediaRoute2ProviderInfo providerInfo) {
@@ -357,12 +376,12 @@
 
         @Override
         public void requestCreateSession(String packageName, String routeId,
-                String controlCategory, long requestId) {
+                String routeType, long requestId) {
             if (!checkCallerisSystem()) {
                 return;
             }
             mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onCreateSession,
-                    MediaRoute2ProviderService.this, packageName, routeId, controlCategory,
+                    MediaRoute2ProviderService.this, packageName, routeId, routeType,
                     requestId));
         }
         @Override
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index bddfa69..dea8b04 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -39,7 +39,6 @@
 
 import java.lang.annotation.Retention;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -48,6 +47,7 @@
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
 
 /**
  * A new Media Router
@@ -118,7 +118,7 @@
     final Map<String, MediaRoute2Info> mRoutes = new HashMap<>();
 
     @GuardedBy("sLock")
-    private List<String> mControlCategories = Collections.emptyList();
+    private RouteDiscoveryRequest mDiscoveryRequest = RouteDiscoveryRequest.EMPTY;
 
     // TODO: Make MediaRouter2 is always connected to the MediaRouterService.
     @GuardedBy("sLock")
@@ -152,7 +152,6 @@
         mMediaRouterService = IMediaRouterService.Stub.asInterface(
                 ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
         mPackageName = mContext.getPackageName();
-        //TODO: read control categories from the manifest
         mHandler = new Handler(Looper.getMainLooper());
 
         List<MediaRoute2Info> currentSystemRoutes = null;
@@ -188,24 +187,18 @@
 
     /**
      * Registers a callback to discover routes and to receive events when they change.
-     */
-    public void registerRouteCallback(@NonNull @CallbackExecutor Executor executor,
-            @NonNull RouteCallback routeCallback) {
-        registerRouteCallback(executor, routeCallback, 0);
-    }
-
-    /**
-     * Registers a callback to discover routes and to receive events when they change.
      * <p>
      * If you register the same callback twice or more, it will be ignored.
      * </p>
      */
     public void registerRouteCallback(@NonNull @CallbackExecutor Executor executor,
-            @NonNull RouteCallback routeCallback, int flags) {
+            @NonNull RouteCallback routeCallback,
+            @NonNull RouteDiscoveryRequest request) {
         Objects.requireNonNull(executor, "executor must not be null");
         Objects.requireNonNull(routeCallback, "callback must not be null");
+        Objects.requireNonNull(request, "request must not be null");
 
-        RouteCallbackRecord record = new RouteCallbackRecord(executor, routeCallback, flags);
+        RouteCallbackRecord record = new RouteCallbackRecord(executor, routeCallback, request);
         if (!mRouteCallbackRecords.addIfAbsent(record)) {
             Log.w(TAG, "Ignoring the same callback");
             return;
@@ -216,7 +209,8 @@
                 Client2 client = new Client2();
                 try {
                     mMediaRouterService.registerClient2(client, mPackageName);
-                    mMediaRouterService.setControlCategories(client, mControlCategories);
+                    updateDiscoveryRequestLocked();
+                    mMediaRouterService.setDiscoveryRequest2(client, mDiscoveryRequest);
                     mClient = client;
                 } catch (RemoteException ex) {
                     Log.e(TAG, "Unable to register media router.", ex);
@@ -238,7 +232,7 @@
         Objects.requireNonNull(routeCallback, "callback must not be null");
 
         if (!mRouteCallbackRecords.remove(
-                new RouteCallbackRecord(null, routeCallback, 0))) {
+                new RouteCallbackRecord(null, routeCallback, null))) {
             Log.w(TAG, "Ignoring unknown callback");
             return;
         }
@@ -256,30 +250,10 @@
         }
     }
 
-    //TODO(b/139033746): Rename "Control Category" when it's finalized.
-    /**
-     * Sets the control categories of the application.
-     * Routes that support at least one of the given control categories are handled
-     * by the media router.
-     */
-    public void setControlCategories(@NonNull Collection<String> controlCategories) {
-        Objects.requireNonNull(controlCategories, "control categories must not be null");
-
-        List<String> newControlCategories = new ArrayList<>(controlCategories);
-
-        synchronized (sRouterLock) {
-            mShouldUpdateRoutes = true;
-
-            // invoke callbacks due to control categories change
-            handleControlCategoriesChangedLocked(newControlCategories);
-            if (mClient != null) {
-                try {
-                    mMediaRouterService.setControlCategories(mClient, mControlCategories);
-                } catch (RemoteException ex) {
-                    Log.e(TAG, "Unable to set control categories.", ex);
-                }
-            }
-        }
+    private void updateDiscoveryRequestLocked() {
+        mDiscoveryRequest = new RouteDiscoveryRequest.Builder(
+                mRouteCallbackRecords.stream().map(record -> record.mRequest).collect(
+                        Collectors.toList())).build();
     }
 
     /**
@@ -287,8 +261,8 @@
      * known to the media router.
      * Please note that the list can be changed before callbacks are invoked.
      *
-     * @return the list of routes that support at least one of the control categories set by
-     * the application
+     * @return the list of routes that contains at least one of the route types in discovery
+     * requests registered by the application
      */
     @NonNull
     public List<MediaRoute2Info> getRoutes() {
@@ -298,7 +272,7 @@
 
                 List<MediaRoute2Info> filteredRoutes = new ArrayList<>();
                 for (MediaRoute2Info route : mRoutes.values()) {
-                    if (route.supportsControlCategories(mControlCategories)) {
+                    if (route.containsRouteTypes(mDiscoveryRequest.getRouteTypes())) {
                         filteredRoutes.add(route);
                     }
                 }
@@ -350,26 +324,26 @@
      * Requests the media route provider service to create a session with the given route.
      *
      * @param route the route you want to create a session with.
-     * @param controlCategory the control category of the session. Should not be empty
+     * @param routeType the route type of the session. Should not be empty
      *
      * @see SessionCallback#onSessionCreated
      * @see SessionCallback#onSessionCreationFailed
      */
     @NonNull
     public void requestCreateSession(@NonNull MediaRoute2Info route,
-            @NonNull String controlCategory) {
+            @NonNull String routeType) {
         Objects.requireNonNull(route, "route must not be null");
-        if (TextUtils.isEmpty(controlCategory)) {
-            throw new IllegalArgumentException("controlCategory must not be empty");
+        if (TextUtils.isEmpty(routeType)) {
+            throw new IllegalArgumentException("routeType must not be empty");
         }
         // TODO: Check the given route exists
-        // TODO: Check the route supports the given controlCategory
+        // TODO: Check the route supports the given routeType
 
         final int requestId;
         requestId = mSessionCreationRequestCnt.getAndIncrement();
 
         SessionCreationRequest request = new SessionCreationRequest(
-                requestId, route, controlCategory);
+                requestId, route, routeType);
         mSessionCreationRequests.add(request);
 
         Client2 client;
@@ -379,7 +353,7 @@
         if (client != null) {
             try {
                 mMediaRouterService.requestCreateSession(
-                        client, route, controlCategory, requestId);
+                        client, route, routeType, requestId);
             } catch (RemoteException ex) {
                 Log.e(TAG, "Unable to request to create session.", ex);
                 mHandler.sendMessage(obtainMessage(MediaRouter2::createControllerOnHandler,
@@ -461,36 +435,6 @@
         }
     }
 
-    private void handleControlCategoriesChangedLocked(List<String> newControlCategories) {
-        List<MediaRoute2Info> addedRoutes = new ArrayList<>();
-        List<MediaRoute2Info> removedRoutes = new ArrayList<>();
-
-        List<String> prevControlCategories = mControlCategories;
-        mControlCategories = newControlCategories;
-
-        for (MediaRoute2Info route : mRoutes.values()) {
-            boolean preSupported = route.supportsControlCategories(prevControlCategories);
-            boolean postSupported = route.supportsControlCategories(newControlCategories);
-            if (preSupported == postSupported) {
-                continue;
-            }
-            if (preSupported) {
-                removedRoutes.add(route);
-            } else {
-                addedRoutes.add(route);
-            }
-        }
-
-        if (removedRoutes.size() > 0) {
-            mHandler.sendMessage(obtainMessage(MediaRouter2::notifyRoutesRemoved,
-                    MediaRouter2.this, removedRoutes));
-        }
-        if (addedRoutes.size() > 0) {
-            mHandler.sendMessage(obtainMessage(MediaRouter2::notifyRoutesAdded,
-                    MediaRouter2.this, addedRoutes));
-        }
-    }
-
     void addRoutesOnHandler(List<MediaRoute2Info> routes) {
         // TODO: When onRoutesAdded is first called,
         //  1) clear mRoutes before adding the routes
@@ -500,7 +444,7 @@
         synchronized (sRouterLock) {
             for (MediaRoute2Info route : routes) {
                 mRoutes.put(route.getId(), route);
-                if (route.supportsControlCategories(mControlCategories)) {
+                if (route.containsRouteTypes(mDiscoveryRequest.getRouteTypes())) {
                     addedRoutes.add(route);
                 }
             }
@@ -516,7 +460,7 @@
         synchronized (sRouterLock) {
             for (MediaRoute2Info route : routes) {
                 mRoutes.remove(route.getId());
-                if (route.supportsControlCategories(mControlCategories)) {
+                if (route.containsRouteTypes(mDiscoveryRequest.getRouteTypes())) {
                     removedRoutes.add(route);
                 }
             }
@@ -532,7 +476,7 @@
         synchronized (sRouterLock) {
             for (MediaRoute2Info route : routes) {
                 mRoutes.put(route.getId(), route);
-                if (route.supportsControlCategories(mControlCategories)) {
+                if (route.containsRouteTypes(mDiscoveryRequest.getRouteTypes())) {
                     changedRoutes.add(route);
                 }
             }
@@ -562,27 +506,27 @@
             mSessionCreationRequests.remove(matchingRequest);
 
             MediaRoute2Info requestedRoute = matchingRequest.mRoute;
-            String requestedControlCategory = matchingRequest.mControlCategory;
+            String requestedRouteType = matchingRequest.mRouteType;
 
             if (sessionInfo == null) {
                 // TODO: We may need to distinguish between failure and rejection.
                 //       One way can be introducing 'reason'.
-                notifySessionCreationFailed(requestedRoute, requestedControlCategory);
+                notifySessionCreationFailed(requestedRoute, requestedRouteType);
                 return;
-            } else if (!TextUtils.equals(requestedControlCategory,
-                    sessionInfo.getControlCategory())) {
-                Log.w(TAG, "The session has different control category from what we requested. "
-                        + "(requested=" + requestedControlCategory
-                        + ", actual=" + sessionInfo.getControlCategory()
+            } else if (!TextUtils.equals(requestedRouteType,
+                    sessionInfo.getRouteType())) {
+                Log.w(TAG, "The session has different route type from what we requested. "
+                        + "(requested=" + requestedRouteType
+                        + ", actual=" + sessionInfo.getRouteType()
                         + ")");
-                notifySessionCreationFailed(requestedRoute, requestedControlCategory);
+                notifySessionCreationFailed(requestedRoute, requestedRouteType);
                 return;
             } else if (!sessionInfo.getSelectedRoutes().contains(requestedRoute.getId())) {
                 Log.w(TAG, "The session does not contain the requested route. "
                         + "(requestedRouteId=" + requestedRoute.getId()
                         + ", actualRoutes=" + sessionInfo.getSelectedRoutes()
                         + ")");
-                notifySessionCreationFailed(requestedRoute, requestedControlCategory);
+                notifySessionCreationFailed(requestedRoute, requestedRouteType);
                 return;
             } else if (!TextUtils.equals(requestedRoute.getProviderId(),
                     sessionInfo.getProviderId())) {
@@ -590,7 +534,7 @@
                         + "(requested route's providerId=" + requestedRoute.getProviderId()
                         + ", actual providerId=" + sessionInfo.getProviderId()
                         + ")");
-                notifySessionCreationFailed(requestedRoute, requestedControlCategory);
+                notifySessionCreationFailed(requestedRoute, requestedRouteType);
                 return;
             }
         }
@@ -666,24 +610,41 @@
         notifyControllerReleased(matchingController);
     }
 
+    private List<MediaRoute2Info> filterRoutes(List<MediaRoute2Info> routes,
+            RouteDiscoveryRequest discoveryRequest) {
+        return routes.stream()
+                .filter(
+                        route -> route.containsRouteTypes(discoveryRequest.getRouteTypes()))
+                .collect(Collectors.toList());
+    }
+
     private void notifyRoutesAdded(List<MediaRoute2Info> routes) {
         for (RouteCallbackRecord record: mRouteCallbackRecords) {
-            record.mExecutor.execute(
-                    () -> record.mRouteCallback.onRoutesAdded(routes));
+            List<MediaRoute2Info> filteredRoutes = filterRoutes(routes, record.mRequest);
+            if (!filteredRoutes.isEmpty()) {
+                record.mExecutor.execute(
+                        () -> record.mRouteCallback.onRoutesAdded(filteredRoutes));
+            }
         }
     }
 
     private void notifyRoutesRemoved(List<MediaRoute2Info> routes) {
         for (RouteCallbackRecord record: mRouteCallbackRecords) {
-            record.mExecutor.execute(
-                    () -> record.mRouteCallback.onRoutesRemoved(routes));
+            List<MediaRoute2Info> filteredRoutes = filterRoutes(routes, record.mRequest);
+            if (!filteredRoutes.isEmpty()) {
+                record.mExecutor.execute(
+                        () -> record.mRouteCallback.onRoutesRemoved(filteredRoutes));
+            }
         }
     }
 
     private void notifyRoutesChanged(List<MediaRoute2Info> routes) {
         for (RouteCallbackRecord record: mRouteCallbackRecords) {
-            record.mExecutor.execute(
-                    () -> record.mRouteCallback.onRoutesChanged(routes));
+            List<MediaRoute2Info> filteredRoutes = filterRoutes(routes, record.mRequest);
+            if (!filteredRoutes.isEmpty()) {
+                record.mExecutor.execute(
+                        () -> record.mRouteCallback.onRoutesChanged(filteredRoutes));
+            }
         }
     }
 
@@ -694,10 +655,10 @@
         }
     }
 
-    private void notifySessionCreationFailed(MediaRoute2Info route, String controlCategory) {
+    private void notifySessionCreationFailed(MediaRoute2Info route, String routeType) {
         for (SessionCallbackRecord record: mSessionCallbackRecords) {
             record.mExecutor.execute(
-                    () -> record.mSessionCallback.onSessionCreationFailed(route, controlCategory));
+                    () -> record.mSessionCallback.onSessionCreationFailed(route, routeType));
         }
     }
 
@@ -764,10 +725,10 @@
          * Called when the session creation request failed.
          *
          * @param requestedRoute the route info which was used for the request
-         * @param requestedControlCategory the control category which was used for the request
+         * @param requestedRouteType the route type which was used for the request
          */
         public void onSessionCreationFailed(@NonNull MediaRoute2Info requestedRoute,
-                @NonNull String requestedControlCategory) {}
+                @NonNull String requestedRouteType) {}
 
         /**
          * Called when the session info has changed.
@@ -840,12 +801,12 @@
         }
 
         /**
-         * @return the category of routes that the session includes.
+         * @return the type of routes that the session includes.
          */
         @NonNull
-        public String getControlCategory() {
+        public String getRouteType() {
             synchronized (mControllerLock) {
-                return mSessionInfo.getControlCategory();
+                return mSessionInfo.getRouteType();
             }
         }
 
@@ -1104,11 +1065,11 @@
         // TODO: This method uses two locks (mLock outside, sLock inside).
         //       Check if there is any possiblity of deadlock.
         private List<MediaRoute2Info> getRoutesWithIdsLocked(List<String> routeIds) {
+
             List<MediaRoute2Info> routes = new ArrayList<>();
             synchronized (sRouterLock) {
                 for (String routeId : routeIds) {
-                    MediaRoute2Info route = mRoutes.get(
-                            MediaRoute2Info.toUniqueId(mSessionInfo.mProviderId, routeId));
+                    MediaRoute2Info route = mRoutes.get(routeId);
                     if (route != null) {
                         routes.add(route);
                     }
@@ -1121,13 +1082,13 @@
     final class RouteCallbackRecord {
         public final Executor mExecutor;
         public final RouteCallback mRouteCallback;
-        public final int mFlags;
+        public final RouteDiscoveryRequest mRequest;
 
         RouteCallbackRecord(@Nullable Executor executor, @NonNull RouteCallback routeCallback,
-                int flags) {
+                @Nullable RouteDiscoveryRequest request) {
             mRouteCallback = routeCallback;
             mExecutor = executor;
-            mFlags = flags;
+            mRequest = request;
         }
 
         @Override
@@ -1176,13 +1137,13 @@
 
     final class SessionCreationRequest {
         public final MediaRoute2Info mRoute;
-        public final String mControlCategory;
+        public final String mRouteType;
         public final int mRequestId;
 
         SessionCreationRequest(int requestId, @NonNull MediaRoute2Info route,
-                @NonNull String controlCategory) {
+                @NonNull String routeType) {
             mRoute = route;
-            mControlCategory = controlCategory;
+            mRouteType = routeType;
             mRequestId = requestId;
         }
     }
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 3cbbea1..1e6ec51 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -65,7 +65,7 @@
     @GuardedBy("mRoutesLock")
     private final Map<String, MediaRoute2Info> mRoutes = new HashMap<>();
     @NonNull
-    final ConcurrentMap<String, List<String>> mControlCategoryMap = new ConcurrentHashMap<>();
+    final ConcurrentMap<String, List<String>> mRouteTypeMap = new ConcurrentHashMap<>();
 
     private AtomicInteger mNextRequestId = new AtomicInteger(1);
 
@@ -144,7 +144,7 @@
                 }
                 //TODO: clear mRoutes?
                 mClient = null;
-                mControlCategoryMap.clear();
+                mRouteTypeMap.clear();
             }
         }
     }
@@ -160,14 +160,14 @@
     public List<MediaRoute2Info> getAvailableRoutes(@NonNull String packageName) {
         Objects.requireNonNull(packageName, "packageName must not be null");
 
-        List<String> controlCategories = mControlCategoryMap.get(packageName);
-        if (controlCategories == null) {
+        List<String> routeTypes = mRouteTypeMap.get(packageName);
+        if (routeTypes == null) {
             return Collections.emptyList();
         }
         List<MediaRoute2Info> routes = new ArrayList<>();
         synchronized (mRoutesLock) {
             for (MediaRoute2Info route : mRoutes.values()) {
-                if (route.supportsControlCategories(controlCategories)) {
+                if (route.containsRouteTypes(routeTypes)) {
                     routes.add(route);
                 }
             }
@@ -352,15 +352,15 @@
         }
     }
 
-    void updateControlCategories(String packageName, List<String> categories) {
-        List<String> prevCategories = mControlCategoryMap.put(packageName, categories);
-        if ((prevCategories == null && categories.size() == 0)
-                || Objects.equals(categories, prevCategories)) {
+    void updateRouteTypes(String packageName, List<String> routeTypes) {
+        List<String> prevTypes = mRouteTypeMap.put(packageName, routeTypes);
+        if ((prevTypes == null && routeTypes.size() == 0)
+                || Objects.equals(routeTypes, prevTypes)) {
             return;
         }
         for (CallbackRecord record : mCallbackRecords) {
             record.mExecutor.execute(
-                    () -> record.mCallback.onControlCategoriesChanged(packageName, categories));
+                    () -> record.mCallback.onControlCategoriesChanged(packageName, routeTypes));
         }
     }
 
@@ -398,13 +398,13 @@
 
 
         /**
-         * Called when the control categories of an app is changed.
+         * Called when the route types of an app is changed.
          *
          * @param packageName the package name of the application
-         * @param controlCategories the list of control categories set by an application.
+         * @param routeTypes the list of route types set by an application.
          */
         public void onControlCategoriesChanged(@NonNull String packageName,
-                @NonNull List<String> controlCategories) {}
+                @NonNull List<String> routeTypes) {}
     }
 
     final class CallbackRecord {
@@ -440,10 +440,9 @@
                     MediaRouter2Manager.this, packageName, route));
         }
 
-        @Override
-        public void notifyControlCategoriesChanged(String packageName, List<String> categories) {
-            mHandler.sendMessage(obtainMessage(MediaRouter2Manager::updateControlCategories,
-                    MediaRouter2Manager.this, packageName, categories));
+        public void notifyRouteTypesChanged(String packageName, List<String> routeTypes) {
+            mHandler.sendMessage(obtainMessage(MediaRouter2Manager::updateRouteTypes,
+                    MediaRouter2Manager.this, packageName, routeTypes));
         }
 
         @Override
diff --git a/media/java/android/media/RouteDiscoveryRequest.aidl b/media/java/android/media/RouteDiscoveryRequest.aidl
new file mode 100644
index 0000000..744f656
--- /dev/null
+++ b/media/java/android/media/RouteDiscoveryRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+parcelable RouteDiscoveryRequest;
diff --git a/media/java/android/media/RouteDiscoveryRequest.java b/media/java/android/media/RouteDiscoveryRequest.java
new file mode 100644
index 0000000..88b31fb
--- /dev/null
+++ b/media/java/android/media/RouteDiscoveryRequest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+
+/**
+ * @hide
+ */
+public final class RouteDiscoveryRequest implements Parcelable {
+    @NonNull
+    public static final Creator<RouteDiscoveryRequest> CREATOR =
+            new Creator<RouteDiscoveryRequest>() {
+                @Override
+                public RouteDiscoveryRequest createFromParcel(Parcel in) {
+                    return new RouteDiscoveryRequest(in);
+                }
+
+                @Override
+                public RouteDiscoveryRequest[] newArray(int size) {
+                    return new RouteDiscoveryRequest[size];
+                }
+            };
+
+    @NonNull
+    private final List<String> mRouteTypes;
+    private final boolean mActiveScan;
+    @Nullable
+    private final Bundle mExtras;
+
+    /**
+     * @hide
+     */
+    public static final RouteDiscoveryRequest EMPTY =
+            new Builder(Collections.emptyList(), false).build();
+
+    RouteDiscoveryRequest(@NonNull Builder builder) {
+        mRouteTypes = builder.mRouteTypes;
+        mActiveScan = builder.mActiveScan;
+        mExtras = builder.mExtras;
+    }
+
+    RouteDiscoveryRequest(@NonNull Parcel in) {
+        mRouteTypes = in.createStringArrayList();
+        mActiveScan = in.readBoolean();
+        mExtras = in.readBundle();
+    }
+
+    @NonNull
+    public List<String> getRouteTypes() {
+        return mRouteTypes;
+    }
+
+    public boolean isActiveScan() {
+        return mActiveScan;
+    }
+
+    /**
+     * @hide
+     */
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeStringList(mRouteTypes);
+        dest.writeBoolean(mActiveScan);
+        dest.writeBundle(mExtras);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder result = new StringBuilder()
+                .append("RouteDiscoveryRequest{ ")
+                .append("routeTypes={")
+                .append(String.join(", ", mRouteTypes))
+                .append("}")
+                .append(", activeScan=")
+                .append(mActiveScan)
+                .append(" }");
+
+        return result.toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof RouteDiscoveryRequest)) {
+            return false;
+        }
+        RouteDiscoveryRequest other = (RouteDiscoveryRequest) o;
+        return Objects.equals(mRouteTypes, other.mRouteTypes)
+                && mActiveScan == other.mActiveScan;
+    }
+
+    /**
+     * Builder for {@link RouteDiscoveryRequest}.
+     */
+    public static final class Builder {
+        List<String> mRouteTypes;
+        boolean mActiveScan;
+        Bundle mExtras;
+
+        public Builder(@NonNull List<String> routeTypes, boolean activeScan) {
+            mRouteTypes = new ArrayList<>(
+                    Objects.requireNonNull(routeTypes, "routeTypes must not be null"));
+            mActiveScan = activeScan;
+        }
+
+        public Builder(@NonNull RouteDiscoveryRequest request) {
+            Objects.requireNonNull(request, "request must not be null");
+
+            mRouteTypes = request.getRouteTypes();
+            mActiveScan = request.isActiveScan();
+            mExtras = request.getExtras();
+        }
+
+        /**
+         * A constructor to combine all of the requests into a single request.
+         * It ignores extras of requests.
+         */
+        Builder(@NonNull Collection<RouteDiscoveryRequest> requests) {
+            Set<String> routeTypeSet = new HashSet<>();
+            mActiveScan = false;
+            for (RouteDiscoveryRequest request : requests) {
+                routeTypeSet.addAll(request.mRouteTypes);
+                mActiveScan |= request.mActiveScan;
+            }
+            mRouteTypes = new ArrayList<>(routeTypeSet);
+        }
+
+        /**
+         * Sets route types to discover.
+         */
+        public Builder setRouteTypes(@NonNull List<String> routeTypes) {
+            mRouteTypes = new ArrayList<>(
+                    Objects.requireNonNull(routeTypes, "routeTypes must not be null"));
+            return this;
+        }
+
+        /**
+         * Sets if active scanning should be performed.
+         */
+        public Builder setActiveScan(boolean activeScan) {
+            mActiveScan = activeScan;
+            return this;
+        }
+
+        /**
+         * Sets the extras of the route.
+         * @hide
+         */
+        public Builder setExtras(@Nullable Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * Builds the {@link RouteDiscoveryRequest}.
+         */
+        public RouteDiscoveryRequest build() {
+            return new RouteDiscoveryRequest(this);
+        }
+    }
+}
diff --git a/media/java/android/media/RouteSessionInfo.java b/media/java/android/media/RouteSessionInfo.java
index 4a9298a..cb168860 100644
--- a/media/java/android/media/RouteSessionInfo.java
+++ b/media/java/android/media/RouteSessionInfo.java
@@ -48,7 +48,7 @@
 
     final int mSessionId;
     final String mPackageName;
-    final String mControlCategory;
+    final String mRouteType;
     @Nullable
     final String mProviderId;
     final List<String> mSelectedRoutes;
@@ -63,7 +63,7 @@
 
         mSessionId = builder.mSessionId;
         mPackageName = builder.mPackageName;
-        mControlCategory = builder.mControlCategory;
+        mRouteType = builder.mRouteType;
         mProviderId = builder.mProviderId;
 
         mSelectedRoutes = Collections.unmodifiableList(builder.mSelectedRoutes);
@@ -79,7 +79,7 @@
 
         mSessionId = src.readInt();
         mPackageName = ensureString(src.readString());
-        mControlCategory = ensureString(src.readString());
+        mRouteType = ensureString(src.readString());
         mProviderId = src.readString();
 
         mSelectedRoutes = ensureList(src.createStringArrayList());
@@ -154,7 +154,7 @@
      */
     public boolean isValid() {
         return !TextUtils.isEmpty(mPackageName)
-                && !TextUtils.isEmpty(mControlCategory)
+                && !TextUtils.isEmpty(mRouteType)
                 && mSelectedRoutes.size() > 0;
     }
 
@@ -175,12 +175,12 @@
     }
 
     /**
-     * Gets the control category of the session.
-     * Routes that don't support the category can't be added to the session.
+     * Gets the route type of the session.
+     * Routes that don't have the type can't be added to the session.
      */
     @NonNull
-    public String getControlCategory() {
-        return mControlCategory;
+    public String getRouteType() {
+        return mRouteType;
     }
 
     /**
@@ -254,7 +254,7 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeInt(mSessionId);
         dest.writeString(mPackageName);
-        dest.writeString(mControlCategory);
+        dest.writeString(mRouteType);
         dest.writeString(mProviderId);
         dest.writeStringList(mSelectedRoutes);
         dest.writeStringList(mSelectableRoutes);
@@ -268,7 +268,7 @@
         StringBuilder result = new StringBuilder()
                 .append("RouteSessionInfo{ ")
                 .append("sessionId=").append(mSessionId)
-                .append(", controlCategory=").append(mControlCategory)
+                .append(", routeType=").append(mRouteType)
                 .append(", selectedRoutes={")
                 .append(String.join(",", mSelectedRoutes))
                 .append("}")
@@ -291,7 +291,7 @@
     public static final class Builder {
         final String mPackageName;
         final int mSessionId;
-        final String mControlCategory;
+        final String mRouteType;
         String mProviderId;
         final List<String> mSelectedRoutes;
         final List<String> mSelectableRoutes;
@@ -300,11 +300,11 @@
         Bundle mControlHints;
 
         public Builder(int sessionId, @NonNull String packageName,
-                @NonNull String controlCategory) {
+                @NonNull String routeType) {
             mSessionId = sessionId;
             mPackageName = Objects.requireNonNull(packageName, "packageName must not be null");
-            mControlCategory = Objects.requireNonNull(controlCategory,
-                    "controlCategory must not be null");
+            mRouteType = Objects.requireNonNull(routeType,
+                    "routeType must not be null");
 
             mSelectedRoutes = new ArrayList<>();
             mSelectableRoutes = new ArrayList<>();
@@ -315,7 +315,7 @@
         public Builder(RouteSessionInfo sessionInfo) {
             mSessionId = sessionInfo.mSessionId;
             mPackageName = sessionInfo.mPackageName;
-            mControlCategory = sessionInfo.mControlCategory;
+            mRouteType = sessionInfo.mRouteType;
             mProviderId = sessionInfo.mProviderId;
 
             mSelectedRoutes = new ArrayList<>(sessionInfo.mSelectedRoutes);
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 04fccc7..ec17732 100644
--- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
@@ -46,8 +46,8 @@
     public static final String ROUTE_ID5_TO_TRANSFER_TO = "route_id5_to_transfer_to";
     public static final String ROUTE_NAME5 = "Sample Route 5 - Route to transfer to";
 
-    public static final String ROUTE_ID_SPECIAL_CATEGORY = "route_special_category";
-    public static final String ROUTE_NAME_SPECIAL_CATEGORY = "Special Category Route";
+    public static final String ROUTE_ID_SPECIAL_TYPE = "route_special_type";
+    public static final String ROUTE_NAME_SPECIAL_TYPE = "Special Type Route";
 
     public static final int VOLUME_MAX = 100;
     public static final String ROUTE_ID_FIXED_VOLUME = "route_fixed_volume";
@@ -58,10 +58,10 @@
     public static final String ACTION_REMOVE_ROUTE =
             "com.android.mediarouteprovider.action_remove_route";
 
-    public static final String CATEGORY_SAMPLE =
-            "com.android.mediarouteprovider.CATEGORY_SAMPLE";
-    public static final String CATEGORY_SPECIAL =
-            "com.android.mediarouteprovider.CATEGORY_SPECIAL";
+    public static final String TYPE_SAMPLE =
+            "com.android.mediarouteprovider.TYPE_SAMPLE";
+    public static final String TYPE_SPECIAL =
+            "com.android.mediarouteprovider.TYPE_SPECIAL";
 
     Map<String, MediaRoute2Info> mRoutes = new HashMap<>();
     Map<String, Integer> mRouteSessionMap = new HashMap<>();
@@ -69,38 +69,38 @@
 
     private void initializeRoutes() {
         MediaRoute2Info route1 = new MediaRoute2Info.Builder(ROUTE_ID1, ROUTE_NAME1)
-                .addSupportedCategory(CATEGORY_SAMPLE)
+                .addRouteType(TYPE_SAMPLE)
                 .setDeviceType(DEVICE_TYPE_TV)
                 .build();
         MediaRoute2Info route2 = new MediaRoute2Info.Builder(ROUTE_ID2, ROUTE_NAME2)
-                .addSupportedCategory(CATEGORY_SAMPLE)
+                .addRouteType(TYPE_SAMPLE)
                 .setDeviceType(DEVICE_TYPE_SPEAKER)
                 .build();
         MediaRoute2Info route3 = new MediaRoute2Info.Builder(
                 ROUTE_ID3_SESSION_CREATION_FAILED, ROUTE_NAME3)
-                .addSupportedCategory(CATEGORY_SAMPLE)
+                .addRouteType(TYPE_SAMPLE)
                 .build();
         MediaRoute2Info route4 = new MediaRoute2Info.Builder(
                 ROUTE_ID4_TO_SELECT_AND_DESELECT, ROUTE_NAME4)
-                .addSupportedCategory(CATEGORY_SAMPLE)
+                .addRouteType(TYPE_SAMPLE)
                 .build();
         MediaRoute2Info route5 = new MediaRoute2Info.Builder(
                 ROUTE_ID5_TO_TRANSFER_TO, ROUTE_NAME5)
-                .addSupportedCategory(CATEGORY_SAMPLE)
+                .addRouteType(TYPE_SAMPLE)
                 .build();
         MediaRoute2Info routeSpecial =
-                new MediaRoute2Info.Builder(ROUTE_ID_SPECIAL_CATEGORY, ROUTE_NAME_SPECIAL_CATEGORY)
-                        .addSupportedCategory(CATEGORY_SAMPLE)
-                        .addSupportedCategory(CATEGORY_SPECIAL)
+                new MediaRoute2Info.Builder(ROUTE_ID_SPECIAL_TYPE, ROUTE_NAME_SPECIAL_TYPE)
+                        .addRouteType(TYPE_SAMPLE)
+                        .addRouteType(TYPE_SPECIAL)
                         .build();
         MediaRoute2Info fixedVolumeRoute =
                 new MediaRoute2Info.Builder(ROUTE_ID_FIXED_VOLUME, ROUTE_NAME_FIXED_VOLUME)
-                        .addSupportedCategory(CATEGORY_SAMPLE)
+                        .addRouteType(TYPE_SAMPLE)
                         .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_FIXED)
                         .build();
         MediaRoute2Info variableVolumeRoute =
                 new MediaRoute2Info.Builder(ROUTE_ID_VARIABLE_VOLUME, ROUTE_NAME_VARIABLE_VOLUME)
-                        .addSupportedCategory(CATEGORY_SAMPLE)
+                        .addRouteType(TYPE_SAMPLE)
                         .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
                         .setVolumeMax(VOLUME_MAX)
                         .build();
@@ -167,7 +167,7 @@
     }
 
     @Override
-    public void onCreateSession(String packageName, String routeId, String controlCategory,
+    public void onCreateSession(String packageName, String routeId, String routeType,
             long requestId) {
         MediaRoute2Info route = mRoutes.get(routeId);
         if (route == null || TextUtils.equals(ROUTE_ID3_SESSION_CREATION_FAILED, routeId)) {
@@ -186,7 +186,7 @@
         mRouteSessionMap.put(routeId, sessionId);
 
         RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder(
-                sessionId, packageName, controlCategory)
+                sessionId, packageName, routeType)
                 .addSelectedRoute(routeId)
                 .addSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
                 .addTransferrableRoute(ROUTE_ID5_TO_TRANSFER_TO)
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
index 86b9706..af69c7e 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
@@ -23,18 +23,18 @@
 import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
 import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
 
-import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORIES_ALL;
-import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORIES_SPECIAL;
-import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORY_SAMPLE;
-import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORY_SPECIAL;
 import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID1;
 import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID2;
 import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID3_SESSION_CREATION_FAILED;
 import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID4_TO_SELECT_AND_DESELECT;
 import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID5_TO_TRANSFER_TO;
-import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID_SPECIAL_CATEGORY;
+import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID_SPECIAL_TYPE;
 import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID_VARIABLE_VOLUME;
 import static com.android.mediaroutertest.MediaRouterManagerTest.SYSTEM_PROVIDER_ID;
+import static com.android.mediaroutertest.MediaRouterManagerTest.TYPES_ALL;
+import static com.android.mediaroutertest.MediaRouterManagerTest.TYPES_SPECIAL;
+import static com.android.mediaroutertest.MediaRouterManagerTest.TYPE_SAMPLE;
+import static com.android.mediaroutertest.MediaRouterManagerTest.TYPE_SPECIAL;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -50,6 +50,7 @@
 import android.media.MediaRouter2.RouteCallback;
 import android.media.MediaRouter2.RouteSessionController;
 import android.media.MediaRouter2.SessionCallback;
+import android.media.RouteDiscoveryRequest;
 import android.media.RouteSessionInfo;
 import android.net.Uri;
 import android.os.Parcel;
@@ -95,14 +96,14 @@
     }
 
     /**
-     * Tests if we get proper routes for application that has special control category.
+     * Tests if we get proper routes for application that has special route type.
      */
     @Test
     public void testGetRoutes() throws Exception {
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CATEGORIES_SPECIAL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(TYPES_SPECIAL);
 
         assertEquals(1, routes.size());
-        assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
+        assertNotNull(routes.get(ROUTE_ID_SPECIAL_TYPE));
     }
 
     @Test
@@ -114,7 +115,7 @@
                 .setIconUri(new Uri.Builder().path("icon").build())
                 .setVolume(5)
                 .setVolumeMax(20)
-                .addSupportedCategory(CATEGORY_SAMPLE)
+                .addRouteType(TYPE_SAMPLE)
                 .setVolumeHandling(PLAYBACK_VOLUME_VARIABLE)
                 .setDeviceType(DEVICE_TYPE_SPEAKER)
                 .build();
@@ -137,7 +138,7 @@
                 .setClientPackageName("com.android.mediaroutertest")
                 .setConnectionState(CONNECTION_STATE_CONNECTING)
                 .setIconUri(new Uri.Builder().path("icon").build())
-                .addSupportedCategory(CATEGORY_SAMPLE)
+                .addRouteType(TYPE_SAMPLE)
                 .setVolume(5)
                 .setVolumeMax(20)
                 .setVolumeHandling(PLAYBACK_VOLUME_VARIABLE)
@@ -168,9 +169,9 @@
                 .setClientPackageName("another.client.package").build();
         assertNotEquals(route, routeClient);
 
-        MediaRoute2Info routeCategory = new MediaRoute2Info.Builder(route)
-                .addSupportedCategory(CATEGORY_SPECIAL).build();
-        assertNotEquals(route, routeCategory);
+        MediaRoute2Info routeType = new MediaRoute2Info.Builder(route)
+                .addRouteType(TYPE_SPECIAL).build();
+        assertNotEquals(route, routeType);
 
         MediaRoute2Info routeVolume = new MediaRoute2Info.Builder(route)
                 .setVolume(10).build();
@@ -191,7 +192,7 @@
 
     @Test
     public void testControlVolumeWithRouter() throws Exception {
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CATEGORIES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(TYPES_ALL);
 
         MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
         assertNotNull(volRoute);
@@ -202,12 +203,14 @@
         awaitOnRouteChanged(
                 () -> mRouter2.requestUpdateVolume(volRoute, deltaVolume),
                 ROUTE_ID_VARIABLE_VOLUME,
-                (route -> route.getVolume() == originalVolume + deltaVolume));
+                (route -> route.getVolume() == originalVolume + deltaVolume),
+                TYPES_ALL);
 
         awaitOnRouteChanged(
                 () -> mRouter2.requestSetVolume(volRoute, originalVolume),
                 ROUTE_ID_VARIABLE_VOLUME,
-                (route -> route.getVolume() == originalVolume));
+                (route -> route.getVolume() == originalVolume),
+                TYPES_ALL);
     }
 
     @Test
@@ -234,13 +237,13 @@
     @Test
     public void testRequestCreateSessionWithInvalidArguments() {
         MediaRoute2Info route = new MediaRoute2Info.Builder("id", "name").build();
-        String controlCategory = "controlCategory";
+        String routeType = "routeType";
 
         // Tests null route
         assertThrows(NullPointerException.class,
-                () -> mRouter2.requestCreateSession(null, controlCategory));
+                () -> mRouter2.requestCreateSession(null, routeType));
 
-        // Tests null or empty control category
+        // Tests null or empty route type
         assertThrows(IllegalArgumentException.class,
                 () -> mRouter2.requestCreateSession(route, null));
         assertThrows(IllegalArgumentException.class,
@@ -249,10 +252,10 @@
 
     @Test
     public void testRequestCreateSessionSuccess() throws Exception {
-        final List<String> sampleControlCategory = new ArrayList<>();
-        sampleControlCategory.add(CATEGORY_SAMPLE);
+        final List<String> sampleRouteType = new ArrayList<>();
+        sampleRouteType.add(TYPE_SAMPLE);
 
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
         MediaRoute2Info route = routes.get(ROUTE_ID1);
         assertNotNull(route);
 
@@ -266,25 +269,25 @@
             public void onSessionCreated(RouteSessionController controller) {
                 assertNotNull(controller);
                 assertTrue(createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1));
-                assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller.getControlCategory()));
+                assertTrue(TextUtils.equals(TYPE_SAMPLE, controller.getRouteType()));
                 controllers.add(controller);
                 successLatch.countDown();
             }
 
             @Override
             public void onSessionCreationFailed(MediaRoute2Info requestedRoute,
-                    String requestedControlCategory) {
+                    String requestedRouteType) {
                 failureLatch.countDown();
             }
         };
 
         // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
         RouteCallback routeCallback = new RouteCallback();
-        mRouter2.registerRouteCallback(mExecutor, routeCallback);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(route, CATEGORY_SAMPLE);
+            mRouter2.requestCreateSession(route, TYPE_SAMPLE);
             assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             // onSessionCreationFailed should not be called.
@@ -298,10 +301,10 @@
 
     @Test
     public void testRequestCreateSessionFailure() throws Exception {
-        final List<String> sampleControlCategory = new ArrayList<>();
-        sampleControlCategory.add(CATEGORY_SAMPLE);
+        final List<String> sampleRouteType = new ArrayList<>();
+        sampleRouteType.add(TYPE_SAMPLE);
 
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
         MediaRoute2Info route = routes.get(ROUTE_ID3_SESSION_CREATION_FAILED);
         assertNotNull(route);
 
@@ -319,20 +322,20 @@
 
             @Override
             public void onSessionCreationFailed(MediaRoute2Info requestedRoute,
-                    String requestedControlCategory) {
+                    String requestedRouteType) {
                 assertEquals(route, requestedRoute);
-                assertTrue(TextUtils.equals(CATEGORY_SAMPLE, requestedControlCategory));
+                assertTrue(TextUtils.equals(TYPE_SAMPLE, requestedRouteType));
                 failureLatch.countDown();
             }
         };
 
         // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
         RouteCallback routeCallback = new RouteCallback();
-        mRouter2.registerRouteCallback(mExecutor, routeCallback);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(route, CATEGORY_SAMPLE);
+            mRouter2.requestCreateSession(route, TYPE_SAMPLE);
             assertTrue(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             // onSessionCreated should not be called.
@@ -346,8 +349,8 @@
 
     @Test
     public void testRequestCreateSessionMultipleSessions() throws Exception {
-        final List<String> sampleControlCategory = new ArrayList<>();
-        sampleControlCategory.add(CATEGORY_SAMPLE);
+        final List<String> sampleRouteType = new ArrayList<>();
+        sampleRouteType.add(TYPE_SAMPLE);
 
         final CountDownLatch successLatch = new CountDownLatch(2);
         final CountDownLatch failureLatch = new CountDownLatch(1);
@@ -363,12 +366,12 @@
 
             @Override
             public void onSessionCreationFailed(MediaRoute2Info requestedRoute,
-                    String requestedControlCategory) {
+                    String requestedRouteType) {
                 failureLatch.countDown();
             }
         };
 
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
         MediaRoute2Info route1 = routes.get(ROUTE_ID1);
         MediaRoute2Info route2 = routes.get(ROUTE_ID2);
         assertNotNull(route1);
@@ -376,12 +379,12 @@
 
         // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
         RouteCallback routeCallback = new RouteCallback();
-        mRouter2.registerRouteCallback(mExecutor, routeCallback);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(route1, CATEGORY_SAMPLE);
-            mRouter2.requestCreateSession(route2, CATEGORY_SAMPLE);
+            mRouter2.requestCreateSession(route1, TYPE_SAMPLE);
+            mRouter2.requestCreateSession(route2, TYPE_SAMPLE);
             assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             // onSessionCreationFailed should not be called.
@@ -395,8 +398,8 @@
             assertNotEquals(controller1.getSessionId(), controller2.getSessionId());
             assertTrue(createRouteMap(controller1.getSelectedRoutes()).containsKey(ROUTE_ID1));
             assertTrue(createRouteMap(controller2.getSelectedRoutes()).containsKey(ROUTE_ID2));
-            assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller1.getControlCategory()));
-            assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller2.getControlCategory()));
+            assertTrue(TextUtils.equals(TYPE_SAMPLE, controller1.getRouteType()));
+            assertTrue(TextUtils.equals(TYPE_SAMPLE, controller2.getRouteType()));
         } finally {
             releaseControllers(createdControllers);
             mRouter2.unregisterRouteCallback(routeCallback);
@@ -406,10 +409,10 @@
 
     @Test
     public void testSessionCallbackIsNotCalledAfterUnregistered() throws Exception {
-        final List<String> sampleControlCategory = new ArrayList<>();
-        sampleControlCategory.add(CATEGORY_SAMPLE);
+        final List<String> sampleRouteType = new ArrayList<>();
+        sampleRouteType.add(TYPE_SAMPLE);
 
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
         MediaRoute2Info route = routes.get(ROUTE_ID1);
         assertNotNull(route);
 
@@ -427,18 +430,18 @@
 
             @Override
             public void onSessionCreationFailed(MediaRoute2Info requestedRoute,
-                    String requestedControlCategory) {
+                    String requestedRouteType) {
                 failureLatch.countDown();
             }
         };
 
         // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
         RouteCallback routeCallback = new RouteCallback();
-        mRouter2.registerRouteCallback(mExecutor, routeCallback);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(route, CATEGORY_SAMPLE);
+            mRouter2.requestCreateSession(route, TYPE_SAMPLE);
 
             // Unregisters session callback
             mRouter2.unregisterSessionCallback(sessionCallback);
@@ -456,10 +459,10 @@
     // TODO: Add tests for illegal inputs if needed (e.g. selecting already selected route)
     @Test
     public void testRouteSessionControllerSelectAndDeselectRoute() throws Exception {
-        final List<String> sampleControlCategory = new ArrayList<>();
-        sampleControlCategory.add(CATEGORY_SAMPLE);
+        final List<String> sampleRouteType = new ArrayList<>();
+        sampleRouteType.add(TYPE_SAMPLE);
 
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
         MediaRoute2Info routeToCreateSessionWith = routes.get(ROUTE_ID1);
         assertNotNull(routeToCreateSessionWith);
 
@@ -474,7 +477,7 @@
             public void onSessionCreated(RouteSessionController controller) {
                 assertNotNull(controller);
                 assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1));
-                assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller.getControlCategory()));
+                assertTrue(TextUtils.equals(TYPE_SAMPLE, controller.getRouteType()));
                 controllers.add(controller);
                 onSessionCreatedLatch.countDown();
             }
@@ -522,11 +525,11 @@
 
         // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
         RouteCallback routeCallback = new RouteCallback();
-        mRouter2.registerRouteCallback(mExecutor, routeCallback);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(routeToCreateSessionWith, CATEGORY_SAMPLE);
+            mRouter2.requestCreateSession(routeToCreateSessionWith, TYPE_SAMPLE);
             assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             assertEquals(1, controllers.size());
@@ -554,10 +557,10 @@
 
     @Test
     public void testRouteSessionControllerTransferToRoute() throws Exception {
-        final List<String> sampleControlCategory = new ArrayList<>();
-        sampleControlCategory.add(CATEGORY_SAMPLE);
+        final List<String> sampleRouteType = new ArrayList<>();
+        sampleRouteType.add(TYPE_SAMPLE);
 
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
         MediaRoute2Info routeToCreateSessionWith = routes.get(ROUTE_ID1);
         assertNotNull(routeToCreateSessionWith);
 
@@ -570,8 +573,12 @@
             @Override
             public void onSessionCreated(RouteSessionController controller) {
                 assertNotNull(controller);
+                android.util.Log.d(TAG, "selected route ids ");
+                for (String routeId : getRouteIds(controller.getSelectedRoutes())) {
+                    android.util.Log.d(TAG, "route id : " + routeId);
+                }
                 assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1));
-                assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller.getControlCategory()));
+                assertTrue(TextUtils.equals(TYPE_SAMPLE, controller.getRouteType()));
                 controllers.add(controller);
                 onSessionCreatedLatch.countDown();
             }
@@ -603,11 +610,11 @@
 
         // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
         RouteCallback routeCallback = new RouteCallback();
-        mRouter2.registerRouteCallback(mExecutor, routeCallback);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(routeToCreateSessionWith, CATEGORY_SAMPLE);
+            mRouter2.requestCreateSession(routeToCreateSessionWith, TYPE_SAMPLE);
             assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             assertEquals(1, controllers.size());
@@ -632,10 +639,10 @@
 
     @Test
     public void testRouteSessionControllerReleaseShouldIgnoreTransferTo() throws Exception {
-        final List<String> sampleControlCategory = new ArrayList<>();
-        sampleControlCategory.add(CATEGORY_SAMPLE);
+        final List<String> sampleRouteType = new ArrayList<>();
+        sampleRouteType.add(TYPE_SAMPLE);
 
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
         MediaRoute2Info routeToCreateSessionWith = routes.get(ROUTE_ID1);
         assertNotNull(routeToCreateSessionWith);
 
@@ -649,7 +656,7 @@
             public void onSessionCreated(RouteSessionController controller) {
                 assertNotNull(controller);
                 assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1));
-                assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller.getControlCategory()));
+                assertTrue(TextUtils.equals(TYPE_SAMPLE, controller.getRouteType()));
                 controllers.add(controller);
                 onSessionCreatedLatch.countDown();
             }
@@ -667,11 +674,11 @@
 
         // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
         RouteCallback routeCallback = new RouteCallback();
-        mRouter2.registerRouteCallback(mExecutor, routeCallback);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(routeToCreateSessionWith, CATEGORY_SAMPLE);
+            mRouter2.requestCreateSession(routeToCreateSessionWith, TYPE_SAMPLE);
             assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             assertEquals(1, controllers.size());
@@ -706,11 +713,11 @@
         return routeMap;
     }
 
-    Map<String, MediaRoute2Info> waitAndGetRoutes(List<String> controlCategories)
+    Map<String, MediaRoute2Info> waitAndGetRoutes(List<String> routeTypes)
             throws Exception {
         CountDownLatch latch = new CountDownLatch(1);
 
-        // A dummy callback is required to send control category info.
+        // A dummy callback is required to send route type info.
         RouteCallback routeCallback = new RouteCallback() {
             @Override
             public void onRoutesAdded(List<MediaRoute2Info> routes) {
@@ -723,8 +730,8 @@
             }
         };
 
-        mRouter2.setControlCategories(controlCategories);
-        mRouter2.registerRouteCallback(mExecutor, routeCallback);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback,
+                new RouteDiscoveryRequest.Builder(routeTypes, true).build());
         try {
             latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
             return createRouteMap(mRouter2.getRoutes());
@@ -751,7 +758,8 @@
     }
 
     void awaitOnRouteChanged(Runnable task, String routeId,
-            Predicate<MediaRoute2Info> predicate) throws Exception {
+            Predicate<MediaRoute2Info> predicate,
+            List<String> routeTypes) throws Exception {
         CountDownLatch latch = new CountDownLatch(1);
         RouteCallback routeCallback = new RouteCallback() {
             @Override
@@ -762,7 +770,8 @@
                 }
             }
         };
-        mRouter2.registerRouteCallback(mExecutor, routeCallback);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback,
+                new RouteDiscoveryRequest.Builder(routeTypes, true).build());
         try {
             task.run();
             assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index 1fd0141..c23a5b0 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -31,6 +31,7 @@
 import android.media.MediaRouter2.RouteCallback;
 import android.media.MediaRouter2.SessionCallback;
 import android.media.MediaRouter2Manager;
+import android.media.RouteDiscoveryRequest;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -75,9 +76,9 @@
             SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_id5_to_transfer_to";
     public static final String ROUTE_NAME5 = "Sample Route 5 - Route to transfer to";
 
-    public static final String ROUTE_ID_SPECIAL_CATEGORY =
-            SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_special_category";
-    public static final String ROUTE_NAME_SPECIAL_CATEGORY = "Special Category Route";
+    public static final String ROUTE_ID_SPECIAL_TYPE =
+            SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_special_type";
+    public static final String ROUTE_NAME_SPECIAL_TYPE = "Special Type Route";
 
     public static final String SYSTEM_PROVIDER_ID =
             "com.android.server.media/.SystemMediaRoute2Provider";
@@ -93,12 +94,12 @@
     public static final String ACTION_REMOVE_ROUTE =
             "com.android.mediarouteprovider.action_remove_route";
 
-    public static final String CATEGORY_SAMPLE =
-            "com.android.mediarouteprovider.CATEGORY_SAMPLE";
-    public static final String CATEGORY_SPECIAL =
-            "com.android.mediarouteprovider.CATEGORY_SPECIAL";
+    public static final String TYPE_SAMPLE =
+            "com.android.mediarouteprovider.TYPE_SAMPLE";
+    public static final String TYPE_SPECIAL =
+            "com.android.mediarouteprovider.TYPE_SPECIAL";
 
-    private static final String CATEGORY_LIVE_AUDIO = "android.media.intent.category.LIVE_AUDIO";
+    private static final String TYPE_LIVE_AUDIO = "android.media.intent.category.LIVE_AUDIO";
 
     private static final int TIMEOUT_MS = 5000;
 
@@ -112,18 +113,18 @@
     private final List<RouteCallback> mRouteCallbacks = new ArrayList<>();
     private final List<SessionCallback> mSessionCallbacks = new ArrayList<>();
 
-    public static final List<String> CATEGORIES_ALL = new ArrayList();
-    public static final List<String> CATEGORIES_SPECIAL = new ArrayList();
-    private static final List<String> CATEGORIES_LIVE_AUDIO = new ArrayList<>();
+    public static final List<String> TYPES_ALL = new ArrayList();
+    public static final List<String> TYPES_SPECIAL = new ArrayList();
+    private static final List<String> TYPES_LIVE_AUDIO = new ArrayList<>();
 
     static {
-        CATEGORIES_ALL.add(CATEGORY_SAMPLE);
-        CATEGORIES_ALL.add(CATEGORY_SPECIAL);
-        CATEGORIES_ALL.add(CATEGORY_LIVE_AUDIO);
+        TYPES_ALL.add(TYPE_SAMPLE);
+        TYPES_ALL.add(TYPE_SPECIAL);
+        TYPES_ALL.add(TYPE_LIVE_AUDIO);
 
-        CATEGORIES_SPECIAL.add(CATEGORY_SPECIAL);
+        TYPES_SPECIAL.add(TYPE_SPECIAL);
 
-        CATEGORIES_LIVE_AUDIO.add(CATEGORY_LIVE_AUDIO);
+        TYPES_LIVE_AUDIO.add(TYPE_LIVE_AUDIO);
     }
 
     @Before
@@ -180,7 +181,7 @@
     @Test
     public void testOnRoutesRemoved() throws Exception {
         CountDownLatch latch = new CountDownLatch(1);
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL);
 
         addRouterCallback(new RouteCallback());
         addManagerCallback(new MediaRouter2Manager.Callback() {
@@ -202,14 +203,14 @@
     }
 
     /**
-     * Tests if we get proper routes for application that has special control category.
+     * Tests if we get proper routes for application that has special route type.
      */
     @Test
-    public void testControlCategory() throws Exception {
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_SPECIAL);
+    public void testRouteType() throws Exception {
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_SPECIAL);
 
         assertEquals(1, routes.size());
-        assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
+        assertNotNull(routes.get(ROUTE_ID_SPECIAL_TYPE));
     }
 
     /**
@@ -218,7 +219,7 @@
      */
     @Test
     public void testRouterOnSessionCreated() throws Exception {
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL);
 
         CountDownLatch latch = new CountDownLatch(1);
 
@@ -254,7 +255,7 @@
     @Ignore("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);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL);
 
         addRouterCallback(new RouteCallback());
         addManagerCallback(new MediaRouter2Manager.Callback() {
@@ -284,7 +285,7 @@
     public void testGetActiveRoutes() throws Exception {
         CountDownLatch latch = new CountDownLatch(1);
 
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL);
         addRouterCallback(new RouteCallback());
         addManagerCallback(new MediaRouter2Manager.Callback() {
             @Override
@@ -320,7 +321,7 @@
     @Test
     @Ignore("TODO: enable when session is released")
     public void testSingleProviderSelect() throws Exception {
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL);
         addRouterCallback(new RouteCallback());
 
         awaitOnRouteChangedManager(
@@ -345,7 +346,7 @@
 
     @Test
     public void testControlVolumeWithManager() throws Exception {
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL);
         MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
 
         int originalVolume = volRoute.getVolume();
@@ -364,7 +365,7 @@
 
     @Test
     public void testVolumeHandling() throws Exception {
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL);
 
         MediaRoute2Info fixedVolumeRoute = routes.get(ROUTE_ID_FIXED_VOLUME);
         MediaRoute2Info variableVolumeRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
@@ -374,11 +375,11 @@
         assertEquals(VOLUME_MAX, variableVolumeRoute.getVolumeMax());
     }
 
-    Map<String, MediaRoute2Info> waitAndGetRoutesWithManager(List<String> controlCategories)
+    Map<String, MediaRoute2Info> waitAndGetRoutesWithManager(List<String> routeTypes)
             throws Exception {
         CountDownLatch latch = new CountDownLatch(2);
 
-        // A dummy callback is required to send control category info.
+        // A dummy callback is required to send route type info.
         RouteCallback routeCallback = new RouteCallback();
         MediaRouter2Manager.Callback managerCallback = new MediaRouter2Manager.Callback() {
             @Override
@@ -393,16 +394,16 @@
             }
 
             @Override
-            public void onControlCategoriesChanged(String packageName, List<String> categories) {
+            public void onControlCategoriesChanged(String packageName, List<String> routeTypes) {
                 if (TextUtils.equals(mPackageName, packageName)
-                        && controlCategories.equals(categories)) {
+                        && routeTypes.equals(routeTypes)) {
                     latch.countDown();
                 }
             }
         };
         mManager.registerCallback(mExecutor, managerCallback);
-        mRouter2.setControlCategories(controlCategories);
-        mRouter2.registerRouteCallback(mExecutor, routeCallback);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback,
+                new RouteDiscoveryRequest.Builder(routeTypes, true).build());
         try {
             latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
             return createRouteMap(mManager.getAvailableRoutes(mPackageName));
@@ -449,7 +450,7 @@
 
     private void addRouterCallback(RouteCallback routeCallback) {
         mRouteCallbacks.add(routeCallback);
-        mRouter2.registerRouteCallback(mExecutor, routeCallback);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
     }
 
     private void addSessionCallback(SessionCallback sessionCallback) {
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteDiscoveryRequestTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteDiscoveryRequestTest.java
new file mode 100644
index 0000000..60d131a
--- /dev/null
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteDiscoveryRequestTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaroutertest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.media.RouteDiscoveryRequest;
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class RouteDiscoveryRequestTest {
+    @Before
+    public void setUp() throws Exception { }
+
+    @After
+    public void tearDown() throws Exception { }
+
+    @Test
+    public void testEquality() {
+        List<String> testTypes = new ArrayList<>();
+        testTypes.add("TEST_TYPE_1");
+        testTypes.add("TEST_TYPE_2");
+        RouteDiscoveryRequest request = new RouteDiscoveryRequest.Builder(testTypes, true)
+                .build();
+
+        RouteDiscoveryRequest requestRebuilt = new RouteDiscoveryRequest.Builder(request)
+                .build();
+
+        assertEquals(request, requestRebuilt);
+
+        Parcel parcel = Parcel.obtain();
+        parcel.writeParcelable(request, 0);
+        parcel.setDataPosition(0);
+        RouteDiscoveryRequest requestFromParcel = parcel.readParcelable(null);
+
+        assertEquals(request, requestFromParcel);
+    }
+
+    @Test
+    public void testInequality() {
+        List<String> testTypes = new ArrayList<>();
+        testTypes.add("TEST_TYPE_1");
+        testTypes.add("TEST_TYPE_2");
+
+        List<String> testTypes2 = new ArrayList<>();
+        testTypes.add("TEST_TYPE_3");
+
+        RouteDiscoveryRequest request = new RouteDiscoveryRequest.Builder(testTypes, true)
+                .build();
+
+        RouteDiscoveryRequest requestTypes = new RouteDiscoveryRequest.Builder(request)
+                .setRouteTypes(testTypes2)
+                .build();
+        assertNotEquals(request, requestTypes);
+
+        RouteDiscoveryRequest requestActiveScan = new RouteDiscoveryRequest.Builder(request)
+                .setActiveScan(false)
+                .build();
+        assertNotEquals(request, requestActiveScan);
+    }
+}
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 55c4e21..9ca302e 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -46,7 +46,7 @@
     }
 
     public abstract void requestCreateSession(String packageName, String routeId,
-            String controlCategory, long requestId);
+            String routeType, long requestId);
     public abstract void releaseSession(int sessionId);
 
     public abstract void selectRoute(int sessionId, String routeId);
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index 28bb034..5cc2b16 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -76,10 +76,10 @@
     }
 
     @Override
-    public void requestCreateSession(String packageName, String routeId, String controlCategory,
+    public void requestCreateSession(String packageName, String routeId, String routeType,
             long requestId) {
         if (mConnectionReady) {
-            mActiveConnection.requestCreateSession(packageName, routeId, controlCategory,
+            mActiveConnection.requestCreateSession(packageName, routeId, routeType,
                     requestId);
             updateBinding();
         }
@@ -345,11 +345,11 @@
             mClient.dispose();
         }
 
-        public void requestCreateSession(String packageName, String routeId, String controlCategory,
+        public void requestCreateSession(String packageName, String routeId, String routeType,
                 long requestId) {
             try {
                 mProvider.requestCreateSession(packageName, routeId,
-                        controlCategory, requestId);
+                        routeType, requestId);
             } catch (RemoteException ex) {
                 Slog.e(TAG, "Failed to deliver request to create a session.", ex);
             }
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index a5ffbb8..b482432 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -28,6 +28,7 @@
 import android.media.IMediaRouter2Manager;
 import android.media.MediaRoute2Info;
 import android.media.MediaRoute2ProviderInfo;
+import android.media.RouteDiscoveryRequest;
 import android.media.RouteSessionInfo;
 import android.os.Binder;
 import android.os.Bundle;
@@ -174,18 +175,18 @@
     }
 
     public void requestCreateSession(IMediaRouter2Client client, MediaRoute2Info route,
-            String controlCategory, int requestId) {
+            String routeType, int requestId) {
         Objects.requireNonNull(client, "client must not be null");
         Objects.requireNonNull(route, "route must not be null");
-        if (TextUtils.isEmpty(controlCategory)) {
-            throw new IllegalArgumentException("controlCategory must not be empty");
+        if (TextUtils.isEmpty(routeType)) {
+            throw new IllegalArgumentException("routeType must not be empty");
         }
 
         final long token = Binder.clearCallingIdentity();
 
         try {
             synchronized (mLock) {
-                requestCreateSessionLocked(client, route, controlCategory, requestId);
+                requestCreateSessionLocked(client, route, routeType, requestId);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -267,16 +268,16 @@
         }
     }
 
-    public void setControlCategories(@NonNull IMediaRouter2Client client,
-            @NonNull List<String> categories) {
+    public void setDiscoveryRequest2(@NonNull IMediaRouter2Client client,
+            @NonNull RouteDiscoveryRequest request) {
         Objects.requireNonNull(client, "client must not be null");
-        Objects.requireNonNull(categories, "categories must not be null");
+        Objects.requireNonNull(request, "request must not be null");
 
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
                 Client2Record clientRecord = mAllClientRecords.get(client.asBinder());
-                setControlCategoriesLocked(clientRecord, categories);
+                setDiscoveryRequestLocked(clientRecord, request);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -434,7 +435,7 @@
     }
 
     private void requestCreateSessionLocked(@NonNull IMediaRouter2Client client,
-            @NonNull MediaRoute2Info route, @NonNull String controlCategory, long requestId) {
+            @NonNull MediaRoute2Info route, @NonNull String routeType, long requestId) {
         final IBinder binder = client.asBinder();
         final Client2Record clientRecord = mAllClientRecords.get(binder);
 
@@ -447,7 +448,7 @@
             clientRecord.mUserRecord.mHandler.sendMessage(
                     obtainMessage(UserHandler::requestCreateSessionOnHandler,
                             clientRecord.mUserRecord.mHandler,
-                            clientRecord, route, controlCategory, requestId));
+                            clientRecord, route, routeType, requestId));
         }
     }
 
@@ -502,13 +503,14 @@
         }
     }
 
-    private void setControlCategoriesLocked(Client2Record clientRecord, List<String> categories) {
+    private void setDiscoveryRequestLocked(Client2Record clientRecord,
+            RouteDiscoveryRequest discoveryRequest) {
         if (clientRecord != null) {
-            if (clientRecord.mControlCategories.equals(categories)) {
+            if (clientRecord.mDiscoveryRequest.equals(discoveryRequest)) {
                 return;
             }
 
-            clientRecord.mControlCategories = categories;
+            clientRecord.mDiscoveryRequest = discoveryRequest;
             clientRecord.mUserRecord.mHandler.sendMessage(
                     obtainMessage(UserHandler::updateClientUsage,
                             clientRecord.mUserRecord.mHandler, clientRecord));
@@ -607,7 +609,7 @@
             if (clientRecord != null && managerRecord.mTrusted) {
                 //TODO: select category properly
                 requestCreateSessionLocked(clientRecord.mClient, route,
-                        route.getSupportedCategories().get(0), uniqueRequestId);
+                        route.getRouteTypes().get(0), uniqueRequestId);
             }
         }
     }
@@ -725,7 +727,7 @@
         public final boolean mTrusted;
         public final int mClientId;
 
-        public List<String> mControlCategories;
+        public RouteDiscoveryRequest mDiscoveryRequest;
         public boolean mIsManagerSelecting;
         public MediaRoute2Info mSelectingRoute;
         public MediaRoute2Info mSelectedRoute;
@@ -735,7 +737,7 @@
             mUserRecord = userRecord;
             mPackageName = packageName;
             mSelectRouteSequenceNumbers = new ArrayList<>();
-            mControlCategories = Collections.emptyList();
+            mDiscoveryRequest = RouteDiscoveryRequest.EMPTY;
             mClient = client;
             mUid = uid;
             mPid = pid;
@@ -961,7 +963,7 @@
         }
 
         private void requestCreateSessionOnHandler(Client2Record clientRecord,
-                MediaRoute2Info route, String controlCategory, long requestId) {
+                MediaRoute2Info route, String routeType, long requestId) {
 
             final MediaRoute2Provider provider = findProvider(route.getProviderId());
             if (provider == null) {
@@ -971,20 +973,20 @@
                 return;
             }
 
-            if (!route.getSupportedCategories().contains(controlCategory)) {
+            if (!route.getRouteTypes().contains(routeType)) {
                 Slog.w(TAG, "Ignoring session creation request since the given route=" + route
-                        + " doesn't support the given category=" + controlCategory);
+                        + " doesn't support the given type=" + routeType);
                 notifySessionCreationFailed(clientRecord, toClientRequestId(requestId));
                 return;
             }
 
             // TODO: Apply timeout for each request (How many seconds should we wait?)
             SessionCreationRequest request = new SessionCreationRequest(
-                    clientRecord, route, controlCategory, requestId);
+                    clientRecord, route, routeType, requestId);
             mSessionCreationRequests.add(request);
 
             provider.requestCreateSession(clientRecord.mPackageName, route.getOriginalId(),
-                    controlCategory, requestId);
+                    routeType, requestId);
         }
 
         private void selectRouteOnHandler(@NonNull Client2Record clientRecord,
@@ -1144,12 +1146,12 @@
             }
 
             String originalRouteId = matchingRequest.mRoute.getId();
-            String originalCategory = matchingRequest.mControlCategory;
+            String originalCategory = matchingRequest.mRouteType;
             Client2Record client2Record = matchingRequest.mClientRecord;
 
             if (!sessionInfo.getSelectedRoutes().contains(originalRouteId)
                     || !TextUtils.equals(originalCategory,
-                        sessionInfo.getControlCategory())) {
+                        sessionInfo.getRouteType())) {
                 Slog.w(TAG, "Created session doesn't match the original request."
                         + " originalRouteId=" + originalRouteId
                         + ", originalCategory=" + originalCategory + ", requestId=" + requestId
@@ -1404,8 +1406,8 @@
                 try {
                     manager.notifyRouteSelected(clientRecord.mPackageName,
                             clientRecord.mSelectedRoute);
-                    manager.notifyControlCategoriesChanged(clientRecord.mPackageName,
-                            clientRecord.mControlCategories);
+                    manager.notifyRouteTypesChanged(clientRecord.mPackageName,
+                            clientRecord.mDiscoveryRequest.getRouteTypes());
                 } catch (RemoteException ex) {
                     Slog.w(TAG, "Failed to update client usage. Manager probably died.", ex);
                 }
@@ -1424,15 +1426,15 @@
         final class SessionCreationRequest {
             public final Client2Record mClientRecord;
             public final MediaRoute2Info mRoute;
-            public final String mControlCategory;
+            public final String mRouteType;
             public final long mRequestId;
 
             SessionCreationRequest(@NonNull Client2Record clientRecord,
                     @NonNull MediaRoute2Info route,
-                    @NonNull String controlCategory, long requestId) {
+                    @NonNull String routeType, long requestId) {
                 mClientRecord = clientRecord;
                 mRoute = route;
-                mControlCategory = controlCategory;
+                mRouteType = routeType;
                 mRequestId = requestId;
             }
         }
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index d77f43b..c76555c 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -39,6 +39,7 @@
 import android.media.MediaRouterClientState;
 import android.media.RemoteDisplayState;
 import android.media.RemoteDisplayState.RemoteDisplayInfo;
+import android.media.RouteDiscoveryRequest;
 import android.media.RouteSessionInfo;
 import android.os.Binder;
 import android.os.Handler;
@@ -459,8 +460,8 @@
     // Binder call
     @Override
     public void requestCreateSession(IMediaRouter2Client client, MediaRoute2Info route,
-            String controlCategory, int requestId) {
-        mService2.requestCreateSession(client, route, controlCategory, requestId);
+            String routeType, int requestId) {
+        mService2.requestCreateSession(client, route, routeType, requestId);
     }
 
     // Binder call
@@ -519,8 +520,8 @@
     }
     // Binder call
     @Override
-    public void setControlCategories(IMediaRouter2Client client, List<String> categories) {
-        mService2.setControlCategories(client, categories);
+    public void setDiscoveryRequest2(IMediaRouter2Client client, RouteDiscoveryRequest request) {
+        mService2.setDiscoveryRequest2(client, request);
     }
 
     // Binder call
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 5302765..daf6030 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -91,7 +91,7 @@
     }
 
     @Override
-    public void requestCreateSession(String packageName, String routeId, String controlCategory,
+    public void requestCreateSession(String packageName, String routeId, String routeType,
             long requestId) {
         // Do nothing
     }
@@ -141,8 +141,8 @@
                         : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
                 .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
                 .setVolume(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC))
-                .addSupportedCategory(CATEGORY_LIVE_AUDIO)
-                .addSupportedCategory(CATEGORY_LIVE_VIDEO)
+                .addRouteType(CATEGORY_LIVE_AUDIO)
+                .addRouteType(CATEGORY_LIVE_VIDEO)
                 .build();
 
         AudioRoutesInfo newAudioRoutes = null;
@@ -181,8 +181,8 @@
                         : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
                 .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
                 .setVolume(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC))
-                .addSupportedCategory(CATEGORY_LIVE_AUDIO)
-                .addSupportedCategory(CATEGORY_LIVE_VIDEO)
+                .addRouteType(CATEGORY_LIVE_AUDIO)
+                .addRouteType(CATEGORY_LIVE_VIDEO)
                 .build();
 
         if (!TextUtils.equals(newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) {
@@ -193,7 +193,7 @@
                         mCurAudioRoutesInfo.bluetoothName)
                         .setDescription(mContext.getResources().getText(
                                 R.string.bluetooth_a2dp_audio_route_name).toString())
-                        .addSupportedCategory(CATEGORY_LIVE_AUDIO)
+                        .addRouteType(CATEGORY_LIVE_AUDIO)
                         .build();
             } else {
                 mBluetoothA2dpRoute = null;