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 android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.res.Resources; 22 import android.os.Bundle; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 import android.text.TextUtils; 26 27 import com.android.internal.util.Preconditions; 28 29 import java.io.FileDescriptor; 30 import java.io.PrintWriter; 31 import java.util.ArrayList; 32 import java.util.Collections; 33 import java.util.List; 34 import java.util.Objects; 35 36 /** 37 * Describes a routing session which is created when a media route is selected. 38 */ 39 public final class RoutingSessionInfo implements Parcelable { 40 @NonNull 41 public static final Creator<RoutingSessionInfo> CREATOR = 42 new Creator<RoutingSessionInfo>() { 43 @Override 44 public RoutingSessionInfo createFromParcel(Parcel in) { 45 return new RoutingSessionInfo(in); 46 } 47 @Override 48 public RoutingSessionInfo[] newArray(int size) { 49 return new RoutingSessionInfo[size]; 50 } 51 }; 52 53 private static final String TAG = "RoutingSessionInfo"; 54 55 private static final String KEY_GROUP_ROUTE = "androidx.mediarouter.media.KEY_GROUP_ROUTE"; 56 private static final String KEY_VOLUME_HANDLING = "volumeHandling"; 57 58 final String mId; 59 final CharSequence mName; 60 final String mOwnerPackageName; 61 final String mClientPackageName; 62 @Nullable 63 final String mProviderId; 64 final List<String> mSelectedRoutes; 65 final List<String> mSelectableRoutes; 66 final List<String> mDeselectableRoutes; 67 final List<String> mTransferableRoutes; 68 69 final int mVolumeHandling; 70 final int mVolumeMax; 71 final int mVolume; 72 73 @Nullable 74 final Bundle mControlHints; 75 final boolean mIsSystemSession; 76 77 RoutingSessionInfo(@onNull Builder builder)78 RoutingSessionInfo(@NonNull Builder builder) { 79 Objects.requireNonNull(builder, "builder must not be null."); 80 81 mId = builder.mId; 82 mName = builder.mName; 83 mOwnerPackageName = builder.mOwnerPackageName; 84 mClientPackageName = builder.mClientPackageName; 85 mProviderId = builder.mProviderId; 86 87 mSelectedRoutes = Collections.unmodifiableList( 88 convertToUniqueRouteIds(builder.mSelectedRoutes)); 89 mSelectableRoutes = Collections.unmodifiableList( 90 convertToUniqueRouteIds(builder.mSelectableRoutes)); 91 mDeselectableRoutes = Collections.unmodifiableList( 92 convertToUniqueRouteIds(builder.mDeselectableRoutes)); 93 mTransferableRoutes = Collections.unmodifiableList( 94 convertToUniqueRouteIds(builder.mTransferableRoutes)); 95 96 mVolumeMax = builder.mVolumeMax; 97 mVolume = builder.mVolume; 98 99 mIsSystemSession = builder.mIsSystemSession; 100 101 boolean volumeAdjustmentForRemoteGroupSessions = Resources.getSystem().getBoolean( 102 com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions); 103 mVolumeHandling = defineVolumeHandling(builder.mVolumeHandling, mSelectedRoutes, 104 volumeAdjustmentForRemoteGroupSessions); 105 106 mControlHints = updateVolumeHandlingInHints(builder.mControlHints, mVolumeHandling); 107 } 108 RoutingSessionInfo(@onNull Parcel src)109 RoutingSessionInfo(@NonNull Parcel src) { 110 mId = src.readString(); 111 Preconditions.checkArgument(!TextUtils.isEmpty(mId)); 112 113 mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(src); 114 mOwnerPackageName = src.readString(); 115 mClientPackageName = ensureString(src.readString()); 116 mProviderId = src.readString(); 117 118 mSelectedRoutes = ensureList(src.createStringArrayList()); 119 Preconditions.checkArgument(!mSelectedRoutes.isEmpty()); 120 121 mSelectableRoutes = ensureList(src.createStringArrayList()); 122 mDeselectableRoutes = ensureList(src.createStringArrayList()); 123 mTransferableRoutes = ensureList(src.createStringArrayList()); 124 125 mVolumeHandling = src.readInt(); 126 mVolumeMax = src.readInt(); 127 mVolume = src.readInt(); 128 129 mControlHints = src.readBundle(); 130 mIsSystemSession = src.readBoolean(); 131 } 132 updateVolumeHandlingInHints(Bundle controlHints, int volumeHandling)133 private static Bundle updateVolumeHandlingInHints(Bundle controlHints, int volumeHandling) { 134 // Workaround to preserve retro-compatibility with androidx. 135 // See b/228021646 for more details. 136 if (controlHints != null && controlHints.containsKey(KEY_GROUP_ROUTE)) { 137 Bundle groupRoute = controlHints.getBundle(KEY_GROUP_ROUTE); 138 139 if (groupRoute != null && groupRoute.containsKey(KEY_VOLUME_HANDLING) 140 && volumeHandling != groupRoute.getInt(KEY_VOLUME_HANDLING)) { 141 //Creating copy of controlHints with updated value. 142 Bundle newGroupRoute = new Bundle(groupRoute); 143 newGroupRoute.putInt(KEY_VOLUME_HANDLING, volumeHandling); 144 Bundle newControlHints = new Bundle(controlHints); 145 newControlHints.putBundle(KEY_GROUP_ROUTE, newGroupRoute); 146 return newControlHints; 147 } 148 } 149 //Return same Bundle. 150 return controlHints; 151 } 152 defineVolumeHandling(int volumeHandling, List<String> selectedRoutes, boolean volumeAdjustmentForRemoteGroupSessions)153 private static int defineVolumeHandling(int volumeHandling, List<String> selectedRoutes, 154 boolean volumeAdjustmentForRemoteGroupSessions) { 155 if (!volumeAdjustmentForRemoteGroupSessions && selectedRoutes.size() > 1) { 156 return MediaRoute2Info.PLAYBACK_VOLUME_FIXED; 157 } 158 return volumeHandling; 159 } 160 ensureString(String str)161 private static String ensureString(String str) { 162 return str != null ? str : ""; 163 } 164 ensureList(List<? extends T> list)165 private static <T> List<T> ensureList(List<? extends T> list) { 166 if (list != null) { 167 return Collections.unmodifiableList(list); 168 } 169 return Collections.emptyList(); 170 } 171 172 /** 173 * Gets the id of the session. The sessions which are given by {@link MediaRouter2} will have 174 * unique IDs. 175 * <p> 176 * In order to ensure uniqueness in {@link MediaRouter2} side, the value of this method 177 * can be different from what was set in {@link MediaRoute2ProviderService}. 178 * 179 * @see Builder#Builder(String, String) 180 */ 181 @NonNull getId()182 public String getId() { 183 if (!TextUtils.isEmpty(mProviderId)) { 184 return MediaRouter2Utils.toUniqueId(mProviderId, mId); 185 } else { 186 return mId; 187 } 188 } 189 190 /** 191 * Gets the user-visible name of the session. It may be {@code null}. 192 */ 193 @Nullable getName()194 public CharSequence getName() { 195 return mName; 196 } 197 198 /** 199 * Gets the original id set by {@link Builder#Builder(String, String)}. 200 * @hide 201 */ 202 @NonNull getOriginalId()203 public String getOriginalId() { 204 return mId; 205 } 206 207 /** 208 * Gets the package name of the session owner. 209 * @hide 210 */ 211 @Nullable getOwnerPackageName()212 public String getOwnerPackageName() { 213 return mOwnerPackageName; 214 } 215 216 /** 217 * Gets the client package name of the session 218 */ 219 @NonNull getClientPackageName()220 public String getClientPackageName() { 221 return mClientPackageName; 222 } 223 224 /** 225 * Gets the provider id of the session. 226 * @hide 227 */ 228 @Nullable getProviderId()229 public String getProviderId() { 230 return mProviderId; 231 } 232 233 /** 234 * Gets the list of IDs of selected routes for the session. It shouldn't be empty. 235 */ 236 @NonNull getSelectedRoutes()237 public List<String> getSelectedRoutes() { 238 return mSelectedRoutes; 239 } 240 241 /** 242 * Gets the list of IDs of selectable routes for the session. 243 */ 244 @NonNull getSelectableRoutes()245 public List<String> getSelectableRoutes() { 246 return mSelectableRoutes; 247 } 248 249 /** 250 * Gets the list of IDs of deselectable routes for the session. 251 */ 252 @NonNull getDeselectableRoutes()253 public List<String> getDeselectableRoutes() { 254 return mDeselectableRoutes; 255 } 256 257 /** 258 * Gets the list of IDs of transferable routes for the session. 259 */ 260 @NonNull getTransferableRoutes()261 public List<String> getTransferableRoutes() { 262 return mTransferableRoutes; 263 } 264 265 /** 266 * Gets the information about how volume is handled on the session. 267 * 268 * @return {@link MediaRoute2Info#PLAYBACK_VOLUME_FIXED} or 269 * {@link MediaRoute2Info#PLAYBACK_VOLUME_VARIABLE}. 270 */ 271 @MediaRoute2Info.PlaybackVolume getVolumeHandling()272 public int getVolumeHandling() { 273 return mVolumeHandling; 274 } 275 276 /** 277 * Gets the maximum volume of the session. 278 */ getVolumeMax()279 public int getVolumeMax() { 280 return mVolumeMax; 281 } 282 283 /** 284 * Gets the current volume of the session. 285 * <p> 286 * When it's available, it represents the volume of routing session, which is a group 287 * of selected routes. To get the volume of each route, use {@link MediaRoute2Info#getVolume()}. 288 * </p> 289 * @see MediaRoute2Info#getVolume() 290 */ getVolume()291 public int getVolume() { 292 return mVolume; 293 } 294 295 /** 296 * Gets the control hints 297 */ 298 @Nullable getControlHints()299 public Bundle getControlHints() { 300 return mControlHints; 301 } 302 303 /** 304 * Gets whether this session is in system media route provider. 305 * @hide 306 */ 307 @Nullable isSystemSession()308 public boolean isSystemSession() { 309 return mIsSystemSession; 310 } 311 312 @Override describeContents()313 public int describeContents() { 314 return 0; 315 } 316 317 @Override writeToParcel(@onNull Parcel dest, int flags)318 public void writeToParcel(@NonNull Parcel dest, int flags) { 319 dest.writeString(mId); 320 dest.writeCharSequence(mName); 321 dest.writeString(mOwnerPackageName); 322 dest.writeString(mClientPackageName); 323 dest.writeString(mProviderId); 324 dest.writeStringList(mSelectedRoutes); 325 dest.writeStringList(mSelectableRoutes); 326 dest.writeStringList(mDeselectableRoutes); 327 dest.writeStringList(mTransferableRoutes); 328 dest.writeInt(mVolumeHandling); 329 dest.writeInt(mVolumeMax); 330 dest.writeInt(mVolume); 331 dest.writeBundle(mControlHints); 332 dest.writeBoolean(mIsSystemSession); 333 } 334 335 /** 336 * Dumps current state of the instance. Use with {@code dumpsys}. 337 * 338 * See {@link android.os.Binder#dump(FileDescriptor, PrintWriter, String[])}. 339 * 340 * @hide 341 */ dump(@onNull PrintWriter pw, @NonNull String prefix)342 public void dump(@NonNull PrintWriter pw, @NonNull String prefix) { 343 pw.println(prefix + "RoutingSessionInfo"); 344 345 String indent = prefix + " "; 346 347 pw.println(indent + "mId=" + mId); 348 pw.println(indent + "mName=" + mName); 349 pw.println(indent + "mOwnerPackageName=" + mOwnerPackageName); 350 pw.println(indent + "mClientPackageName=" + mClientPackageName); 351 pw.println(indent + "mProviderId=" + mProviderId); 352 pw.println(indent + "mSelectedRoutes=" + mSelectedRoutes); 353 pw.println(indent + "mSelectableRoutes=" + mSelectableRoutes); 354 pw.println(indent + "mDeselectableRoutes=" + mDeselectableRoutes); 355 pw.println(indent + "mTransferableRoutes=" + mTransferableRoutes); 356 pw.println(indent + "mVolumeHandling=" + mVolumeHandling); 357 pw.println(indent + "mVolumeMax=" + mVolumeMax); 358 pw.println(indent + "mVolume=" + mVolume); 359 pw.println(indent + "mControlHints=" + mControlHints); 360 pw.println(indent + "mIsSystemSession=" + mIsSystemSession); 361 } 362 363 @Override equals(Object obj)364 public boolean equals(Object obj) { 365 if (this == obj) { 366 return true; 367 } 368 if (!(obj instanceof RoutingSessionInfo)) { 369 return false; 370 } 371 372 RoutingSessionInfo other = (RoutingSessionInfo) obj; 373 return Objects.equals(mId, other.mId) 374 && Objects.equals(mName, other.mName) 375 && Objects.equals(mOwnerPackageName, other.mOwnerPackageName) 376 && Objects.equals(mClientPackageName, other.mClientPackageName) 377 && Objects.equals(mProviderId, other.mProviderId) 378 && Objects.equals(mSelectedRoutes, other.mSelectedRoutes) 379 && Objects.equals(mSelectableRoutes, other.mSelectableRoutes) 380 && Objects.equals(mDeselectableRoutes, other.mDeselectableRoutes) 381 && Objects.equals(mTransferableRoutes, other.mTransferableRoutes) 382 && (mVolumeHandling == other.mVolumeHandling) 383 && (mVolumeMax == other.mVolumeMax) 384 && (mVolume == other.mVolume); 385 } 386 387 @Override hashCode()388 public int hashCode() { 389 return Objects.hash(mId, mName, mOwnerPackageName, mClientPackageName, mProviderId, 390 mSelectedRoutes, mSelectableRoutes, mDeselectableRoutes, mTransferableRoutes, 391 mVolumeMax, mVolumeHandling, mVolume); 392 } 393 394 @Override toString()395 public String toString() { 396 StringBuilder result = new StringBuilder() 397 .append("RoutingSessionInfo{ ") 398 .append("sessionId=").append(getId()) 399 .append(", name=").append(getName()) 400 .append(", clientPackageName=").append(getClientPackageName()) 401 .append(", selectedRoutes={") 402 .append(String.join(",", getSelectedRoutes())) 403 .append("}") 404 .append(", selectableRoutes={") 405 .append(String.join(",", getSelectableRoutes())) 406 .append("}") 407 .append(", deselectableRoutes={") 408 .append(String.join(",", getDeselectableRoutes())) 409 .append("}") 410 .append(", transferableRoutes={") 411 .append(String.join(",", getTransferableRoutes())) 412 .append("}") 413 .append(", volumeHandling=").append(getVolumeHandling()) 414 .append(", volumeMax=").append(getVolumeMax()) 415 .append(", volume=").append(getVolume()) 416 .append(" }"); 417 return result.toString(); 418 } 419 420 /** 421 * Provides a new list with unique route IDs if {@link #mProviderId} is set, or the original IDs 422 * otherwise. 423 * 424 * @param routeIds list of route IDs to convert 425 * @return new list with unique IDs or original IDs 426 */ 427 428 @NonNull convertToUniqueRouteIds(@onNull List<String> routeIds)429 private List<String> convertToUniqueRouteIds(@NonNull List<String> routeIds) { 430 Objects.requireNonNull(routeIds, "RouteIds cannot be null."); 431 432 // mProviderId can be null if not set. Return the original list for this case. 433 if (TextUtils.isEmpty(mProviderId)) { 434 return new ArrayList<>(routeIds); 435 } 436 437 List<String> result = new ArrayList<>(); 438 for (String routeId : routeIds) { 439 result.add(MediaRouter2Utils.toUniqueId(mProviderId, routeId)); 440 } 441 return result; 442 } 443 444 /** 445 * Builder class for {@link RoutingSessionInfo}. 446 */ 447 public static final class Builder { 448 // TODO: Reorder these (important ones first) 449 final String mId; 450 CharSequence mName; 451 String mOwnerPackageName; 452 String mClientPackageName; 453 String mProviderId; 454 final List<String> mSelectedRoutes; 455 final List<String> mSelectableRoutes; 456 final List<String> mDeselectableRoutes; 457 final List<String> mTransferableRoutes; 458 int mVolumeHandling = MediaRoute2Info.PLAYBACK_VOLUME_FIXED; 459 int mVolumeMax; 460 int mVolume; 461 Bundle mControlHints; 462 boolean mIsSystemSession; 463 464 /** 465 * Constructor for builder to create {@link RoutingSessionInfo}. 466 * <p> 467 * In order to ensure ID uniqueness in {@link MediaRouter2} side, the value of 468 * {@link RoutingSessionInfo#getId()} can be different from what was set in 469 * {@link MediaRoute2ProviderService}. 470 * </p> 471 * 472 * @param id ID of the session. Must not be empty. 473 * @param clientPackageName package name of the client app which uses this session. 474 * If is is unknown, then just use an empty string. 475 * @see MediaRoute2Info#getId() 476 */ Builder(@onNull String id, @NonNull String clientPackageName)477 public Builder(@NonNull String id, @NonNull String clientPackageName) { 478 if (TextUtils.isEmpty(id)) { 479 throw new IllegalArgumentException("id must not be empty"); 480 } 481 482 mId = id; 483 mClientPackageName = 484 Objects.requireNonNull(clientPackageName, "clientPackageName must not be null"); 485 mSelectedRoutes = new ArrayList<>(); 486 mSelectableRoutes = new ArrayList<>(); 487 mDeselectableRoutes = new ArrayList<>(); 488 mTransferableRoutes = new ArrayList<>(); 489 } 490 491 /** 492 * Constructor for builder to create {@link RoutingSessionInfo} with 493 * existing {@link RoutingSessionInfo} instance. 494 * 495 * @param sessionInfo the existing instance to copy data from. 496 */ Builder(@onNull RoutingSessionInfo sessionInfo)497 public Builder(@NonNull RoutingSessionInfo sessionInfo) { 498 Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); 499 500 mId = sessionInfo.mId; 501 mName = sessionInfo.mName; 502 mClientPackageName = sessionInfo.mClientPackageName; 503 mProviderId = sessionInfo.mProviderId; 504 505 mSelectedRoutes = new ArrayList<>(sessionInfo.mSelectedRoutes); 506 mSelectableRoutes = new ArrayList<>(sessionInfo.mSelectableRoutes); 507 mDeselectableRoutes = new ArrayList<>(sessionInfo.mDeselectableRoutes); 508 mTransferableRoutes = new ArrayList<>(sessionInfo.mTransferableRoutes); 509 510 if (mProviderId != null) { 511 // They must have unique IDs. 512 mSelectedRoutes.replaceAll(MediaRouter2Utils::getOriginalId); 513 mSelectableRoutes.replaceAll(MediaRouter2Utils::getOriginalId); 514 mDeselectableRoutes.replaceAll(MediaRouter2Utils::getOriginalId); 515 mTransferableRoutes.replaceAll(MediaRouter2Utils::getOriginalId); 516 } 517 518 mVolumeHandling = sessionInfo.mVolumeHandling; 519 mVolumeMax = sessionInfo.mVolumeMax; 520 mVolume = sessionInfo.mVolume; 521 522 mControlHints = sessionInfo.mControlHints; 523 mIsSystemSession = sessionInfo.mIsSystemSession; 524 } 525 526 /** 527 * Sets the user-visible name of the session. 528 */ 529 @NonNull setName(@ullable CharSequence name)530 public Builder setName(@Nullable CharSequence name) { 531 mName = name; 532 return this; 533 } 534 535 /** 536 * Sets the package name of the session owner. It is expected to be called by the system. 537 * 538 * @hide 539 */ 540 @NonNull setOwnerPackageName(@ullable String packageName)541 public Builder setOwnerPackageName(@Nullable String packageName) { 542 mOwnerPackageName = packageName; 543 return this; 544 } 545 546 /** 547 * Sets the client package name of the session. 548 * 549 * @hide 550 */ 551 @NonNull setClientPackageName(@ullable String packageName)552 public Builder setClientPackageName(@Nullable String packageName) { 553 mClientPackageName = packageName; 554 return this; 555 } 556 557 /** 558 * Sets the provider ID of the session. 559 * 560 * @hide 561 */ 562 @NonNull setProviderId(@onNull String providerId)563 public Builder setProviderId(@NonNull String providerId) { 564 if (TextUtils.isEmpty(providerId)) { 565 throw new IllegalArgumentException("providerId must not be empty"); 566 } 567 mProviderId = providerId; 568 return this; 569 } 570 571 /** 572 * Clears the selected routes. 573 */ 574 @NonNull clearSelectedRoutes()575 public Builder clearSelectedRoutes() { 576 mSelectedRoutes.clear(); 577 return this; 578 } 579 580 /** 581 * Adds a route to the selected routes. The {@code routeId} must not be empty. 582 */ 583 @NonNull addSelectedRoute(@onNull String routeId)584 public Builder addSelectedRoute(@NonNull String routeId) { 585 if (TextUtils.isEmpty(routeId)) { 586 throw new IllegalArgumentException("routeId must not be empty"); 587 } 588 mSelectedRoutes.add(routeId); 589 return this; 590 } 591 592 /** 593 * Removes a route from the selected routes. The {@code routeId} must not be empty. 594 */ 595 @NonNull removeSelectedRoute(@onNull String routeId)596 public Builder removeSelectedRoute(@NonNull String routeId) { 597 if (TextUtils.isEmpty(routeId)) { 598 throw new IllegalArgumentException("routeId must not be empty"); 599 } 600 mSelectedRoutes.remove(routeId); 601 return this; 602 } 603 604 /** 605 * Clears the selectable routes. 606 */ 607 @NonNull clearSelectableRoutes()608 public Builder clearSelectableRoutes() { 609 mSelectableRoutes.clear(); 610 return this; 611 } 612 613 /** 614 * Adds a route to the selectable routes. The {@code routeId} must not be empty. 615 */ 616 @NonNull addSelectableRoute(@onNull String routeId)617 public Builder addSelectableRoute(@NonNull String routeId) { 618 if (TextUtils.isEmpty(routeId)) { 619 throw new IllegalArgumentException("routeId must not be empty"); 620 } 621 mSelectableRoutes.add(routeId); 622 return this; 623 } 624 625 /** 626 * Removes a route from the selectable routes. The {@code routeId} must not be empty. 627 */ 628 @NonNull removeSelectableRoute(@onNull String routeId)629 public Builder removeSelectableRoute(@NonNull String routeId) { 630 if (TextUtils.isEmpty(routeId)) { 631 throw new IllegalArgumentException("routeId must not be empty"); 632 } 633 mSelectableRoutes.remove(routeId); 634 return this; 635 } 636 637 /** 638 * Clears the deselectable routes. 639 */ 640 @NonNull clearDeselectableRoutes()641 public Builder clearDeselectableRoutes() { 642 mDeselectableRoutes.clear(); 643 return this; 644 } 645 646 /** 647 * Adds a route to the deselectable routes. The {@code routeId} must not be empty. 648 */ 649 @NonNull addDeselectableRoute(@onNull String routeId)650 public Builder addDeselectableRoute(@NonNull String routeId) { 651 if (TextUtils.isEmpty(routeId)) { 652 throw new IllegalArgumentException("routeId must not be empty"); 653 } 654 mDeselectableRoutes.add(routeId); 655 return this; 656 } 657 658 /** 659 * Removes a route from the deselectable routes. The {@code routeId} must not be empty. 660 */ 661 @NonNull removeDeselectableRoute(@onNull String routeId)662 public Builder removeDeselectableRoute(@NonNull String routeId) { 663 if (TextUtils.isEmpty(routeId)) { 664 throw new IllegalArgumentException("routeId must not be empty"); 665 } 666 mDeselectableRoutes.remove(routeId); 667 return this; 668 } 669 670 /** 671 * Clears the transferable routes. 672 */ 673 @NonNull clearTransferableRoutes()674 public Builder clearTransferableRoutes() { 675 mTransferableRoutes.clear(); 676 return this; 677 } 678 679 /** 680 * Adds a route to the transferable routes. The {@code routeId} must not be empty. 681 */ 682 @NonNull addTransferableRoute(@onNull String routeId)683 public Builder addTransferableRoute(@NonNull String routeId) { 684 if (TextUtils.isEmpty(routeId)) { 685 throw new IllegalArgumentException("routeId must not be empty"); 686 } 687 mTransferableRoutes.add(routeId); 688 return this; 689 } 690 691 /** 692 * Removes a route from the transferable routes. The {@code routeId} must not be empty. 693 */ 694 @NonNull removeTransferableRoute(@onNull String routeId)695 public Builder removeTransferableRoute(@NonNull String routeId) { 696 if (TextUtils.isEmpty(routeId)) { 697 throw new IllegalArgumentException("routeId must not be empty"); 698 } 699 mTransferableRoutes.remove(routeId); 700 return this; 701 } 702 703 /** 704 * Sets the session's volume handling. 705 * {@link MediaRoute2Info#PLAYBACK_VOLUME_FIXED} or 706 * {@link MediaRoute2Info#PLAYBACK_VOLUME_VARIABLE}. 707 */ 708 @NonNull setVolumeHandling( @ediaRoute2Info.PlaybackVolume int volumeHandling)709 public RoutingSessionInfo.Builder setVolumeHandling( 710 @MediaRoute2Info.PlaybackVolume int volumeHandling) { 711 mVolumeHandling = volumeHandling; 712 return this; 713 } 714 715 /** 716 * Sets the session's maximum volume, or 0 if unknown. 717 */ 718 @NonNull setVolumeMax(int volumeMax)719 public RoutingSessionInfo.Builder setVolumeMax(int volumeMax) { 720 mVolumeMax = volumeMax; 721 return this; 722 } 723 724 /** 725 * Sets the session's current volume, or 0 if unknown. 726 */ 727 @NonNull setVolume(int volume)728 public RoutingSessionInfo.Builder setVolume(int volume) { 729 mVolume = volume; 730 return this; 731 } 732 733 /** 734 * Sets control hints. 735 */ 736 @NonNull setControlHints(@ullable Bundle controlHints)737 public Builder setControlHints(@Nullable Bundle controlHints) { 738 mControlHints = controlHints; 739 return this; 740 } 741 742 /** 743 * Sets whether this session is in system media route provider. 744 * @hide 745 */ 746 @NonNull setSystemSession(boolean isSystemSession)747 public Builder setSystemSession(boolean isSystemSession) { 748 mIsSystemSession = isSystemSession; 749 return this; 750 } 751 752 /** 753 * Builds a routing session info. 754 * 755 * @throws IllegalArgumentException if no selected routes are added. 756 */ 757 @NonNull build()758 public RoutingSessionInfo build() { 759 if (mSelectedRoutes.isEmpty()) { 760 throw new IllegalArgumentException("selectedRoutes must not be empty"); 761 } 762 return new RoutingSessionInfo(this); 763 } 764 } 765 } 766