1 /* 2 * Copyright (C) 2013 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 package androidx.mediarouter.media; 17 18 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; 19 20 import android.content.IntentFilter; 21 import android.content.IntentSender; 22 import android.net.Uri; 23 import android.os.Bundle; 24 import android.text.TextUtils; 25 26 import androidx.annotation.RestrictTo; 27 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 import java.util.Collection; 31 import java.util.Collections; 32 import java.util.List; 33 34 /** 35 * Describes the properties of a route. 36 * <p> 37 * Each route is uniquely identified by an opaque id string. This token 38 * may take any form as long as it is unique within the media route provider. 39 * </p><p> 40 * This object is immutable once created using a {@link Builder} instance. 41 * </p> 42 */ 43 public final class MediaRouteDescriptor { 44 static final String KEY_ID = "id"; 45 static final String KEY_GROUP_MEMBER_IDS = "groupMemberIds"; 46 static final String KEY_NAME = "name"; 47 static final String KEY_DESCRIPTION = "status"; 48 static final String KEY_ICON_URI = "iconUri"; 49 static final String KEY_ENABLED = "enabled"; 50 static final String KEY_CONNECTING = "connecting"; 51 static final String KEY_CONNECTION_STATE = "connectionState"; 52 static final String KEY_CONTROL_FILTERS = "controlFilters"; 53 static final String KEY_PLAYBACK_TYPE = "playbackType"; 54 static final String KEY_PLAYBACK_STREAM = "playbackStream"; 55 static final String KEY_DEVICE_TYPE = "deviceType"; 56 static final String KEY_VOLUME = "volume"; 57 static final String KEY_VOLUME_MAX = "volumeMax"; 58 static final String KEY_VOLUME_HANDLING = "volumeHandling"; 59 static final String KEY_PRESENTATION_DISPLAY_ID = "presentationDisplayId"; 60 static final String KEY_EXTRAS = "extras"; 61 static final String KEY_CAN_DISCONNECT = "canDisconnect"; 62 static final String KEY_SETTINGS_INTENT = "settingsIntent"; 63 static final String KEY_MIN_CLIENT_VERSION = "minClientVersion"; 64 static final String KEY_MAX_CLIENT_VERSION = "maxClientVersion"; 65 66 final Bundle mBundle; 67 List<IntentFilter> mControlFilters; 68 MediaRouteDescriptor(Bundle bundle, List<IntentFilter> controlFilters)69 MediaRouteDescriptor(Bundle bundle, List<IntentFilter> controlFilters) { 70 mBundle = bundle; 71 mControlFilters = controlFilters; 72 } 73 74 /** 75 * Gets the unique id of the route. 76 * <p> 77 * The route id associated with a route descriptor functions as a stable 78 * identifier for the route and must be unique among all routes offered 79 * by the provider. 80 * </p> 81 */ getId()82 public String getId() { 83 return mBundle.getString(KEY_ID); 84 } 85 86 /** 87 * Gets the group member ids of the route. 88 * <p> 89 * A route descriptor that has one or more group member route ids 90 * represents a route group. A member route may belong to another group. 91 * </p> 92 * @hide 93 */ 94 @RestrictTo(LIBRARY_GROUP) getGroupMemberIds()95 public List<String> getGroupMemberIds() { 96 return mBundle.getStringArrayList(KEY_GROUP_MEMBER_IDS); 97 } 98 99 /** 100 * Gets the user-visible name of the route. 101 * <p> 102 * The route name identifies the destination represented by the route. 103 * It may be a user-supplied name, an alias, or device serial number. 104 * </p> 105 */ getName()106 public String getName() { 107 return mBundle.getString(KEY_NAME); 108 } 109 110 /** 111 * Gets the user-visible description of the route. 112 * <p> 113 * The route description describes the kind of destination represented by the route. 114 * It may be a user-supplied string, a model number or brand of device. 115 * </p> 116 */ getDescription()117 public String getDescription() { 118 return mBundle.getString(KEY_DESCRIPTION); 119 } 120 121 /** 122 * Gets the URI of the icon representing this route. 123 * <p> 124 * This icon will be used in picker UIs if available. 125 * </p> 126 */ getIconUri()127 public Uri getIconUri() { 128 String iconUri = mBundle.getString(KEY_ICON_URI); 129 return iconUri == null ? null : Uri.parse(iconUri); 130 } 131 132 /** 133 * Gets whether the route is enabled. 134 */ isEnabled()135 public boolean isEnabled() { 136 return mBundle.getBoolean(KEY_ENABLED, true); 137 } 138 139 /** 140 * Gets whether the route is connecting. 141 * @deprecated Use {@link #getConnectionState} instead 142 */ 143 @Deprecated isConnecting()144 public boolean isConnecting() { 145 return mBundle.getBoolean(KEY_CONNECTING, false); 146 } 147 148 /** 149 * Gets the connection state of the route. 150 * 151 * @return The connection state of this route: 152 * {@link MediaRouter.RouteInfo#CONNECTION_STATE_DISCONNECTED}, 153 * {@link MediaRouter.RouteInfo#CONNECTION_STATE_CONNECTING}, or 154 * {@link MediaRouter.RouteInfo#CONNECTION_STATE_CONNECTED}. 155 */ getConnectionState()156 public int getConnectionState() { 157 return mBundle.getInt(KEY_CONNECTION_STATE, 158 MediaRouter.RouteInfo.CONNECTION_STATE_DISCONNECTED); 159 } 160 161 /** 162 * Gets whether the route can be disconnected without stopping playback. 163 * <p> 164 * The route can normally be disconnected without stopping playback when 165 * the destination device on the route is connected to two or more source 166 * devices. The route provider should update the route immediately when the 167 * number of connected devices changes. 168 * </p><p> 169 * To specify that the route should disconnect without stopping use 170 * {@link MediaRouter#unselect(int)} with 171 * {@link MediaRouter#UNSELECT_REASON_DISCONNECTED}. 172 * </p> 173 */ canDisconnectAndKeepPlaying()174 public boolean canDisconnectAndKeepPlaying() { 175 return mBundle.getBoolean(KEY_CAN_DISCONNECT, false); 176 } 177 178 /** 179 * Gets an {@link IntentSender} for starting a settings activity for this 180 * route. The activity may have specific route settings or general settings 181 * for the connected device or route provider. 182 * 183 * @return An {@link IntentSender} to start a settings activity. 184 */ getSettingsActivity()185 public IntentSender getSettingsActivity() { 186 return mBundle.getParcelable(KEY_SETTINGS_INTENT); 187 } 188 189 /** 190 * Gets the route's {@link MediaControlIntent media control intent} filters. 191 */ getControlFilters()192 public List<IntentFilter> getControlFilters() { 193 ensureControlFilters(); 194 return mControlFilters; 195 } 196 ensureControlFilters()197 void ensureControlFilters() { 198 if (mControlFilters == null) { 199 mControlFilters = mBundle.<IntentFilter>getParcelableArrayList(KEY_CONTROL_FILTERS); 200 if (mControlFilters == null) { 201 mControlFilters = Collections.<IntentFilter>emptyList(); 202 } 203 } 204 } 205 206 /** 207 * Gets the type of playback associated with this route. 208 * 209 * @return The type of playback associated with this route: 210 * {@link MediaRouter.RouteInfo#PLAYBACK_TYPE_LOCAL} or 211 * {@link MediaRouter.RouteInfo#PLAYBACK_TYPE_REMOTE}. 212 */ getPlaybackType()213 public int getPlaybackType() { 214 return mBundle.getInt(KEY_PLAYBACK_TYPE, MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE); 215 } 216 217 /** 218 * Gets the route's playback stream. 219 */ getPlaybackStream()220 public int getPlaybackStream() { 221 return mBundle.getInt(KEY_PLAYBACK_STREAM, -1); 222 } 223 224 /** 225 * Gets the type of the receiver device associated with this route. 226 * 227 * @return The type of the receiver device associated with this route: 228 * {@link MediaRouter.RouteInfo#DEVICE_TYPE_TV} or 229 * {@link MediaRouter.RouteInfo#DEVICE_TYPE_SPEAKER}. 230 */ getDeviceType()231 public int getDeviceType() { 232 return mBundle.getInt(KEY_DEVICE_TYPE); 233 } 234 235 /** 236 * Gets the route's current volume, or 0 if unknown. 237 */ getVolume()238 public int getVolume() { 239 return mBundle.getInt(KEY_VOLUME); 240 } 241 242 /** 243 * Gets the route's maximum volume, or 0 if unknown. 244 */ getVolumeMax()245 public int getVolumeMax() { 246 return mBundle.getInt(KEY_VOLUME_MAX); 247 } 248 249 /** 250 * Gets information about how volume is handled on the route. 251 * 252 * @return How volume is handled on the route: 253 * {@link MediaRouter.RouteInfo#PLAYBACK_VOLUME_FIXED} or 254 * {@link MediaRouter.RouteInfo#PLAYBACK_VOLUME_VARIABLE}. 255 */ getVolumeHandling()256 public int getVolumeHandling() { 257 return mBundle.getInt(KEY_VOLUME_HANDLING, 258 MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED); 259 } 260 261 /** 262 * Gets the route's presentation display id, or -1 if none. 263 */ getPresentationDisplayId()264 public int getPresentationDisplayId() { 265 return mBundle.getInt( 266 KEY_PRESENTATION_DISPLAY_ID, MediaRouter.RouteInfo.PRESENTATION_DISPLAY_ID_NONE); 267 } 268 269 /** 270 * Gets a bundle of extras for this route descriptor. 271 * The extras will be ignored by the media router but they may be used 272 * by applications. 273 */ getExtras()274 public Bundle getExtras() { 275 return mBundle.getBundle(KEY_EXTRAS); 276 } 277 278 /** 279 * Gets the minimum client version required for this route. 280 * @hide 281 */ 282 @RestrictTo(LIBRARY_GROUP) getMinClientVersion()283 public int getMinClientVersion() { 284 return mBundle.getInt(KEY_MIN_CLIENT_VERSION, 285 MediaRouteProviderProtocol.CLIENT_VERSION_START); 286 } 287 288 /** 289 * Gets the maximum client version required for this route. 290 * @hide 291 */ 292 @RestrictTo(LIBRARY_GROUP) getMaxClientVersion()293 public int getMaxClientVersion() { 294 return mBundle.getInt(KEY_MAX_CLIENT_VERSION, Integer.MAX_VALUE); 295 } 296 297 /** 298 * Returns true if the route descriptor has all of the required fields. 299 */ isValid()300 public boolean isValid() { 301 ensureControlFilters(); 302 if (TextUtils.isEmpty(getId()) 303 || TextUtils.isEmpty(getName()) 304 || mControlFilters.contains(null)) { 305 return false; 306 } 307 return true; 308 } 309 310 @Override toString()311 public String toString() { 312 StringBuilder result = new StringBuilder(); 313 result.append("MediaRouteDescriptor{ "); 314 result.append("id=").append(getId()); 315 result.append(", groupMemberIds=").append(getGroupMemberIds()); 316 result.append(", name=").append(getName()); 317 result.append(", description=").append(getDescription()); 318 result.append(", iconUri=").append(getIconUri()); 319 result.append(", isEnabled=").append(isEnabled()); 320 result.append(", isConnecting=").append(isConnecting()); 321 result.append(", connectionState=").append(getConnectionState()); 322 result.append(", controlFilters=").append(Arrays.toString(getControlFilters().toArray())); 323 result.append(", playbackType=").append(getPlaybackType()); 324 result.append(", playbackStream=").append(getPlaybackStream()); 325 result.append(", deviceType=").append(getDeviceType()); 326 result.append(", volume=").append(getVolume()); 327 result.append(", volumeMax=").append(getVolumeMax()); 328 result.append(", volumeHandling=").append(getVolumeHandling()); 329 result.append(", presentationDisplayId=").append(getPresentationDisplayId()); 330 result.append(", extras=").append(getExtras()); 331 result.append(", isValid=").append(isValid()); 332 result.append(", minClientVersion=").append(getMinClientVersion()); 333 result.append(", maxClientVersion=").append(getMaxClientVersion()); 334 result.append(" }"); 335 return result.toString(); 336 } 337 338 /** 339 * Converts this object to a bundle for serialization. 340 * 341 * @return The contents of the object represented as a bundle. 342 */ asBundle()343 public Bundle asBundle() { 344 return mBundle; 345 } 346 347 /** 348 * Creates an instance from a bundle. 349 * 350 * @param bundle The bundle, or null if none. 351 * @return The new instance, or null if the bundle was null. 352 */ fromBundle(Bundle bundle)353 public static MediaRouteDescriptor fromBundle(Bundle bundle) { 354 return bundle != null ? new MediaRouteDescriptor(bundle, null) : null; 355 } 356 357 /** 358 * Builder for {@link MediaRouteDescriptor media route descriptors}. 359 */ 360 public static final class Builder { 361 private final Bundle mBundle; 362 private ArrayList<String> mGroupMemberIds; 363 private ArrayList<IntentFilter> mControlFilters; 364 365 /** 366 * Creates a media route descriptor builder. 367 * 368 * @param id The unique id of the route. 369 * @param name The user-visible name of the route. 370 */ Builder(String id, String name)371 public Builder(String id, String name) { 372 mBundle = new Bundle(); 373 setId(id); 374 setName(name); 375 } 376 377 /** 378 * Creates a media route descriptor builder whose initial contents are 379 * copied from an existing descriptor. 380 */ Builder(MediaRouteDescriptor descriptor)381 public Builder(MediaRouteDescriptor descriptor) { 382 if (descriptor == null) { 383 throw new IllegalArgumentException("descriptor must not be null"); 384 } 385 386 mBundle = new Bundle(descriptor.mBundle); 387 388 descriptor.ensureControlFilters(); 389 if (!descriptor.mControlFilters.isEmpty()) { 390 mControlFilters = new ArrayList<IntentFilter>(descriptor.mControlFilters); 391 } 392 } 393 394 /** 395 * Sets the unique id of the route. 396 * <p> 397 * The route id associated with a route descriptor functions as a stable 398 * identifier for the route and must be unique among all routes offered 399 * by the provider. 400 * </p> 401 */ setId(String id)402 public Builder setId(String id) { 403 mBundle.putString(KEY_ID, id); 404 return this; 405 } 406 407 /** 408 * Adds a group member id of the route. 409 * <p> 410 * A route descriptor that has one or more group member route ids 411 * represents a route group. A member route may belong to another group. 412 * </p> 413 * @hide 414 */ 415 @RestrictTo(LIBRARY_GROUP) addGroupMemberId(String groupMemberId)416 public Builder addGroupMemberId(String groupMemberId) { 417 if (TextUtils.isEmpty(groupMemberId)) { 418 throw new IllegalArgumentException("groupMemberId must not be empty"); 419 } 420 421 if (mGroupMemberIds == null) { 422 mGroupMemberIds = new ArrayList<>(); 423 } 424 if (!mGroupMemberIds.contains(groupMemberId)) { 425 mGroupMemberIds.add(groupMemberId); 426 } 427 return this; 428 } 429 430 /** 431 * Adds a list of group member ids of the route. 432 * <p> 433 * A route descriptor that has one or more group member route ids 434 * represents a route group. A member route may belong to another group. 435 * </p> 436 * @hide 437 */ 438 @RestrictTo(LIBRARY_GROUP) addGroupMemberIds(Collection<String> groupMemberIds)439 public Builder addGroupMemberIds(Collection<String> groupMemberIds) { 440 if (groupMemberIds == null) { 441 throw new IllegalArgumentException("groupMemberIds must not be null"); 442 } 443 444 if (!groupMemberIds.isEmpty()) { 445 for (String groupMemberId : groupMemberIds) { 446 addGroupMemberId(groupMemberId); 447 } 448 } 449 return this; 450 } 451 452 /** 453 * Sets the user-visible name of the route. 454 * <p> 455 * The route name identifies the destination represented by the route. 456 * It may be a user-supplied name, an alias, or device serial number. 457 * </p> 458 */ setName(String name)459 public Builder setName(String name) { 460 mBundle.putString(KEY_NAME, name); 461 return this; 462 } 463 464 /** 465 * Sets the user-visible description of the route. 466 * <p> 467 * The route description describes the kind of destination represented by the route. 468 * It may be a user-supplied string, a model number or brand of device. 469 * </p> 470 */ setDescription(String description)471 public Builder setDescription(String description) { 472 mBundle.putString(KEY_DESCRIPTION, description); 473 return this; 474 } 475 476 /** 477 * Sets the URI of the icon representing this route. 478 * <p> 479 * This icon will be used in picker UIs if available. 480 * </p><p> 481 * The URI must be one of the following formats: 482 * <ul> 483 * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li> 484 * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE}) 485 * </li> 486 * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li> 487 * </ul> 488 * </p> 489 */ setIconUri(Uri iconUri)490 public Builder setIconUri(Uri iconUri) { 491 if (iconUri == null) { 492 throw new IllegalArgumentException("iconUri must not be null"); 493 } 494 mBundle.putString(KEY_ICON_URI, iconUri.toString()); 495 return this; 496 } 497 498 /** 499 * Sets whether the route is enabled. 500 * <p> 501 * Disabled routes represent routes that a route provider knows about, such as paired 502 * Wifi Display receivers, but that are not currently available for use. 503 * </p> 504 */ setEnabled(boolean enabled)505 public Builder setEnabled(boolean enabled) { 506 mBundle.putBoolean(KEY_ENABLED, enabled); 507 return this; 508 } 509 510 /** 511 * Sets whether the route is in the process of connecting and is not yet 512 * ready for use. 513 * @deprecated Use {@link #setConnectionState} instead. 514 */ 515 @Deprecated setConnecting(boolean connecting)516 public Builder setConnecting(boolean connecting) { 517 mBundle.putBoolean(KEY_CONNECTING, connecting); 518 return this; 519 } 520 521 /** 522 * Sets the route's connection state. 523 * 524 * @param connectionState The connection state of the route: 525 * {@link MediaRouter.RouteInfo#CONNECTION_STATE_DISCONNECTED}, 526 * {@link MediaRouter.RouteInfo#CONNECTION_STATE_CONNECTING}, or 527 * {@link MediaRouter.RouteInfo#CONNECTION_STATE_CONNECTED}. 528 */ setConnectionState(int connectionState)529 public Builder setConnectionState(int connectionState) { 530 mBundle.putInt(KEY_CONNECTION_STATE, connectionState); 531 return this; 532 } 533 534 /** 535 * Sets whether the route can be disconnected without stopping playback. 536 */ setCanDisconnect(boolean canDisconnect)537 public Builder setCanDisconnect(boolean canDisconnect) { 538 mBundle.putBoolean(KEY_CAN_DISCONNECT, canDisconnect); 539 return this; 540 } 541 542 /** 543 * Sets an intent sender for launching the settings activity for this 544 * route. 545 */ setSettingsActivity(IntentSender is)546 public Builder setSettingsActivity(IntentSender is) { 547 mBundle.putParcelable(KEY_SETTINGS_INTENT, is); 548 return this; 549 } 550 551 /** 552 * Adds a {@link MediaControlIntent media control intent} filter for the route. 553 */ addControlFilter(IntentFilter filter)554 public Builder addControlFilter(IntentFilter filter) { 555 if (filter == null) { 556 throw new IllegalArgumentException("filter must not be null"); 557 } 558 559 if (mControlFilters == null) { 560 mControlFilters = new ArrayList<IntentFilter>(); 561 } 562 if (!mControlFilters.contains(filter)) { 563 mControlFilters.add(filter); 564 } 565 return this; 566 } 567 568 /** 569 * Adds a list of {@link MediaControlIntent media control intent} filters for the route. 570 */ addControlFilters(Collection<IntentFilter> filters)571 public Builder addControlFilters(Collection<IntentFilter> filters) { 572 if (filters == null) { 573 throw new IllegalArgumentException("filters must not be null"); 574 } 575 576 if (!filters.isEmpty()) { 577 for (IntentFilter filter : filters) { 578 addControlFilter(filter); 579 } 580 } 581 return this; 582 } 583 584 /** 585 * Sets the route's playback type. 586 * 587 * @param playbackType The playback type of the route: 588 * {@link MediaRouter.RouteInfo#PLAYBACK_TYPE_LOCAL} or 589 * {@link MediaRouter.RouteInfo#PLAYBACK_TYPE_REMOTE}. 590 */ setPlaybackType(int playbackType)591 public Builder setPlaybackType(int playbackType) { 592 mBundle.putInt(KEY_PLAYBACK_TYPE, playbackType); 593 return this; 594 } 595 596 /** 597 * Sets the route's playback stream. 598 */ setPlaybackStream(int playbackStream)599 public Builder setPlaybackStream(int playbackStream) { 600 mBundle.putInt(KEY_PLAYBACK_STREAM, playbackStream); 601 return this; 602 } 603 604 /** 605 * Sets the route's receiver device type. 606 * 607 * @param deviceType The receive device type of the route: 608 * {@link MediaRouter.RouteInfo#DEVICE_TYPE_TV} or 609 * {@link MediaRouter.RouteInfo#DEVICE_TYPE_SPEAKER}. 610 */ setDeviceType(int deviceType)611 public Builder setDeviceType(int deviceType) { 612 mBundle.putInt(KEY_DEVICE_TYPE, deviceType); 613 return this; 614 } 615 616 /** 617 * Sets the route's current volume, or 0 if unknown. 618 */ setVolume(int volume)619 public Builder setVolume(int volume) { 620 mBundle.putInt(KEY_VOLUME, volume); 621 return this; 622 } 623 624 /** 625 * Sets the route's maximum volume, or 0 if unknown. 626 */ setVolumeMax(int volumeMax)627 public Builder setVolumeMax(int volumeMax) { 628 mBundle.putInt(KEY_VOLUME_MAX, volumeMax); 629 return this; 630 } 631 632 /** 633 * Sets the route's volume handling. 634 * 635 * @param volumeHandling how volume is handled on the route: 636 * {@link MediaRouter.RouteInfo#PLAYBACK_VOLUME_FIXED} or 637 * {@link MediaRouter.RouteInfo#PLAYBACK_VOLUME_VARIABLE}. 638 */ setVolumeHandling(int volumeHandling)639 public Builder setVolumeHandling(int volumeHandling) { 640 mBundle.putInt(KEY_VOLUME_HANDLING, volumeHandling); 641 return this; 642 } 643 644 /** 645 * Sets the route's presentation display id, or -1 if none. 646 */ setPresentationDisplayId(int presentationDisplayId)647 public Builder setPresentationDisplayId(int presentationDisplayId) { 648 mBundle.putInt(KEY_PRESENTATION_DISPLAY_ID, presentationDisplayId); 649 return this; 650 } 651 652 /** 653 * Sets a bundle of extras for this route descriptor. 654 * The extras will be ignored by the media router but they may be used 655 * by applications. 656 */ setExtras(Bundle extras)657 public Builder setExtras(Bundle extras) { 658 mBundle.putBundle(KEY_EXTRAS, extras); 659 return this; 660 } 661 662 /** 663 * Sets the route's minimum client version. 664 * A router whose version is lower than this will not be able to connect to this route. 665 * @hide 666 */ 667 @RestrictTo(LIBRARY_GROUP) setMinClientVersion(int minVersion)668 public Builder setMinClientVersion(int minVersion) { 669 mBundle.putInt(KEY_MIN_CLIENT_VERSION, minVersion); 670 return this; 671 } 672 673 /** 674 * Sets the route's maximum client version. 675 * A router whose version is higher than this will not be able to connect to this route. 676 * @hide 677 */ 678 @RestrictTo(LIBRARY_GROUP) setMaxClientVersion(int maxVersion)679 public Builder setMaxClientVersion(int maxVersion) { 680 mBundle.putInt(KEY_MAX_CLIENT_VERSION, maxVersion); 681 return this; 682 } 683 684 /** 685 * Builds the {@link MediaRouteDescriptor media route descriptor}. 686 */ build()687 public MediaRouteDescriptor build() { 688 if (mControlFilters != null) { 689 mBundle.putParcelableArrayList(KEY_CONTROL_FILTERS, mControlFilters); 690 } 691 if (mGroupMemberIds != null) { 692 mBundle.putStringArrayList(KEY_GROUP_MEMBER_IDS, mGroupMemberIds); 693 } 694 return new MediaRouteDescriptor(mBundle, mControlFilters); 695 } 696 } 697 } 698