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 static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER;
22 import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES;
23 import static com.android.media.flags.Flags.FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES;
24 
25 import android.annotation.FlaggedApi;
26 import android.annotation.IntDef;
27 import android.annotation.NonNull;
28 import android.annotation.Nullable;
29 import android.annotation.SuppressLint;
30 import android.annotation.TestApi;
31 import android.net.Uri;
32 import android.os.Bundle;
33 import android.os.Parcel;
34 import android.os.Parcelable;
35 import android.text.TextUtils;
36 
37 import com.android.internal.util.Preconditions;
38 
39 import java.io.PrintWriter;
40 import java.lang.annotation.Retention;
41 import java.lang.annotation.RetentionPolicy;
42 import java.util.ArrayList;
43 import java.util.Collection;
44 import java.util.List;
45 import java.util.Locale;
46 import java.util.Objects;
47 import java.util.Set;
48 
49 /**
50  * Describes the properties of a route.
51  */
52 public final class MediaRoute2Info implements Parcelable {
53     @NonNull
54     public static final Creator<MediaRoute2Info> CREATOR = new Creator<MediaRoute2Info>() {
55         @Override
56         public MediaRoute2Info createFromParcel(Parcel in) {
57             return new MediaRoute2Info(in);
58         }
59 
60         @Override
61         public MediaRoute2Info[] newArray(int size) {
62             return new MediaRoute2Info[size];
63         }
64     };
65 
66     /**
67      * The {@link #getOriginalId() original id} of the route that represents the built-in media
68      * route.
69      *
70      * <p>A route with this id will only be visible to apps with permission to do system routing,
71      * which means having {@link android.Manifest.permission#BLUETOOTH_CONNECT} and {@link
72      * android.Manifest.permission#BLUETOOTH_SCAN}, or {@link
73      * android.Manifest.permission#MODIFY_AUDIO_ROUTING}.
74      *
75      * @hide
76      */
77     public static final String ROUTE_ID_DEVICE = "DEVICE_ROUTE";
78 
79     /**
80      * The {@link #getOriginalId() original id} of the route that represents the default system
81      * media route.
82      *
83      * <p>A route with this id will be visible to apps with no permission over system routing. See
84      * {@link #ROUTE_ID_DEVICE} for details.
85      *
86      * @hide
87      */
88     public static final String ROUTE_ID_DEFAULT = "DEFAULT_ROUTE";
89 
90     /** @hide */
91     @IntDef({CONNECTION_STATE_DISCONNECTED, CONNECTION_STATE_CONNECTING,
92             CONNECTION_STATE_CONNECTED})
93     @Retention(RetentionPolicy.SOURCE)
94     public @interface ConnectionState {}
95 
96     /**
97      * The default connection state indicating the route is disconnected.
98      *
99      * @see #getConnectionState
100      */
101     public static final int CONNECTION_STATE_DISCONNECTED = 0;
102 
103     /**
104      * A connection state indicating the route is in the process of connecting and is not yet
105      * ready for use.
106      *
107      * @see #getConnectionState
108      */
109     public static final int CONNECTION_STATE_CONNECTING = 1;
110 
111     /**
112      * A connection state indicating the route is connected.
113      *
114      * @see #getConnectionState
115      */
116     public static final int CONNECTION_STATE_CONNECTED = 2;
117 
118     /** @hide */
119     @IntDef({PLAYBACK_VOLUME_FIXED, PLAYBACK_VOLUME_VARIABLE})
120     @Retention(RetentionPolicy.SOURCE)
121     public @interface PlaybackVolume {}
122 
123     /**
124      * Playback information indicating the playback volume is fixed, i&#46;e&#46; it cannot be
125      * controlled from this object. An example of fixed playback volume is a remote player,
126      * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
127      * than attenuate at the source.
128      *
129      * @see #getVolumeHandling()
130      */
131     public static final int PLAYBACK_VOLUME_FIXED = 0;
132     /**
133      * Playback information indicating the playback volume is variable and can be controlled
134      * from this object.
135      *
136      * @see #getVolumeHandling()
137      */
138     public static final int PLAYBACK_VOLUME_VARIABLE = 1;
139 
140     /** @hide */
141     @IntDef(
142             prefix = {"TYPE_"},
143             value = {
144                 TYPE_UNKNOWN,
145                 TYPE_BUILTIN_SPEAKER,
146                 TYPE_WIRED_HEADSET,
147                 TYPE_WIRED_HEADPHONES,
148                 TYPE_BLUETOOTH_A2DP,
149                 TYPE_HDMI,
150                 TYPE_HDMI_ARC,
151                 TYPE_HDMI_EARC,
152                 TYPE_USB_DEVICE,
153                 TYPE_USB_ACCESSORY,
154                 TYPE_DOCK,
155                 TYPE_USB_HEADSET,
156                 TYPE_HEARING_AID,
157                 TYPE_BLE_HEADSET,
158                 TYPE_REMOTE_TV,
159                 TYPE_REMOTE_SPEAKER,
160                 TYPE_REMOTE_AUDIO_VIDEO_RECEIVER,
161                 TYPE_REMOTE_TABLET,
162                 TYPE_REMOTE_TABLET_DOCKED,
163                 TYPE_REMOTE_COMPUTER,
164                 TYPE_REMOTE_GAME_CONSOLE,
165                 TYPE_REMOTE_CAR,
166                 TYPE_REMOTE_SMARTWATCH,
167                 TYPE_REMOTE_SMARTPHONE,
168                 TYPE_GROUP
169             })
170     @Retention(RetentionPolicy.SOURCE)
171     public @interface Type {}
172 
173     /**
174      * Indicates the route's type is unknown or undefined.
175      *
176      * @see #getType
177      */
178     public static final int TYPE_UNKNOWN = 0;
179 
180     /**
181      * Indicates the route is the speaker system (i.e. a mono speaker or stereo speakers) built into
182      * the device.
183      *
184      * @see #getType
185      */
186     public static final int TYPE_BUILTIN_SPEAKER = AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
187 
188     /**
189      * Indicates the route is a headset, which is the combination of a headphones and a microphone.
190      *
191      * @see #getType
192      */
193     public static final int TYPE_WIRED_HEADSET = AudioDeviceInfo.TYPE_WIRED_HEADSET;
194 
195     /**
196      * Indicates the route is a pair of wired headphones.
197      *
198      * @see #getType
199      */
200     public static final int TYPE_WIRED_HEADPHONES = AudioDeviceInfo.TYPE_WIRED_HEADPHONES;
201 
202     /**
203      * Indicates the route is a bluetooth device, such as a bluetooth speaker or headphones.
204      *
205      * @see #getType
206      */
207     public static final int TYPE_BLUETOOTH_A2DP = AudioDeviceInfo.TYPE_BLUETOOTH_A2DP;
208 
209     /**
210      * Indicates the route is an HDMI connection.
211      *
212      * @see #getType
213      */
214     public static final int TYPE_HDMI = AudioDeviceInfo.TYPE_HDMI;
215 
216     /**
217      * Indicates the route is an Audio Return Channel of an HDMI connection.
218      *
219      * @see #getType
220      */
221     @FlaggedApi(FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER)
222     public static final int TYPE_HDMI_ARC = AudioDeviceInfo.TYPE_HDMI_ARC;
223 
224     /**
225      * Indicates the route is an Enhanced Audio Return Channel of an HDMI connection.
226      *
227      * @see #getType
228      */
229     @FlaggedApi(FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER)
230     public static final int TYPE_HDMI_EARC = AudioDeviceInfo.TYPE_HDMI_EARC;
231 
232     /**
233      * Indicates the route is a USB audio device.
234      *
235      * @see #getType
236      */
237     public static final int TYPE_USB_DEVICE = AudioDeviceInfo.TYPE_USB_DEVICE;
238 
239     /**
240      * Indicates the route is a USB audio device in accessory mode.
241      *
242      * @see #getType
243      */
244     public static final int TYPE_USB_ACCESSORY = AudioDeviceInfo.TYPE_USB_ACCESSORY;
245 
246     /**
247      * Indicates the route is the audio device associated with a dock.
248      *
249      * @see #getType
250      */
251     public static final int TYPE_DOCK = AudioDeviceInfo.TYPE_DOCK;
252 
253     /**
254      * Indicates the route is a USB audio headset.
255      *
256      * @see #getType
257      */
258     public static final int TYPE_USB_HEADSET = AudioDeviceInfo.TYPE_USB_HEADSET;
259 
260     /**
261      * Indicates the route is a hearing aid.
262      *
263      * @see #getType
264      */
265     public static final int TYPE_HEARING_AID = AudioDeviceInfo.TYPE_HEARING_AID;
266 
267     /**
268      * Indicates the route is a Bluetooth Low Energy (BLE) HEADSET.
269      *
270      * @see #getType
271      */
272     public static final int TYPE_BLE_HEADSET = AudioDeviceInfo.TYPE_BLE_HEADSET;
273 
274     /**
275      * Indicates the route is a remote TV.
276      *
277      * <p>A remote device uses a routing protocol managed by the application, as opposed to the
278      * routing being done by the system.
279      *
280      * @see #getType
281      */
282     public static final int TYPE_REMOTE_TV = 1001;
283 
284     /**
285      * Indicates the route is a remote speaker.
286      *
287      * <p>A remote device uses a routing protocol managed by the application, as opposed to the
288      * routing being done by the system.
289      *
290      * @see #getType
291      */
292     public static final int TYPE_REMOTE_SPEAKER = 1002;
293 
294     /**
295      * Indicates the route is a remote Audio/Video Receiver (AVR).
296      *
297      * <p>A remote device uses a routing protocol managed by the application, as opposed to the
298      * routing being done by the system.
299      *
300      * @see #getType
301      */
302     public static final int TYPE_REMOTE_AUDIO_VIDEO_RECEIVER = 1003;
303 
304     /**
305      * Indicates the route is a remote tablet.
306      *
307      * <p>A remote device uses a routing protocol managed by the application, as opposed to the
308      * routing being done by the system.
309      *
310      * @see #getType
311      */
312     @FlaggedApi(FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES)
313     public static final int TYPE_REMOTE_TABLET = 1004;
314 
315     /**
316      * Indicates the route is a remote docked tablet.
317      *
318      * <p>A remote device uses a routing protocol managed by the application, as opposed to the
319      * routing being done by the system.
320      *
321      * @see #getType
322      */
323     @FlaggedApi(FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES)
324     public static final int TYPE_REMOTE_TABLET_DOCKED = 1005;
325 
326     /**
327      * Indicates the route is a remote computer.
328      *
329      * <p>A remote device uses a routing protocol managed by the application, as opposed to the
330      * routing being done by the system.
331      *
332      * @see #getType
333      */
334     @FlaggedApi(FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES)
335     public static final int TYPE_REMOTE_COMPUTER = 1006;
336 
337     /**
338      * Indicates the route is a remote gaming console.
339      *
340      * <p>A remote device uses a routing protocol managed by the application, as opposed to the
341      * routing being done by the system.
342      *
343      * @see #getType
344      */
345     @FlaggedApi(FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES)
346     public static final int TYPE_REMOTE_GAME_CONSOLE = 1007;
347 
348     /**
349      * Indicates the route is a remote car.
350      *
351      * <p>A remote device uses a routing protocol managed by the application, as opposed to the
352      * routing being done by the system.
353      *
354      * @see #getType
355      */
356     @FlaggedApi(FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES)
357     public static final int TYPE_REMOTE_CAR = 1008;
358 
359     /**
360      * Indicates the route is a remote smartwatch.
361      *
362      * <p>A remote device uses a routing protocol managed by the application, as opposed to the
363      * routing being done by the system.
364      *
365      * @see #getType
366      */
367     @FlaggedApi(FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES)
368     public static final int TYPE_REMOTE_SMARTWATCH = 1009;
369 
370     /**
371      * Indicates the route is a remote smartphone.
372      *
373      * <p>A remote device uses a routing protocol managed by the application, as opposed to the
374      * routing being done by the system.
375      *
376      * @see #getType
377      */
378     @FlaggedApi(FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES)
379     public static final int TYPE_REMOTE_SMARTPHONE = 1010;
380 
381     /**
382      * Indicates the route is a group of devices.
383      *
384      * @see #getType
385      */
386     public static final int TYPE_GROUP = 2000;
387 
388     /**
389      * Route feature: Live audio.
390      * <p>
391      * A route that supports live audio routing will allow the media audio stream
392      * to be sent to supported destinations.  This can include internal speakers or
393      * audio jacks on the device itself, A2DP devices, and more.
394      * </p><p>
395      * When a live audio route is selected, audio routing is transparent to the application.
396      * All audio played on the media stream will be routed to the selected destination.
397      * </p><p>
398      * Refer to the class documentation for details about live audio routes.
399      * </p>
400      */
401     public static final String FEATURE_LIVE_AUDIO = "android.media.route.feature.LIVE_AUDIO";
402 
403     /**
404      * Route feature: Live video.
405      * <p>
406      * A route that supports live video routing will allow a mirrored version
407      * of the device's primary display or a customized
408      * {@link android.app.Presentation Presentation} to be sent to supported
409      * destinations.
410      * </p><p>
411      * When a live video route is selected, audio and video routing is transparent
412      * to the application.  By default, audio and video is routed to the selected
413      * destination.  For certain live video routes, the application may also use a
414      * {@link android.app.Presentation Presentation} to replace the mirrored view
415      * on the external display with different content.
416      * </p><p>
417      * Refer to the class documentation for details about live video routes.
418      * </p>
419      *
420      * @see android.app.Presentation
421      */
422     public static final String FEATURE_LIVE_VIDEO = "android.media.route.feature.LIVE_VIDEO";
423 
424     /**
425      * Route feature: Local playback.
426      * @hide
427      */
428     public static final String FEATURE_LOCAL_PLAYBACK =
429             "android.media.route.feature.LOCAL_PLAYBACK";
430 
431     /**
432      * Route feature: Remote playback.
433      * <p>
434      * A route that supports remote playback routing will allow an application to send
435      * requests to play content remotely to supported destinations.
436      * A route may only support {@link #FEATURE_REMOTE_AUDIO_PLAYBACK audio playback} or
437      * {@link #FEATURE_REMOTE_VIDEO_PLAYBACK video playback}.
438      * </p><p>
439      * Remote playback routes destinations operate independently of the local device.
440      * When a remote playback route is selected, the application can control the content
441      * playing on the destination using {@link MediaRouter2.RoutingController#getControlHints()}.
442      * The application may also receive status updates from the route regarding remote playback.
443      * </p><p>
444      * Refer to the class documentation for details about remote playback routes.
445      * </p>
446      * @see #FEATURE_REMOTE_AUDIO_PLAYBACK
447      * @see #FEATURE_REMOTE_VIDEO_PLAYBACK
448      */
449     public static final String FEATURE_REMOTE_PLAYBACK =
450             "android.media.route.feature.REMOTE_PLAYBACK";
451 
452     /**
453      * Route feature: Remote audio playback.
454      * <p>
455      * A route that supports remote audio playback routing will allow an application to send
456      * requests to play audio content remotely to supported destinations.
457      *
458      * @see #FEATURE_REMOTE_PLAYBACK
459      * @see #FEATURE_REMOTE_VIDEO_PLAYBACK
460      */
461     public static final String FEATURE_REMOTE_AUDIO_PLAYBACK =
462             "android.media.route.feature.REMOTE_AUDIO_PLAYBACK";
463 
464     /**
465      * Route feature: Remote video playback.
466      * <p>
467      * A route that supports remote video playback routing will allow an application to send
468      * requests to play video content remotely to supported destinations.
469      *
470      * @see #FEATURE_REMOTE_PLAYBACK
471      * @see #FEATURE_REMOTE_AUDIO_PLAYBACK
472      */
473     public static final String FEATURE_REMOTE_VIDEO_PLAYBACK =
474             "android.media.route.feature.REMOTE_VIDEO_PLAYBACK";
475 
476     /**
477      * Route feature: Remote group playback.
478      * <p>
479      * @hide
480      */
481     public static final String FEATURE_REMOTE_GROUP_PLAYBACK =
482             "android.media.route.feature.REMOTE_GROUP_PLAYBACK";
483 
484     /** Indicates the route is always suitable for media playback. */
485     @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
486     public static final int SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER = 0;
487 
488     /**
489      * Indicates that the route is suitable for media playback only after explicit user selection.
490      */
491     @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
492     public static final int SUITABILITY_STATUS_SUITABLE_FOR_MANUAL_TRANSFER = 1;
493 
494     /** Indicates that the route is never suitable for media playback. */
495     @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
496     public static final int SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER = 2;
497 
498     /**
499      * Route suitability status.
500      *
501      * <p>Signals whether the route is suitable to play media.
502      *
503      * @hide
504      */
505     @IntDef(
506             value = {
507                 SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER,
508                 SUITABILITY_STATUS_SUITABLE_FOR_MANUAL_TRANSFER,
509                 SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER
510             })
511     @Retention(RetentionPolicy.SOURCE)
512     @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
513     public @interface SuitabilityStatus {}
514 
515     private final String mId;
516     private final CharSequence mName;
517     private final List<String> mFeatures;
518     @Type
519     private final int mType;
520     private final boolean mIsSystem;
521     private final Uri mIconUri;
522     private final CharSequence mDescription;
523     @ConnectionState
524     private final int mConnectionState;
525     private final String mClientPackageName;
526     private final String mPackageName;
527     @PlaybackVolume private final int mVolumeHandling;
528     private final int mVolumeMax;
529     private final int mVolume;
530     private final String mAddress;
531     private final Set<String> mDeduplicationIds;
532     private final Bundle mExtras;
533     private final String mProviderId;
534     private final boolean mIsVisibilityRestricted;
535     private final Set<String> mAllowedPackages;
536     @SuitabilityStatus private final int mSuitabilityStatus;
537 
MediaRoute2Info(@onNull Builder builder)538     MediaRoute2Info(@NonNull Builder builder) {
539         mId = builder.mId;
540         mName = builder.mName;
541         mFeatures = builder.mFeatures;
542         mType = builder.mType;
543         mIsSystem = builder.mIsSystem;
544         mIconUri = builder.mIconUri;
545         mDescription = builder.mDescription;
546         mConnectionState = builder.mConnectionState;
547         mClientPackageName = builder.mClientPackageName;
548         mPackageName = builder.mPackageName;
549         mVolumeHandling = builder.mVolumeHandling;
550         mVolumeMax = builder.mVolumeMax;
551         mVolume = builder.mVolume;
552         mAddress = builder.mAddress;
553         mDeduplicationIds = builder.mDeduplicationIds;
554         mExtras = builder.mExtras;
555         mProviderId = builder.mProviderId;
556         mIsVisibilityRestricted = builder.mIsVisibilityRestricted;
557         mAllowedPackages = builder.mAllowedPackages;
558         mSuitabilityStatus = builder.mSuitabilityStatus;
559     }
560 
MediaRoute2Info(@onNull Parcel in)561     MediaRoute2Info(@NonNull Parcel in) {
562         mId = in.readString();
563         Preconditions.checkArgument(!TextUtils.isEmpty(mId));
564         mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
565         mFeatures = in.createStringArrayList();
566         mType = in.readInt();
567         mIsSystem = in.readBoolean();
568         mIconUri = in.readParcelable(null, android.net.Uri.class);
569         mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
570         mConnectionState = in.readInt();
571         mClientPackageName = in.readString();
572         mPackageName = in.readString();
573         mVolumeHandling = in.readInt();
574         mVolumeMax = in.readInt();
575         mVolume = in.readInt();
576         mAddress = in.readString();
577         mDeduplicationIds = Set.of(in.readStringArray());
578         mExtras = in.readBundle();
579         mProviderId = in.readString();
580         mIsVisibilityRestricted = in.readBoolean();
581         mAllowedPackages = Set.of(in.createString8Array());
582         mSuitabilityStatus = in.readInt();
583     }
584 
585     /**
586      * Gets the id of the route. The routes which are given by {@link MediaRouter2} will have
587      * unique IDs.
588      * <p>
589      * In order to ensure uniqueness in {@link MediaRouter2} side, the value of this method
590      * can be different from what was set in {@link MediaRoute2ProviderService}.
591      *
592      * @see Builder#Builder(String, CharSequence)
593      */
594     @NonNull
getId()595     public String getId() {
596         if (!TextUtils.isEmpty(mProviderId)) {
597             return toUniqueId(mProviderId, mId);
598         } else {
599             return mId;
600         }
601     }
602 
603     /**
604      * Gets the user-visible name of the route.
605      */
606     @NonNull
getName()607     public CharSequence getName() {
608         return mName;
609     }
610 
611     /**
612      * Gets the supported features of the route.
613      */
614     @NonNull
getFeatures()615     public List<String> getFeatures() {
616         return mFeatures;
617     }
618 
619     /**
620      * Returns the type of this route.
621      */
622     @Type
getType()623     public int getType() {
624         return mType;
625     }
626 
627     /**
628      * Returns whether the route is a system route or not.
629      * <p>
630      * System routes are media routes directly controlled by the system
631      * such as phone speaker, wired headset, and Bluetooth devices.
632      * </p>
633      */
isSystemRoute()634     public boolean isSystemRoute() {
635         return mIsSystem;
636     }
637 
638     /**
639      * Gets the URI of the icon representing this route.
640      * <p>
641      * This icon will be used in picker UIs if available.
642      *
643      * @return The URI of the icon representing this route, or null if none.
644      */
645     @Nullable
getIconUri()646     public Uri getIconUri() {
647         return mIconUri;
648     }
649 
650     /**
651      * Gets the user-visible description of the route.
652      */
653     @Nullable
getDescription()654     public CharSequence getDescription() {
655         return mDescription;
656     }
657 
658     /**
659      * Gets the connection state of the route.
660      *
661      * @return The connection state of this route: {@link #CONNECTION_STATE_DISCONNECTED},
662      * {@link #CONNECTION_STATE_CONNECTING}, or {@link #CONNECTION_STATE_CONNECTED}.
663      */
664     @ConnectionState
getConnectionState()665     public int getConnectionState() {
666         return mConnectionState;
667     }
668 
669     /**
670      * Gets the package name of the app using the route.
671      * Returns null if no apps are using this route.
672      */
673     @Nullable
getClientPackageName()674     public String getClientPackageName() {
675         return mClientPackageName;
676     }
677 
678     /**
679      * Gets the package name of the provider that published the route.
680      * <p>
681      * It is set by the system service.
682      * @hide
683      */
684     @Nullable
getPackageName()685     public String getPackageName() {
686         return mPackageName;
687     }
688 
689     /**
690      * Gets information about how volume is handled on the route.
691      *
692      * @return {@link #PLAYBACK_VOLUME_FIXED} or {@link #PLAYBACK_VOLUME_VARIABLE}
693      */
694     @PlaybackVolume
getVolumeHandling()695     public int getVolumeHandling() {
696         return mVolumeHandling;
697     }
698 
699     /**
700      * Gets the maximum volume of the route.
701      */
getVolumeMax()702     public int getVolumeMax() {
703         return mVolumeMax;
704     }
705 
706     /**
707      * Gets the current volume of the route. This may be invalid if the route is not selected.
708      */
getVolume()709     public int getVolume() {
710         return mVolume;
711     }
712 
713     /**
714      * Gets the hardware address of the route if available.
715      * @hide
716      */
717     @Nullable
getAddress()718     public String getAddress() {
719         return mAddress;
720     }
721 
722     /**
723      * Gets the deduplication IDs associated to the route.
724      *
725      * <p>Two routes with a matching deduplication ID originate from the same receiver device.
726      */
727     @NonNull
getDeduplicationIds()728     public Set<String> getDeduplicationIds() {
729         return mDeduplicationIds;
730     }
731 
732     /**
733      * Gets an optional bundle with extra data.
734      */
735     @Nullable
getExtras()736     public Bundle getExtras() {
737         return mExtras == null ? null : new Bundle(mExtras);
738     }
739 
740     /**
741      * Gets the original id set by {@link Builder#Builder(String, CharSequence)}.
742      * @hide
743      */
744     @NonNull
745     @TestApi
getOriginalId()746     public String getOriginalId() {
747         return mId;
748     }
749 
750     /**
751      * Gets the provider id of the route. It is assigned automatically by
752      * {@link com.android.server.media.MediaRouterService}.
753      *
754      * @return provider id of the route or null if it's not set.
755      * @hide
756      */
757     @Nullable
getProviderId()758     public String getProviderId() {
759         return mProviderId;
760     }
761 
762     /**
763      * Returns if the route has at least one of the specified route features.
764      *
765      * @param features the list of route features to consider
766      * @return {@code true} if the route has at least one feature in the list
767      * @hide
768      */
hasAnyFeatures(@onNull Collection<String> features)769     public boolean hasAnyFeatures(@NonNull Collection<String> features) {
770         Objects.requireNonNull(features, "features must not be null");
771         for (String feature : features) {
772             if (getFeatures().contains(feature)) {
773                 return true;
774             }
775         }
776         return false;
777     }
778 
779     /**
780      * Returns if the route has all the specified route features.
781      *
782      * @hide
783      */
hasAllFeatures(@onNull Collection<String> features)784     public boolean hasAllFeatures(@NonNull Collection<String> features) {
785         Objects.requireNonNull(features, "features must not be null");
786         for (String feature : features) {
787             if (!getFeatures().contains(feature)) {
788                 return false;
789             }
790         }
791         return true;
792     }
793 
794     /**
795      * Returns true if the route info has all of the required field.
796      * A route is valid if and only if it is obtained from
797      * {@link com.android.server.media.MediaRouterService}.
798      * @hide
799      */
isValid()800     public boolean isValid() {
801         if (TextUtils.isEmpty(getId()) || TextUtils.isEmpty(getName())
802                 || TextUtils.isEmpty(getProviderId())) {
803             return false;
804         }
805         return true;
806     }
807 
808     /**
809      * Returns whether this route is visible to the package with the given name.
810      * @hide
811      */
isVisibleTo(String packageName)812     public boolean isVisibleTo(String packageName) {
813         return !mIsVisibilityRestricted || getPackageName().equals(packageName)
814                 || mAllowedPackages.contains(packageName);
815     }
816 
817     /**
818      * Returns whether this route's type can only be published by the system route provider.
819      *
820      * @see #isSystemRoute()
821      * @hide
822      */
823     // The default case catches all other types.
824     @SuppressLint("SwitchIntDef")
isSystemRouteType()825     public boolean isSystemRouteType() {
826         return switch (mType) {
827             case TYPE_BUILTIN_SPEAKER,
828                             TYPE_BLUETOOTH_A2DP,
829                             TYPE_DOCK,
830                             TYPE_BLE_HEADSET,
831                             TYPE_HEARING_AID,
832                             TYPE_HDMI,
833                             TYPE_HDMI_ARC,
834                             TYPE_HDMI_EARC,
835                             TYPE_USB_ACCESSORY,
836                             TYPE_USB_DEVICE,
837                             TYPE_USB_HEADSET,
838                             TYPE_WIRED_HEADPHONES,
839                             TYPE_WIRED_HEADSET ->
840                     true;
841             default -> false;
842         };
843     }
844 
845     /** Returns the route suitability status. */
846     @SuitabilityStatus
847     @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
getSuitabilityStatus()848     public int getSuitabilityStatus() {
849         return mSuitabilityStatus;
850     }
851 
852     /**
853      * Dumps the current state of the object to the given {@code pw} as a human-readable string.
854      *
855      * <p> Used in the context of dumpsys. </p>
856      *
857      * @hide
858      */
dump(@onNull PrintWriter pw, @NonNull String prefix)859     public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
860         pw.println(prefix + "MediaRoute2Info");
861 
862         String indent = prefix + "  ";
863 
864         pw.println(indent + "mId=" + mId);
865         pw.println(indent + "mName=" + mName);
866         pw.println(indent + "mFeatures=" + mFeatures);
867         pw.println(indent + "mType=" + getDeviceTypeString(mType));
868         pw.println(indent + "mIsSystem=" + mIsSystem);
869         pw.println(indent + "mIconUri=" + mIconUri);
870         pw.println(indent + "mDescription=" + mDescription);
871         pw.println(indent + "mConnectionState=" + mConnectionState);
872         pw.println(indent + "mClientPackageName=" + mClientPackageName);
873         pw.println(indent + "mPackageName=" + mPackageName);
874 
875         dumpVolume(pw, indent);
876 
877         pw.println(indent + "mAddress=" + mAddress);
878         pw.println(indent + "mDeduplicationIds=" + mDeduplicationIds);
879         pw.println(indent + "mExtras=" + mExtras);
880         pw.println(indent + "mProviderId=" + mProviderId);
881         pw.println(indent + "mIsVisibilityRestricted=" + mIsVisibilityRestricted);
882         pw.println(indent + "mAllowedPackages=" + mAllowedPackages);
883         pw.println(indent + "mSuitabilityStatus=" + mSuitabilityStatus);
884     }
885 
dumpVolume(@onNull PrintWriter pw, @NonNull String prefix)886     private void dumpVolume(@NonNull PrintWriter pw, @NonNull String prefix) {
887         pw.println(prefix + getVolumeString(mVolume, mVolumeMax, mVolumeHandling));
888     }
889 
890     @Override
equals(Object obj)891     public boolean equals(Object obj) {
892         if (this == obj) {
893             return true;
894         }
895         if (!(obj instanceof MediaRoute2Info)) {
896             return false;
897         }
898         MediaRoute2Info other = (MediaRoute2Info) obj;
899 
900         // Note: mExtras is not included.
901         return Objects.equals(mId, other.mId)
902                 && Objects.equals(mName, other.mName)
903                 && Objects.equals(mFeatures, other.mFeatures)
904                 && (mType == other.mType)
905                 && (mIsSystem == other.mIsSystem)
906                 && Objects.equals(mIconUri, other.mIconUri)
907                 && Objects.equals(mDescription, other.mDescription)
908                 && (mConnectionState == other.mConnectionState)
909                 && Objects.equals(mClientPackageName, other.mClientPackageName)
910                 && Objects.equals(mPackageName, other.mPackageName)
911                 && (mVolumeHandling == other.mVolumeHandling)
912                 && (mVolumeMax == other.mVolumeMax)
913                 && (mVolume == other.mVolume)
914                 && Objects.equals(mAddress, other.mAddress)
915                 && Objects.equals(mDeduplicationIds, other.mDeduplicationIds)
916                 && Objects.equals(mProviderId, other.mProviderId)
917                 && (mIsVisibilityRestricted == other.mIsVisibilityRestricted)
918                 && Objects.equals(mAllowedPackages, other.mAllowedPackages)
919                 && mSuitabilityStatus == other.mSuitabilityStatus;
920     }
921 
922     @Override
hashCode()923     public int hashCode() {
924         // Note: mExtras is not included.
925         return Objects.hash(
926                 mId,
927                 mName,
928                 mFeatures,
929                 mType,
930                 mIsSystem,
931                 mIconUri,
932                 mDescription,
933                 mConnectionState,
934                 mClientPackageName,
935                 mPackageName,
936                 mVolumeHandling,
937                 mVolumeMax,
938                 mVolume,
939                 mAddress,
940                 mDeduplicationIds,
941                 mProviderId,
942                 mIsVisibilityRestricted,
943                 mAllowedPackages,
944                 mSuitabilityStatus);
945     }
946 
947     @Override
toString()948     public String toString() {
949         // Note: mExtras is not printed here.
950         return new StringBuilder()
951                 .append("MediaRoute2Info{ ")
952                 .append("id=")
953                 .append(getId())
954                 .append(", name=")
955                 .append(getName())
956                 .append(", type=")
957                 .append(getDeviceTypeString(getType()))
958                 .append(", isSystem=")
959                 .append(isSystemRoute())
960                 .append(", features=")
961                 .append(getFeatures())
962                 .append(", iconUri=")
963                 .append(getIconUri())
964                 .append(", description=")
965                 .append(getDescription())
966                 .append(", connectionState=")
967                 .append(getConnectionState())
968                 .append(", clientPackageName=")
969                 .append(getClientPackageName())
970                 .append(", ")
971                 .append(getVolumeString(mVolume, mVolumeMax, mVolumeHandling))
972                 .append(", address=")
973                 .append(getAddress())
974                 .append(", deduplicationIds=")
975                 .append(String.join(",", getDeduplicationIds()))
976                 .append(", providerId=")
977                 .append(getProviderId())
978                 .append(", isVisibilityRestricted=")
979                 .append(mIsVisibilityRestricted)
980                 .append(", allowedPackages=")
981                 .append(String.join(",", mAllowedPackages))
982                 .append(", suitabilityStatus=")
983                 .append(mSuitabilityStatus)
984                 .append(" }")
985                 .toString();
986     }
987 
988     @Override
describeContents()989     public int describeContents() {
990         return 0;
991     }
992 
993     @Override
writeToParcel(@onNull Parcel dest, int flags)994     public void writeToParcel(@NonNull Parcel dest, int flags) {
995         dest.writeString(mId);
996         TextUtils.writeToParcel(mName, dest, flags);
997         dest.writeStringList(mFeatures);
998         dest.writeInt(mType);
999         dest.writeBoolean(mIsSystem);
1000         dest.writeParcelable(mIconUri, flags);
1001         TextUtils.writeToParcel(mDescription, dest, flags);
1002         dest.writeInt(mConnectionState);
1003         dest.writeString(mClientPackageName);
1004         dest.writeString(mPackageName);
1005         dest.writeInt(mVolumeHandling);
1006         dest.writeInt(mVolumeMax);
1007         dest.writeInt(mVolume);
1008         dest.writeString(mAddress);
1009         dest.writeStringArray(mDeduplicationIds.toArray(new String[mDeduplicationIds.size()]));
1010         dest.writeBundle(mExtras);
1011         dest.writeString(mProviderId);
1012         dest.writeBoolean(mIsVisibilityRestricted);
1013         dest.writeString8Array(mAllowedPackages.toArray(new String[0]));
1014         dest.writeInt(mSuitabilityStatus);
1015     }
1016 
1017     /**
1018      * Returns a human readable string describing the given volume values.
1019      *
1020      * @param volume The current volume.
1021      * @param maxVolume The maximum volume.
1022      * @param volumeHandling The {@link PlaybackVolume}.
1023      */
getVolumeString( int volume, int maxVolume, @PlaybackVolume int volumeHandling)1024     /* package */ static String getVolumeString(
1025             int volume, int maxVolume, @PlaybackVolume int volumeHandling) {
1026         String volumeHandlingName;
1027         switch (volumeHandling) {
1028             case PLAYBACK_VOLUME_FIXED:
1029                 volumeHandlingName = "FIXED";
1030                 break;
1031             case PLAYBACK_VOLUME_VARIABLE:
1032                 volumeHandlingName = "VARIABLE";
1033                 break;
1034             default:
1035                 volumeHandlingName = "UNKNOWN";
1036                 break;
1037         }
1038         return String.format(
1039                 Locale.US,
1040                 "volume(current=%d, max=%d, handling=%s(%d))",
1041                 volume,
1042                 maxVolume,
1043                 volumeHandlingName,
1044                 volumeHandling);
1045     }
1046 
getDeviceTypeString(@ype int deviceType)1047     private static String getDeviceTypeString(@Type int deviceType) {
1048         switch (deviceType) {
1049             case TYPE_BUILTIN_SPEAKER:
1050                 return "BUILTIN_SPEAKER";
1051             case TYPE_WIRED_HEADSET:
1052                 return "WIRED_HEADSET";
1053             case TYPE_WIRED_HEADPHONES:
1054                 return "WIRED_HEADPHONES";
1055             case TYPE_BLUETOOTH_A2DP:
1056                 return "BLUETOOTH_A2DP";
1057             case TYPE_HDMI:
1058                 return "HDMI";
1059             case TYPE_HDMI_ARC:
1060                 return "HDMI_ARC";
1061             case TYPE_HDMI_EARC:
1062                 return "HDMI_EARC";
1063             case TYPE_DOCK:
1064                 return "DOCK";
1065             case TYPE_USB_DEVICE:
1066                 return "USB_DEVICE";
1067             case TYPE_USB_ACCESSORY:
1068                 return "USB_ACCESSORY";
1069             case TYPE_USB_HEADSET:
1070                 return "USB_HEADSET";
1071             case TYPE_HEARING_AID:
1072                 return "HEARING_AID";
1073             case TYPE_REMOTE_TV:
1074                 return "REMOTE_TV";
1075             case TYPE_REMOTE_SPEAKER:
1076                 return "REMOTE_SPEAKER";
1077             case TYPE_REMOTE_AUDIO_VIDEO_RECEIVER:
1078                 return "REMOTE_AUDIO_VIDEO_RECEIVER";
1079             case TYPE_REMOTE_TABLET:
1080                 return "REMOTE_TABLET";
1081             case TYPE_REMOTE_TABLET_DOCKED:
1082                 return "REMOTE_TABLET_DOCKED";
1083             case TYPE_REMOTE_COMPUTER:
1084                 return "REMOTE_COMPUTER";
1085             case TYPE_REMOTE_GAME_CONSOLE:
1086                 return "REMOTE_GAME_CONSOLE";
1087             case TYPE_REMOTE_CAR:
1088                 return "REMOTE_CAR";
1089             case TYPE_REMOTE_SMARTWATCH:
1090                 return "REMOTE_SMARTWATCH";
1091             case TYPE_REMOTE_SMARTPHONE:
1092                 return "REMOTE_SMARTPHONE";
1093             case TYPE_GROUP:
1094                 return "GROUP";
1095             case TYPE_UNKNOWN:
1096             default:
1097                 return TextUtils.formatSimple("UNKNOWN(%d)", deviceType);
1098         }
1099     }
1100 
1101     /**
1102      * Builder for {@link MediaRoute2Info media route info}.
1103      */
1104     public static final class Builder {
1105         private final String mId;
1106         private final CharSequence mName;
1107         private final List<String> mFeatures;
1108 
1109         @Type
1110         private int mType = TYPE_UNKNOWN;
1111         private boolean mIsSystem;
1112         private Uri mIconUri;
1113         private CharSequence mDescription;
1114         @ConnectionState
1115         private int mConnectionState;
1116         private String mClientPackageName;
1117         private String mPackageName;
1118         @PlaybackVolume private int mVolumeHandling = PLAYBACK_VOLUME_FIXED;
1119         private int mVolumeMax;
1120         private int mVolume;
1121         private String mAddress;
1122         private Set<String> mDeduplicationIds;
1123         private Bundle mExtras;
1124         private String mProviderId;
1125         private boolean mIsVisibilityRestricted;
1126         private Set<String> mAllowedPackages;
1127         @SuitabilityStatus private int mSuitabilityStatus;
1128 
1129         /**
1130          * Constructor for builder to create {@link MediaRoute2Info}.
1131          * <p>
1132          * In order to ensure ID uniqueness, the {@link MediaRoute2Info#getId() ID} of a route info
1133          * obtained from {@link MediaRouter2} can be different from what was set in
1134          * {@link MediaRoute2ProviderService}.
1135          * </p>
1136          * @param id The ID of the route. Must not be empty.
1137          * @param name The user-visible name of the route.
1138          */
Builder(@onNull String id, @NonNull CharSequence name)1139         public Builder(@NonNull String id, @NonNull CharSequence name) {
1140             if (TextUtils.isEmpty(id)) {
1141                 throw new IllegalArgumentException("id must not be empty");
1142             }
1143             if (TextUtils.isEmpty(name)) {
1144                 throw new IllegalArgumentException("name must not be empty");
1145             }
1146             mId = id;
1147             mName = name;
1148             mFeatures = new ArrayList<>();
1149             mDeduplicationIds = Set.of();
1150             mAllowedPackages = Set.of();
1151             mSuitabilityStatus = SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER;
1152         }
1153 
1154         /**
1155          * Constructor for builder to create {@link MediaRoute2Info} with existing
1156          * {@link MediaRoute2Info} instance.
1157          *
1158          * @param routeInfo the existing instance to copy data from.
1159          */
Builder(@onNull MediaRoute2Info routeInfo)1160         public Builder(@NonNull MediaRoute2Info routeInfo) {
1161             this(routeInfo.mId, routeInfo);
1162         }
1163 
1164         /**
1165          * Constructor for builder to create {@link MediaRoute2Info} with existing
1166          * {@link MediaRoute2Info} instance and replace ID with the given {@code id}.
1167          *
1168          * @param id The ID of the new route. Must not be empty.
1169          * @param routeInfo the existing instance to copy data from.
1170          * @hide
1171          */
Builder(@onNull String id, @NonNull MediaRoute2Info routeInfo)1172         public Builder(@NonNull String id, @NonNull MediaRoute2Info routeInfo) {
1173             if (TextUtils.isEmpty(id)) {
1174                 throw new IllegalArgumentException("id must not be empty");
1175             }
1176             Objects.requireNonNull(routeInfo, "routeInfo must not be null");
1177 
1178             mId = id;
1179             mName = routeInfo.mName;
1180             mFeatures = new ArrayList<>(routeInfo.mFeatures);
1181             mType = routeInfo.mType;
1182             mIsSystem = routeInfo.mIsSystem;
1183             mIconUri = routeInfo.mIconUri;
1184             mDescription = routeInfo.mDescription;
1185             mConnectionState = routeInfo.mConnectionState;
1186             mClientPackageName = routeInfo.mClientPackageName;
1187             mPackageName = routeInfo.mPackageName;
1188             mVolumeHandling = routeInfo.mVolumeHandling;
1189             mVolumeMax = routeInfo.mVolumeMax;
1190             mVolume = routeInfo.mVolume;
1191             mAddress = routeInfo.mAddress;
1192             mDeduplicationIds = Set.copyOf(routeInfo.mDeduplicationIds);
1193             if (routeInfo.mExtras != null) {
1194                 mExtras = new Bundle(routeInfo.mExtras);
1195             }
1196             mProviderId = routeInfo.mProviderId;
1197             mIsVisibilityRestricted = routeInfo.mIsVisibilityRestricted;
1198             mAllowedPackages = routeInfo.mAllowedPackages;
1199             mSuitabilityStatus = routeInfo.mSuitabilityStatus;
1200         }
1201 
1202         /**
1203          * Adds a feature for the route.
1204          * @param feature a feature that the route has. May be one of predefined features
1205          *                such as {@link #FEATURE_LIVE_AUDIO}, {@link #FEATURE_LIVE_VIDEO} or
1206          *                {@link #FEATURE_REMOTE_PLAYBACK} or a custom feature defined by
1207          *                a provider.
1208          *
1209          * @see #addFeatures(Collection)
1210          */
1211         @NonNull
addFeature(@onNull String feature)1212         public Builder addFeature(@NonNull String feature) {
1213             if (TextUtils.isEmpty(feature)) {
1214                 throw new IllegalArgumentException("feature must not be null or empty");
1215             }
1216             mFeatures.add(feature);
1217             return this;
1218         }
1219 
1220         /**
1221          * Adds features for the route. A route must support at least one route type.
1222          * @param features features that the route has. May include predefined features
1223          *                such as {@link #FEATURE_LIVE_AUDIO}, {@link #FEATURE_LIVE_VIDEO} or
1224          *                {@link #FEATURE_REMOTE_PLAYBACK} or custom features defined by
1225          *                a provider.
1226          *
1227          * @see #addFeature(String)
1228          */
1229         @NonNull
addFeatures(@onNull Collection<String> features)1230         public Builder addFeatures(@NonNull Collection<String> features) {
1231             Objects.requireNonNull(features, "features must not be null");
1232             for (String feature : features) {
1233                 addFeature(feature);
1234             }
1235             return this;
1236         }
1237 
1238         /**
1239          * Clears the features of the route. A route must support at least one route type.
1240          */
1241         @NonNull
clearFeatures()1242         public Builder clearFeatures() {
1243             mFeatures.clear();
1244             return this;
1245         }
1246 
1247         /**
1248          * Sets the route's type.
1249          *
1250          * @see MediaRoute2Info#getType()
1251          */
1252         @NonNull
setType(@ype int type)1253         public Builder setType(@Type int type) {
1254             mType = type;
1255             return this;
1256         }
1257 
1258         /**
1259          * Sets whether the route is a system route or not.
1260          * @hide
1261          */
1262         @NonNull
setSystemRoute(boolean isSystem)1263         public Builder setSystemRoute(boolean isSystem) {
1264             mIsSystem = isSystem;
1265             return this;
1266         }
1267 
1268         /**
1269          * Sets the URI of the icon representing this route.
1270          * <p>
1271          * This icon will be used in picker UIs if available.
1272          * </p><p>
1273          * The URI must be one of the following formats:
1274          * <ul>
1275          * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
1276          * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
1277          * </li>
1278          * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
1279          * </ul>
1280          * </p>
1281          */
1282         @NonNull
setIconUri(@ullable Uri iconUri)1283         public Builder setIconUri(@Nullable Uri iconUri) {
1284             mIconUri = iconUri;
1285             return this;
1286         }
1287 
1288         /**
1289          * Sets the user-visible description of the route.
1290          */
1291         @NonNull
setDescription(@ullable CharSequence description)1292         public Builder setDescription(@Nullable CharSequence description) {
1293             mDescription = description;
1294             return this;
1295         }
1296 
1297         /**
1298         * Sets the route's connection state.
1299         *
1300         * {@link #CONNECTION_STATE_DISCONNECTED},
1301         * {@link #CONNECTION_STATE_CONNECTING}, or
1302         * {@link #CONNECTION_STATE_CONNECTED}.
1303         */
1304         @NonNull
setConnectionState(@onnectionState int connectionState)1305         public Builder setConnectionState(@ConnectionState int connectionState) {
1306             mConnectionState = connectionState;
1307             return this;
1308         }
1309 
1310         /**
1311          * Sets the package name of the app using the route.
1312          */
1313         @NonNull
setClientPackageName(@ullable String packageName)1314         public Builder setClientPackageName(@Nullable String packageName) {
1315             mClientPackageName = packageName;
1316             return this;
1317         }
1318 
1319         /**
1320          * Sets the package name of the route.
1321          * @hide
1322          */
1323         @NonNull
setPackageName(@onNull String packageName)1324         public Builder setPackageName(@NonNull String packageName) {
1325             mPackageName = packageName;
1326             return this;
1327         }
1328 
1329         /**
1330          * Sets the route's volume handling.
1331          */
1332         @NonNull
setVolumeHandling(@laybackVolume int volumeHandling)1333         public Builder setVolumeHandling(@PlaybackVolume int volumeHandling) {
1334             mVolumeHandling = volumeHandling;
1335             return this;
1336         }
1337 
1338         /**
1339          * Sets the route's maximum volume, or 0 if unknown.
1340          */
1341         @NonNull
setVolumeMax(int volumeMax)1342         public Builder setVolumeMax(int volumeMax) {
1343             mVolumeMax = volumeMax;
1344             return this;
1345         }
1346 
1347         /**
1348          * Sets the route's current volume, or 0 if unknown.
1349          */
1350         @NonNull
setVolume(int volume)1351         public Builder setVolume(int volume) {
1352             mVolume = volume;
1353             return this;
1354         }
1355 
1356         /**
1357          * Sets the hardware address of the route.
1358          * @hide
1359          */
1360         @NonNull
setAddress(String address)1361         public Builder setAddress(String address) {
1362             mAddress = address;
1363             return this;
1364         }
1365 
1366         /**
1367          * Sets the {@link MediaRoute2Info#getDeduplicationIds() deduplication IDs} of the route.
1368          */
1369         @NonNull
setDeduplicationIds(@onNull Set<String> id)1370         public Builder setDeduplicationIds(@NonNull Set<String> id) {
1371             mDeduplicationIds = Set.copyOf(id);
1372             return this;
1373         }
1374 
1375         /**
1376          * Sets a bundle of extras for the route.
1377          * <p>
1378          * Note: The extras will not affect the result of {@link MediaRoute2Info#equals(Object)}.
1379          */
1380         @NonNull
setExtras(@ullable Bundle extras)1381         public Builder setExtras(@Nullable Bundle extras) {
1382             if (extras == null) {
1383                 mExtras = null;
1384                 return this;
1385             }
1386             mExtras = new Bundle(extras);
1387             return this;
1388         }
1389 
1390         /**
1391          * Sets the provider id of the route.
1392          * @hide
1393          */
1394         @NonNull
setProviderId(@onNull String providerId)1395         public Builder setProviderId(@NonNull String providerId) {
1396             if (TextUtils.isEmpty(providerId)) {
1397                 throw new IllegalArgumentException("providerId must not be null or empty");
1398             }
1399             mProviderId = providerId;
1400             return this;
1401         }
1402 
1403         /**
1404          * Sets the visibility of this route to public.
1405          *
1406          * <p>By default, unless you call {@link #setVisibilityRestricted}, the new route will be
1407          * public.
1408          *
1409          * <p>Public routes are visible to any application with a matching {@link
1410          * RouteDiscoveryPreference#getPreferredFeatures feature}.
1411          *
1412          * <p>Calls to this method override previous calls to {@link #setVisibilityPublic} and
1413          * {@link #setVisibilityRestricted}.
1414          */
1415         @NonNull
setVisibilityPublic()1416         public Builder setVisibilityPublic() {
1417             mIsVisibilityRestricted = false;
1418             mAllowedPackages = Set.of();
1419             return this;
1420         }
1421 
1422         /**
1423          * Sets the visibility of this route to restricted.
1424          *
1425          * <p>Routes with restricted visibility are only visible to its publisher application and
1426          * applications whose package name is included in the provided {@code allowedPackages} set
1427          * with a matching {@link RouteDiscoveryPreference#getPreferredFeatures feature}.
1428          *
1429          * <p>Calls to this method override previous calls to {@link #setVisibilityPublic} and
1430          * {@link #setVisibilityRestricted}.
1431          *
1432          * @see #setVisibilityPublic
1433          * @param allowedPackages set of package names which are allowed to see this route.
1434          */
1435         @NonNull
setVisibilityRestricted(@onNull Set<String> allowedPackages)1436         public Builder setVisibilityRestricted(@NonNull Set<String> allowedPackages) {
1437             mIsVisibilityRestricted = true;
1438             mAllowedPackages = Set.copyOf(allowedPackages);
1439             return this;
1440         }
1441 
1442         /**
1443          * Sets route suitability status.
1444          *
1445          * <p>The default value is {@link
1446          * MediaRoute2Info#SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER}.
1447          *
1448          * <p> Apps are not supposed to set {@link
1449          * MediaRoute2Info#SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER}. Publishing a non-system
1450          * route with such status throws {@link SecurityException}.
1451          */
1452         @NonNull
1453         @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
setSuitabilityStatus(@uitabilityStatus int suitabilityStatus)1454         public Builder setSuitabilityStatus(@SuitabilityStatus int suitabilityStatus) {
1455             mSuitabilityStatus = suitabilityStatus;
1456             return this;
1457         }
1458 
1459         /**
1460          * Builds the {@link MediaRoute2Info media route info}.
1461          *
1462          * @throws IllegalArgumentException if no features are added.
1463          */
1464         @NonNull
build()1465         public MediaRoute2Info build() {
1466             if (mFeatures.isEmpty()) {
1467                 throw new IllegalArgumentException("features must not be empty!");
1468             }
1469             return new MediaRoute2Info(this);
1470         }
1471     }
1472 }
1473