Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 083297ec authored by Oliver Woodman's avatar Oliver Woodman Committed by Android (Google) Code Review
Browse files

Merge "Add more options on RouteDiscoveryPreference"

parents ebd845bf 13e06b44
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -22728,6 +22728,7 @@ package android.media {
    method public int describeContents();
    method @Nullable public String getClientPackageName();
    method public int getConnectionState();
    method @NonNull public java.util.Set<java.lang.String> getDeduplicationIds();
    method @Nullable public CharSequence getDescription();
    method @Nullable public android.os.Bundle getExtras();
    method @NonNull public java.util.List<java.lang.String> getFeatures();
@@ -22761,6 +22762,7 @@ package android.media {
    method @NonNull public android.media.MediaRoute2Info.Builder clearFeatures();
    method @NonNull public android.media.MediaRoute2Info.Builder setClientPackageName(@Nullable String);
    method @NonNull public android.media.MediaRoute2Info.Builder setConnectionState(int);
    method @NonNull public android.media.MediaRoute2Info.Builder setDeduplicationIds(@NonNull java.util.Set<java.lang.String>);
    method @NonNull public android.media.MediaRoute2Info.Builder setDescription(@Nullable CharSequence);
    method @NonNull public android.media.MediaRoute2Info.Builder setExtras(@Nullable android.os.Bundle);
    method @NonNull public android.media.MediaRoute2Info.Builder setIconUri(@Nullable android.net.Uri);
@@ -23287,8 +23289,12 @@ package android.media {
  public final class RouteDiscoveryPreference implements android.os.Parcelable {
    method public int describeContents();
    method @NonNull public java.util.List<java.lang.String> getAllowedPackages();
    method @NonNull public java.util.List<java.lang.String> getDeduplicationPackageOrder();
    method @NonNull public java.util.List<java.lang.String> getPreferredFeatures();
    method @NonNull public java.util.List<java.lang.String> getRequiredFeatures();
    method public boolean shouldPerformActiveScan();
    method public boolean shouldRemoveDuplicates();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.media.RouteDiscoveryPreference> CREATOR;
  }
@@ -23297,7 +23303,10 @@ package android.media {
    ctor public RouteDiscoveryPreference.Builder(@NonNull java.util.List<java.lang.String>, boolean);
    ctor public RouteDiscoveryPreference.Builder(@NonNull android.media.RouteDiscoveryPreference);
    method @NonNull public android.media.RouteDiscoveryPreference build();
    method @NonNull public android.media.RouteDiscoveryPreference.Builder setAllowedPackages(@NonNull java.util.List<java.lang.String>);
    method @NonNull public android.media.RouteDiscoveryPreference.Builder setDeduplicationPackageOrder(@Nullable java.util.List<java.lang.String>);
    method @NonNull public android.media.RouteDiscoveryPreference.Builder setPreferredFeatures(@NonNull java.util.List<java.lang.String>);
    method @NonNull public android.media.RouteDiscoveryPreference.Builder setRequiredFeatures(@NonNull java.util.List<java.lang.String>);
    method @NonNull public android.media.RouteDiscoveryPreference.Builder setShouldPerformActiveScan(boolean);
  }
+3 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.media;

import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2Info;
import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;

/**
@@ -27,7 +28,8 @@ oneway interface IMediaRouter2Manager {
    void notifySessionCreated(int requestId, in RoutingSessionInfo session);
    void notifySessionUpdated(in RoutingSessionInfo session);
    void notifySessionReleased(in RoutingSessionInfo session);
    void notifyPreferredFeaturesChanged(String packageName, in List<String> preferredFeatures);
    void notifyDiscoveryPreferenceChanged(String packageName,
            in RouteDiscoveryPreference discoveryPreference);
    void notifyRoutesAdded(in List<MediaRoute2Info> routes);
    void notifyRoutesRemoved(in List<MediaRoute2Info> routes);
    void notifyRoutesChanged(in List<MediaRoute2Info> routes);
+82 −3
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * Describes the properties of a route.
@@ -340,10 +341,12 @@ public final class MediaRoute2Info implements Parcelable {
    @ConnectionState
    final int mConnectionState;
    final String mClientPackageName;
    final String mPackageName;
    final int mVolumeHandling;
    final int mVolumeMax;
    final int mVolume;
    final String mAddress;
    final Set<String> mDeduplicationIds;
    final Bundle mExtras;
    final String mProviderId;

@@ -357,10 +360,12 @@ public final class MediaRoute2Info implements Parcelable {
        mDescription = builder.mDescription;
        mConnectionState = builder.mConnectionState;
        mClientPackageName = builder.mClientPackageName;
        mPackageName = builder.mPackageName;
        mVolumeHandling = builder.mVolumeHandling;
        mVolumeMax = builder.mVolumeMax;
        mVolume = builder.mVolume;
        mAddress = builder.mAddress;
        mDeduplicationIds = builder.mDeduplicationIds;
        mExtras = builder.mExtras;
        mProviderId = builder.mProviderId;
    }
@@ -375,10 +380,12 @@ public final class MediaRoute2Info implements Parcelable {
        mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
        mConnectionState = in.readInt();
        mClientPackageName = in.readString();
        mPackageName = in.readString();
        mVolumeHandling = in.readInt();
        mVolumeMax = in.readInt();
        mVolume = in.readInt();
        mAddress = in.readString();
        mDeduplicationIds = Set.of(in.readStringArray());
        mExtras = in.readBundle();
        mProviderId = in.readString();
    }
@@ -485,6 +492,17 @@ public final class MediaRoute2Info implements Parcelable {
        return mClientPackageName;
    }

    /**
     * Gets the package name of the provider that published the route.
     * <p>
     * It is set by the system service.
     * @hide
     */
    @Nullable
    public String getPackageName() {
        return mPackageName;
    }

    /**
     * Gets information about how volume is handled on the route.
     *
@@ -518,6 +536,18 @@ public final class MediaRoute2Info implements Parcelable {
        return mAddress;
    }

    /**
     * Gets the Deduplication ID of the route if available.
     * @see RouteDiscoveryPreference#shouldRemoveDuplicates()
     */
    @NonNull
    public Set<String> getDeduplicationIds() {
        return mDeduplicationIds;
    }

    /**
     * Gets an optional bundle with extra data.
     */
    @Nullable
    public Bundle getExtras() {
        return mExtras == null ? null : new Bundle(mExtras);
@@ -549,7 +579,7 @@ public final class MediaRoute2Info implements Parcelable {
     * Returns if the route has at least one of the specified route features.
     *
     * @param features the list of route features to consider
     * @return true if the route has at least one feature in the list
     * @return {@code true} if the route has at least one feature in the list
     * @hide
     */
    public boolean hasAnyFeatures(@NonNull Collection<String> features) {
@@ -562,6 +592,21 @@ public final class MediaRoute2Info implements Parcelable {
        return false;
    }

    /**
     * Returns if the route has all the specified route features.
     *
     * @hide
     */
    public boolean hasAllFeatures(@NonNull Collection<String> features) {
        Objects.requireNonNull(features, "features must not be null");
        for (String feature : features) {
            if (!getFeatures().contains(feature)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Returns true if the route info has all of the required field.
     * A route is valid if and only if it is obtained from
@@ -596,10 +641,12 @@ public final class MediaRoute2Info implements Parcelable {
                && Objects.equals(mDescription, other.mDescription)
                && (mConnectionState == other.mConnectionState)
                && Objects.equals(mClientPackageName, other.mClientPackageName)
                && Objects.equals(mPackageName, other.mPackageName)
                && (mVolumeHandling == other.mVolumeHandling)
                && (mVolumeMax == other.mVolumeMax)
                && (mVolume == other.mVolume)
                && Objects.equals(mAddress, other.mAddress)
                && Objects.equals(mDeduplicationIds, other.mDeduplicationIds)
                && Objects.equals(mProviderId, other.mProviderId);
    }

@@ -607,8 +654,8 @@ public final class MediaRoute2Info implements Parcelable {
    public int hashCode() {
        // Note: mExtras is not included.
        return Objects.hash(mId, mName, mFeatures, mType, mIsSystem, mIconUri, mDescription,
                mConnectionState, mClientPackageName, mVolumeHandling, mVolumeMax, mVolume,
                mAddress, mProviderId);
                mConnectionState, mClientPackageName, mPackageName, mVolumeHandling, mVolumeMax,
                mVolume, mAddress, mDeduplicationIds, mProviderId);
    }

    @Override
@@ -626,6 +673,7 @@ public final class MediaRoute2Info implements Parcelable {
                .append(", volumeHandling=").append(getVolumeHandling())
                .append(", volumeMax=").append(getVolumeMax())
                .append(", volume=").append(getVolume())
                .append(", deduplicationIds=").append(String.join(",", getDeduplicationIds()))
                .append(", providerId=").append(getProviderId())
                .append(" }");
        return result.toString();
@@ -647,10 +695,12 @@ public final class MediaRoute2Info implements Parcelable {
        TextUtils.writeToParcel(mDescription, dest, flags);
        dest.writeInt(mConnectionState);
        dest.writeString(mClientPackageName);
        dest.writeString(mPackageName);
        dest.writeInt(mVolumeHandling);
        dest.writeInt(mVolumeMax);
        dest.writeInt(mVolume);
        dest.writeString(mAddress);
        dest.writeStringArray(mDeduplicationIds.toArray(new String[mDeduplicationIds.size()]));
        dest.writeBundle(mExtras);
        dest.writeString(mProviderId);
    }
@@ -671,10 +721,12 @@ public final class MediaRoute2Info implements Parcelable {
        @ConnectionState
        int mConnectionState;
        String mClientPackageName;
        String mPackageName;
        int mVolumeHandling = PLAYBACK_VOLUME_FIXED;
        int mVolumeMax;
        int mVolume;
        String mAddress;
        Set<String> mDeduplicationIds;
        Bundle mExtras;
        String mProviderId;

@@ -698,6 +750,7 @@ public final class MediaRoute2Info implements Parcelable {
            mId = id;
            mName = name;
            mFeatures = new ArrayList<>();
            mDeduplicationIds = Set.of();
        }

        /**
@@ -733,10 +786,12 @@ public final class MediaRoute2Info implements Parcelable {
            mDescription = routeInfo.mDescription;
            mConnectionState = routeInfo.mConnectionState;
            mClientPackageName = routeInfo.mClientPackageName;
            mPackageName = routeInfo.mPackageName;
            mVolumeHandling = routeInfo.mVolumeHandling;
            mVolumeMax = routeInfo.mVolumeMax;
            mVolume = routeInfo.mVolume;
            mAddress = routeInfo.mAddress;
            mDeduplicationIds = Set.copyOf(routeInfo.mDeduplicationIds);
            if (routeInfo.mExtras != null) {
                mExtras = new Bundle(routeInfo.mExtras);
            }
@@ -859,6 +914,16 @@ public final class MediaRoute2Info implements Parcelable {
            return this;
        }

        /**
         * Sets the package name of the route.
         * @hide
         */
        @NonNull
        public Builder setPackageName(@NonNull String packageName) {
            mPackageName = packageName;
            return this;
        }

        /**
         * Sets the route's volume handling.
         */
@@ -896,6 +961,20 @@ public final class MediaRoute2Info implements Parcelable {
            return this;
        }

        /**
         * Sets the deduplication ID of the route.
         * Routes have the same ID could be removed even when
         * they are from different providers.
         * <p>
         * If it's {@code null}, the route will not be removed.
         * @see RouteDiscoveryPreference#shouldRemoveDuplicates()
         */
        @NonNull
        public Builder setDeduplicationIds(@NonNull Set<String> id) {
            mDeduplicationIds = Set.copyOf(id);
            return this;
        }

        /**
         * Sets a bundle of extras for the route.
         * <p>
+45 −6
Original line number Diff line number Diff line
@@ -34,15 +34,18 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;

import com.android.internal.annotations.GuardedBy;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
@@ -302,8 +305,7 @@ public final class MediaRouter2 {
        mSystemController = new SystemRoutingController(
                ensureClientPackageNameForSystemSession(
                        sManager.getSystemRoutingSession(clientPackageName)));
        mDiscoveryPreference = new RouteDiscoveryPreference.Builder(
                sManager.getPreferredFeatures(clientPackageName), true).build();
        mDiscoveryPreference = sManager.getDiscoveryPreference(clientPackageName);
        updateAllRoutesFromManager();

        // Only used by non-system MediaRouter2.
@@ -1060,11 +1062,48 @@ public final class MediaRouter2 {
                .build();
    }

    private List<MediaRoute2Info> getSortedRoutes(List<MediaRoute2Info> routes,
            RouteDiscoveryPreference preference) {
        if (!preference.shouldRemoveDuplicates()) {
            return routes;
        }
        Map<String, Integer> packagePriority = new ArrayMap<>();
        int count = preference.getDeduplicationPackageOrder().size();
        for (int i = 0; i < count; i++) {
            // the last package will have 1 as the priority
            packagePriority.put(preference.getDeduplicationPackageOrder().get(i), count - i);
        }
        ArrayList<MediaRoute2Info> sortedRoutes = new ArrayList<>(routes);
        // take the negative for descending order
        sortedRoutes.sort(Comparator.comparingInt(
                r -> -packagePriority.getOrDefault(r.getPackageName(), 0)));
        return sortedRoutes;
    }

    private List<MediaRoute2Info> filterRoutes(List<MediaRoute2Info> routes,
            RouteDiscoveryPreference discoveryRequest) {
        return routes.stream()
                .filter(route -> route.hasAnyFeatures(discoveryRequest.getPreferredFeatures()))
                .collect(Collectors.toList());
            RouteDiscoveryPreference discoveryPreference) {

        Set<String> deduplicationIdSet = new ArraySet<>();

        List<MediaRoute2Info> filteredRoutes = new ArrayList<>();
        for (MediaRoute2Info route : getSortedRoutes(routes, discoveryPreference)) {
            if (!route.hasAllFeatures(discoveryPreference.getRequiredFeatures())
                    || !route.hasAnyFeatures(discoveryPreference.getPreferredFeatures())) {
                continue;
            }
            if (!discoveryPreference.getAllowedPackages().isEmpty()
                    && !discoveryPreference.getAllowedPackages().contains(route.getPackageName())) {
                continue;
            }
            if (discoveryPreference.shouldRemoveDuplicates()) {
                if (Collections.disjoint(deduplicationIdSet, route.getDeduplicationIds())) {
                    continue;
                }
                deduplicationIdSet.addAll(route.getDeduplicationIds());
            }
            filteredRoutes.add(route);
        }
        return filteredRoutes;
    }

    private void updateAllRoutesFromManager() {
+89 −79
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@ import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;

import com.android.internal.annotations.GuardedBy;
@@ -36,15 +38,18 @@ import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
@@ -84,7 +89,8 @@ public final class MediaRouter2Manager {
    @GuardedBy("mRoutesLock")
    private final Map<String, MediaRoute2Info> mRoutes = new HashMap<>();
    @NonNull
    final ConcurrentMap<String, List<String>> mPreferredFeaturesMap = new ConcurrentHashMap<>();
    final ConcurrentMap<String, RouteDiscoveryPreference> mDiscoveryPreferenceMap =
            new ConcurrentHashMap<>();

    private final AtomicInteger mNextRequestId = new AtomicInteger(1);
    private final CopyOnWriteArrayList<TransferRequest> mTransferRequests =
@@ -247,25 +253,8 @@ public final class MediaRouter2Manager {
     */
    @NonNull
    public List<MediaRoute2Info> getAvailableRoutes(@NonNull RoutingSessionInfo sessionInfo) {
        Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");

        List<MediaRoute2Info> routes = new ArrayList<>();

        String packageName = sessionInfo.getClientPackageName();
        List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName);
        if (preferredFeatures == null) {
            preferredFeatures = Collections.emptyList();
        }
        synchronized (mRoutesLock) {
            for (MediaRoute2Info route : mRoutes.values()) {
                if (route.hasAnyFeatures(preferredFeatures)
                        || sessionInfo.getSelectedRoutes().contains(route.getId())
                        || sessionInfo.getTransferableRoutes().contains(route.getId())) {
                    routes.add(route);
                }
            }
        }
        return routes;
        return getFilteredRoutes(sessionInfo, /*includeSelectedRoutes=*/true,
                null);
    }

    /**
@@ -281,73 +270,82 @@ public final class MediaRouter2Manager {
     */
    @NonNull
    public List<MediaRoute2Info> getTransferableRoutes(@NonNull RoutingSessionInfo sessionInfo) {
        return getFilteredRoutes(sessionInfo, /*includeSelectedRoutes=*/false,
                (route) -> sessionInfo.isSystemSession() ^ route.isSystemRoute());
    }

    private List<MediaRoute2Info> getSortedRoutes(RouteDiscoveryPreference preference) {
        if (!preference.shouldRemoveDuplicates()) {
            synchronized (mRoutesLock) {
                return List.copyOf(mRoutes.values());
            }
        }
        Map<String, Integer> packagePriority = new ArrayMap<>();
        int count = preference.getDeduplicationPackageOrder().size();
        for (int i = 0; i < count; i++) {
            // the last package will have 1 as the priority
            packagePriority.put(preference.getDeduplicationPackageOrder().get(i), count - i);
        }
        ArrayList<MediaRoute2Info> routes;
        synchronized (mRoutesLock) {
            routes = new ArrayList<>(mRoutes.values());
        }
        // take the negative for descending order
        routes.sort(Comparator.comparingInt(
                r -> -packagePriority.getOrDefault(r.getPackageName(), 0)));
        return routes;
    }

    private List<MediaRoute2Info> getFilteredRoutes(@NonNull RoutingSessionInfo sessionInfo,
            boolean includeSelectedRoutes,
            @Nullable Predicate<MediaRoute2Info> additionalFilter) {
        Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");

        List<MediaRoute2Info> routes = new ArrayList<>();

        Set<String> deduplicationIdSet = new ArraySet<>();
        String packageName = sessionInfo.getClientPackageName();
        List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName);
        if (preferredFeatures == null) {
            preferredFeatures = Collections.emptyList();
        }
        synchronized (mRoutesLock) {
            for (MediaRoute2Info route : mRoutes.values()) {
                if (sessionInfo.getTransferableRoutes().contains(route.getId())) {
        RouteDiscoveryPreference discoveryPreference =
                mDiscoveryPreferenceMap.getOrDefault(packageName, RouteDiscoveryPreference.EMPTY);

        for (MediaRoute2Info route : getSortedRoutes(discoveryPreference)) {
            if (sessionInfo.getTransferableRoutes().contains(route.getId())
                    || (includeSelectedRoutes
                    && sessionInfo.getSelectedRoutes().contains(route.getId()))) {
                routes.add(route);
                continue;
            }
                // Add Phone -> Cast and Cast -> Phone
                if (route.hasAnyFeatures(preferredFeatures)
                        && (sessionInfo.isSystemSession() ^ route.isSystemRoute())) {
                    routes.add(route);
            if (!route.hasAllFeatures(discoveryPreference.getRequiredFeatures())
                    || !route.hasAnyFeatures(discoveryPreference.getPreferredFeatures())) {
                continue;
            }
            if (!discoveryPreference.getAllowedPackages().isEmpty()
                    && !discoveryPreference.getAllowedPackages()
                    .contains(route.getPackageName())) {
                continue;
            }
            if (additionalFilter != null && !additionalFilter.test(route)) {
                continue;
            }
            if (discoveryPreference.shouldRemoveDuplicates()) {
                if (Collections.disjoint(deduplicationIdSet, route.getDeduplicationIds())) {
                    continue;
                }
        return routes;
                deduplicationIdSet.addAll(route.getDeduplicationIds());
            }

    /**
     * Returns the preferred features of the specified package name.
     */
    @NonNull
    public List<String> getPreferredFeatures(@NonNull String packageName) {
        Objects.requireNonNull(packageName, "packageName must not be null");

        List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName);
        if (preferredFeatures == null) {
            preferredFeatures = Collections.emptyList();
            routes.add(route);
        }
        return preferredFeatures;
        return routes;
    }

    /**
     * Returns a list of routes which are related to the given package name in the given route list.
     * Returns the preferred features of the specified package name.
     */
    @NonNull
    public List<MediaRoute2Info> filterRoutesForPackage(@NonNull List<MediaRoute2Info> routes,
            @NonNull String packageName) {
        Objects.requireNonNull(routes, "routes must not be null");
    public RouteDiscoveryPreference getDiscoveryPreference(@NonNull String packageName) {
        Objects.requireNonNull(packageName, "packageName must not be null");

        List<RoutingSessionInfo> sessions = getRoutingSessions(packageName);
        RoutingSessionInfo sessionInfo = sessions.get(sessions.size() - 1);

        List<MediaRoute2Info> result = new ArrayList<>();
        List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName);
        if (preferredFeatures == null) {
            preferredFeatures = Collections.emptyList();
        }

        synchronized (mRoutesLock) {
            for (MediaRoute2Info route : routes) {
                if (route.hasAnyFeatures(preferredFeatures)
                        || sessionInfo.getSelectedRoutes().contains(route.getId())
                        || sessionInfo.getTransferableRoutes().contains(route.getId())) {
                    result.add(route);
                }
            }
        }
        return result;
        return mDiscoveryPreferenceMap.getOrDefault(packageName, RouteDiscoveryPreference.EMPTY);
    }

    /**
@@ -713,19 +711,19 @@ public final class MediaRouter2Manager {
        }
    }

    void updatePreferredFeatures(String packageName, List<String> preferredFeatures) {
        if (preferredFeatures == null) {
            mPreferredFeaturesMap.remove(packageName);
    void updateDiscoveryPreference(String packageName, RouteDiscoveryPreference preference) {
        if (preference == null) {
            mDiscoveryPreferenceMap.remove(packageName);
            return;
        }
        List<String> prevFeatures = mPreferredFeaturesMap.put(packageName, preferredFeatures);
        if ((prevFeatures == null && preferredFeatures.size() == 0)
                || Objects.equals(preferredFeatures, prevFeatures)) {
        RouteDiscoveryPreference prevPreference =
                mDiscoveryPreferenceMap.put(packageName, preference);
        if (Objects.equals(preference, prevPreference)) {
            return;
        }
        for (CallbackRecord record : mCallbackRecords) {
            record.mExecutor.execute(() -> record.mCallback
                    .onPreferredFeaturesChanged(packageName, preferredFeatures));
                    .onDiscoveryPreferenceChanged(packageName, preference));
        }
    }

@@ -1046,6 +1044,17 @@ public final class MediaRouter2Manager {
        default void onPreferredFeaturesChanged(@NonNull String packageName,
                @NonNull List<String> preferredFeatures) {}

        /**
         * Called when the preferred route features of an app is changed.
         *
         * @param packageName the package name of the application
         * @param discoveryPreference the new discovery preference set by the application.
         */
        default void onDiscoveryPreferenceChanged(@NonNull String packageName,
                @NonNull RouteDiscoveryPreference discoveryPreference) {
            onPreferredFeaturesChanged(packageName, discoveryPreference.getPreferredFeatures());
        }

        /**
         * Called when a previous request has failed.
         *
@@ -1125,9 +1134,10 @@ public final class MediaRouter2Manager {
        }

        @Override
        public void notifyPreferredFeaturesChanged(String packageName, List<String> features) {
            mHandler.sendMessage(obtainMessage(MediaRouter2Manager::updatePreferredFeatures,
                    MediaRouter2Manager.this, packageName, features));
        public void notifyDiscoveryPreferenceChanged(String packageName,
                RouteDiscoveryPreference discoveryPreference) {
            mHandler.sendMessage(obtainMessage(MediaRouter2Manager::updateDiscoveryPreference,
                    MediaRouter2Manager.this, packageName, discoveryPreference));
        }

        @Override
Loading