• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.media;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.SystemApi;
22 import android.os.Bundle;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 import android.text.TextUtils;
26 
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.HashSet;
30 import java.util.List;
31 import java.util.Objects;
32 import java.util.Set;
33 import java.util.stream.Collectors;
34 
35 /**
36  * A media route discovery preference describing the features of routes that media router
37  * would like to discover and whether to perform active scanning.
38  * <p>
39  * When {@link MediaRouter2} instances set discovery preferences by calling
40  * {@link MediaRouter2#registerRouteCallback}, they are merged into a single discovery preference
41  * and it is delivered to call {@link MediaRoute2ProviderService#onDiscoveryPreferenceChanged}.
42  * </p><p>
43  * According to the given discovery preference, {@link MediaRoute2ProviderService} discovers
44  * routes and publishes them.
45  * </p>
46  *
47  * @see MediaRouter2#registerRouteCallback
48  */
49 public final class RouteDiscoveryPreference implements Parcelable {
50     @NonNull
51     public static final Creator<RouteDiscoveryPreference> CREATOR =
52             new Creator<RouteDiscoveryPreference>() {
53                 @Override
54                 public RouteDiscoveryPreference createFromParcel(Parcel in) {
55                     return new RouteDiscoveryPreference(in);
56                 }
57 
58                 @Override
59                 public RouteDiscoveryPreference[] newArray(int size) {
60                     return new RouteDiscoveryPreference[size];
61                 }
62             };
63 
64     @NonNull
65     private final List<String> mPreferredFeatures;
66     @NonNull
67     private final List<String> mPackageOrder;
68     @NonNull
69     private final List<String> mAllowedPackages;
70 
71     private final boolean mShouldPerformActiveScan;
72     @Nullable
73     private final Bundle mExtras;
74 
75     /**
76      * An empty discovery preference.
77      * @hide
78      */
79     @SystemApi
80     public static final RouteDiscoveryPreference EMPTY =
81             new Builder(Collections.emptyList(), false).build();
82 
RouteDiscoveryPreference(@onNull Builder builder)83     RouteDiscoveryPreference(@NonNull Builder builder) {
84         mPreferredFeatures = builder.mPreferredFeatures;
85         mPackageOrder = builder.mPackageOrder;
86         mAllowedPackages = builder.mAllowedPackages;
87         mShouldPerformActiveScan = builder.mActiveScan;
88         mExtras = builder.mExtras;
89     }
90 
RouteDiscoveryPreference(@onNull Parcel in)91     RouteDiscoveryPreference(@NonNull Parcel in) {
92         mPreferredFeatures = in.createStringArrayList();
93         mPackageOrder = in.createStringArrayList();
94         mAllowedPackages = in.createStringArrayList();
95         mShouldPerformActiveScan = in.readBoolean();
96         mExtras = in.readBundle();
97     }
98 
99     /**
100      * Gets the features of routes that media router would like to discover.
101      * <p>
102      * Routes that have at least one of the features will be discovered.
103      * They may include predefined features such as
104      * {@link MediaRoute2Info#FEATURE_LIVE_AUDIO}, {@link MediaRoute2Info#FEATURE_LIVE_VIDEO},
105      * or {@link MediaRoute2Info#FEATURE_REMOTE_PLAYBACK} or custom features defined by a provider.
106      * </p>
107      */
108     @NonNull
getPreferredFeatures()109     public List<String> getPreferredFeatures() {
110         return mPreferredFeatures;
111     }
112 
113     /**
114      * Gets the ordered list of package names used to remove duplicate routes.
115      * <p>
116      * Duplicate route removal is enabled if the returned list is non-empty. Routes are deduplicated
117      * based on their {@link MediaRoute2Info#getDeduplicationIds() deduplication IDs}. If two routes
118      * have a deduplication ID in common, only the route from the provider whose package name is
119      * first in the provided list will remain.
120      *
121      * @see #shouldRemoveDuplicates()
122      * @hide
123      */
124     @NonNull
getDeduplicationPackageOrder()125     public List<String> getDeduplicationPackageOrder() {
126         return mPackageOrder;
127     }
128 
129     /**
130      * Gets the list of allowed packages.
131      * <p>
132      * If it's not empty, it will only discover routes from the provider whose package name
133      * belongs to the list.
134      * @hide
135      */
136     @NonNull
getAllowedPackages()137     public List<String> getAllowedPackages() {
138         return mAllowedPackages;
139     }
140 
141     /**
142      * Gets whether active scanning should be performed.
143      * <p>
144      * If any of discovery preferences sets this as {@code true}, active scanning will
145      * be performed regardless of other discovery preferences.
146      * </p>
147      */
shouldPerformActiveScan()148     public boolean shouldPerformActiveScan() {
149         return mShouldPerformActiveScan;
150     }
151 
152     /**
153      * Gets whether duplicate routes removal is enabled.
154      *
155      * @see #getDeduplicationPackageOrder()
156      * @hide
157      */
shouldRemoveDuplicates()158     public boolean shouldRemoveDuplicates() {
159         return !mPackageOrder.isEmpty();
160     }
161 
162     /**
163      * @hide
164      */
getExtras()165     public Bundle getExtras() {
166         return mExtras;
167     }
168 
169     @Override
describeContents()170     public int describeContents() {
171         return 0;
172     }
173 
174     @Override
writeToParcel(@onNull Parcel dest, int flags)175     public void writeToParcel(@NonNull Parcel dest, int flags) {
176         dest.writeStringList(mPreferredFeatures);
177         dest.writeStringList(mPackageOrder);
178         dest.writeStringList(mAllowedPackages);
179         dest.writeBoolean(mShouldPerformActiveScan);
180         dest.writeBundle(mExtras);
181     }
182 
183     @Override
toString()184     public String toString() {
185         StringBuilder result = new StringBuilder()
186                 .append("RouteDiscoveryRequest{ ")
187                 .append("preferredFeatures={")
188                 .append(String.join(", ", mPreferredFeatures))
189                 .append("}")
190                 .append(", activeScan=")
191                 .append(mShouldPerformActiveScan)
192                 .append(" }");
193 
194         return result.toString();
195     }
196 
197     @Override
equals(Object o)198     public boolean equals(Object o) {
199         if (this == o) {
200             return true;
201         }
202         if (!(o instanceof RouteDiscoveryPreference)) {
203             return false;
204         }
205         RouteDiscoveryPreference other = (RouteDiscoveryPreference) o;
206         return Objects.equals(mPreferredFeatures, other.mPreferredFeatures)
207                 && Objects.equals(mPackageOrder, other.mPackageOrder)
208                 && Objects.equals(mAllowedPackages, other.mAllowedPackages)
209                 && mShouldPerformActiveScan == other.mShouldPerformActiveScan;
210     }
211 
212     @Override
hashCode()213     public int hashCode() {
214         return Objects.hash(mPreferredFeatures, mPackageOrder, mAllowedPackages,
215                 mShouldPerformActiveScan);
216     }
217 
218     /**
219      * Builder for {@link RouteDiscoveryPreference}.
220      */
221     public static final class Builder {
222         List<String> mPreferredFeatures;
223         List<String> mPackageOrder;
224         List<String> mAllowedPackages;
225 
226         boolean mActiveScan;
227 
228         Bundle mExtras;
229 
Builder(@onNull List<String> preferredFeatures, boolean activeScan)230         public Builder(@NonNull List<String> preferredFeatures, boolean activeScan) {
231             Objects.requireNonNull(preferredFeatures, "preferredFeatures must not be null");
232             mPreferredFeatures = preferredFeatures.stream().filter(str -> !TextUtils.isEmpty(str))
233                     .collect(Collectors.toList());
234             mPackageOrder = List.of();
235             mAllowedPackages = List.of();
236             mActiveScan = activeScan;
237         }
238 
Builder(@onNull RouteDiscoveryPreference preference)239         public Builder(@NonNull RouteDiscoveryPreference preference) {
240             Objects.requireNonNull(preference, "preference must not be null");
241 
242             mPreferredFeatures = preference.getPreferredFeatures();
243             mPackageOrder = preference.getDeduplicationPackageOrder();
244             mAllowedPackages = preference.getAllowedPackages();
245             mActiveScan = preference.shouldPerformActiveScan();
246             mExtras = preference.getExtras();
247         }
248 
249         /**
250          * A constructor to combine multiple preferences into a single preference.
251          * It ignores extras of preferences.
252          *
253          * @hide
254          */
Builder(@onNull Collection<RouteDiscoveryPreference> preferences)255         public Builder(@NonNull Collection<RouteDiscoveryPreference> preferences) {
256             Objects.requireNonNull(preferences, "preferences must not be null");
257 
258             Set<String> preferredFeatures = new HashSet<>();
259             Set<String> allowedPackages = new HashSet<>();
260             mPackageOrder = List.of();
261             boolean activeScan = false;
262             for (RouteDiscoveryPreference preference : preferences) {
263                 preferredFeatures.addAll(preference.mPreferredFeatures);
264 
265                 allowedPackages.addAll(preference.mAllowedPackages);
266                 activeScan |= preference.mShouldPerformActiveScan;
267                 // Choose one of either
268                 if (mPackageOrder.isEmpty() && !preference.mPackageOrder.isEmpty()) {
269                     mPackageOrder = List.copyOf(preference.mPackageOrder);
270                 }
271             }
272             mPreferredFeatures = List.copyOf(preferredFeatures);
273             mAllowedPackages = List.copyOf(allowedPackages);
274             mActiveScan = activeScan;
275         }
276 
277         /**
278          * Sets preferred route features to discover.
279          * @param preferredFeatures features of routes that media router would like to discover.
280          *                          May include predefined features
281          *                          such as {@link MediaRoute2Info#FEATURE_LIVE_AUDIO},
282          *                          {@link MediaRoute2Info#FEATURE_LIVE_VIDEO},
283          *                          or {@link MediaRoute2Info#FEATURE_REMOTE_PLAYBACK}
284          *                          or custom features defined by a provider.
285          */
286         @NonNull
setPreferredFeatures(@onNull List<String> preferredFeatures)287         public Builder setPreferredFeatures(@NonNull List<String> preferredFeatures) {
288             Objects.requireNonNull(preferredFeatures, "preferredFeatures must not be null");
289             mPreferredFeatures = preferredFeatures.stream().filter(str -> !TextUtils.isEmpty(str))
290                     .collect(Collectors.toList());
291             return this;
292         }
293 
294         /**
295          * Sets the list of package names of providers that media router would like to discover.
296          * <p>
297          * If it's non-empty, media router only discovers route from the provider in the list.
298          * The default value is empty, which discovers routes from all providers.
299          * @hide
300          */
301         @NonNull
setAllowedPackages(@onNull List<String> allowedPackages)302         public Builder setAllowedPackages(@NonNull List<String> allowedPackages) {
303             Objects.requireNonNull(allowedPackages, "allowedPackages must not be null");
304             mAllowedPackages = List.copyOf(allowedPackages);
305             return this;
306         }
307 
308         /**
309          * Sets if active scanning should be performed.
310          * <p>
311          * Since active scanning uses more system resources, set this as {@code true} only
312          * when it's necessary.
313          * </p>
314          */
315         @NonNull
setShouldPerformActiveScan(boolean activeScan)316         public Builder setShouldPerformActiveScan(boolean activeScan) {
317             mActiveScan = activeScan;
318             return this;
319         }
320 
321         /**
322          * Sets the order of packages to use when removing duplicate routes.
323          * <p>
324          * Routes are deduplicated based on their
325          * {@link MediaRoute2Info#getDeduplicationIds() deduplication IDs}.
326          * If two routes have a deduplication ID in common, only the route from the provider whose
327          * package name is first in the provided list will remain.
328          *
329          * @param packageOrder ordered list of package names used to remove duplicate routes, or an
330          *                     empty list if deduplication should not be enabled.
331          * @hide
332          */
333         @NonNull
setDeduplicationPackageOrder(@onNull List<String> packageOrder)334         public Builder setDeduplicationPackageOrder(@NonNull List<String> packageOrder) {
335             Objects.requireNonNull(packageOrder, "packageOrder must not be null");
336             mPackageOrder = List.copyOf(packageOrder);
337             return this;
338         }
339 
340         /**
341          * Sets the extras of the route.
342          * @hide
343          */
344         @NonNull
setExtras(@ullable Bundle extras)345         public Builder setExtras(@Nullable Bundle extras) {
346             mExtras = extras;
347             return this;
348         }
349 
350         /**
351          * Builds the {@link RouteDiscoveryPreference}.
352          */
353         @NonNull
build()354         public RouteDiscoveryPreference build() {
355             return new RouteDiscoveryPreference(this);
356         }
357     }
358 }
359