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