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