Merge "Add missing tests for filtering routes"

This commit is contained in:
Kyunglyul Hyun
2022-02-09 10:57:14 +00:00
committed by Android (Google) Code Review
8 changed files with 103 additions and 91 deletions

View File

@@ -23351,7 +23351,6 @@ package android.media {
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);
@@ -23365,7 +23364,6 @@ package android.media {
method @NonNull public android.media.RouteDiscoveryPreference.Builder setAllowedPackages(@NonNull java.util.List<java.lang.String>);
method @NonNull public android.media.RouteDiscoveryPreference.Builder setDeduplicationPackageOrder(@NonNull 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

@@ -151,7 +151,7 @@ public final class MediaRoute2ProviderInfo implements Parcelable {
}
/**
* Sets the unique id of the provider info.
* Sets the package name and unique id of the provider info.
* <p>
* The unique id is automatically set by
* {@link com.android.server.media.MediaRouterService} and used to identify providers.
@@ -160,7 +160,7 @@ public final class MediaRoute2ProviderInfo implements Parcelable {
* @hide
*/
@NonNull
public Builder setUniqueId(@Nullable String uniqueId) {
public Builder setUniqueId(@Nullable String packageName, @Nullable String uniqueId) {
if (TextUtils.equals(mUniqueId, uniqueId)) {
return this;
}
@@ -169,6 +169,7 @@ public final class MediaRoute2ProviderInfo implements Parcelable {
final ArrayMap<String, MediaRoute2Info> newRoutes = new ArrayMap<>();
for (Map.Entry<String, MediaRoute2Info> entry : mRoutes.entrySet()) {
MediaRoute2Info routeWithProviderId = new MediaRoute2Info.Builder(entry.getValue())
.setPackageName(packageName)
.setProviderId(mUniqueId)
.build();
newRoutes.put(routeWithProviderId.getOriginalId(), routeWithProviderId);

View File

@@ -478,13 +478,8 @@ public final class MediaRouter2 {
if (mShouldUpdateRoutes) {
mShouldUpdateRoutes = false;
List<MediaRoute2Info> filteredRoutes = new ArrayList<>();
for (MediaRoute2Info route : mRoutes.values()) {
if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
filteredRoutes.add(route);
}
}
mFilteredRoutes = Collections.unmodifiableList(filteredRoutes);
mFilteredRoutes = Collections.unmodifiableList(
filterRoutes(List.copyOf(mRoutes.values()), mDiscoveryPreference));
}
}
return mFilteredRoutes;
@@ -1087,16 +1082,17 @@ public final class MediaRouter2 {
List<MediaRoute2Info> filteredRoutes = new ArrayList<>();
for (MediaRoute2Info route : getSortedRoutes(routes, discoveryPreference)) {
if (!route.hasAllFeatures(discoveryPreference.getRequiredFeatures())
|| !route.hasAnyFeatures(discoveryPreference.getPreferredFeatures())) {
if (!route.hasAnyFeatures(discoveryPreference.getPreferredFeatures())) {
continue;
}
if (!discoveryPreference.getAllowedPackages().isEmpty()
&& !discoveryPreference.getAllowedPackages().contains(route.getPackageName())) {
&& (route.getPackageName() == null
|| !discoveryPreference.getAllowedPackages()
.contains(route.getPackageName()))) {
continue;
}
if (discoveryPreference.shouldRemoveDuplicates()) {
if (Collections.disjoint(deduplicationIdSet, route.getDeduplicationIds())) {
if (!Collections.disjoint(deduplicationIdSet, route.getDeduplicationIds())) {
continue;
}
deduplicationIdSet.addAll(route.getDeduplicationIds());
@@ -2087,19 +2083,17 @@ public final class MediaRouter2 {
}
@Override
public void onPreferredFeaturesChanged(@NonNull String packageName,
@NonNull List<String> preferredFeatures) {
public void onDiscoveryPreferenceChanged(@NonNull String packageName,
@NonNull RouteDiscoveryPreference preference) {
if (!TextUtils.equals(mClientPackageName, packageName)) {
return;
}
synchronized (mLock) {
mDiscoveryPreference = new RouteDiscoveryPreference.Builder(
preferredFeatures, true).build();
mDiscoveryPreference = preference;
}
updateAllRoutesFromManager();
notifyPreferredFeaturesChanged(preferredFeatures);
notifyPreferredFeaturesChanged(preference.getPreferredFeatures());
}
@Override

View File

@@ -254,7 +254,7 @@ public final class MediaRouter2Manager {
@NonNull
public List<MediaRoute2Info> getAvailableRoutes(@NonNull RoutingSessionInfo sessionInfo) {
return getFilteredRoutes(sessionInfo, /*includeSelectedRoutes=*/true,
null);
/*additionalFilter=*/null);
}
/**
@@ -315,20 +315,20 @@ public final class MediaRouter2Manager {
routes.add(route);
continue;
}
if (!route.hasAllFeatures(discoveryPreference.getRequiredFeatures())
|| !route.hasAnyFeatures(discoveryPreference.getPreferredFeatures())) {
if (!route.hasAnyFeatures(discoveryPreference.getPreferredFeatures())) {
continue;
}
if (!discoveryPreference.getAllowedPackages().isEmpty()
&& !discoveryPreference.getAllowedPackages()
.contains(route.getPackageName())) {
&& (route.getPackageName() == null
|| !discoveryPreference.getAllowedPackages()
.contains(route.getPackageName()))) {
continue;
}
if (additionalFilter != null && !additionalFilter.test(route)) {
continue;
}
if (discoveryPreference.shouldRemoveDuplicates()) {
if (Collections.disjoint(deduplicationIdSet, route.getDeduplicationIds())) {
if (!Collections.disjoint(deduplicationIdSet, route.getDeduplicationIds())) {
continue;
}
deduplicationIdSet.addAll(route.getDeduplicationIds());

View File

@@ -24,7 +24,6 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
@@ -65,8 +64,6 @@ public final class RouteDiscoveryPreference implements Parcelable {
@NonNull
private final List<String> mPreferredFeatures;
@NonNull
private final List<String> mRequiredFeatures;
@NonNull
private final List<String> mPackageOrder;
@NonNull
private final List<String> mAllowedPackages;
@@ -85,7 +82,6 @@ public final class RouteDiscoveryPreference implements Parcelable {
RouteDiscoveryPreference(@NonNull Builder builder) {
mPreferredFeatures = builder.mPreferredFeatures;
mRequiredFeatures = builder.mRequiredFeatures;
mPackageOrder = builder.mPackageOrder;
mAllowedPackages = builder.mAllowedPackages;
mShouldPerformActiveScan = builder.mActiveScan;
@@ -94,7 +90,6 @@ public final class RouteDiscoveryPreference implements Parcelable {
RouteDiscoveryPreference(@NonNull Parcel in) {
mPreferredFeatures = in.createStringArrayList();
mRequiredFeatures = in.createStringArrayList();
mPackageOrder = in.createStringArrayList();
mAllowedPackages = in.createStringArrayList();
mShouldPerformActiveScan = in.readBoolean();
@@ -109,29 +104,12 @@ 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.
* 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>
@@ -193,7 +171,6 @@ public final class RouteDiscoveryPreference implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeStringList(mPreferredFeatures);
dest.writeStringList(mRequiredFeatures);
dest.writeStringList(mPackageOrder);
dest.writeStringList(mAllowedPackages);
dest.writeBoolean(mShouldPerformActiveScan);
@@ -224,7 +201,6 @@ public final class RouteDiscoveryPreference implements Parcelable {
}
RouteDiscoveryPreference other = (RouteDiscoveryPreference) o;
return Objects.equals(mPreferredFeatures, other.mPreferredFeatures)
&& Objects.equals(mRequiredFeatures, other.mRequiredFeatures)
&& Objects.equals(mPackageOrder, other.mPackageOrder)
&& Objects.equals(mAllowedPackages, other.mAllowedPackages)
&& mShouldPerformActiveScan == other.mShouldPerformActiveScan;
@@ -232,7 +208,7 @@ public final class RouteDiscoveryPreference implements Parcelable {
@Override
public int hashCode() {
return Objects.hash(mPreferredFeatures, mRequiredFeatures, mPackageOrder, mAllowedPackages,
return Objects.hash(mPreferredFeatures, mPackageOrder, mAllowedPackages,
mShouldPerformActiveScan);
}
@@ -241,7 +217,6 @@ public final class RouteDiscoveryPreference implements Parcelable {
*/
public static final class Builder {
List<String> mPreferredFeatures;
List<String> mRequiredFeatures;
List<String> mPackageOrder;
List<String> mAllowedPackages;
@@ -253,7 +228,6 @@ public final class RouteDiscoveryPreference implements Parcelable {
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;
@@ -263,7 +237,6 @@ 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();
@@ -271,14 +244,8 @@ public final class RouteDiscoveryPreference implements Parcelable {
}
/**
* A constructor to combine multiple preferences into a single preference. The combined
* preference will discover a superset of the union of the routes discoverable by each of
* the individual preferences.
* <p>
* When routes need to be discovered for multiple preferences, the combined preference can
* be used to query route providers once and obtain all routes of interest. The obtained
* routes can then be filtered for each of the individual preferences. This is typically
* more efficient than querying route providers with each of the individual preferences.
* A constructor to combine multiple preferences into a single preference.
* It ignores extras of preferences.
*
* @hide
*/
@@ -286,15 +253,21 @@ public final class RouteDiscoveryPreference implements Parcelable {
Objects.requireNonNull(preferences, "preferences must not be null");
Set<String> preferredFeatures = new HashSet<>();
Set<String> allowedPackages = new HashSet<>();
mPackageOrder = List.of();
boolean activeScan = false;
for (RouteDiscoveryPreference preference : preferences) {
preferredFeatures.addAll(preference.mPreferredFeatures);
allowedPackages.addAll(preference.mAllowedPackages);
activeScan |= preference.mShouldPerformActiveScan;
// Choose one of either
if (mPackageOrder.isEmpty() && !preference.mPackageOrder.isEmpty()) {
mPackageOrder = List.copyOf(preference.mPackageOrder);
}
}
mPreferredFeatures = new ArrayList<>(preferredFeatures);
mRequiredFeatures = List.of();
mPackageOrder = List.of();
mAllowedPackages = List.of();
mPreferredFeatures = List.copyOf(preferredFeatures);
mAllowedPackages = List.copyOf(allowedPackages);
mActiveScan = activeScan;
}
@@ -315,17 +288,6 @@ 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>

View File

@@ -71,6 +71,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -276,6 +277,42 @@ public class MediaRouter2ManagerTest {
assertNotNull(routes.get(ROUTE_ID_SPECIAL_FEATURE));
}
@Test
public void testNoAllowedPackages_returnsZeroRoutes() throws Exception {
RouteDiscoveryPreference preference =
new RouteDiscoveryPreference.Builder(FEATURES_ALL, true)
.setAllowedPackages(List.of("random package name"))
.build();
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(preference);
int remoteRouteCount = 0;
for (MediaRoute2Info route : routes.values()) {
if (!route.isSystemRoute()) {
remoteRouteCount++;
}
}
assertEquals(0, remoteRouteCount);
}
@Test
public void testAllowedPackages() throws Exception {
RouteDiscoveryPreference preference =
new RouteDiscoveryPreference.Builder(FEATURES_ALL, true)
.setAllowedPackages(List.of("com.android.mediaroutertest"))
.build();
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(preference);
int remoteRouteCount = 0;
for (MediaRoute2Info route : routes.values()) {
if (!route.isSystemRoute()) {
remoteRouteCount++;
}
}
assertTrue(remoteRouteCount > 0);
}
/**
* Tests if MR2.SessionCallback.onSessionCreated is called
* when a route is selected from MR2Manager.
@@ -818,8 +855,14 @@ public class MediaRouter2ManagerTest {
Map<String, MediaRoute2Info> waitAndGetRoutesWithManager(List<String> routeFeatures)
throws Exception {
return waitAndGetRoutesWithManager(
new RouteDiscoveryPreference.Builder(routeFeatures, true).build());
}
Map<String, MediaRoute2Info> waitAndGetRoutesWithManager(RouteDiscoveryPreference preference)
throws Exception {
CountDownLatch addedLatch = new CountDownLatch(1);
CountDownLatch featuresLatch = new CountDownLatch(1);
CountDownLatch preferenceLatch = new CountDownLatch(1);
// A dummy callback is required to send route feature info.
RouteCallback routeCallback = new RouteCallback() {};
@@ -828,7 +871,8 @@ public class MediaRouter2ManagerTest {
public void onRoutesAdded(List<MediaRoute2Info> routes) {
for (MediaRoute2Info route : routes) {
if (!route.isSystemRoute()
&& hasMatchingFeature(route.getFeatures(), routeFeatures)) {
&& hasMatchingFeature(route.getFeatures(), preference
.getPreferredFeatures())) {
addedLatch.countDown();
break;
}
@@ -836,20 +880,19 @@ public class MediaRouter2ManagerTest {
}
@Override
public void onPreferredFeaturesChanged(String packageName,
List<String> preferredFeatures) {
public void onDiscoveryPreferenceChanged(String packageName,
RouteDiscoveryPreference discoveryPreference) {
if (TextUtils.equals(mPackageName, packageName)
&& preferredFeatures.size() == routeFeatures.size()
&& preferredFeatures.containsAll(routeFeatures)) {
featuresLatch.countDown();
&& Objects.equals(preference, discoveryPreference)) {
preferenceLatch.countDown();
}
}
};
mManager.registerCallback(mExecutor, managerCallback);
mRouter2.registerRouteCallback(mExecutor, routeCallback,
new RouteDiscoveryPreference.Builder(routeFeatures, true).build());
mRouter2.registerRouteCallback(mExecutor, routeCallback, preference);
try {
featuresLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS);
preferenceLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS);
if (mManager.getAvailableRoutes(mPackageName).isEmpty()) {
addedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS);
}

View File

@@ -86,7 +86,7 @@ abstract class MediaRoute2Provider {
mProviderInfo = null;
} else {
mProviderInfo = new MediaRoute2ProviderInfo.Builder(providerInfo)
.setUniqueId(mUniqueId)
.setUniqueId(mComponentName.getPackageName(), mUniqueId)
.setSystemRouteProvider(mIsSystemRouteProvider)
.build();
}

View File

@@ -64,9 +64,11 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
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.atomic.AtomicInteger;
import java.util.stream.Collectors;
@@ -2200,9 +2202,21 @@ class MediaRouter2ServiceImpl {
}
}
// Build a composite RouteDiscoveryPreference that matches all of the routes
// that match one or more of the individual discovery preferences. It may also
// match additional routes. The composite RouteDiscoveryPreference can be used
// to query route providers once to obtain all of the routes of interest, which
// can be subsequently filtered for the individual discovery preferences.
Set<String> preferredFeatures = new HashSet<>();
boolean activeScan = false;
for (RouteDiscoveryPreference preference : discoveryPreferences) {
preferredFeatures.addAll(preference.getPreferredFeatures());
activeScan |= preference.shouldPerformActiveScan();
}
RouteDiscoveryPreference newPreference = new RouteDiscoveryPreference.Builder(
List.copyOf(preferredFeatures), activeScan).build();
synchronized (service.mLock) {
RouteDiscoveryPreference newPreference =
new RouteDiscoveryPreference.Builder(discoveryPreferences).build();
if (newPreference.equals(mUserRecord.mCompositeDiscoveryPreference)) {
return;
}