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.e. 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