• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 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 static android.media.MediaRouter2Utils.toUniqueId;
20 
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.TestApi;
25 import android.net.Uri;
26 import android.os.Bundle;
27 import android.os.Parcel;
28 import android.os.Parcelable;
29 import android.text.TextUtils;
30 
31 import java.lang.annotation.Retention;
32 import java.lang.annotation.RetentionPolicy;
33 import java.util.ArrayList;
34 import java.util.Collection;
35 import java.util.List;
36 import java.util.Objects;
37 import java.util.Set;
38 
39 /**
40  * Describes the properties of a route.
41  */
42 public final class MediaRoute2Info implements Parcelable {
43     @NonNull
44     public static final Creator<MediaRoute2Info> CREATOR = new Creator<MediaRoute2Info>() {
45         @Override
46         public MediaRoute2Info createFromParcel(Parcel in) {
47             return new MediaRoute2Info(in);
48         }
49 
50         @Override
51         public MediaRoute2Info[] newArray(int size) {
52             return new MediaRoute2Info[size];
53         }
54     };
55 
56     /** @hide */
57     @IntDef({CONNECTION_STATE_DISCONNECTED, CONNECTION_STATE_CONNECTING,
58             CONNECTION_STATE_CONNECTED})
59     @Retention(RetentionPolicy.SOURCE)
60     public @interface ConnectionState {}
61 
62     /**
63      * The default connection state indicating the route is disconnected.
64      *
65      * @see #getConnectionState
66      */
67     public static final int CONNECTION_STATE_DISCONNECTED = 0;
68 
69     /**
70      * A connection state indicating the route is in the process of connecting and is not yet
71      * ready for use.
72      *
73      * @see #getConnectionState
74      */
75     public static final int CONNECTION_STATE_CONNECTING = 1;
76 
77     /**
78      * A connection state indicating the route is connected.
79      *
80      * @see #getConnectionState
81      */
82     public static final int CONNECTION_STATE_CONNECTED = 2;
83 
84     /** @hide */
85     @IntDef({PLAYBACK_VOLUME_FIXED, PLAYBACK_VOLUME_VARIABLE})
86     @Retention(RetentionPolicy.SOURCE)
87     public @interface PlaybackVolume {}
88 
89     /**
90      * Playback information indicating the playback volume is fixed, i&#46;e&#46; it cannot be
91      * controlled from this object. An example of fixed playback volume is a remote player,
92      * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
93      * than attenuate at the source.
94      *
95      * @see #getVolumeHandling()
96      */
97     public static final int PLAYBACK_VOLUME_FIXED = 0;
98     /**
99      * Playback information indicating the playback volume is variable and can be controlled
100      * from this object.
101      *
102      * @see #getVolumeHandling()
103      */
104     public static final int PLAYBACK_VOLUME_VARIABLE = 1;
105 
106     /** @hide */
107     @IntDef({
108             TYPE_UNKNOWN, TYPE_BUILTIN_SPEAKER, TYPE_WIRED_HEADSET,
109             TYPE_WIRED_HEADPHONES, TYPE_BLUETOOTH_A2DP, TYPE_HDMI, TYPE_USB_DEVICE,
110             TYPE_USB_ACCESSORY, TYPE_DOCK, TYPE_USB_HEADSET, TYPE_HEARING_AID, TYPE_BLE_HEADSET,
111             TYPE_REMOTE_TV, TYPE_REMOTE_SPEAKER, TYPE_GROUP})
112     @Retention(RetentionPolicy.SOURCE)
113     public @interface Type {}
114 
115     /**
116      * The default route type indicating the type is unknown.
117      *
118      * @see #getType
119      * @hide
120      */
121     public static final int TYPE_UNKNOWN = 0;
122 
123     /**
124      * A route type describing the speaker system (i.e. a mono speaker or stereo speakers) built
125      * in a device.
126      *
127      * @see #getType
128      * @hide
129      */
130     public static final int TYPE_BUILTIN_SPEAKER = AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
131 
132     /**
133      * A route type describing a headset, which is the combination of a headphones and microphone.
134      *
135      * @see #getType
136      * @hide
137      */
138     public static final int TYPE_WIRED_HEADSET = AudioDeviceInfo.TYPE_WIRED_HEADSET;
139 
140     /**
141      * A route type describing a pair of wired headphones.
142      *
143      * @see #getType
144      * @hide
145      */
146     public static final int TYPE_WIRED_HEADPHONES = AudioDeviceInfo.TYPE_WIRED_HEADPHONES;
147 
148     /**
149      * A route type indicating the presentation of the media is happening
150      * on a bluetooth device such as a bluetooth speaker.
151      *
152      * @see #getType
153      * @hide
154      */
155     public static final int TYPE_BLUETOOTH_A2DP = AudioDeviceInfo.TYPE_BLUETOOTH_A2DP;
156 
157     /**
158      * A route type describing an HDMI connection.
159      *
160      * @see #getType
161      * @hide
162      */
163     public static final int TYPE_HDMI = AudioDeviceInfo.TYPE_HDMI;
164 
165     /**
166      * A route type describing a USB audio device.
167      *
168      * @see #getType
169      * @hide
170      */
171     public static final int TYPE_USB_DEVICE = AudioDeviceInfo.TYPE_USB_DEVICE;
172 
173     /**
174      * A route type describing a USB audio device in accessory mode.
175      *
176      * @see #getType
177      * @hide
178      */
179     public static final int TYPE_USB_ACCESSORY = AudioDeviceInfo.TYPE_USB_ACCESSORY;
180 
181     /**
182      * A route type describing the audio device associated with a dock.
183      *
184      * @see #getType
185      * @hide
186      */
187     public static final int TYPE_DOCK = AudioDeviceInfo.TYPE_DOCK;
188 
189     /**
190      * A device type describing a USB audio headset.
191      *
192      * @see #getType
193      * @hide
194      */
195     public static final int TYPE_USB_HEADSET = AudioDeviceInfo.TYPE_USB_HEADSET;
196 
197     /**
198      * A route type describing a Hearing Aid.
199      *
200      * @see #getType
201      * @hide
202      */
203     public static final int TYPE_HEARING_AID = AudioDeviceInfo.TYPE_HEARING_AID;
204 
205     /**
206      * A route type describing a BLE HEADSET.
207      *
208      * @see #getType
209      * @hide
210      */
211     public static final int TYPE_BLE_HEADSET = AudioDeviceInfo.TYPE_BLE_HEADSET;
212 
213     /**
214      * A route type indicating the presentation of the media is happening on a TV.
215      *
216      * @see #getType
217      * @hide
218      */
219     public static final int TYPE_REMOTE_TV = 1001;
220 
221     /**
222      * A route type indicating the presentation of the media is happening on a speaker.
223      *
224      * @see #getType
225      * @hide
226      */
227     public static final int TYPE_REMOTE_SPEAKER = 1002;
228 
229     /**
230      * A route type indicating the presentation of the media is happening on multiple devices.
231      *
232      * @see #getType
233      * @hide
234      */
235     public static final int TYPE_GROUP = 2000;
236 
237     /**
238      * Route feature: Live audio.
239      * <p>
240      * A route that supports live audio routing will allow the media audio stream
241      * to be sent to supported destinations.  This can include internal speakers or
242      * audio jacks on the device itself, A2DP devices, and more.
243      * </p><p>
244      * When a live audio route is selected, audio routing is transparent to the application.
245      * All audio played on the media stream will be routed to the selected destination.
246      * </p><p>
247      * Refer to the class documentation for details about live audio routes.
248      * </p>
249      */
250     public static final String FEATURE_LIVE_AUDIO = "android.media.route.feature.LIVE_AUDIO";
251 
252     /**
253      * Route feature: Live video.
254      * <p>
255      * A route that supports live video routing will allow a mirrored version
256      * of the device's primary display or a customized
257      * {@link android.app.Presentation Presentation} to be sent to supported
258      * destinations.
259      * </p><p>
260      * When a live video route is selected, audio and video routing is transparent
261      * to the application.  By default, audio and video is routed to the selected
262      * destination.  For certain live video routes, the application may also use a
263      * {@link android.app.Presentation Presentation} to replace the mirrored view
264      * on the external display with different content.
265      * </p><p>
266      * Refer to the class documentation for details about live video routes.
267      * </p>
268      *
269      * @see android.app.Presentation
270      */
271     public static final String FEATURE_LIVE_VIDEO = "android.media.route.feature.LIVE_VIDEO";
272 
273     /**
274      * Route feature: Local playback.
275      * @hide
276      */
277     public static final String FEATURE_LOCAL_PLAYBACK =
278             "android.media.route.feature.LOCAL_PLAYBACK";
279 
280     /**
281      * Route feature: Remote playback.
282      * <p>
283      * A route that supports remote playback routing will allow an application to send
284      * requests to play content remotely to supported destinations.
285      * A route may only support {@link #FEATURE_REMOTE_AUDIO_PLAYBACK audio playback} or
286      * {@link #FEATURE_REMOTE_VIDEO_PLAYBACK video playback}.
287      * </p><p>
288      * Remote playback routes destinations operate independently of the local device.
289      * When a remote playback route is selected, the application can control the content
290      * playing on the destination using {@link MediaRouter2.RoutingController#getControlHints()}.
291      * The application may also receive status updates from the route regarding remote playback.
292      * </p><p>
293      * Refer to the class documentation for details about remote playback routes.
294      * </p>
295      * @see #FEATURE_REMOTE_AUDIO_PLAYBACK
296      * @see #FEATURE_REMOTE_VIDEO_PLAYBACK
297      */
298     public static final String FEATURE_REMOTE_PLAYBACK =
299             "android.media.route.feature.REMOTE_PLAYBACK";
300 
301     /**
302      * Route feature: Remote audio playback.
303      * <p>
304      * A route that supports remote audio playback routing will allow an application to send
305      * requests to play audio content remotely to supported destinations.
306      *
307      * @see #FEATURE_REMOTE_PLAYBACK
308      * @see #FEATURE_REMOTE_VIDEO_PLAYBACK
309      */
310     public static final String FEATURE_REMOTE_AUDIO_PLAYBACK =
311             "android.media.route.feature.REMOTE_AUDIO_PLAYBACK";
312 
313     /**
314      * Route feature: Remote video playback.
315      * <p>
316      * A route that supports remote video playback routing will allow an application to send
317      * requests to play video content remotely to supported destinations.
318      *
319      * @see #FEATURE_REMOTE_PLAYBACK
320      * @see #FEATURE_REMOTE_AUDIO_PLAYBACK
321      */
322     public static final String FEATURE_REMOTE_VIDEO_PLAYBACK =
323             "android.media.route.feature.REMOTE_VIDEO_PLAYBACK";
324 
325     /**
326      * Route feature: Remote group playback.
327      * <p>
328      * @hide
329      */
330     public static final String FEATURE_REMOTE_GROUP_PLAYBACK =
331             "android.media.route.feature.REMOTE_GROUP_PLAYBACK";
332 
333     final String mId;
334     final CharSequence mName;
335     final List<String> mFeatures;
336     @Type
337     final int mType;
338     final boolean mIsSystem;
339     final Uri mIconUri;
340     final CharSequence mDescription;
341     @ConnectionState
342     final int mConnectionState;
343     final String mClientPackageName;
344     final String mPackageName;
345     final int mVolumeHandling;
346     final int mVolumeMax;
347     final int mVolume;
348     final String mAddress;
349     final Set<String> mDeduplicationIds;
350     final Bundle mExtras;
351     final String mProviderId;
352 
MediaRoute2Info(@onNull Builder builder)353     MediaRoute2Info(@NonNull Builder builder) {
354         mId = builder.mId;
355         mName = builder.mName;
356         mFeatures = builder.mFeatures;
357         mType = builder.mType;
358         mIsSystem = builder.mIsSystem;
359         mIconUri = builder.mIconUri;
360         mDescription = builder.mDescription;
361         mConnectionState = builder.mConnectionState;
362         mClientPackageName = builder.mClientPackageName;
363         mPackageName = builder.mPackageName;
364         mVolumeHandling = builder.mVolumeHandling;
365         mVolumeMax = builder.mVolumeMax;
366         mVolume = builder.mVolume;
367         mAddress = builder.mAddress;
368         mDeduplicationIds = builder.mDeduplicationIds;
369         mExtras = builder.mExtras;
370         mProviderId = builder.mProviderId;
371     }
372 
MediaRoute2Info(@onNull Parcel in)373     MediaRoute2Info(@NonNull Parcel in) {
374         mId = in.readString();
375         mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
376         mFeatures = in.createStringArrayList();
377         mType = in.readInt();
378         mIsSystem = in.readBoolean();
379         mIconUri = in.readParcelable(null, android.net.Uri.class);
380         mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
381         mConnectionState = in.readInt();
382         mClientPackageName = in.readString();
383         mPackageName = in.readString();
384         mVolumeHandling = in.readInt();
385         mVolumeMax = in.readInt();
386         mVolume = in.readInt();
387         mAddress = in.readString();
388         mDeduplicationIds = Set.of(in.readStringArray());
389         mExtras = in.readBundle();
390         mProviderId = in.readString();
391     }
392 
393     /**
394      * Gets the id of the route. The routes which are given by {@link MediaRouter2} will have
395      * unique IDs.
396      * <p>
397      * In order to ensure uniqueness in {@link MediaRouter2} side, the value of this method
398      * can be different from what was set in {@link MediaRoute2ProviderService}.
399      *
400      * @see Builder#Builder(String, CharSequence)
401      */
402     @NonNull
getId()403     public String getId() {
404         if (mProviderId != null) {
405             return toUniqueId(mProviderId, mId);
406         } else {
407             return mId;
408         }
409     }
410 
411     /**
412      * Gets the user-visible name of the route.
413      */
414     @NonNull
getName()415     public CharSequence getName() {
416         return mName;
417     }
418 
419     /**
420      * Gets the supported features of the route.
421      */
422     @NonNull
getFeatures()423     public List<String> getFeatures() {
424         return mFeatures;
425     }
426 
427     /**
428      * Gets the type of this route.
429      *
430      * @return The type of this route:
431      * {@link #TYPE_UNKNOWN},
432      * {@link #TYPE_BUILTIN_SPEAKER}, {@link #TYPE_WIRED_HEADSET}, {@link #TYPE_WIRED_HEADPHONES},
433      * {@link #TYPE_BLUETOOTH_A2DP}, {@link #TYPE_HDMI}, {@link #TYPE_DOCK},
434      * {@Link #TYPE_USB_DEVICE}, {@link #TYPE_USB_ACCESSORY}, {@link #TYPE_USB_HEADSET}
435      * {@link #TYPE_HEARING_AID},
436      * {@link #TYPE_REMOTE_TV}, {@link #TYPE_REMOTE_SPEAKER}, {@link #TYPE_GROUP}.
437      * @hide
438      */
439     @Type
getType()440     public int getType() {
441         return mType;
442     }
443 
444     /**
445      * Returns whether the route is a system route or not.
446      * <p>
447      * System routes are media routes directly controlled by the system
448      * such as phone speaker, wired headset, and Bluetooth devices.
449      * </p>
450      */
isSystemRoute()451     public boolean isSystemRoute() {
452         return mIsSystem;
453     }
454 
455     /**
456      * Gets the URI of the icon representing this route.
457      * <p>
458      * This icon will be used in picker UIs if available.
459      *
460      * @return The URI of the icon representing this route, or null if none.
461      */
462     @Nullable
getIconUri()463     public Uri getIconUri() {
464         return mIconUri;
465     }
466 
467     /**
468      * Gets the user-visible description of the route.
469      */
470     @Nullable
getDescription()471     public CharSequence getDescription() {
472         return mDescription;
473     }
474 
475     /**
476      * Gets the connection state of the route.
477      *
478      * @return The connection state of this route: {@link #CONNECTION_STATE_DISCONNECTED},
479      * {@link #CONNECTION_STATE_CONNECTING}, or {@link #CONNECTION_STATE_CONNECTED}.
480      */
481     @ConnectionState
getConnectionState()482     public int getConnectionState() {
483         return mConnectionState;
484     }
485 
486     /**
487      * Gets the package name of the app using the route.
488      * Returns null if no apps are using this route.
489      */
490     @Nullable
getClientPackageName()491     public String getClientPackageName() {
492         return mClientPackageName;
493     }
494 
495     /**
496      * Gets the package name of the provider that published the route.
497      * <p>
498      * It is set by the system service.
499      * @hide
500      */
501     @Nullable
getPackageName()502     public String getPackageName() {
503         return mPackageName;
504     }
505 
506     /**
507      * Gets information about how volume is handled on the route.
508      *
509      * @return {@link #PLAYBACK_VOLUME_FIXED} or {@link #PLAYBACK_VOLUME_VARIABLE}
510      */
511     @PlaybackVolume
getVolumeHandling()512     public int getVolumeHandling() {
513         return mVolumeHandling;
514     }
515 
516     /**
517      * Gets the maximum volume of the route.
518      */
getVolumeMax()519     public int getVolumeMax() {
520         return mVolumeMax;
521     }
522 
523     /**
524      * Gets the current volume of the route. This may be invalid if the route is not selected.
525      */
getVolume()526     public int getVolume() {
527         return mVolume;
528     }
529 
530     /**
531      * Gets the hardware address of the route if available.
532      * @hide
533      */
534     @Nullable
getAddress()535     public String getAddress() {
536         return mAddress;
537     }
538 
539     /**
540      * Gets the Deduplication ID of the route if available.
541      * @see RouteDiscoveryPreference#shouldRemoveDuplicates()
542      * @hide
543      */
544     @NonNull
getDeduplicationIds()545     public Set<String> getDeduplicationIds() {
546         return mDeduplicationIds;
547     }
548 
549     /**
550      * Gets an optional bundle with extra data.
551      */
552     @Nullable
getExtras()553     public Bundle getExtras() {
554         return mExtras == null ? null : new Bundle(mExtras);
555     }
556 
557     /**
558      * Gets the original id set by {@link Builder#Builder(String, CharSequence)}.
559      * @hide
560      */
561     @NonNull
562     @TestApi
getOriginalId()563     public String getOriginalId() {
564         return mId;
565     }
566 
567     /**
568      * Gets the provider id of the route. It is assigned automatically by
569      * {@link com.android.server.media.MediaRouterService}.
570      *
571      * @return provider id of the route or null if it's not set.
572      * @hide
573      */
574     @Nullable
getProviderId()575     public String getProviderId() {
576         return mProviderId;
577     }
578 
579     /**
580      * Returns if the route has at least one of the specified route features.
581      *
582      * @param features the list of route features to consider
583      * @return {@code true} if the route has at least one feature in the list
584      * @hide
585      */
hasAnyFeatures(@onNull Collection<String> features)586     public boolean hasAnyFeatures(@NonNull Collection<String> features) {
587         Objects.requireNonNull(features, "features must not be null");
588         for (String feature : features) {
589             if (getFeatures().contains(feature)) {
590                 return true;
591             }
592         }
593         return false;
594     }
595 
596     /**
597      * Returns if the route has all the specified route features.
598      *
599      * @hide
600      */
hasAllFeatures(@onNull Collection<String> features)601     public boolean hasAllFeatures(@NonNull Collection<String> features) {
602         Objects.requireNonNull(features, "features must not be null");
603         for (String feature : features) {
604             if (!getFeatures().contains(feature)) {
605                 return false;
606             }
607         }
608         return true;
609     }
610 
611     /**
612      * Returns true if the route info has all of the required field.
613      * A route is valid if and only if it is obtained from
614      * {@link com.android.server.media.MediaRouterService}.
615      * @hide
616      */
isValid()617     public boolean isValid() {
618         if (TextUtils.isEmpty(getId()) || TextUtils.isEmpty(getName())
619                 || TextUtils.isEmpty(getProviderId())) {
620             return false;
621         }
622         return true;
623     }
624 
625     @Override
equals(Object obj)626     public boolean equals(Object obj) {
627         if (this == obj) {
628             return true;
629         }
630         if (!(obj instanceof MediaRoute2Info)) {
631             return false;
632         }
633         MediaRoute2Info other = (MediaRoute2Info) obj;
634 
635         // Note: mExtras is not included.
636         return Objects.equals(mId, other.mId)
637                 && Objects.equals(mName, other.mName)
638                 && Objects.equals(mFeatures, other.mFeatures)
639                 && (mType == other.mType)
640                 && (mIsSystem == other.mIsSystem)
641                 && Objects.equals(mIconUri, other.mIconUri)
642                 && Objects.equals(mDescription, other.mDescription)
643                 && (mConnectionState == other.mConnectionState)
644                 && Objects.equals(mClientPackageName, other.mClientPackageName)
645                 && Objects.equals(mPackageName, other.mPackageName)
646                 && (mVolumeHandling == other.mVolumeHandling)
647                 && (mVolumeMax == other.mVolumeMax)
648                 && (mVolume == other.mVolume)
649                 && Objects.equals(mAddress, other.mAddress)
650                 && Objects.equals(mDeduplicationIds, other.mDeduplicationIds)
651                 && Objects.equals(mProviderId, other.mProviderId);
652     }
653 
654     @Override
hashCode()655     public int hashCode() {
656         // Note: mExtras is not included.
657         return Objects.hash(mId, mName, mFeatures, mType, mIsSystem, mIconUri, mDescription,
658                 mConnectionState, mClientPackageName, mPackageName, mVolumeHandling, mVolumeMax,
659                 mVolume, mAddress, mDeduplicationIds, mProviderId);
660     }
661 
662     @Override
toString()663     public String toString() {
664         // Note: mExtras is not printed here.
665         StringBuilder result = new StringBuilder()
666                 .append("MediaRoute2Info{ ")
667                 .append("id=").append(getId())
668                 .append(", name=").append(getName())
669                 .append(", features=").append(getFeatures())
670                 .append(", iconUri=").append(getIconUri())
671                 .append(", description=").append(getDescription())
672                 .append(", connectionState=").append(getConnectionState())
673                 .append(", clientPackageName=").append(getClientPackageName())
674                 .append(", volumeHandling=").append(getVolumeHandling())
675                 .append(", volumeMax=").append(getVolumeMax())
676                 .append(", volume=").append(getVolume())
677                 .append(", deduplicationIds=").append(String.join(",", getDeduplicationIds()))
678                 .append(", providerId=").append(getProviderId())
679                 .append(" }");
680         return result.toString();
681     }
682 
683     @Override
describeContents()684     public int describeContents() {
685         return 0;
686     }
687 
688     @Override
writeToParcel(@onNull Parcel dest, int flags)689     public void writeToParcel(@NonNull Parcel dest, int flags) {
690         dest.writeString(mId);
691         TextUtils.writeToParcel(mName, dest, flags);
692         dest.writeStringList(mFeatures);
693         dest.writeInt(mType);
694         dest.writeBoolean(mIsSystem);
695         dest.writeParcelable(mIconUri, flags);
696         TextUtils.writeToParcel(mDescription, dest, flags);
697         dest.writeInt(mConnectionState);
698         dest.writeString(mClientPackageName);
699         dest.writeString(mPackageName);
700         dest.writeInt(mVolumeHandling);
701         dest.writeInt(mVolumeMax);
702         dest.writeInt(mVolume);
703         dest.writeString(mAddress);
704         dest.writeStringArray(mDeduplicationIds.toArray(new String[mDeduplicationIds.size()]));
705         dest.writeBundle(mExtras);
706         dest.writeString(mProviderId);
707     }
708 
709     /**
710      * Builder for {@link MediaRoute2Info media route info}.
711      */
712     public static final class Builder {
713         final String mId;
714         final CharSequence mName;
715         final List<String> mFeatures;
716 
717         @Type
718         int mType = TYPE_UNKNOWN;
719         boolean mIsSystem;
720         Uri mIconUri;
721         CharSequence mDescription;
722         @ConnectionState
723         int mConnectionState;
724         String mClientPackageName;
725         String mPackageName;
726         int mVolumeHandling = PLAYBACK_VOLUME_FIXED;
727         int mVolumeMax;
728         int mVolume;
729         String mAddress;
730         Set<String> mDeduplicationIds;
731         Bundle mExtras;
732         String mProviderId;
733 
734         /**
735          * Constructor for builder to create {@link MediaRoute2Info}.
736          * <p>
737          * In order to ensure ID uniqueness, the {@link MediaRoute2Info#getId() ID} of a route info
738          * obtained from {@link MediaRouter2} can be different from what was set in
739          * {@link MediaRoute2ProviderService}.
740          * </p>
741          * @param id The ID of the route. Must not be empty.
742          * @param name The user-visible name of the route.
743          */
Builder(@onNull String id, @NonNull CharSequence name)744         public Builder(@NonNull String id, @NonNull CharSequence name) {
745             if (TextUtils.isEmpty(id)) {
746                 throw new IllegalArgumentException("id must not be empty");
747             }
748             if (TextUtils.isEmpty(name)) {
749                 throw new IllegalArgumentException("name must not be empty");
750             }
751             mId = id;
752             mName = name;
753             mFeatures = new ArrayList<>();
754             mDeduplicationIds = Set.of();
755         }
756 
757         /**
758          * Constructor for builder to create {@link MediaRoute2Info} with existing
759          * {@link MediaRoute2Info} instance.
760          *
761          * @param routeInfo the existing instance to copy data from.
762          */
Builder(@onNull MediaRoute2Info routeInfo)763         public Builder(@NonNull MediaRoute2Info routeInfo) {
764             this(routeInfo.mId, routeInfo);
765         }
766 
767         /**
768          * Constructor for builder to create {@link MediaRoute2Info} with existing
769          * {@link MediaRoute2Info} instance and replace ID with the given {@code id}.
770          *
771          * @param id The ID of the new route. Must not be empty.
772          * @param routeInfo the existing instance to copy data from.
773          * @hide
774          */
Builder(@onNull String id, @NonNull MediaRoute2Info routeInfo)775         public Builder(@NonNull String id, @NonNull MediaRoute2Info routeInfo) {
776             if (TextUtils.isEmpty(id)) {
777                 throw new IllegalArgumentException("id must not be empty");
778             }
779             Objects.requireNonNull(routeInfo, "routeInfo must not be null");
780 
781             mId = id;
782             mName = routeInfo.mName;
783             mFeatures = new ArrayList<>(routeInfo.mFeatures);
784             mType = routeInfo.mType;
785             mIsSystem = routeInfo.mIsSystem;
786             mIconUri = routeInfo.mIconUri;
787             mDescription = routeInfo.mDescription;
788             mConnectionState = routeInfo.mConnectionState;
789             mClientPackageName = routeInfo.mClientPackageName;
790             mPackageName = routeInfo.mPackageName;
791             mVolumeHandling = routeInfo.mVolumeHandling;
792             mVolumeMax = routeInfo.mVolumeMax;
793             mVolume = routeInfo.mVolume;
794             mAddress = routeInfo.mAddress;
795             mDeduplicationIds = Set.copyOf(routeInfo.mDeduplicationIds);
796             if (routeInfo.mExtras != null) {
797                 mExtras = new Bundle(routeInfo.mExtras);
798             }
799             mProviderId = routeInfo.mProviderId;
800         }
801 
802         /**
803          * Adds a feature for the route.
804          * @param feature a feature that the route has. May be one of predefined features
805          *                such as {@link #FEATURE_LIVE_AUDIO}, {@link #FEATURE_LIVE_VIDEO} or
806          *                {@link #FEATURE_REMOTE_PLAYBACK} or a custom feature defined by
807          *                a provider.
808          *
809          * @see #addFeatures(Collection)
810          */
811         @NonNull
addFeature(@onNull String feature)812         public Builder addFeature(@NonNull String feature) {
813             if (TextUtils.isEmpty(feature)) {
814                 throw new IllegalArgumentException("feature must not be null or empty");
815             }
816             mFeatures.add(feature);
817             return this;
818         }
819 
820         /**
821          * Adds features for the route. A route must support at least one route type.
822          * @param features features that the route has. May include predefined features
823          *                such as {@link #FEATURE_LIVE_AUDIO}, {@link #FEATURE_LIVE_VIDEO} or
824          *                {@link #FEATURE_REMOTE_PLAYBACK} or custom features defined by
825          *                a provider.
826          *
827          * @see #addFeature(String)
828          */
829         @NonNull
addFeatures(@onNull Collection<String> features)830         public Builder addFeatures(@NonNull Collection<String> features) {
831             Objects.requireNonNull(features, "features must not be null");
832             for (String feature : features) {
833                 addFeature(feature);
834             }
835             return this;
836         }
837 
838         /**
839          * Clears the features of the route. A route must support at least one route type.
840          */
841         @NonNull
clearFeatures()842         public Builder clearFeatures() {
843             mFeatures.clear();
844             return this;
845         }
846 
847         /**
848          * Sets the route's type.
849          * @hide
850          */
851         @NonNull
setType(@ype int type)852         public Builder setType(@Type int type) {
853             mType = type;
854             return this;
855         }
856 
857         /**
858          * Sets whether the route is a system route or not.
859          * @hide
860          */
861         @NonNull
setSystemRoute(boolean isSystem)862         public Builder setSystemRoute(boolean isSystem) {
863             mIsSystem = isSystem;
864             return this;
865         }
866 
867         /**
868          * Sets the URI of the icon representing this route.
869          * <p>
870          * This icon will be used in picker UIs if available.
871          * </p><p>
872          * The URI must be one of the following formats:
873          * <ul>
874          * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
875          * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
876          * </li>
877          * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
878          * </ul>
879          * </p>
880          */
881         @NonNull
setIconUri(@ullable Uri iconUri)882         public Builder setIconUri(@Nullable Uri iconUri) {
883             mIconUri = iconUri;
884             return this;
885         }
886 
887         /**
888          * Sets the user-visible description of the route.
889          */
890         @NonNull
setDescription(@ullable CharSequence description)891         public Builder setDescription(@Nullable CharSequence description) {
892             mDescription = description;
893             return this;
894         }
895 
896         /**
897         * Sets the route's connection state.
898         *
899         * {@link #CONNECTION_STATE_DISCONNECTED},
900         * {@link #CONNECTION_STATE_CONNECTING}, or
901         * {@link #CONNECTION_STATE_CONNECTED}.
902         */
903         @NonNull
setConnectionState(@onnectionState int connectionState)904         public Builder setConnectionState(@ConnectionState int connectionState) {
905             mConnectionState = connectionState;
906             return this;
907         }
908 
909         /**
910          * Sets the package name of the app using the route.
911          */
912         @NonNull
setClientPackageName(@ullable String packageName)913         public Builder setClientPackageName(@Nullable String packageName) {
914             mClientPackageName = packageName;
915             return this;
916         }
917 
918         /**
919          * Sets the package name of the route.
920          * @hide
921          */
922         @NonNull
setPackageName(@onNull String packageName)923         public Builder setPackageName(@NonNull String packageName) {
924             mPackageName = packageName;
925             return this;
926         }
927 
928         /**
929          * Sets the route's volume handling.
930          */
931         @NonNull
setVolumeHandling(@laybackVolume int volumeHandling)932         public Builder setVolumeHandling(@PlaybackVolume int volumeHandling) {
933             mVolumeHandling = volumeHandling;
934             return this;
935         }
936 
937         /**
938          * Sets the route's maximum volume, or 0 if unknown.
939          */
940         @NonNull
setVolumeMax(int volumeMax)941         public Builder setVolumeMax(int volumeMax) {
942             mVolumeMax = volumeMax;
943             return this;
944         }
945 
946         /**
947          * Sets the route's current volume, or 0 if unknown.
948          */
949         @NonNull
setVolume(int volume)950         public Builder setVolume(int volume) {
951             mVolume = volume;
952             return this;
953         }
954 
955         /**
956          * Sets the hardware address of the route.
957          * @hide
958          */
959         @NonNull
setAddress(String address)960         public Builder setAddress(String address) {
961             mAddress = address;
962             return this;
963         }
964 
965         /**
966          * Sets the deduplication ID of the route.
967          * Routes have the same ID could be removed even when
968          * they are from different providers.
969          * <p>
970          * If it's {@code null}, the route will not be removed.
971          * @see RouteDiscoveryPreference#shouldRemoveDuplicates()
972          * @hide
973          */
974         @NonNull
setDeduplicationIds(@onNull Set<String> id)975         public Builder setDeduplicationIds(@NonNull Set<String> id) {
976             mDeduplicationIds = Set.copyOf(id);
977             return this;
978         }
979 
980         /**
981          * Sets a bundle of extras for the route.
982          * <p>
983          * Note: The extras will not affect the result of {@link MediaRoute2Info#equals(Object)}.
984          */
985         @NonNull
setExtras(@ullable Bundle extras)986         public Builder setExtras(@Nullable Bundle extras) {
987             if (extras == null) {
988                 mExtras = null;
989                 return this;
990             }
991             mExtras = new Bundle(extras);
992             return this;
993         }
994 
995         /**
996          * Sets the provider id of the route.
997          * @hide
998          */
999         @NonNull
setProviderId(@onNull String providerId)1000         public Builder setProviderId(@NonNull String providerId) {
1001             if (TextUtils.isEmpty(providerId)) {
1002                 throw new IllegalArgumentException("providerId must not be null or empty");
1003             }
1004             mProviderId = providerId;
1005             return this;
1006         }
1007 
1008         /**
1009          * Builds the {@link MediaRoute2Info media route info}.
1010          *
1011          * @throws IllegalArgumentException if no features are added.
1012          */
1013         @NonNull
build()1014         public MediaRoute2Info build() {
1015             if (mFeatures.isEmpty()) {
1016                 throw new IllegalArgumentException("features must not be empty!");
1017             }
1018             return new MediaRoute2Info(this);
1019         }
1020     }
1021 }
1022