Merge "Add more options on RouteDiscoveryPreference"

This commit is contained in:
Oliver Woodman
2022-01-31 15:06:15 +00:00
committed by Android (Google) Code Review
7 changed files with 365 additions and 103 deletions

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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>

View File

@@ -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() {

View File

@@ -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,27 +270,70 @@ 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())) {
routes.add(route);
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;
}
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;
}
// Add Phone -> Cast and Cast -> Phone
if (route.hasAnyFeatures(preferredFeatures)
&& (sessionInfo.isSystemSession() ^ route.isSystemRoute())) {
routes.add(route);
}
deduplicationIdSet.addAll(route.getDeduplicationIds());
}
routes.add(route);
}
return routes;
}
@@ -310,44 +342,10 @@ public final class MediaRouter2Manager {
* Returns the preferred features of the specified package name.
*/
@NonNull
public List<String> getPreferredFeatures(@NonNull String packageName) {
public RouteDiscoveryPreference getDiscoveryPreference(@NonNull String packageName) {
Objects.requireNonNull(packageName, "packageName must not be null");
List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName);
if (preferredFeatures == null) {
preferredFeatures = Collections.emptyList();
}
return preferredFeatures;
}
/**
* Returns a list of routes which are related to the given package name in the given route list.
*/
@NonNull
public List<MediaRoute2Info> filterRoutesForPackage(@NonNull List<MediaRoute2Info> routes,
@NonNull String packageName) {
Objects.requireNonNull(routes, "routes must not be null");
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

View File

@@ -64,6 +64,10 @@ public final class RouteDiscoveryPreference implements Parcelable {
@NonNull
private final List<String> mPreferredFeatures;
private final List<String> mRequiredFeatures;
private final List<String> mPackagesOrder;
private final List<String> mAllowedPackages;
private final boolean mShouldPerformActiveScan;
@Nullable
private final Bundle mExtras;
@@ -78,12 +82,18 @@ public final class RouteDiscoveryPreference implements Parcelable {
RouteDiscoveryPreference(@NonNull Builder builder) {
mPreferredFeatures = builder.mPreferredFeatures;
mRequiredFeatures = builder.mRequiredFeatures;
mPackagesOrder = builder.mPackageOrder;
mAllowedPackages = builder.mAllowedPackages;
mShouldPerformActiveScan = builder.mActiveScan;
mExtras = builder.mExtras;
}
RouteDiscoveryPreference(@NonNull Parcel in) {
mPreferredFeatures = in.createStringArrayList();
mRequiredFeatures = in.createStringArrayList();
mPackagesOrder = in.createStringArrayList();
mAllowedPackages = in.createStringArrayList();
mShouldPerformActiveScan = in.readBoolean();
mExtras = in.readBundle();
}
@@ -96,12 +106,52 @@ public final class RouteDiscoveryPreference implements Parcelable {
* {@link MediaRoute2Info#FEATURE_LIVE_AUDIO}, {@link MediaRoute2Info#FEATURE_LIVE_VIDEO},
* or {@link MediaRoute2Info#FEATURE_REMOTE_PLAYBACK} or custom features defined by a provider.
* </p>
*
* @see #getRequiredFeatures()
*/
@NonNull
public List<String> getPreferredFeatures() {
return mPreferredFeatures;
}
/**
* Gets the required features of routes that media router would like to discover.
* <p>
* Routes that have all the required features will be discovered.
* This precedes {@link #getPreferredFeatures()}.
* They may include predefined features such as
* {@link MediaRoute2Info#FEATURE_LIVE_AUDIO}, {@link MediaRoute2Info#FEATURE_LIVE_VIDEO},
* or {@link MediaRoute2Info#FEATURE_REMOTE_PLAYBACK} or custom features defined by a provider.
*
* @see #getPreferredFeatures()
*/
@NonNull
public List<String> getRequiredFeatures() {
return mRequiredFeatures;
}
/**
* Gets the ordered list of package names used to remove duplicate routes.
* <p>
* When the app enables duplicate removal, all the routes that have the same deduplication ID
* except one from the provider whose package name appears first in the list will be removed.
*/
@NonNull
public List<String> getDeduplicationPackageOrder() {
return mPackagesOrder == null ? List.of() : mPackagesOrder;
}
/**
* Gets the list of allowed packages.
* <p>
* If it's not empty, it will only discover routes from the provider whose package name
* belongs to the list.
*/
@NonNull
public List<String> getAllowedPackages() {
return mAllowedPackages;
}
/**
* Gets whether active scanning should be performed.
* <p>
@@ -113,6 +163,16 @@ public final class RouteDiscoveryPreference implements Parcelable {
return mShouldPerformActiveScan;
}
/**
* Gets whether duplicate routes should be removed.
* <p>
* If it is {@code true}, only one of routes that have
* the same deduplication ID will be obtained.
*/
public boolean shouldRemoveDuplicates() {
return !mPackagesOrder.isEmpty();
}
/**
* @hide
*/
@@ -128,6 +188,9 @@ public final class RouteDiscoveryPreference implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeStringList(mPreferredFeatures);
dest.writeStringList(mRequiredFeatures);
dest.writeStringList(mPackagesOrder);
dest.writeStringList(mAllowedPackages);
dest.writeBoolean(mShouldPerformActiveScan);
dest.writeBundle(mExtras);
}
@@ -155,14 +218,17 @@ public final class RouteDiscoveryPreference implements Parcelable {
return false;
}
RouteDiscoveryPreference other = (RouteDiscoveryPreference) o;
//TODO: Make this order-free
return Objects.equals(mPreferredFeatures, other.mPreferredFeatures)
&& Objects.equals(mRequiredFeatures, other.mRequiredFeatures)
&& Objects.equals(mPackagesOrder, other.mPackagesOrder)
&& Objects.equals(mAllowedPackages, other.mAllowedPackages)
&& mShouldPerformActiveScan == other.mShouldPerformActiveScan;
}
@Override
public int hashCode() {
return Objects.hash(mPreferredFeatures, mShouldPerformActiveScan);
return Objects.hash(mPreferredFeatures, mRequiredFeatures, mPackagesOrder, mAllowedPackages,
mShouldPerformActiveScan);
}
/**
@@ -170,13 +236,21 @@ public final class RouteDiscoveryPreference implements Parcelable {
*/
public static final class Builder {
List<String> mPreferredFeatures;
List<String> mRequiredFeatures;
List<String> mPackageOrder;
List<String> mAllowedPackages;
boolean mActiveScan;
Bundle mExtras;
public Builder(@NonNull List<String> preferredFeatures, boolean activeScan) {
Objects.requireNonNull(preferredFeatures, "preferredFeatures must not be null");
mPreferredFeatures = preferredFeatures.stream().filter(str -> !TextUtils.isEmpty(str))
.collect(Collectors.toList());
mRequiredFeatures = List.of();
mPackageOrder = List.of();
mAllowedPackages = List.of();
mActiveScan = activeScan;
}
@@ -184,12 +258,15 @@ public final class RouteDiscoveryPreference implements Parcelable {
Objects.requireNonNull(preference, "preference must not be null");
mPreferredFeatures = preference.getPreferredFeatures();
mRequiredFeatures = preference.getRequiredFeatures();
mPackageOrder = preference.getDeduplicationPackageOrder();
mAllowedPackages = preference.getAllowedPackages();
mActiveScan = preference.shouldPerformActiveScan();
mExtras = preference.getExtras();
}
/**
* A constructor to combine all of the preferences into a single preference.
* A constructor to combine all the preferences into a single preference.
* It ignores extras of preferences.
*
* @hide
@@ -223,6 +300,30 @@ public final class RouteDiscoveryPreference implements Parcelable {
return this;
}
/**
* Sets the required route features to discover.
*/
@NonNull
public Builder setRequiredFeatures(@NonNull List<String> requiredFeatures) {
Objects.requireNonNull(requiredFeatures, "preferredFeatures must not be null");
mRequiredFeatures = requiredFeatures.stream().filter(str -> !TextUtils.isEmpty(str))
.collect(Collectors.toList());
return this;
}
/**
* Sets the list of package names of providers that media router would like to discover.
* <p>
* If it's non-empty, media router only discovers route from the provider in the list.
* The default value is empty, which discovers routes from all providers.
*/
@NonNull
public Builder setAllowedPackages(@NonNull List<String> allowedPackages) {
Objects.requireNonNull(allowedPackages, "allowedPackages must not be null");
mAllowedPackages = List.copyOf(allowedPackages);
return this;
}
/**
* Sets if active scanning should be performed.
* <p>
@@ -236,6 +337,27 @@ public final class RouteDiscoveryPreference implements Parcelable {
return this;
}
/**
* Sets the order of packages when removing duplicate routes.
* <p>
* Routes are removed based on its
* {@link MediaRoute2Info#getDeduplicationIds() deduplication ID}.
* If two routes have the same ID, even if they are from different providers,
* one of them is removed from the list.
* <p>
* Routes from the provider whose package name appears first in the given package order
* will remain.
* If unspecified, any route can be selected.
*
* @param packageOrder list of package names for choosing routes to be removed or
* {@code null} not to remove duplicate routes.
*/
@NonNull
public Builder setDeduplicationPackageOrder(@Nullable List<String> packageOrder) {
mPackageOrder = (packageOrder == null) ? null : List.copyOf(packageOrder);
return this;
}
/**
* Sets the extras of the route.
* @hide

View File

@@ -677,9 +677,9 @@ class MediaRouter2ServiceImpl {
UserRecord userRecord = routerRecord.mUserRecord;
userRecord.mRouterRecords.remove(routerRecord);
routerRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::notifyPreferredFeaturesChangedToManagers,
obtainMessage(UserHandler::notifyDiscoveryPreferenceChangedToManagers,
routerRecord.mUserRecord.mHandler,
routerRecord.mPackageName, /* preferredFeatures=*/ null));
routerRecord.mPackageName, null));
userRecord.mHandler.sendMessage(
obtainMessage(UserHandler::updateDiscoveryPreferenceOnHandler,
userRecord.mHandler));
@@ -694,10 +694,10 @@ class MediaRouter2ServiceImpl {
}
routerRecord.mDiscoveryPreference = discoveryRequest;
routerRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::notifyPreferredFeaturesChangedToManagers,
obtainMessage(UserHandler::notifyDiscoveryPreferenceChangedToManagers,
routerRecord.mUserRecord.mHandler,
routerRecord.mPackageName,
routerRecord.mDiscoveryPreference.getPreferredFeatures()));
routerRecord.mDiscoveryPreference));
routerRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::updateDiscoveryPreferenceOnHandler,
routerRecord.mUserRecord.mHandler));
@@ -921,7 +921,7 @@ class MediaRouter2ServiceImpl {
// TODO: UserRecord <-> routerRecord, why do they reference each other?
// How about removing mUserRecord from routerRecord?
routerRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::notifyPreferredFeaturesChangedToManager,
obtainMessage(UserHandler::notifyDiscoveryPreferenceChangedToManager,
routerRecord.mUserRecord.mHandler, routerRecord, manager));
}
@@ -2118,19 +2118,19 @@ class MediaRouter2ServiceImpl {
}
}
private void notifyPreferredFeaturesChangedToManager(@NonNull RouterRecord routerRecord,
private void notifyDiscoveryPreferenceChangedToManager(@NonNull RouterRecord routerRecord,
@NonNull IMediaRouter2Manager manager) {
try {
manager.notifyPreferredFeaturesChanged(routerRecord.mPackageName,
routerRecord.mDiscoveryPreference.getPreferredFeatures());
manager.notifyDiscoveryPreferenceChanged(routerRecord.mPackageName,
routerRecord.mDiscoveryPreference);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to notify preferred features changed."
+ " Manager probably died.", ex);
}
}
private void notifyPreferredFeaturesChangedToManagers(@NonNull String routerPackageName,
@Nullable List<String> preferredFeatures) {
private void notifyDiscoveryPreferenceChangedToManagers(@NonNull String routerPackageName,
@Nullable RouteDiscoveryPreference discoveryPreference) {
MediaRouter2ServiceImpl service = mServiceRef.get();
if (service == null) {
return;
@@ -2143,7 +2143,8 @@ class MediaRouter2ServiceImpl {
}
for (IMediaRouter2Manager manager : managers) {
try {
manager.notifyPreferredFeaturesChanged(routerPackageName, preferredFeatures);
manager.notifyDiscoveryPreferenceChanged(routerPackageName,
discoveryPreference);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to notify preferred features changed."
+ " Manager probably died.", ex);