MediaRouter.java revision 14a7007aaefb96b3b6fb06171aa10e1200116d6a
1c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown/*
2c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Copyright (C) 2013 The Android Open Source Project
3c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown *
4c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Licensed under the Apache License, Version 2.0 (the "License");
5c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * you may not use this file except in compliance with the License.
6c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * You may obtain a copy of the License at
7c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown *
8c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown *      http://www.apache.org/licenses/LICENSE-2.0
9c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown *
10c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Unless required by applicable law or agreed to in writing, software
11c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * distributed under the License is distributed on an "AS IS" BASIS,
12c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * See the License for the specific language governing permissions and
14c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * limitations under the License.
15c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */
16c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
17b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brownpackage android.support.v7.media;
18c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
19fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brownimport android.app.ActivityManager;
20adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brownimport android.content.ComponentName;
21c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.ContentResolver;
22c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.Context;
23c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.Intent;
24c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.IntentFilter;
2594be6100218126ce6a08bf1f56209578500b361fRoboErikimport android.content.IntentSender;
26fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.pm.PackageManager.NameNotFoundException;
27fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.res.Resources;
28119de6bb3353341cfd465cea9e545abec3762d20Jae Seoimport android.net.Uri;
29c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Bundle;
30c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Handler;
31c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Looper;
32c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Message;
33429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport android.support.annotation.IntDef;
34429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport android.support.annotation.NonNull;
35429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport android.support.annotation.Nullable;
36fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brownimport android.support.v4.app.ActivityManagerCompat;
37c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.support.v4.hardware.display.DisplayManagerCompat;
38bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErikimport android.support.v4.media.VolumeProviderCompat;
39bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErikimport android.support.v4.media.session.MediaSessionCompat;
40e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seoimport android.support.v4.util.Pair;
41b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brownimport android.support.v7.media.MediaRouteProvider.ProviderMetadata;
42bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kangimport android.support.v7.media.MediaRouteProvider.RouteController;
43c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.util.Log;
44c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.view.Display;
45c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
46429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport java.lang.annotation.Retention;
47429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport java.lang.annotation.RetentionPolicy;
489fcedc160282e6620f409ea46bf6728b35d011ddJeff Brownimport java.lang.ref.WeakReference;
49c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.ArrayList;
50c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.Collections;
5138e4efe885198f63fb26b73d79d4636263ba6a5cJae Seoimport java.util.HashMap;
52c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.List;
53cb63b6ecac9786891514f241dec71695f09d3efbJeff Brownimport java.util.Locale;
5438e4efe885198f63fb26b73d79d4636263ba6a5cJae Seoimport java.util.Map;
55c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
56c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown/**
57c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter allows applications to control the routing of media channels
58c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * and streams from the current device to external speakers and destination devices.
59c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p>
60c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * A MediaRouter instance is retrieved through {@link #getInstance}.  Applications
61c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * can query the media router about the currently selected route and its capabilities
62c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to determine how to send content to the route's destination.  Applications can
63c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * also {@link RouteInfo#sendControlRequest send control requests} to the route
64c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to ask the route's destination to perform certain remote control functions
65fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * such as playing media.
66c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p>
67c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * See also {@link MediaRouteProvider} for information on how an application
68c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * can publish new media routes to the media router.
69c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p>
70c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The media router API is not thread-safe; all interactions with it must be
71c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * done from the main thread of the process.
72c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p>
73c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */
74c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownpublic final class MediaRouter {
75c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    private static final String TAG = "MediaRouter";
76f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
77c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
7894be6100218126ce6a08bf1f56209578500b361fRoboErik    /**
79b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang     * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)}
80b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang     * when the reason the route was unselected is unknown.
8194be6100218126ce6a08bf1f56209578500b361fRoboErik     */
8294be6100218126ce6a08bf1f56209578500b361fRoboErik    public static final int UNSELECT_REASON_UNKNOWN = 0;
8394be6100218126ce6a08bf1f56209578500b361fRoboErik    /**
84b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang     * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)}
85b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang     * when the user pressed the disconnect button to disconnect and keep playing.
8694be6100218126ce6a08bf1f56209578500b361fRoboErik     * <p>
8794be6100218126ce6a08bf1f56209578500b361fRoboErik     *
8894be6100218126ce6a08bf1f56209578500b361fRoboErik     * @see {@link MediaRouteDescriptor#canDisconnectAndKeepPlaying()}.
8994be6100218126ce6a08bf1f56209578500b361fRoboErik     */
9094be6100218126ce6a08bf1f56209578500b361fRoboErik    public static final int UNSELECT_REASON_DISCONNECTED = 1;
9194be6100218126ce6a08bf1f56209578500b361fRoboErik    /**
92b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang     * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)}
93b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang     * when the user pressed the stop casting button.
9494be6100218126ce6a08bf1f56209578500b361fRoboErik     */
9594be6100218126ce6a08bf1f56209578500b361fRoboErik    public static final int UNSELECT_REASON_STOPPED = 2;
9694be6100218126ce6a08bf1f56209578500b361fRoboErik    /**
97b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang     * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)}
98b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang     * when the user selected a different route.
9994be6100218126ce6a08bf1f56209578500b361fRoboErik     */
10094be6100218126ce6a08bf1f56209578500b361fRoboErik    public static final int UNSELECT_REASON_ROUTE_CHANGED = 3;
10194be6100218126ce6a08bf1f56209578500b361fRoboErik
102c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    // Maintains global media router state for the process.
103c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    // This field is initialized in MediaRouter.getInstance() before any
104c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    // MediaRouter objects are instantiated so it is guaranteed to be
105c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    // valid whenever any instance method is invoked.
106c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    static GlobalMediaRouter sGlobal;
107c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
108c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    // Context-bound state of the media router.
109c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    final Context mContext;
1109fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown    final ArrayList<CallbackRecord> mCallbackRecords = new ArrayList<CallbackRecord>();
11111417b1cfde8f1749905f2d735623af9214148afJeff Brown
112429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    /** @hide */
113429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    @IntDef(flag = true,
114429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye            value = {
115429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye                    CALLBACK_FLAG_PERFORM_ACTIVE_SCAN,
116429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye                    CALLBACK_FLAG_REQUEST_DISCOVERY,
117429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye                    CALLBACK_FLAG_UNFILTERED_EVENTS
118429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye            }
119429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    )
120429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    @Retention(RetentionPolicy.SOURCE)
121429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    private @interface CallbackFlags {}
122429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye
12311417b1cfde8f1749905f2d735623af9214148afJeff Brown    /**
12411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Flag for {@link #addCallback}: Actively scan for routes while this callback
12511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * is registered.
12611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * <p>
12711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * When this flag is specified, the media router will actively scan for new
12811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * routes.  Certain routes, such as wifi display routes, may not be discoverable
12911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * except when actively scanning.  This flag is typically used when the route picker
13011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * dialog has been opened by the user to ensure that the route information is
13111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * up to date.
13211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p><p>
13311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Active scanning may consume a significant amount of power and may have intrusive
13411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * effects on wireless connectivity.  Therefore it is important that active scanning
13511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * only be requested when it is actually needed to satisfy a user request to
13611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * discover and select a new route.
137f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * </p><p>
138f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * This flag implies {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} but performing
139f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * active scans is much more expensive than a normal discovery request.
14011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p>
141f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *
142f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * @see #CALLBACK_FLAG_REQUEST_DISCOVERY
14311417b1cfde8f1749905f2d735623af9214148afJeff Brown     */
144f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1 << 0;
14511417b1cfde8f1749905f2d735623af9214148afJeff Brown
14611417b1cfde8f1749905f2d735623af9214148afJeff Brown    /**
14711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Flag for {@link #addCallback}: Do not filter route events.
14811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * <p>
14911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * When this flag is specified, the callback will be invoked for events that affect any
1503efa63d3b896244713e84acbb5945562dce41d77Jeff Brown     * route even if they do not match the callback's filter.
15111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p>
15211417b1cfde8f1749905f2d735623af9214148afJeff Brown     */
15311417b1cfde8f1749905f2d735623af9214148afJeff Brown    public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 1 << 1;
15411417b1cfde8f1749905f2d735623af9214148afJeff Brown
15511417b1cfde8f1749905f2d735623af9214148afJeff Brown    /**
156fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * Flag for {@link #addCallback}: Request passive route discovery while this
157fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * callback is registered, except on {@link ActivityManager#isLowRamDevice low-RAM devices}.
158f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * <p>
159f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * When this flag is specified, the media router will try to discover routes.
160f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * Although route discovery is intended to be efficient, checking for new routes may
161f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * result in some network activity and could slowly drain the battery.  Therefore
162f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * applications should only specify {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} when
163f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * they are running in the foreground and would like to provide the user with the
164f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * option of connecting to new routes.
165f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * </p><p>
166f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * Applications should typically add a callback using this flag in the
167f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * {@link android.app.Activity activity's} {@link android.app.Activity#onStart onStart}
168f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * method and remove it in the {@link android.app.Activity#onStop onStop} method.
169f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * The {@link android.support.v7.app.MediaRouteDiscoveryFragment} fragment may
170f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * also be used for this purpose.
171fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * </p><p class="note">
172fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * On {@link ActivityManager#isLowRamDevice low-RAM devices} this flag
173fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * will be ignored.  Refer to
174fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * {@link #addCallback(MediaRouteSelector, Callback, int) addCallback} for details.
175f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * </p>
176f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *
177f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * @see android.support.v7.app.MediaRouteDiscoveryFragment
178f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     */
179f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 1 << 2;
180f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
181f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    /**
182fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * Flag for {@link #addCallback}: Request passive route discovery while this
183fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * callback is registered, even on {@link ActivityManager#isLowRamDevice low-RAM devices}.
184fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * <p class="note">
185fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * This flag has a significant performance impact on low-RAM devices
186fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * since it may cause many media route providers to be started simultaneously.
187fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * It is much better to use {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} instead to avoid
188fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * performing passive discovery on these devices altogether.  Refer to
189fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * {@link #addCallback(MediaRouteSelector, Callback, int) addCallback} for details.
190fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * </p>
191fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     *
192fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * @see android.support.v7.app.MediaRouteDiscoveryFragment
193fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     */
194fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown    public static final int CALLBACK_FLAG_FORCE_DISCOVERY = 1 << 3;
195fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown
196fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown    /**
19711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Flag for {@link #isRouteAvailable}: Ignore the default route.
19811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * <p>
19911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * This flag is used to determine whether a matching non-default route is available.
20011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * This constraint may be used to decide whether to offer the route chooser dialog
20111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * to the user.  There is no point offering the chooser if there are no
20211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * non-default choices.
20311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p>
20411417b1cfde8f1749905f2d735623af9214148afJeff Brown     */
20511417b1cfde8f1749905f2d735623af9214148afJeff Brown    public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1 << 0;
20611417b1cfde8f1749905f2d735623af9214148afJeff Brown
207fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown    /**
208fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * Flag for {@link #isRouteAvailable}: Require an actual route to be matched.
209fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * <p>
210fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * If this flag is not set, then {@link #isRouteAvailable} will return true
211fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * if it is possible to discover a matching route even if discovery is not in
212fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * progress or if no matching route has yet been found.  This feature is used to
213fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * save resources by removing the need to perform passive route discovery on
214fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * {@link ActivityManager#isLowRamDevice low-RAM devices}.
215fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * </p><p>
216fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * If this flag is set, then {@link #isRouteAvailable} will only return true if
217fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * a matching route has actually been discovered.
218fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * </p>
219fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     */
220fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown    public static final int AVAILABILITY_FLAG_REQUIRE_MATCH = 1 << 1;
221fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown
222c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    MediaRouter(Context context) {
223c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        mContext = context;
224c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
225c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
226c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
2279fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * Gets an instance of the media router service associated with the context.
2289fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * <p>
2299fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * The application is responsible for holding a strong reference to the returned
2309fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * {@link MediaRouter} instance, such as by storing the instance in a field of
2319fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * the {@link android.app.Activity}, to ensure that the media router remains alive
2329fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * as long as the application is using its features.
2339fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * </p><p>
2349fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * In other words, the support library only holds a {@link WeakReference weak reference}
2359fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * to each media router instance.  When there are no remaining strong references to the
2369fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * media router instance, all of its callbacks will be removed and route discovery
2379fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * will no longer be performed on its behalf.
2389fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * </p>
2399fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     *
2409fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * @return The media router instance for the context.  The application must hold
2419fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * a strong reference to this object as long as it is in use.
242c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
243429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    public static MediaRouter getInstance(@NonNull Context context) {
244c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (context == null) {
245c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            throw new IllegalArgumentException("context must not be null");
246c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
247c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
248c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
249c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (sGlobal == null) {
250c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            sGlobal = new GlobalMediaRouter(context.getApplicationContext());
251fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            sGlobal.start();
252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
253c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return sGlobal.getRouter(context);
254c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
255c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
256c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
257fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Gets information about the {@link MediaRouter.RouteInfo routes} currently known to
258fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * this media router.
259c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
260c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public List<RouteInfo> getRoutes() {
261c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
262c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return sGlobal.getRoutes();
263c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
264c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
265c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
266fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Gets information about the {@link MediaRouter.ProviderInfo route providers}
267fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * currently known to this media router.
268fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     */
269fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public List<ProviderInfo> getProviders() {
270fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        checkCallingThread();
271fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        return sGlobal.getProviders();
272fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
273fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
274fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    /**
275c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Gets the default route for playing media content on the system.
276c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
277c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * The system always provides a default route.
278c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
279c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
280c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @return The default route, which is guaranteed to never be null.
281c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
282429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    @NonNull
283c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public RouteInfo getDefaultRoute() {
284c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
285c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return sGlobal.getDefaultRoute();
286c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
287c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
288c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
289c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Gets the currently selected route.
290c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
291c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * The application should examine the route's
292fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * {@link RouteInfo#getControlFilters media control intent filters} to assess the
293c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * capabilities of the route before attempting to use it.
294c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
295c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
296c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <h3>Example</h3>
297c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <pre>
298c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * public boolean playMovie() {
299c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     MediaRouter mediaRouter = MediaRouter.getInstance(context);
300c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute();
301c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
302c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     // First try using the remote playback interface, if supported.
303c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
304c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         // The route supports remote playback.
305c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         // Try to send it the Uri of the movie to play.
306c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         Intent intent = new Intent(MediaControlIntent.ACTION_PLAY);
307c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
308c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         intent.setDataAndType("http://example.com/videos/movie.mp4", "video/mp4");
309fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     *         if (route.supportsControlRequest(intent)) {
310fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     *             route.sendControlRequest(intent, null);
311fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     *             return true; // sent the request to play the movie
312c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         }
313c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     }
314c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
315c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     // If remote playback was not possible, then play locally.
316c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     if (route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)) {
317c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         // The route supports live video streaming.
318c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         // Prepare to play content locally in a window or in a presentation.
319c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         return playMovieInWindow();
320c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     }
321c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
322c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     // Neither interface is supported, so we can't play the movie to this route.
323c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     return false;
324c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * }
325c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </pre>
326c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
327c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @return The selected route, which is guaranteed to never be null.
328c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
329fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * @see RouteInfo#getControlFilters
330c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see RouteInfo#supportsControlCategory
331c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see RouteInfo#supportsControlRequest
332c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
333429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    @NonNull
334c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public RouteInfo getSelectedRoute() {
335c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
336c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return sGlobal.getSelectedRoute();
337c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
338c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
339c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
34028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * Returns the selected route if it matches the specified selector, otherwise
34128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * selects the default route and returns it.
34228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
34328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @param selector The selector to match.
34428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @return The previously selected route if it matched the selector, otherwise the
34528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * newly selected default route which is guaranteed to never be null.
34628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
34711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see MediaRouteSelector
34828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @see RouteInfo#matchesSelector
34928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @see RouteInfo#isDefault
35028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     */
351429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    @NonNull
352429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    public RouteInfo updateSelectedRoute(@NonNull MediaRouteSelector selector) {
35328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        if (selector == null) {
35428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            throw new IllegalArgumentException("selector must not be null");
35528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
35628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        checkCallingThread();
35728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
35811417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
35911417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "updateSelectedRoute: " + selector);
36011417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
36128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        RouteInfo route = sGlobal.getSelectedRoute();
36211417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (!route.isDefault() && !route.matchesSelector(selector)) {
36328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            route = sGlobal.getDefaultRoute();
36428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            sGlobal.selectRoute(route);
36528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
36628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        return route;
36728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    }
36828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
36928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    /**
370c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Selects the specified route.
371c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
372c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @param route The route to select.
373c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
374429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    public void selectRoute(@NonNull RouteInfo route) {
375c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (route == null) {
376c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            throw new IllegalArgumentException("route must not be null");
377c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
378c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
379c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
38011417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
38111417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "selectRoute: " + route);
38211417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
383c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        sGlobal.selectRoute(route);
384c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
385c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
386c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
38794be6100218126ce6a08bf1f56209578500b361fRoboErik     * Unselects the current round and selects the default route instead.
38894be6100218126ce6a08bf1f56209578500b361fRoboErik     * <p>
38994be6100218126ce6a08bf1f56209578500b361fRoboErik     * The reason given must be one of:
39094be6100218126ce6a08bf1f56209578500b361fRoboErik     * <ul>
39194be6100218126ce6a08bf1f56209578500b361fRoboErik     * <li>{@link MediaRouter#UNSELECT_REASON_UNKNOWN}</li>
39294be6100218126ce6a08bf1f56209578500b361fRoboErik     * <li>{@link MediaRouter#UNSELECT_REASON_DISCONNECTED}</li>
39394be6100218126ce6a08bf1f56209578500b361fRoboErik     * <li>{@link MediaRouter#UNSELECT_REASON_STOPPED}</li>
39494be6100218126ce6a08bf1f56209578500b361fRoboErik     * <li>{@link MediaRouter#UNSELECT_REASON_ROUTE_CHANGED}</li>
39594be6100218126ce6a08bf1f56209578500b361fRoboErik     * </ul>
39694be6100218126ce6a08bf1f56209578500b361fRoboErik     *
39794be6100218126ce6a08bf1f56209578500b361fRoboErik     * @param reason The reason for disconnecting the current route.
39894be6100218126ce6a08bf1f56209578500b361fRoboErik     */
39994be6100218126ce6a08bf1f56209578500b361fRoboErik    public void unselect(int reason) {
40094be6100218126ce6a08bf1f56209578500b361fRoboErik        if (reason < MediaRouter.UNSELECT_REASON_UNKNOWN ||
40194be6100218126ce6a08bf1f56209578500b361fRoboErik                reason > MediaRouter.UNSELECT_REASON_ROUTE_CHANGED) {
40294be6100218126ce6a08bf1f56209578500b361fRoboErik            throw new IllegalArgumentException("Unsupported reason to unselect route");
40394be6100218126ce6a08bf1f56209578500b361fRoboErik        }
40494be6100218126ce6a08bf1f56209578500b361fRoboErik        checkCallingThread();
40594be6100218126ce6a08bf1f56209578500b361fRoboErik
40694be6100218126ce6a08bf1f56209578500b361fRoboErik        sGlobal.selectRoute(getDefaultRoute(), reason);
40794be6100218126ce6a08bf1f56209578500b361fRoboErik    }
40894be6100218126ce6a08bf1f56209578500b361fRoboErik
40994be6100218126ce6a08bf1f56209578500b361fRoboErik    /**
410d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown     * Returns true if there is a route that matches the specified selector.
41111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * <p>
41294be6100218126ce6a08bf1f56209578500b361fRoboErik     * This method returns true if there are any available routes that match the
41394be6100218126ce6a08bf1f56209578500b361fRoboErik     * selector regardless of whether they are enabled or disabled. If the
414d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown     * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} flag is specified, then
415d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown     * the method will only consider non-default routes.
41694be6100218126ce6a08bf1f56209578500b361fRoboErik     * </p>
41794be6100218126ce6a08bf1f56209578500b361fRoboErik     * <p class="note">
418fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * On {@link ActivityManager#isLowRamDevice low-RAM devices} this method
419fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * will return true if it is possible to discover a matching route even if
420fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * discovery is not in progress or if no matching route has yet been found.
421fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * Use {@link #AVAILABILITY_FLAG_REQUIRE_MATCH} to require an actual match.
42211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p>
423c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
42411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param selector The selector to match.
42594be6100218126ce6a08bf1f56209578500b361fRoboErik     * @param flags Flags to control the determination of whether a route may be
42694be6100218126ce6a08bf1f56209578500b361fRoboErik     *            available. May be zero or some combination of
42794be6100218126ce6a08bf1f56209578500b361fRoboErik     *            {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} and
42894be6100218126ce6a08bf1f56209578500b361fRoboErik     *            {@link #AVAILABILITY_FLAG_REQUIRE_MATCH}.
42911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @return True if a matching route may be available.
430c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
431429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    public boolean isRouteAvailable(@NonNull MediaRouteSelector selector, int flags) {
43211417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (selector == null) {
43311417b1cfde8f1749905f2d735623af9214148afJeff Brown            throw new IllegalArgumentException("selector must not be null");
434c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
435c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
436c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
43711417b1cfde8f1749905f2d735623af9214148afJeff Brown        return sGlobal.isRouteAvailable(selector, flags);
438c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
439c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
440c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
44111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Registers a callback to discover routes that match the selector and to receive
44211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * events when they change.
44311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * <p>
44411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * This is a convenience method that has the same effect as calling
44511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * {@link #addCallback(MediaRouteSelector, Callback, int)} without flags.
44611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p>
447c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
44811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param selector A route selector that indicates the kinds of routes that the
44911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * callback would like to discover.
45011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param callback The callback to add.
45111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see #removeCallback
452c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
45311417b1cfde8f1749905f2d735623af9214148afJeff Brown    public void addCallback(MediaRouteSelector selector, Callback callback) {
45411417b1cfde8f1749905f2d735623af9214148afJeff Brown        addCallback(selector, callback, 0);
455c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
456c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
457c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
45811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Registers a callback to discover routes that match the selector and to receive
45911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * events when they change.
46028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * <p>
46111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * The selector describes the kinds of routes that the application wants to
46211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * discover.  For example, if the application wants to use
46311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * live audio routes then it should include the
46411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * {@link MediaControlIntent#CATEGORY_LIVE_AUDIO live audio media control intent category}
46511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * in its selector when it adds a callback to the media router.
46611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * The selector may include any number of categories.
46728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * </p><p>
46811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * If the callback has already been registered, then the selector is added to
46911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * the set of selectors being monitored by the callback.
47011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p><p>
47111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * By default, the callback will only be invoked for events that affect routes
47211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * that match the specified selector.  Event filtering may be disabled by specifying
47311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * the {@link #CALLBACK_FLAG_UNFILTERED_EVENTS} flag when the callback is registered.
474fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * </p><p>
475fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * Applications should use the {@link #isRouteAvailable} method to determine
476fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * whether is it possible to discover a route with the desired capabilities
477fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * and therefore whether the media route button should be shown to the user.
478fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * </p><p>
479fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * The {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} flag should be used while the application
480fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * is in the foreground to request that passive discovery be performed if there are
481fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * sufficient resources to allow continuous passive discovery.
482fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * On {@link ActivityManager#isLowRamDevice low-RAM devices} this flag will be
483fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * ignored to conserve resources.
484fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * </p><p>
485fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * The {@link #CALLBACK_FLAG_FORCE_DISCOVERY} flag should be used when
486fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * passive discovery absolutely must be performed, even on low-RAM devices.
487fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * This flag has a significant performance impact on low-RAM devices
488fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * since it may cause many media route providers to be started simultaneously.
489fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * It is much better to use {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} instead to avoid
490fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * performing passive discovery on these devices altogether.
491fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * </p><p>
492fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * The {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} flag should be used when the
493fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * media route chooser dialog is showing to confirm the presence of available
494fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * routes that the user may connect to.  This flag may use substantially more
495fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * power.
49628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * </p>
49728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
49828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * <h3>Example</h3>
49928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * <pre>
50011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * public class MyActivity extends Activity {
50111417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     private MediaRouter mRouter;
50211417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     private MediaRouter.Callback mCallback;
50311417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     private MediaRouteSelector mSelector;
50428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
50511417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     protected void onCreate(Bundle savedInstanceState) {
50611417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         super.onCreate(savedInstanceState);
50711417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
50811417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         mRouter = Mediarouter.getInstance(this);
50911417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         mCallback = new MyCallback();
51011417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         mSelector = new MediaRouteSelector.Builder()
51111417b1cfde8f1749905f2d735623af9214148afJeff Brown     *                 .addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
51211417b1cfde8f1749905f2d735623af9214148afJeff Brown     *                 .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
51311417b1cfde8f1749905f2d735623af9214148afJeff Brown     *                 .build();
51428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *     }
51528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
516f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *     // Add the callback on start to tell the media router what kinds of routes
51711417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     // the application is interested in so that it can try to discover suitable ones.
518f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *     public void onStart() {
519f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *         super.onStart();
52011417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
521f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *         mediaRouter.addCallback(mSelector, mCallback,
522f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *                 MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
52311417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
52411417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         MediaRouter.RouteInfo route = mediaRouter.updateSelectedRoute(mSelector);
52511417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         // do something with the route...
52611417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     }
52711417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
528f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *     // Remove the selector on stop to tell the media router that it no longer
52911417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     // needs to invest effort trying to discover routes of these kinds for now.
530f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *     public void onStop() {
531f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *         super.onStop();
53211417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
53311417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         mediaRouter.removeCallback(mCallback);
53411417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     }
53511417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
53611417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     private final class MyCallback extends MediaRouter.Callback {
53711417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         // Implement callback methods as needed.
53828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *     }
53928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * }
54028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * </pre>
54128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
54211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param selector A route selector that indicates the kinds of routes that the
54311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * callback would like to discover.
54411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param callback The callback to add.
54511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param flags Flags to control the behavior of the callback.
546f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * May be zero or a combination of {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and
54711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * {@link #CALLBACK_FLAG_UNFILTERED_EVENTS}.
54811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see #removeCallback
54928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     */
550429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    public void addCallback(@NonNull MediaRouteSelector selector, @NonNull Callback callback,
551429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye            @CallbackFlags int flags) {
55228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        if (selector == null) {
55328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            throw new IllegalArgumentException("selector must not be null");
55428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
55511417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (callback == null) {
55611417b1cfde8f1749905f2d735623af9214148afJeff Brown            throw new IllegalArgumentException("callback must not be null");
55711417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
55828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        checkCallingThread();
55928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
56011417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
56111417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "addCallback: selector=" + selector
56211417b1cfde8f1749905f2d735623af9214148afJeff Brown                    + ", callback=" + callback + ", flags=" + Integer.toHexString(flags));
56311417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
56411417b1cfde8f1749905f2d735623af9214148afJeff Brown
56511417b1cfde8f1749905f2d735623af9214148afJeff Brown        CallbackRecord record;
56611417b1cfde8f1749905f2d735623af9214148afJeff Brown        int index = findCallbackRecord(callback);
56711417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (index < 0) {
5689fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            record = new CallbackRecord(this, callback);
56911417b1cfde8f1749905f2d735623af9214148afJeff Brown            mCallbackRecords.add(record);
57011417b1cfde8f1749905f2d735623af9214148afJeff Brown        } else {
57111417b1cfde8f1749905f2d735623af9214148afJeff Brown            record = mCallbackRecords.get(index);
57211417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
57311417b1cfde8f1749905f2d735623af9214148afJeff Brown        boolean updateNeeded = false;
57411417b1cfde8f1749905f2d735623af9214148afJeff Brown        if ((flags & ~record.mFlags) != 0) {
57511417b1cfde8f1749905f2d735623af9214148afJeff Brown            record.mFlags |= flags;
57611417b1cfde8f1749905f2d735623af9214148afJeff Brown            updateNeeded = true;
57711417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
57811417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (!record.mSelector.contains(selector)) {
57911417b1cfde8f1749905f2d735623af9214148afJeff Brown            record.mSelector = new MediaRouteSelector.Builder(record.mSelector)
58011417b1cfde8f1749905f2d735623af9214148afJeff Brown                    .addSelector(selector)
58111417b1cfde8f1749905f2d735623af9214148afJeff Brown                    .build();
58211417b1cfde8f1749905f2d735623af9214148afJeff Brown            updateNeeded = true;
58311417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
58411417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (updateNeeded) {
58511417b1cfde8f1749905f2d735623af9214148afJeff Brown            sGlobal.updateDiscoveryRequest();
58628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
58728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    }
58828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
58928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    /**
59011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Removes the specified callback.  It will no longer receive events about
59111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * changes to media routes.
59228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
59311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param callback The callback to remove.
59411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see #addCallback
59528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     */
596429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    public void removeCallback(@NonNull Callback callback) {
59711417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (callback == null) {
59811417b1cfde8f1749905f2d735623af9214148afJeff Brown            throw new IllegalArgumentException("callback must not be null");
59928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
60028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        checkCallingThread();
60128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
60211417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
60311417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "removeCallback: callback=" + callback);
60411417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
60511417b1cfde8f1749905f2d735623af9214148afJeff Brown
60611417b1cfde8f1749905f2d735623af9214148afJeff Brown        int index = findCallbackRecord(callback);
60711417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (index >= 0) {
60811417b1cfde8f1749905f2d735623af9214148afJeff Brown            mCallbackRecords.remove(index);
60911417b1cfde8f1749905f2d735623af9214148afJeff Brown            sGlobal.updateDiscoveryRequest();
61028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
61128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    }
61228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
61311417b1cfde8f1749905f2d735623af9214148afJeff Brown    private int findCallbackRecord(Callback callback) {
61411417b1cfde8f1749905f2d735623af9214148afJeff Brown        final int count = mCallbackRecords.size();
61511417b1cfde8f1749905f2d735623af9214148afJeff Brown        for (int i = 0; i < count; i++) {
61611417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (mCallbackRecords.get(i).mCallback == callback) {
61711417b1cfde8f1749905f2d735623af9214148afJeff Brown                return i;
61811417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
61911417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
62011417b1cfde8f1749905f2d735623af9214148afJeff Brown        return -1;
62111417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
62211417b1cfde8f1749905f2d735623af9214148afJeff Brown
62328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    /**
6249942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * Registers a media route provider within this application process.
6259942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * <p>
6269942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * The provider will be added to the list of providers that all {@link MediaRouter}
6279942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * instances within this process can use to discover routes.
6289942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * </p>
629c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
630fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * @param providerInstance The media route provider instance to add.
631c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
632c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see MediaRouteProvider
63328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @see #removeCallback
634c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
635429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    public void addProvider(@NonNull MediaRouteProvider providerInstance) {
636fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (providerInstance == null) {
637fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            throw new IllegalArgumentException("providerInstance must not be null");
638c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
639c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
640c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
64111417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
64211417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "addProvider: " + providerInstance);
64311417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
644fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        sGlobal.addProvider(providerInstance);
645c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
646c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
647c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
6489942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * Unregisters a media route provider within this application process.
6499942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * <p>
6509942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * The provider will be removed from the list of providers that all {@link MediaRouter}
6519942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * instances within this process can use to discover routes.
6529942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * </p>
653c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
654fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * @param providerInstance The media route provider instance to remove.
655c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
656c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see MediaRouteProvider
65728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @see #addCallback
658c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
659429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    public void removeProvider(@NonNull MediaRouteProvider providerInstance) {
660fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (providerInstance == null) {
661fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            throw new IllegalArgumentException("providerInstance must not be null");
662c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
663c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
664c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
66511417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
66611417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "removeProvider: " + providerInstance);
66728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
66811417b1cfde8f1749905f2d735623af9214148afJeff Brown        sGlobal.removeProvider(providerInstance);
66928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    }
67028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
67128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    /**
672567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * Adds a remote control client to enable remote control of the volume
673567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * of the selected route.
674567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * <p>
675567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * The remote control client must have previously been registered with
676567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * the audio manager using the {@link android.media.AudioManager#registerRemoteControlClient
677567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * AudioManager.registerRemoteControlClient} method.
678567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * </p>
679567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     *
680567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * @param remoteControlClient The {@link android.media.RemoteControlClient} to register.
681567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     */
682429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    public void addRemoteControlClient(@NonNull Object remoteControlClient) {
683567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        if (remoteControlClient == null) {
684567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            throw new IllegalArgumentException("remoteControlClient must not be null");
685567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
686567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        checkCallingThread();
687567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
688567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        if (DEBUG) {
689567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            Log.d(TAG, "addRemoteControlClient: " + remoteControlClient);
690567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
691567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        sGlobal.addRemoteControlClient(remoteControlClient);
692567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown    }
693567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
694567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown    /**
695567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * Removes a remote control client.
696567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     *
697bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik     * @param remoteControlClient The {@link android.media.RemoteControlClient}
698bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik     *            to unregister.
699567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     */
700429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    public void removeRemoteControlClient(@NonNull Object remoteControlClient) {
701567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        if (remoteControlClient == null) {
702567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            throw new IllegalArgumentException("remoteControlClient must not be null");
703567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
704567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
705567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        if (DEBUG) {
706567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            Log.d(TAG, "removeRemoteControlClient: " + remoteControlClient);
707567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
708567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        sGlobal.removeRemoteControlClient(remoteControlClient);
709567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown    }
710567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
711567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown    /**
712bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik     * Sets the media session to enable remote control of the volume of the
713bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik     * selected route. This should be used instead of
714bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik     * {@link #addRemoteControlClient} when using media sessions. Set the
715bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik     * session to null to clear it.
716bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik     *
717bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik     * @param mediaSession The {@link android.media.session.MediaSession} to
718bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik     *            use.
719bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik     */
720bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik    public void setMediaSession(Object mediaSession) {
721bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik        if (DEBUG) {
722bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            Log.d(TAG, "addMediaSession: " + mediaSession);
723bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik        }
724bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik        sGlobal.setMediaSession(mediaSession);
725bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik    }
726bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik
727bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik    /**
72894be6100218126ce6a08bf1f56209578500b361fRoboErik     * Sets a compat media session to enable remote control of the volume of the
72994be6100218126ce6a08bf1f56209578500b361fRoboErik     * selected route. This should be used instead of
73094be6100218126ce6a08bf1f56209578500b361fRoboErik     * {@link #addRemoteControlClient} when using {@link MediaSessionCompat}.
73194be6100218126ce6a08bf1f56209578500b361fRoboErik     * Set the session to null to clear it.
73294be6100218126ce6a08bf1f56209578500b361fRoboErik     *
73394be6100218126ce6a08bf1f56209578500b361fRoboErik     * @param mediaSession
73494be6100218126ce6a08bf1f56209578500b361fRoboErik     */
73594be6100218126ce6a08bf1f56209578500b361fRoboErik    public void setMediaSessionCompat(MediaSessionCompat mediaSession) {
73694be6100218126ce6a08bf1f56209578500b361fRoboErik        if (DEBUG) {
73794be6100218126ce6a08bf1f56209578500b361fRoboErik            Log.d(TAG, "addMediaSessionCompat: " + mediaSession);
73894be6100218126ce6a08bf1f56209578500b361fRoboErik        }
7395c9469e010106467791b47b0fa83efda84491a21RoboErik        sGlobal.setMediaSessionCompat(mediaSession);
74094be6100218126ce6a08bf1f56209578500b361fRoboErik    }
74194be6100218126ce6a08bf1f56209578500b361fRoboErik
74294be6100218126ce6a08bf1f56209578500b361fRoboErik    public MediaSessionCompat.Token getMediaSessionToken() {
74394be6100218126ce6a08bf1f56209578500b361fRoboErik        return sGlobal.getMediaSessionToken();
74494be6100218126ce6a08bf1f56209578500b361fRoboErik    }
74594be6100218126ce6a08bf1f56209578500b361fRoboErik
74694be6100218126ce6a08bf1f56209578500b361fRoboErik    /**
747c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Ensures that calls into the media router are on the correct thread.
748c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * It pays to be a little paranoid when global state invariants are at risk.
749c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
750c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    static void checkCallingThread() {
751c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (Looper.myLooper() != Looper.getMainLooper()) {
752c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            throw new IllegalStateException("The media router service must only be "
753c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + "accessed on the application's main thread.");
754c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
755c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
756c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
757c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    static <T> boolean equal(T a, T b) {
758c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return a == b || (a != null && b != null && a.equals(b));
759c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
760c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
761c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
762c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Provides information about a media route.
763c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
764fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Each media route has a list of {@link MediaControlIntent media control}
765fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * {@link #getControlFilters intent filters} that describe the capabilities of the
766c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * route and the manner in which it is used and controlled.
767c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
768c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
769e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo    public static class RouteInfo {
770fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final ProviderInfo mProvider;
771c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final String mDescriptorId;
772cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        private final String mUniqueId;
773c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private String mName;
774d63957d28aaabcec588b8cde12eac16414783aebJeff Brown        private String mDescription;
775119de6bb3353341cfd465cea9e545abec3762d20Jae Seo        private Uri mIconUri;
776c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private boolean mEnabled;
77711417b1cfde8f1749905f2d735623af9214148afJeff Brown        private boolean mConnecting;
7783766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo        private int mConnectionState;
77994be6100218126ce6a08bf1f56209578500b361fRoboErik        private boolean mCanDisconnect;
780e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        private final ArrayList<IntentFilter> mControlFilters = new ArrayList<>();
781c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mPlaybackType;
782c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mPlaybackStream;
783893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo        private int mDeviceType;
784c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mVolumeHandling;
785c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mVolume;
786c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mVolumeMax;
787c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private Display mPresentationDisplay;
78877367b4a1871417198d0399d9ad074314c758567Jae Seo        private int mPresentationDisplayId = PRESENTATION_DISPLAY_ID_NONE;
789c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private Bundle mExtras;
79094be6100218126ce6a08bf1f56209578500b361fRoboErik        private IntentSender mSettingsIntent;
791e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        MediaRouteDescriptor mDescriptor;
792c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
793429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        /** @hide */
7943766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo        @IntDef({CONNECTION_STATE_DISCONNECTED, CONNECTION_STATE_CONNECTING,
7953766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo                CONNECTION_STATE_CONNECTED})
7963766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo        @Retention(RetentionPolicy.SOURCE)
7973766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo        private @interface ConnectionState {}
7983766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo
7993766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo        /**
8003766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo         * The default connection state indicating the route is disconnected.
8013766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo         *
8023766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo         * @see #getConnectionState
8033766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo         */
8043766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo        public static final int CONNECTION_STATE_DISCONNECTED = 0;
8053766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo
8063766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo        /**
8073766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo         * A connection state indicating the route is in the process of connecting and is not yet
8083766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo         * ready for use.
8093766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo         *
8103766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo         * @see #getConnectionState
8113766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo         */
8123766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo        public static final int CONNECTION_STATE_CONNECTING = 1;
8133766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo
8143766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo        /**
8153766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo         * A connection state indicating the route is connected.
8163766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo         *
8173766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo         * @see #getConnectionState
8183766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo         */
8193766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo        public static final int CONNECTION_STATE_CONNECTED = 2;
8203766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo
8213766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo        /** @hide */
822429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        @IntDef({PLAYBACK_TYPE_LOCAL,PLAYBACK_TYPE_REMOTE})
823429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        @Retention(RetentionPolicy.SOURCE)
824429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        private @interface PlaybackType {}
825429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye
826c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
827c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * The default playback type, "local", indicating the presentation of the media
828c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * is happening on the same device (e.g. a phone, a tablet) as where it is
829c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * controlled from.
830c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
831c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #getPlaybackType
832c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
833c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public static final int PLAYBACK_TYPE_LOCAL = 0;
834c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
835c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
836c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * A playback type indicating the presentation of the media is happening on
837c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * a different device (i.e. the remote device) than where it is controlled from.
838c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
839c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #getPlaybackType
840c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
841c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public static final int PLAYBACK_TYPE_REMOTE = 1;
842c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
843429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        /** @hide */
844893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo        @IntDef({DEVICE_TYPE_UNKNOWN, DEVICE_TYPE_TV, DEVICE_TYPE_SPEAKER, DEVICE_TYPE_BLUETOOTH})
845893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo        @Retention(RetentionPolicy.SOURCE)
846893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo        private @interface DeviceType {}
847893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo
848893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo        /**
849893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         * The default receiver device type of the route indicating the type is unknown.
850893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         *
851893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         * @see #getDeviceType
852893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         * @hide
853893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         */
854893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo        public static final int DEVICE_TYPE_UNKNOWN = 0;
855893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo
856893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo
857893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo        /**
858893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         * A receiver device type of the route indicating the presentation of the media is happening
859893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         * on a bluetooth device such as a bluetooth speaker.
860893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         *
861893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         * @see #getDeviceType
862893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         * @hide
863893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         */
864893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo        public static final int DEVICE_TYPE_BLUETOOTH = -1;
865893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo
866893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo        /**
867893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         * A receiver device type of the route indicating the presentation of the media is happening
868893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         * on a TV.
869893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         *
870893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         * @see #getDeviceType
871893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         */
872893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo        public static final int DEVICE_TYPE_TV = 1;
873893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo
874893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo        /**
875893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         * A receiver device type of the route indicating the presentation of the media is happening
876893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         * on a speaker.
877893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         *
878893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         * @see #getDeviceType
879893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         */
880893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo        public static final int DEVICE_TYPE_SPEAKER = 2;
881893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo
882893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo        /** @hide */
883429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        @IntDef({PLAYBACK_VOLUME_FIXED,PLAYBACK_VOLUME_VARIABLE})
884429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        @Retention(RetentionPolicy.SOURCE)
885429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        private @interface PlaybackVolume {}
886429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye
887c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
888c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Playback information indicating the playback volume is fixed, i.e. it cannot be
889c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * controlled from this object. An example of fixed playback volume is a remote player,
890c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
891c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * than attenuate at the source.
892c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
893c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #getVolumeHandling
894c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
895c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public static final int PLAYBACK_VOLUME_FIXED = 0;
896c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
897c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
898c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Playback information indicating the playback volume is variable and can be controlled
899c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * from this object.
900c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
901c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #getVolumeHandling
902c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
903c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public static final int PLAYBACK_VOLUME_VARIABLE = 1;
904c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
90577367b4a1871417198d0399d9ad074314c758567Jae Seo        /**
90677367b4a1871417198d0399d9ad074314c758567Jae Seo         * The default presentation display id indicating no presentation display is associated
90777367b4a1871417198d0399d9ad074314c758567Jae Seo         * with the route.
90877367b4a1871417198d0399d9ad074314c758567Jae Seo         * @hide
90977367b4a1871417198d0399d9ad074314c758567Jae Seo         */
91077367b4a1871417198d0399d9ad074314c758567Jae Seo        public static final int PRESENTATION_DISPLAY_ID_NONE = -1;
91177367b4a1871417198d0399d9ad074314c758567Jae Seo
912c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        static final int CHANGE_GENERAL = 1 << 0;
913c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        static final int CHANGE_VOLUME = 1 << 1;
914c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        static final int CHANGE_PRESENTATION_DISPLAY = 1 << 2;
915c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
916cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        RouteInfo(ProviderInfo provider, String descriptorId, String uniqueId) {
917fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mProvider = provider;
918c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mDescriptorId = descriptorId;
919cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            mUniqueId = uniqueId;
920c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
921c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
922fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
923fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Gets information about the provider of this media route.
924fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
925fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public ProviderInfo getProvider() {
926fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mProvider;
927c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
928c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
929c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
930cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         * Gets the unique id of the route.
931cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         * <p>
932cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         * The route unique id functions as a stable identifier by which the route is known.
933cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         * For example, an application can use this id as a token to remember the
934cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         * selected route across restarts or to communicate its identity to a service.
935cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         * </p>
936cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         *
937cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         * @return The unique id of the route, never null.
938cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         */
939429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        @NonNull
940cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        public String getId() {
941cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            return mUniqueId;
942cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        }
943cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown
944cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        /**
945d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * Gets the user-visible name of the route.
946d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * <p>
947d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * The route name identifies the destination represented by the route.
948d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * It may be a user-supplied name, an alias, or device serial number.
949d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * </p>
950c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
951d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * @return The user-visible name of a media route.  This is the string presented
952c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * to users who may select this as the active route.
953c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
954c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public String getName() {
955c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mName;
956c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
957c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
958c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
959d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * Gets the user-visible description of the route.
960d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * <p>
961d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * The route description describes the kind of destination represented by the route.
962d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * It may be a user-supplied string, a model number or brand of device.
963d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * </p>
964c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
965d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * @return The description of the route, or null if none.
966c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
967429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        @Nullable
968d63957d28aaabcec588b8cde12eac16414783aebJeff Brown        public String getDescription() {
969d63957d28aaabcec588b8cde12eac16414783aebJeff Brown            return mDescription;
970c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
971c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
972c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
973119de6bb3353341cfd465cea9e545abec3762d20Jae Seo         * Gets the URI of the icon representing this route.
974119de6bb3353341cfd465cea9e545abec3762d20Jae Seo         * <p>
975119de6bb3353341cfd465cea9e545abec3762d20Jae Seo         * This icon will be used in picker UIs if available.
976119de6bb3353341cfd465cea9e545abec3762d20Jae Seo         * </p>
977119de6bb3353341cfd465cea9e545abec3762d20Jae Seo         *
978119de6bb3353341cfd465cea9e545abec3762d20Jae Seo         * @return The URI of the icon representing this route, or null if none.
979119de6bb3353341cfd465cea9e545abec3762d20Jae Seo         */
980119de6bb3353341cfd465cea9e545abec3762d20Jae Seo        public Uri getIconUri() {
981119de6bb3353341cfd465cea9e545abec3762d20Jae Seo            return mIconUri;
982119de6bb3353341cfd465cea9e545abec3762d20Jae Seo        }
983119de6bb3353341cfd465cea9e545abec3762d20Jae Seo
984119de6bb3353341cfd465cea9e545abec3762d20Jae Seo        /**
985c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Returns true if this route is enabled and may be selected.
986c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
98711417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @return True if this route is enabled.
988c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
989c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public boolean isEnabled() {
990c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mEnabled;
991c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
992c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
993c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
99411417b1cfde8f1749905f2d735623af9214148afJeff Brown         * Returns true if the route is in the process of connecting and is not
99511417b1cfde8f1749905f2d735623af9214148afJeff Brown         * yet ready for use.
99611417b1cfde8f1749905f2d735623af9214148afJeff Brown         *
99711417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @return True if this route is in the process of connecting.
99811417b1cfde8f1749905f2d735623af9214148afJeff Brown         */
99911417b1cfde8f1749905f2d735623af9214148afJeff Brown        public boolean isConnecting() {
100011417b1cfde8f1749905f2d735623af9214148afJeff Brown            return mConnecting;
100111417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
100211417b1cfde8f1749905f2d735623af9214148afJeff Brown
100311417b1cfde8f1749905f2d735623af9214148afJeff Brown        /**
10043766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo         * Gets the connection state of the route.
10053766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo         *
10063766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo         * @return The connection state of this route: {@link #CONNECTION_STATE_DISCONNECTED},
10073766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo         * {@link #CONNECTION_STATE_CONNECTING}, or {@link #CONNECTION_STATE_CONNECTED}.
10083766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo         */
10093766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo        @ConnectionState
10103766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo        public int getConnectionState() {
10113766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo            return mConnectionState;
10123766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo        }
10133766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo
10143766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo        /**
1015fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Returns true if this route is currently selected.
1016c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
101711417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @return True if this route is currently selected.
1018fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
1019fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @see MediaRouter#getSelectedRoute
1020fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1021fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean isSelected() {
1022fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
1023fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return sGlobal.getSelectedRoute() == this;
1024fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1025fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1026fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
1027fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Returns true if this route is the default route.
1028fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
102911417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @return True if this route is the default route.
1030fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
1031fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @see MediaRouter#getDefaultRoute
1032fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1033fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean isDefault() {
1034fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
1035fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return sGlobal.getDefaultRoute() == this;
1036fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1037fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1038fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
1039fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Gets a list of {@link MediaControlIntent media control intent} filters that
1040fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * describe the capabilities of this route and the media control actions that
1041fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * it supports.
1042fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
1043fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @return A list of intent filters that specifies the media control intents that
1044fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * this route supports.
1045c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1046c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent
1047c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #supportsControlCategory
1048c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #supportsControlRequest
1049c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1050fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public List<IntentFilter> getControlFilters() {
1051fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mControlFilters;
1052c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1053c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1054c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
105528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * Returns true if the route supports at least one of the capabilities
105628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * described by a media route selector.
105728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         *
105828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * @param selector The selector that specifies the capabilities to check.
105928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * @return True if the route supports at least one of the capabilities
106028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * described in the media route selector.
106128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         */
1062429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        public boolean matchesSelector(@NonNull MediaRouteSelector selector) {
106328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            if (selector == null) {
106428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown                throw new IllegalArgumentException("selector must not be null");
106528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            }
106628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            checkCallingThread();
106711417b1cfde8f1749905f2d735623af9214148afJeff Brown            return selector.matchesControlFilters(mControlFilters);
106828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
106928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
107028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        /**
1071c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Returns true if the route supports the specified
1072c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * {@link MediaControlIntent media control} category.
1073c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
1074c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Media control categories describe the capabilities of this route
1075c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * such as whether it supports live audio streaming or remote playback.
1076c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
1077c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1078c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param category A {@link MediaControlIntent media control} category
1079c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO},
1080c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO},
1081fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined
1082c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * media control category.
108328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * @return True if the route supports the specified intent category.
1084c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1085c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent
1086fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @see #getControlFilters
1087c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1088429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        public boolean supportsControlCategory(@NonNull String category) {
1089c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (category == null) {
1090c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalArgumentException("category must not be null");
1091c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1092fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
1093c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1094fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int count = mControlFilters.size();
1095fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            for (int i = 0; i < count; i++) {
1096fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mControlFilters.get(i).hasCategory(category)) {
1097fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return true;
1098fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
1099fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1100fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
1101c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1102c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1103c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1104c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Returns true if the route supports the specified
1105a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * {@link MediaControlIntent media control} category and action.
1106a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * <p>
1107a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * Media control actions describe specific requests that an application
1108a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * can ask a route to perform.
1109a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * </p>
1110a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         *
1111a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param category A {@link MediaControlIntent media control} category
1112a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO},
1113a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO},
1114a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined
1115a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * media control category.
1116a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param action A {@link MediaControlIntent media control} action
1117a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * such as {@link MediaControlIntent#ACTION_PLAY}.
1118a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @return True if the route supports the specified intent action.
1119a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         *
1120a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @see MediaControlIntent
1121a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @see #getControlFilters
1122a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         */
1123429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        public boolean supportsControlAction(@NonNull String category, @NonNull String action) {
1124a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            if (category == null) {
1125a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                throw new IllegalArgumentException("category must not be null");
1126a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            }
1127a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            if (action == null) {
1128a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                throw new IllegalArgumentException("action must not be null");
1129a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            }
1130a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            checkCallingThread();
1131a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
1132a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            int count = mControlFilters.size();
1133a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            for (int i = 0; i < count; i++) {
1134a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                IntentFilter filter = mControlFilters.get(i);
1135a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                if (filter.hasCategory(category) && filter.hasAction(action)) {
1136a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    return true;
1137a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                }
1138a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            }
1139a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            return false;
1140a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
1141a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
1142a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        /**
1143a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * Returns true if the route supports the specified
1144c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * {@link MediaControlIntent media control} request.
1145c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
1146c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Media control requests are used to request the route to perform
114743f79f79a5117550a7dfedf9c65124afd163ea43Jeff Brown         * actions such as starting remote playback of a media item.
1148c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
1149c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1150c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param intent A {@link MediaControlIntent media control intent}.
1151c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return True if the route can handle the specified intent.
1152c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1153c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent
1154fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @see #getControlFilters
1155c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1156429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        public boolean supportsControlRequest(@NonNull Intent intent) {
1157c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (intent == null) {
1158c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalArgumentException("intent must not be null");
1159c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1160c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            checkCallingThread();
1161c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1162c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            ContentResolver contentResolver = sGlobal.getContentResolver();
1163fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int count = mControlFilters.size();
1164fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            for (int i = 0; i < count; i++) {
1165fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mControlFilters.get(i).match(contentResolver, intent, true, TAG) >= 0) {
1166fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return true;
1167fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
1168fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1169fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
1170c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1171c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1172c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1173c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Sends a {@link MediaControlIntent media control} request to be performed
1174c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * asynchronously by the route's destination.
1175c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
1176c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Media control requests are used to request the route to perform
117743f79f79a5117550a7dfedf9c65124afd163ea43Jeff Brown         * actions such as starting remote playback of a media item.
1178c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p><p>
1179fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * This function may only be called on a selected route.  Control requests
1180fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * sent to unselected routes will fail.
1181c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
1182c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1183c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param intent A {@link MediaControlIntent media control intent}.
1184c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param callback A {@link ControlRequestCallback} to invoke with the result
1185c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * of the request, or null if no result is required.
1186c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1187c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent
1188c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1189429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        public void sendControlRequest(@NonNull Intent intent,
1190429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye                @Nullable ControlRequestCallback callback) {
1191c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (intent == null) {
1192c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalArgumentException("intent must not be null");
1193c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1194c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            checkCallingThread();
1195c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1196fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            sGlobal.sendControlRequest(this, intent, callback);
1197c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1198c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1199c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1200c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets the type of playback associated with this route.
1201c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1202c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The type of playback associated with this route: {@link #PLAYBACK_TYPE_LOCAL}
1203c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * or {@link #PLAYBACK_TYPE_REMOTE}.
1204c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1205429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        @PlaybackType
1206c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getPlaybackType() {
1207c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mPlaybackType;
1208c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1209c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1210c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1211350ba6e4a1b5ec28721a098e50eaf6a508eb28f0Jeff Brown         * Gets the audio stream over which the playback associated with this route is performed.
1212c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1213c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The stream over which the playback associated with this route is performed.
1214c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1215c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getPlaybackStream() {
1216c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mPlaybackStream;
1217c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1218c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1219c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1220893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         * Gets the type of the receiver device associated with this route.
1221893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         *
1222893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         * @return The type of the receiver device associated with this route:
1223050b7ec33bd9a2fe493f0b0b85d49ed213046219Jae Seo         * {@link #DEVICE_TYPE_TV} or {@link #DEVICE_TYPE_SPEAKER}.
1224893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo         */
1225893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo        public int getDeviceType() {
1226893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo            return mDeviceType;
1227893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo        }
1228893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo
1229893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo        /**
1230c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets information about how volume is handled on the route.
1231c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1232c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return How volume is handled on the route: {@link #PLAYBACK_VOLUME_FIXED}
1233c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * or {@link #PLAYBACK_VOLUME_VARIABLE}.
1234c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1235429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        @PlaybackVolume
1236c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getVolumeHandling() {
1237c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mVolumeHandling;
1238c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1239c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1240c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1241c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets the current volume for this route. Depending on the route, this may only
1242c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * be valid if the route is currently selected.
1243c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1244c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The volume at which the playback associated with this route is performed.
1245c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1246c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getVolume() {
1247c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mVolume;
1248c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1249c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1250c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1251c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets the maximum volume at which the playback associated with this route is performed.
1252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1253c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The maximum volume at which the playback associated with
1254c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * this route is performed.
1255c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1256c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getVolumeMax() {
1257c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mVolumeMax;
1258c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1259c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1260c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
126194be6100218126ce6a08bf1f56209578500b361fRoboErik         * Gets whether this route supports disconnecting without interrupting
126294be6100218126ce6a08bf1f56209578500b361fRoboErik         * playback.
126394be6100218126ce6a08bf1f56209578500b361fRoboErik         *
126494be6100218126ce6a08bf1f56209578500b361fRoboErik         * @return True if this route can disconnect without stopping playback,
126594be6100218126ce6a08bf1f56209578500b361fRoboErik         *         false otherwise.
126694be6100218126ce6a08bf1f56209578500b361fRoboErik         */
126794be6100218126ce6a08bf1f56209578500b361fRoboErik        public boolean canDisconnect() {
126894be6100218126ce6a08bf1f56209578500b361fRoboErik            return mCanDisconnect;
126994be6100218126ce6a08bf1f56209578500b361fRoboErik        }
127094be6100218126ce6a08bf1f56209578500b361fRoboErik
127194be6100218126ce6a08bf1f56209578500b361fRoboErik        /**
1272c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Requests a volume change for this route asynchronously.
1273c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
1274c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This function may only be called on a selected route.  It will have
1275c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * no effect if the route is currently unselected.
1276c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
1277c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1278c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param volume The new volume value between 0 and {@link #getVolumeMax}.
1279c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1280c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void requestSetVolume(int volume) {
1281c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            checkCallingThread();
1282c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            sGlobal.requestSetVolume(this, Math.min(mVolumeMax, Math.max(0, volume)));
1283c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1284c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1285c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1286c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Requests an incremental volume update for this route asynchronously.
1287c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
1288c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This function may only be called on a selected route.  It will have
1289c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * no effect if the route is currently unselected.
1290c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
1291c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1292c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param delta The delta to add to the current volume.
1293c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1294c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void requestUpdateVolume(int delta) {
1295c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            checkCallingThread();
1296c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (delta != 0) {
1297c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                sGlobal.requestUpdateVolume(this, delta);
1298c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1299c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1300c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1301c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1302c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets the {@link Display} that should be used by the application to show
1303c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * a {@link android.app.Presentation} on an external display when this route is selected.
1304c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Depending on the route, this may only be valid if the route is currently
1305c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * selected.
1306c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
1307c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * The preferred presentation display may change independently of the route
1308c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * being selected or unselected.  For example, the presentation display
1309c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * of the default system route may change when an external HDMI display is connected
1310c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * or disconnected even though the route itself has not changed.
1311c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p><p>
1312c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This method may return null if there is no external display associated with
1313c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * the route or if the display is not ready to show UI yet.
1314c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p><p>
1315c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * The application should listen for changes to the presentation display
1316c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * using the {@link Callback#onRoutePresentationDisplayChanged} callback and
1317c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * show or dismiss its {@link android.app.Presentation} accordingly when the display
1318c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * becomes available or is removed.
1319c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p><p>
1320c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This method only makes sense for
1321c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO live video} routes.
1322c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
1323c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1324c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The preferred presentation display to use when this route is
1325c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * selected or null if none.
1326c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1327c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent#CATEGORY_LIVE_VIDEO
1328c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see android.app.Presentation
1329c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1330429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        @Nullable
1331c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public Display getPresentationDisplay() {
1332fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
1333c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mPresentationDisplayId >= 0 && mPresentationDisplay == null) {
1334c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mPresentationDisplay = sGlobal.getDisplay(mPresentationDisplayId);
1335c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1336c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mPresentationDisplay;
1337c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1338c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1339c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
134077367b4a1871417198d0399d9ad074314c758567Jae Seo         * Gets the route's presentation display id, or -1 if none.
134177367b4a1871417198d0399d9ad074314c758567Jae Seo         * @hide
134277367b4a1871417198d0399d9ad074314c758567Jae Seo         */
134377367b4a1871417198d0399d9ad074314c758567Jae Seo        public int getPresentationDisplayId() {
134477367b4a1871417198d0399d9ad074314c758567Jae Seo            return mPresentationDisplayId;
134577367b4a1871417198d0399d9ad074314c758567Jae Seo        }
134677367b4a1871417198d0399d9ad074314c758567Jae Seo
134777367b4a1871417198d0399d9ad074314c758567Jae Seo        /**
1348c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets a collection of extra properties about this route that were supplied
1349c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * by its media route provider, or null if none.
1350c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1351429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        @Nullable
1352c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public Bundle getExtras() {
1353c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mExtras;
1354c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1355c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1356fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
135794be6100218126ce6a08bf1f56209578500b361fRoboErik         * Gets an intent sender for launching a settings activity for this
135894be6100218126ce6a08bf1f56209578500b361fRoboErik         * route.
135994be6100218126ce6a08bf1f56209578500b361fRoboErik         */
136094be6100218126ce6a08bf1f56209578500b361fRoboErik        @Nullable
136194be6100218126ce6a08bf1f56209578500b361fRoboErik        public IntentSender getSettingsIntent() {
136294be6100218126ce6a08bf1f56209578500b361fRoboErik            return mSettingsIntent;
136394be6100218126ce6a08bf1f56209578500b361fRoboErik        }
136494be6100218126ce6a08bf1f56209578500b361fRoboErik
136594be6100218126ce6a08bf1f56209578500b361fRoboErik        /**
1366fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Selects this media route.
1367fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1368fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void select() {
1369fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
1370fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            sGlobal.selectRoute(this);
1371fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1372fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1373c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
1374c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public String toString() {
1375cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            return "MediaRouter.RouteInfo{ uniqueId=" + mUniqueId
1376cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                    + ", name=" + mName
1377d63957d28aaabcec588b8cde12eac16414783aebJeff Brown                    + ", description=" + mDescription
1378119de6bb3353341cfd465cea9e545abec3762d20Jae Seo                    + ", iconUri=" + mIconUri
1379c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", enabled=" + mEnabled
138011417b1cfde8f1749905f2d735623af9214148afJeff Brown                    + ", connecting=" + mConnecting
13813766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo                    + ", connectionState=" + mConnectionState
138294be6100218126ce6a08bf1f56209578500b361fRoboErik                    + ", canDisconnect=" + mCanDisconnect
1383c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", playbackType=" + mPlaybackType
1384c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", playbackStream=" + mPlaybackStream
1385893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo                    + ", deviceType=" + mDeviceType
1386c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", volumeHandling=" + mVolumeHandling
1387c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", volume=" + mVolume
1388c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", volumeMax=" + mVolumeMax
1389c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", presentationDisplayId=" + mPresentationDisplayId
1390c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", extras=" + mExtras
139194be6100218126ce6a08bf1f56209578500b361fRoboErik                    + ", settingsIntent=" + mSettingsIntent
1392fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    + ", providerPackageName=" + mProvider.getPackageName()
1393fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    + " }";
1394fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1395fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1396e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        int maybeUpdateDescriptor(MediaRouteDescriptor descriptor) {
1397fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int changes = 0;
1398fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mDescriptor != descriptor) {
1399e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                changes = updateDescriptor(descriptor);
1400e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            }
1401e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            return changes;
1402e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        }
1403e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo
1404e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        int updateDescriptor(MediaRouteDescriptor descriptor) {
1405e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            int changes = 0;
1406e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            mDescriptor = descriptor;
1407e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            if (descriptor != null) {
1408e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (!equal(mName, descriptor.getName())) {
1409e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mName = descriptor.getName();
1410e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL;
1411e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1412e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (!equal(mDescription, descriptor.getDescription())) {
1413e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mDescription = descriptor.getDescription();
1414e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL;
1415e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1416119de6bb3353341cfd465cea9e545abec3762d20Jae Seo                if (!equal(mIconUri, descriptor.getIconUri())) {
1417119de6bb3353341cfd465cea9e545abec3762d20Jae Seo                    mIconUri = descriptor.getIconUri();
1418119de6bb3353341cfd465cea9e545abec3762d20Jae Seo                    changes |= CHANGE_GENERAL;
1419119de6bb3353341cfd465cea9e545abec3762d20Jae Seo                }
1420e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (mEnabled != descriptor.isEnabled()) {
1421e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mEnabled = descriptor.isEnabled();
1422e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL;
1423e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1424e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (mConnecting != descriptor.isConnecting()) {
1425e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mConnecting = descriptor.isConnecting();
1426e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL;
1427e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
14283766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo                if (mConnectionState != descriptor.getConnectionState()) {
14293766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo                    mConnectionState = descriptor.getConnectionState();
14303766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo                    changes |= CHANGE_GENERAL;
14313766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo                }
1432e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (!mControlFilters.equals(descriptor.getControlFilters())) {
1433e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mControlFilters.clear();
1434e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mControlFilters.addAll(descriptor.getControlFilters());
1435e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL;
1436e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1437e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (mPlaybackType != descriptor.getPlaybackType()) {
1438e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mPlaybackType = descriptor.getPlaybackType();
1439e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL;
1440e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1441e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (mPlaybackStream != descriptor.getPlaybackStream()) {
1442e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mPlaybackStream = descriptor.getPlaybackStream();
1443e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL;
1444e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1445893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo                if (mDeviceType != descriptor.getDeviceType()) {
1446893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo                    mDeviceType = descriptor.getDeviceType();
1447893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo                    changes |= CHANGE_GENERAL;
1448893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo                }
1449e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (mVolumeHandling != descriptor.getVolumeHandling()) {
1450e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mVolumeHandling = descriptor.getVolumeHandling();
1451e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL | CHANGE_VOLUME;
1452e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1453e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (mVolume != descriptor.getVolume()) {
1454e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mVolume = descriptor.getVolume();
1455e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL | CHANGE_VOLUME;
1456e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1457e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (mVolumeMax != descriptor.getVolumeMax()) {
1458e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mVolumeMax = descriptor.getVolumeMax();
1459e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL | CHANGE_VOLUME;
1460e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1461e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (mPresentationDisplayId != descriptor.getPresentationDisplayId()) {
1462e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mPresentationDisplayId = descriptor.getPresentationDisplayId();
1463e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mPresentationDisplay = null;
1464e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY;
1465e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1466e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (!equal(mExtras, descriptor.getExtras())) {
1467e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mExtras = descriptor.getExtras();
1468e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL;
1469e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1470e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (!equal(mSettingsIntent, descriptor.getSettingsActivity())) {
1471e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mSettingsIntent = descriptor.getSettingsActivity();
1472e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL;
1473e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1474e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (mCanDisconnect != descriptor.canDisconnectAndKeepPlaying()) {
1475e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mCanDisconnect = descriptor.canDisconnectAndKeepPlaying();
1476e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY;
1477fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
1478fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1479fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return changes;
1480fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1481fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1482fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        String getDescriptorId() {
1483fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mDescriptorId;
1484fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1485fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
148614b631fc79b2fc69f392ab8d2f096b0e86b9bea2Jae Seo        /** @hide */
148714b631fc79b2fc69f392ab8d2f096b0e86b9bea2Jae Seo        public MediaRouteProvider getProviderInstance() {
1488fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mProvider.getProviderInstance();
1489fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1490fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
1491fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1492fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    /**
1493e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo     * Information about a route that consists of multiple other routes in a group.
149414a7007aaefb96b3b6fb06171aa10e1200116d6aJae Seo     * @hide
1495e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo     */
1496e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo    public static class RouteGroup extends RouteInfo {
1497e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        private List<RouteInfo> mRoutes = new ArrayList<>();
1498e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo
1499e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        RouteGroup(ProviderInfo provider, String descriptorId, String uniqueId) {
1500e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            super(provider, descriptorId, uniqueId);
1501e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        }
1502e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo
1503e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        /**
1504e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo         * @return The number of routes in this group
1505e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo         */
1506e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        public int getRouteCount() {
1507e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            return mRoutes.size();
1508e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        }
1509e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo
1510e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        /**
1511e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo         * Returns the route in this group at the specified index
1512e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo         *
1513e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo         * @param index Index to fetch
1514e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo         * @return The route at index
1515e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo         */
1516e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        public RouteInfo getRouteAt(int index) {
1517e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            return mRoutes.get(index);
1518e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        }
1519e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo
15206051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo        /**
15216051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo         * Returns the routes in this group
15226051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo         *
15236051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo         * @return The list of the routes in this group
15246051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo         */
15256051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo        public List<RouteInfo> getRoutes() {
15266051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo            return mRoutes;
15276051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo        }
15286051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo
1529e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        @Override
1530e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        public String toString() {
1531e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            StringBuilder sb = new StringBuilder(super.toString());
1532e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            sb.append('[');
1533e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            final int count = mRoutes.size();
1534e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            for (int i = 0; i < count; i++) {
1535e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (i > 0) sb.append(", ");
1536e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                sb.append(mRoutes.get(i));
1537e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            }
1538e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            sb.append(']');
1539e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            return sb.toString();
1540e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        }
1541e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo
1542e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        @Override
1543e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        int maybeUpdateDescriptor(MediaRouteDescriptor descriptor) {
1544e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            boolean changed = false;
1545e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            if (mDescriptor != descriptor) {
1546e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                mDescriptor = descriptor;
1547e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (descriptor != null) {
15481bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo                    List<String> groupMemberIds = descriptor.getGroupMemberIds();
1549e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    List<RouteInfo> routes = new ArrayList<>();
15501bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo                    changed = groupMemberIds.size() != mRoutes.size();
15511bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo                    for (String groupMemberId : groupMemberIds) {
15521bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo                        String uniqueId = sGlobal.getUniqueId(getProvider(), groupMemberId);
15531bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo                        RouteInfo groupMember = sGlobal.getRoute(uniqueId);
15541bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo                        if (groupMember != null) {
15551bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo                            routes.add(groupMember);
15561bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo                            if (!changed && !mRoutes.contains(groupMember)) {
1557e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                changed = true;
1558e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            }
1559e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        }
1560e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    }
1561e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    if (changed) {
1562e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        mRoutes = routes;
1563e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    }
1564e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1565e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            }
1566e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            return (changed ? CHANGE_GENERAL : 0) | super.updateDescriptor(descriptor);
1567e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        }
1568e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo    }
1569e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo
1570e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo    /**
1571fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Provides information about a media route provider.
1572fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * <p>
1573fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * This object may be used to determine which media route provider has
1574fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * published a particular route.
1575fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * </p>
1576fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     */
1577fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public static final class ProviderInfo {
1578fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final MediaRouteProvider mProviderInstance;
1579e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        private final List<RouteInfo> mRoutes = new ArrayList<>();
1580fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1581fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final ProviderMetadata mMetadata;
158211417b1cfde8f1749905f2d735623af9214148afJeff Brown        private MediaRouteProviderDescriptor mDescriptor;
1583fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private Resources mResources;
1584fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private boolean mResourcesNotAvailable;
1585fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1586fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        ProviderInfo(MediaRouteProvider provider) {
1587fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mProviderInstance = provider;
1588fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mMetadata = provider.getMetadata();
1589fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1590fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1591fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
1592fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Gets the provider's underlying {@link MediaRouteProvider} instance.
1593fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1594fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public MediaRouteProvider getProviderInstance() {
1595fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
1596fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mProviderInstance;
1597fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1598fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1599fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
1600adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown         * Gets the package name of the media route provider.
1601fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1602fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public String getPackageName() {
1603fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mMetadata.getPackageName();
1604fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1605fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1606fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
1607adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown         * Gets the component name of the media route provider.
1608adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown         */
1609adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown        public ComponentName getComponentName() {
1610adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown            return mMetadata.getComponentName();
1611adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown        }
1612adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown
1613adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown        /**
1614fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Gets the {@link MediaRouter.RouteInfo routes} published by this route provider.
1615fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1616fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public List<RouteInfo> getRoutes() {
1617fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
1618fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mRoutes;
1619fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1620fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1621fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        Resources getResources() {
1622fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mResources == null && !mResourcesNotAvailable) {
1623fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                String packageName = getPackageName();
1624fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Context context = sGlobal.getProviderContext(packageName);
1625fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (context != null) {
1626fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mResources = context.getResources();
1627fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                } else {
1628fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    Log.w(TAG, "Unable to obtain resources for route provider package: "
1629fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            + packageName);
1630fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mResourcesNotAvailable = true;
1631fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
1632fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1633fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mResources;
1634fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1635fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
163611417b1cfde8f1749905f2d735623af9214148afJeff Brown        boolean updateDescriptor(MediaRouteProviderDescriptor descriptor) {
1637fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mDescriptor != descriptor) {
1638fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mDescriptor = descriptor;
1639fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return true;
1640fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1641fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
1642fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1643fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1644fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        int findRouteByDescriptorId(String id) {
1645fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            final int count = mRoutes.size();
1646fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            for (int i = 0; i < count; i++) {
1647fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mRoutes.get(i).mDescriptorId.equals(id)) {
1648fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return i;
1649fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
1650fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1651fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return -1;
1652fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1653fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1654fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
1655fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public String toString() {
1656fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return "MediaRouter.RouteProviderInfo{ packageName=" + getPackageName()
1657c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + " }";
1658c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1659c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
1660c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1661c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
1662c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Interface for receiving events about media routing changes.
1663c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * All methods of this interface will be called from the application's main thread.
1664c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
1665c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * A Callback will only receive events relevant to routes that the callback
166611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * was registered for unless the {@link MediaRouter#CALLBACK_FLAG_UNFILTERED_EVENTS}
166711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * flag was specified in {@link MediaRouter#addCallback(MediaRouteSelector, Callback, int)}.
1668c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
1669c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
167011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see MediaRouter#addCallback(MediaRouteSelector, Callback, int)
1671c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see MediaRouter#removeCallback(Callback)
1672c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
1673c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public static abstract class Callback {
1674c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1675fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when the supplied media route becomes selected as the active route.
1676c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1677fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1678c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that has been selected.
1679c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1680c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteSelected(MediaRouter router, RouteInfo route) {
1681c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1682c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1683c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1684fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when the supplied media route becomes unselected as the active route.
1685c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1686fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1687c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that has been unselected.
1688c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1689c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteUnselected(MediaRouter router, RouteInfo route) {
1690c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1691c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1692c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1693fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route has been added.
1694c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1695fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1696c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that has become available for use.
1697c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1698c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteAdded(MediaRouter router, RouteInfo route) {
1699c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1700c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1701c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1702fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route has been removed.
1703c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1704fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1705c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that has been removed from availability.
1706c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1707c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteRemoved(MediaRouter router, RouteInfo route) {
1708c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1709c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1710c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1711fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a property of the indicated media route has changed.
1712c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1713fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1714c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that was changed.
1715c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1716c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteChanged(MediaRouter router, RouteInfo route) {
1717c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1718c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1719c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1720fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route's volume changes.
1721c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1722fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1723c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route whose volume changed.
1724c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1725c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) {
1726c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1727c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1728c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1729fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route's presentation display changes.
1730c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
1731c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This method is called whenever the route's presentation display becomes
1732fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * available, is removed or has changes to some of its properties (such as its size).
1733c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
1734c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1735fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1736c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route whose presentation display changed.
1737c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1738c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see RouteInfo#getPresentationDisplay()
1739c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1740c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo route) {
1741c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1742fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1743fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
1744fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route provider has been added.
1745fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
1746fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1747fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param provider The provider that has become available for use.
1748fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1749fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void onProviderAdded(MediaRouter router, ProviderInfo provider) {
1750fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1751fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1752fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
1753fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route provider has been removed.
1754fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
1755fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1756fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param provider The provider that has been removed from availability.
1757fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1758fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void onProviderRemoved(MediaRouter router, ProviderInfo provider) {
1759fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
176011417b1cfde8f1749905f2d735623af9214148afJeff Brown
176111417b1cfde8f1749905f2d735623af9214148afJeff Brown        /**
176211417b1cfde8f1749905f2d735623af9214148afJeff Brown         * Called when a property of the indicated media route provider has changed.
176311417b1cfde8f1749905f2d735623af9214148afJeff Brown         *
176411417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @param router The media router reporting the event.
176511417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @param provider The provider that was changed.
176611417b1cfde8f1749905f2d735623af9214148afJeff Brown         */
176711417b1cfde8f1749905f2d735623af9214148afJeff Brown        public void onProviderChanged(MediaRouter router, ProviderInfo provider) {
176811417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
1769c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
1770c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1771c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
1772c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Callback which is invoked with the result of a media control request.
1773c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
1774c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see RouteInfo#sendControlRequest
1775c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
1776fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public static abstract class ControlRequestCallback {
1777c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
17783d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * Called when a media control request succeeds.
17793d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         *
17803d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * @param data Result data, or null if none.
17813d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * Contents depend on the {@link MediaControlIntent media control action}.
1782c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
17833d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown        public void onResult(Bundle data) {
17843d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown        }
1785c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1786c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
17873d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * Called when a media control request fails.
1788c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
17893d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * @param error A localized error message which may be shown to the user, or null
17903d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * if the cause of the error is unclear.
17913d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * @param data Error data, or null if none.
17923d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * Contents depend on the {@link MediaControlIntent media control action}.
1793c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
17943d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown        public void onError(String error, Bundle data) {
1795c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1796c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
1797c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
179811417b1cfde8f1749905f2d735623af9214148afJeff Brown    private static final class CallbackRecord {
17999fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown        public final MediaRouter mRouter;
180011417b1cfde8f1749905f2d735623af9214148afJeff Brown        public final Callback mCallback;
180111417b1cfde8f1749905f2d735623af9214148afJeff Brown        public MediaRouteSelector mSelector;
180211417b1cfde8f1749905f2d735623af9214148afJeff Brown        public int mFlags;
180311417b1cfde8f1749905f2d735623af9214148afJeff Brown
18049fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown        public CallbackRecord(MediaRouter router, Callback callback) {
18059fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            mRouter = router;
180611417b1cfde8f1749905f2d735623af9214148afJeff Brown            mCallback = callback;
180711417b1cfde8f1749905f2d735623af9214148afJeff Brown            mSelector = MediaRouteSelector.EMPTY;
180811417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
180911417b1cfde8f1749905f2d735623af9214148afJeff Brown
181011417b1cfde8f1749905f2d735623af9214148afJeff Brown        public boolean filterRouteEvent(RouteInfo route) {
181111417b1cfde8f1749905f2d735623af9214148afJeff Brown            return (mFlags & CALLBACK_FLAG_UNFILTERED_EVENTS) != 0
181211417b1cfde8f1749905f2d735623af9214148afJeff Brown                    || route.matchesSelector(mSelector);
181311417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
181411417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
181511417b1cfde8f1749905f2d735623af9214148afJeff Brown
1816c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
1817c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Global state for the media router.
1818c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
1819c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Media routes and media route providers are global to the process; their
1820c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * state and the bulk of the media router implementation lives here.
1821c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
1822c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
18233efa63d3b896244713e84acbb5945562dce41d77Jeff Brown    private static final class GlobalMediaRouter
18243efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            implements SystemMediaRouteProvider.SyncCallback,
18253efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            RegisteredMediaRouteProviderWatcher.Callback {
1826c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final Context mApplicationContext;
182738e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo        private final ArrayList<WeakReference<MediaRouter>> mRouters = new ArrayList<>();
182838e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo        private final ArrayList<RouteInfo> mRoutes = new ArrayList<>();
182938e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo        private final Map<Pair<String, String>, String> mUniqueIdMap = new HashMap<>();
183038e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo        private final ArrayList<ProviderInfo> mProviders = new ArrayList<>();
1831567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        private final ArrayList<RemoteControlClientRecord> mRemoteControlClients =
183238e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo                new ArrayList<>();
1833567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        private final RemoteControlClientCompat.PlaybackInfo mPlaybackInfo =
1834567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                new RemoteControlClientCompat.PlaybackInfo();
1835c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final ProviderCallback mProviderCallback = new ProviderCallback();
1836c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final CallbackHandler mCallbackHandler = new CallbackHandler();
1837c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final DisplayManagerCompat mDisplayManager;
1838c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final SystemMediaRouteProvider mSystemProvider;
1839fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown        private final boolean mLowRam;
1840c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1841fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private RegisteredMediaRouteProviderWatcher mRegisteredProviderWatcher;
1842c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private RouteInfo mDefaultRoute;
1843c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private RouteInfo mSelectedRoute;
1844bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang        private RouteController mSelectedRouteController;
18451bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo        private Map<String, RouteController> mGroupMemberControllers;
184611417b1cfde8f1749905f2d735623af9214148afJeff Brown        private MediaRouteDiscoveryRequest mDiscoveryRequest;
1847bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik        private MediaSessionRecord mMediaSession;
18485c9469e010106467791b47b0fa83efda84491a21RoboErik        private MediaSessionCompat mRccMediaSession;
1849e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        private MediaSessionCompat mCompatSession;
18505c9469e010106467791b47b0fa83efda84491a21RoboErik        private MediaSessionCompat.OnActiveChangeListener mSessionActiveListener =
18515c9469e010106467791b47b0fa83efda84491a21RoboErik                new MediaSessionCompat.OnActiveChangeListener() {
18525c9469e010106467791b47b0fa83efda84491a21RoboErik            @Override
18535c9469e010106467791b47b0fa83efda84491a21RoboErik            public void onActiveChanged() {
18545c9469e010106467791b47b0fa83efda84491a21RoboErik                if(mRccMediaSession != null) {
18555c9469e010106467791b47b0fa83efda84491a21RoboErik                    if (mRccMediaSession.isActive()) {
18565c9469e010106467791b47b0fa83efda84491a21RoboErik                        addRemoteControlClient(mRccMediaSession.getRemoteControlClient());
18575c9469e010106467791b47b0fa83efda84491a21RoboErik                    } else {
18585c9469e010106467791b47b0fa83efda84491a21RoboErik                        removeRemoteControlClient(mRccMediaSession.getRemoteControlClient());
18595c9469e010106467791b47b0fa83efda84491a21RoboErik                    }
18605c9469e010106467791b47b0fa83efda84491a21RoboErik                }
18615c9469e010106467791b47b0fa83efda84491a21RoboErik            }
18625c9469e010106467791b47b0fa83efda84491a21RoboErik        };
1863c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1864c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        GlobalMediaRouter(Context applicationContext) {
1865c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mApplicationContext = applicationContext;
1866c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mDisplayManager = DisplayManagerCompat.getInstance(applicationContext);
1867fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown            mLowRam = ActivityManagerCompat.isLowRamDevice(
1868fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                    (ActivityManager)applicationContext.getSystemService(
1869fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                            Context.ACTIVITY_SERVICE));
1870fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1871fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // Add the system media route provider for interoperating with
1872fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // the framework media router.  This one is special and receives
1873fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // synchronization messages from the media router.
1874c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mSystemProvider = SystemMediaRouteProvider.obtain(applicationContext, this);
1875fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            addProvider(mSystemProvider);
1876fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1877fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1878fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void start() {
1879fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // Start watching for routes published by registered media route
1880fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // provider services.
1881fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mRegisteredProviderWatcher = new RegisteredMediaRouteProviderWatcher(
18823efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                    mApplicationContext, this);
1883fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mRegisteredProviderWatcher.start();
1884c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1885c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1886c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public MediaRouter getRouter(Context context) {
18879fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            MediaRouter router;
18889fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            for (int i = mRouters.size(); --i >= 0; ) {
18899fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                router = mRouters.get(i).get();
18909fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                if (router == null) {
18919fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    mRouters.remove(i);
18929fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                } else if (router.mContext == context) {
18939fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    return router;
18949fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                }
1895c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
18969fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            router = new MediaRouter(context);
18979fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            mRouters.add(new WeakReference<MediaRouter>(router));
1898c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return router;
1899c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1900c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1901c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public ContentResolver getContentResolver() {
1902c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mApplicationContext.getContentResolver();
1903c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1904c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1905fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public Context getProviderContext(String packageName) {
1906fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (packageName.equals(SystemMediaRouteProvider.PACKAGE_NAME)) {
1907fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return mApplicationContext;
1908fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1909fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            try {
1910fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return mApplicationContext.createPackageContext(
1911fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        packageName, Context.CONTEXT_RESTRICTED);
1912fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            } catch (NameNotFoundException ex) {
1913fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return null;
1914fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1915fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1916fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1917c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public Display getDisplay(int displayId) {
1918c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mDisplayManager.getDisplay(displayId);
1919c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1920c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1921fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void sendControlRequest(RouteInfo route,
1922c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                Intent intent, ControlRequestCallback callback) {
1923c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (route == mSelectedRoute && mSelectedRouteController != null) {
1924129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                if (mSelectedRouteController.onControlRequest(intent, callback)) {
1925fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return;
1926fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
1927fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1928fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (callback != null) {
19293d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown                callback.onError(null, null);
1930c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1931c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1932c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1933c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void requestSetVolume(RouteInfo route, int volume) {
1934c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (route == mSelectedRoute && mSelectedRouteController != null) {
1935129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                mSelectedRouteController.onSetVolume(volume);
19361bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo            } else if (mGroupMemberControllers != null) {
19371bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo                RouteController controller = mGroupMemberControllers.get(route.mDescriptorId);
1938bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang                if (controller != null) {
1939bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang                    controller.onSetVolume(volume);
1940bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang                }
1941c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1942c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1943c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1944c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void requestUpdateVolume(RouteInfo route, int delta) {
1945c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (route == mSelectedRoute && mSelectedRouteController != null) {
1946129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                mSelectedRouteController.onUpdateVolume(delta);
1947c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1948c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1949c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1950e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        public RouteInfo getRoute(String uniqueId) {
1951e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            for (RouteInfo info : mRoutes) {
1952e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (info.mUniqueId.equals(uniqueId)) {
1953e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    return info;
1954e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1955e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            }
1956e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            return null;
1957e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        }
1958e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo
1959c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public List<RouteInfo> getRoutes() {
1960c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mRoutes;
1961c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1962c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1963fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public List<ProviderInfo> getProviders() {
1964fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mProviders;
1965fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1966fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1967c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public RouteInfo getDefaultRoute() {
1968c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mDefaultRoute == null) {
1969c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // This should never happen once the media router has been fully
1970c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // initialized but it is good to check for the error in case there
1971c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // is a bug in provider initialization.
1972c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalStateException("There is no default route.  "
1973c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        + "The media router has not yet been fully initialized.");
1974c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1975c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mDefaultRoute;
1976c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1977c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1978c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public RouteInfo getSelectedRoute() {
1979c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mSelectedRoute == null) {
1980c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // This should never happen once the media router has been fully
1981c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // initialized but it is good to check for the error in case there
1982c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // is a bug in provider initialization.
1983c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalStateException("There is no currently selected route.  "
1984c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        + "The media router has not yet been fully initialized.");
1985c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1986c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mSelectedRoute;
1987c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1988c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1989c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void selectRoute(RouteInfo route) {
199094be6100218126ce6a08bf1f56209578500b361fRoboErik            selectRoute(route, MediaRouter.UNSELECT_REASON_ROUTE_CHANGED);
199194be6100218126ce6a08bf1f56209578500b361fRoboErik        }
199294be6100218126ce6a08bf1f56209578500b361fRoboErik
199394be6100218126ce6a08bf1f56209578500b361fRoboErik        public void selectRoute(RouteInfo route, int unselectReason) {
1994c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (!mRoutes.contains(route)) {
1995c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                Log.w(TAG, "Ignoring attempt to select removed route: " + route);
1996c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                return;
1997c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1998c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (!route.mEnabled) {
1999c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                Log.w(TAG, "Ignoring attempt to select disabled route: " + route);
2000c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                return;
2001c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2002c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
200394be6100218126ce6a08bf1f56209578500b361fRoboErik            setSelectedRouteInternal(route, unselectReason);
2004c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
2005c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
200611417b1cfde8f1749905f2d735623af9214148afJeff Brown        public boolean isRouteAvailable(MediaRouteSelector selector, int flags) {
2007fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown            if (selector.isEmpty()) {
2008fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                return false;
2009fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown            }
2010fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown
2011fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown            // On low-RAM devices, do not rely on actual discovery results unless asked to.
2012fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown            if ((flags & AVAILABILITY_FLAG_REQUIRE_MATCH) == 0 && mLowRam) {
2013fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                return true;
2014fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown            }
2015fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown
201611417b1cfde8f1749905f2d735623af9214148afJeff Brown            // Check whether any existing routes match the selector.
201711417b1cfde8f1749905f2d735623af9214148afJeff Brown            final int routeCount = mRoutes.size();
201811417b1cfde8f1749905f2d735623af9214148afJeff Brown            for (int i = 0; i < routeCount; i++) {
201911417b1cfde8f1749905f2d735623af9214148afJeff Brown                RouteInfo route = mRoutes.get(i);
202011417b1cfde8f1749905f2d735623af9214148afJeff Brown                if ((flags & AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE) != 0
202111417b1cfde8f1749905f2d735623af9214148afJeff Brown                        && route.isDefault()) {
202211417b1cfde8f1749905f2d735623af9214148afJeff Brown                    continue;
202311417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
202411417b1cfde8f1749905f2d735623af9214148afJeff Brown                if (route.matchesSelector(selector)) {
202511417b1cfde8f1749905f2d735623af9214148afJeff Brown                    return true;
202611417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
202711417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
202811417b1cfde8f1749905f2d735623af9214148afJeff Brown
202911417b1cfde8f1749905f2d735623af9214148afJeff Brown            // It doesn't look like we can find a matching route right now.
203011417b1cfde8f1749905f2d735623af9214148afJeff Brown            return false;
203111417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
203211417b1cfde8f1749905f2d735623af9214148afJeff Brown
203311417b1cfde8f1749905f2d735623af9214148afJeff Brown        public void updateDiscoveryRequest() {
203411417b1cfde8f1749905f2d735623af9214148afJeff Brown            // Combine all of the callback selectors and active scan flags.
2035f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            boolean discover = false;
203611417b1cfde8f1749905f2d735623af9214148afJeff Brown            boolean activeScan = false;
203711417b1cfde8f1749905f2d735623af9214148afJeff Brown            MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder();
20389fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            for (int i = mRouters.size(); --i >= 0; ) {
20399fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                MediaRouter router = mRouters.get(i).get();
20409fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                if (router == null) {
20419fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    mRouters.remove(i);
20429fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                } else {
20439fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    final int count = router.mCallbackRecords.size();
20449fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    for (int j = 0; j < count; j++) {
20459fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                        CallbackRecord callback = router.mCallbackRecords.get(j);
20469fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                        builder.addSelector(callback.mSelector);
2047f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                        if ((callback.mFlags & CALLBACK_FLAG_PERFORM_ACTIVE_SCAN) != 0) {
20489fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                            activeScan = true;
2049f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                            discover = true; // perform active scan implies request discovery
2050f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                        }
2051f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                        if ((callback.mFlags & CALLBACK_FLAG_REQUEST_DISCOVERY) != 0) {
2052fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                            if (!mLowRam) {
2053fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                                discover = true;
2054fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                            }
2055fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                        }
2056fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                        if ((callback.mFlags & CALLBACK_FLAG_FORCE_DISCOVERY) != 0) {
2057f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                            discover = true;
20589fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                        }
205911417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
206011417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
206111417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
2062f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            MediaRouteSelector selector = discover ? builder.build() : MediaRouteSelector.EMPTY;
206311417b1cfde8f1749905f2d735623af9214148afJeff Brown
206411417b1cfde8f1749905f2d735623af9214148afJeff Brown            // Create a new discovery request.
206511417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (mDiscoveryRequest != null
206611417b1cfde8f1749905f2d735623af9214148afJeff Brown                    && mDiscoveryRequest.getSelector().equals(selector)
206711417b1cfde8f1749905f2d735623af9214148afJeff Brown                    && mDiscoveryRequest.isActiveScan() == activeScan) {
206811417b1cfde8f1749905f2d735623af9214148afJeff Brown                return; // no change
206911417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
207011417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (selector.isEmpty() && !activeScan) {
207111417b1cfde8f1749905f2d735623af9214148afJeff Brown                // Discovery is not needed.
207211417b1cfde8f1749905f2d735623af9214148afJeff Brown                if (mDiscoveryRequest == null) {
207311417b1cfde8f1749905f2d735623af9214148afJeff Brown                    return; // no change
207411417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
207511417b1cfde8f1749905f2d735623af9214148afJeff Brown                mDiscoveryRequest = null;
207611417b1cfde8f1749905f2d735623af9214148afJeff Brown            } else {
207711417b1cfde8f1749905f2d735623af9214148afJeff Brown                // Discovery is needed.
207811417b1cfde8f1749905f2d735623af9214148afJeff Brown                mDiscoveryRequest = new MediaRouteDiscoveryRequest(selector, activeScan);
207911417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
208011417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (DEBUG) {
208111417b1cfde8f1749905f2d735623af9214148afJeff Brown                Log.d(TAG, "Updated discovery request: " + mDiscoveryRequest);
208211417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
2083fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown            if (discover && !activeScan && mLowRam) {
2084fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                Log.i(TAG, "Forcing passive route discovery on a low-RAM device, "
2085fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                        + "system performance may be affected.  Please consider using "
2086fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                        + "CALLBACK_FLAG_REQUEST_DISCOVERY instead of "
2087fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                        + "CALLBACK_FLAG_FORCE_DISCOVERY.");
2088fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown            }
208911417b1cfde8f1749905f2d735623af9214148afJeff Brown
209011417b1cfde8f1749905f2d735623af9214148afJeff Brown            // Notify providers.
209111417b1cfde8f1749905f2d735623af9214148afJeff Brown            final int providerCount = mProviders.size();
209211417b1cfde8f1749905f2d735623af9214148afJeff Brown            for (int i = 0; i < providerCount; i++) {
209311417b1cfde8f1749905f2d735623af9214148afJeff Brown                mProviders.get(i).mProviderInstance.setDiscoveryRequest(mDiscoveryRequest);
209428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            }
209528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
209628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
20973efa63d3b896244713e84acbb5945562dce41d77Jeff Brown        @Override
2098fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void addProvider(MediaRouteProvider providerInstance) {
2099fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int index = findProviderInfo(providerInstance);
2100c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (index < 0) {
2101c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // 1. Add the provider to the list.
2102fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ProviderInfo provider = new ProviderInfo(providerInstance);
2103fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mProviders.add(provider);
210411417b1cfde8f1749905f2d735623af9214148afJeff Brown                if (DEBUG) {
210511417b1cfde8f1749905f2d735623af9214148afJeff Brown                    Log.d(TAG, "Provider added: " + provider);
210611417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
2107fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_ADDED, provider);
2108c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // 2. Create the provider's contents.
2109fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                updateProviderContents(provider, providerInstance.getDescriptor());
2110c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // 3. Register the provider callback.
211111417b1cfde8f1749905f2d735623af9214148afJeff Brown                providerInstance.setCallback(mProviderCallback);
211211417b1cfde8f1749905f2d735623af9214148afJeff Brown                // 4. Set the discovery request.
211311417b1cfde8f1749905f2d735623af9214148afJeff Brown                providerInstance.setDiscoveryRequest(mDiscoveryRequest);
2114c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2115c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
2116c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
21173efa63d3b896244713e84acbb5945562dce41d77Jeff Brown        @Override
2118fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void removeProvider(MediaRouteProvider providerInstance) {
2119fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int index = findProviderInfo(providerInstance);
2120c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (index >= 0) {
212111417b1cfde8f1749905f2d735623af9214148afJeff Brown                // 1. Unregister the provider callback.
212211417b1cfde8f1749905f2d735623af9214148afJeff Brown                providerInstance.setCallback(null);
212311417b1cfde8f1749905f2d735623af9214148afJeff Brown                // 2. Clear the discovery request.
212411417b1cfde8f1749905f2d735623af9214148afJeff Brown                providerInstance.setDiscoveryRequest(null);
212528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown                // 3. Delete the provider's contents.
2126fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ProviderInfo provider = mProviders.get(index);
2127fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                updateProviderContents(provider, null);
212828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown                // 4. Remove the provider from the list.
212911417b1cfde8f1749905f2d735623af9214148afJeff Brown                if (DEBUG) {
213011417b1cfde8f1749905f2d735623af9214148afJeff Brown                    Log.d(TAG, "Provider removed: " + provider);
213111417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
2132fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_REMOVED, provider);
2133fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mProviders.remove(index);
2134c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2135c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
2136c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2137fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private void updateProviderDescriptor(MediaRouteProvider providerInstance,
213811417b1cfde8f1749905f2d735623af9214148afJeff Brown                MediaRouteProviderDescriptor descriptor) {
2139fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int index = findProviderInfo(providerInstance);
2140c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (index >= 0) {
2141fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                // Update the provider's contents.
2142fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ProviderInfo provider = mProviders.get(index);
2143fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                updateProviderContents(provider, descriptor);
2144c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2145c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
2146c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2147fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int findProviderInfo(MediaRouteProvider providerInstance) {
2148fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            final int count = mProviders.size();
2149c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            for (int i = 0; i < count; i++) {
2150fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mProviders.get(i).mProviderInstance == providerInstance) {
2151c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    return i;
2152c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
2153c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2154c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return -1;
2155c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
2156c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2157fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private void updateProviderContents(ProviderInfo provider,
215811417b1cfde8f1749905f2d735623af9214148afJeff Brown                MediaRouteProviderDescriptor providerDescriptor) {
2159fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (provider.updateDescriptor(providerDescriptor)) {
2160c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // Update all existing routes and reorder them to match
2161c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // the order of their descriptors.
2162c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                int targetIndex = 0;
2163567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                boolean selectedRouteDescriptorChanged = false;
2164c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (providerDescriptor != null) {
2165fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (providerDescriptor.isValid()) {
216611417b1cfde8f1749905f2d735623af9214148afJeff Brown                        final List<MediaRouteDescriptor> routeDescriptors =
216711417b1cfde8f1749905f2d735623af9214148afJeff Brown                                providerDescriptor.getRoutes();
216811417b1cfde8f1749905f2d735623af9214148afJeff Brown                        final int routeCount = routeDescriptors.size();
2169e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        // Updating route group's contents requires all member routes' information.
2170e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        // Add the groups to the lists and update them later.
2171e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        List<Pair<RouteInfo, MediaRouteDescriptor>> addedGroups = new ArrayList<>();
2172e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        List<Pair<RouteInfo, MediaRouteDescriptor>> updatedGroups =
2173e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                new ArrayList<>();
217411417b1cfde8f1749905f2d735623af9214148afJeff Brown                        for (int i = 0; i < routeCount; i++) {
217511417b1cfde8f1749905f2d735623af9214148afJeff Brown                            final MediaRouteDescriptor routeDescriptor = routeDescriptors.get(i);
2176c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            final String id = routeDescriptor.getId();
2177fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            final int sourceIndex = provider.findRouteByDescriptorId(id);
2178c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            if (sourceIndex < 0) {
2179c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                // 1. Add the route to the list.
2180cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                                String uniqueId = assignRouteUniqueId(provider, id);
21811bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo                                boolean isGroup = routeDescriptor.getGroupMemberIds() != null;
2182e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                RouteInfo route = isGroup ? new RouteGroup(provider, id, uniqueId) :
2183e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                        new RouteInfo(provider, id, uniqueId);
2184fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                provider.mRoutes.add(targetIndex++, route);
2185c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                mRoutes.add(route);
2186c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                // 2. Create the route's contents.
2187e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                if (isGroup) {
2188e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                    addedGroups.add(new Pair(route, routeDescriptor));
2189e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                } else {
2190e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                    route.maybeUpdateDescriptor(routeDescriptor);
2191e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                    // 3. Notify clients about addition.
2192e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                    if (DEBUG) {
2193e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                        Log.d(TAG, "Route added: " + route);
2194e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                    }
2195e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route);
219611417b1cfde8f1749905f2d735623af9214148afJeff Brown                                }
2197e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo
2198fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            } else if (sourceIndex < targetIndex) {
2199fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                Log.w(TAG, "Ignoring route descriptor with duplicate id: "
2200fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                        + routeDescriptor);
2201c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            } else {
2202c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                // 1. Reorder the route within the list.
2203fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                RouteInfo route = provider.mRoutes.get(sourceIndex);
2204fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                Collections.swap(provider.mRoutes,
2205c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                        sourceIndex, targetIndex++);
2206c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                // 2. Update the route's contents.
2207e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                if (route instanceof RouteGroup) {
2208e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                    updatedGroups.add(new Pair(route, routeDescriptor));
2209e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                } else {
2210e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                    // 3. Notify clients about changes.
2211e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                    if (updateRouteDescriptorAndNotify(route, routeDescriptor)
2212e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                            != 0) {
2213e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                        if (route == mSelectedRoute) {
2214e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                            selectedRouteDescriptorChanged = true;
2215567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                        }
221611417b1cfde8f1749905f2d735623af9214148afJeff Brown                                    }
2217c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                }
2218c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            }
2219c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        }
2220e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        // Update the new and/or existing groups.
2221e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        for (Pair<RouteInfo, MediaRouteDescriptor> pair : addedGroups) {
2222e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            RouteInfo route = pair.first;
2223e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            route.maybeUpdateDescriptor(pair.second);
2224e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            if (DEBUG) {
2225e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                Log.d(TAG, "Route added: " + route);
2226e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            }
2227e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route);
2228e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        }
2229e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        for (Pair<RouteInfo, MediaRouteDescriptor> pair : updatedGroups) {
2230e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            RouteInfo route = pair.first;
2231e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            if (updateRouteDescriptorAndNotify(route, pair.second) != 0) {
2232e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                if (route == mSelectedRoute) {
2233e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                    selectedRouteDescriptorChanged = true;
2234e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                }
2235e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            }
2236e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        }
2237fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    } else {
2238fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        Log.w(TAG, "Ignoring invalid provider descriptor: " + providerDescriptor);
2239c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
2240c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
2241c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2242c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // Dispose all remaining routes that do not have matching descriptors.
2243fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) {
2244fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    // 1. Delete the route's contents.
2245fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    RouteInfo route = provider.mRoutes.get(i);
2246e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    route.maybeUpdateDescriptor(null);
2247fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    // 2. Remove the route from the list.
224811417b1cfde8f1749905f2d735623af9214148afJeff Brown                    mRoutes.remove(route);
224935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                }
225035af7c925d660508fe0892f38eec99a3845630e4Chong Zhang
2251567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                // Update the selected route if needed.
2252567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                updateSelectedRouteIfNeeded(selectedRouteDescriptorChanged);
225335af7c925d660508fe0892f38eec99a3845630e4Chong Zhang
225435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                // Now notify clients about routes that were removed.
225535af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                // We do this after updating the selected route to ensure
225635af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                // that the framework media router observes the new route
225735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                // selection before the removal since removing the currently
225835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                // selected route may have side-effects.
225935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) {
226035af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                    RouteInfo route = provider.mRoutes.remove(i);
226111417b1cfde8f1749905f2d735623af9214148afJeff Brown                    if (DEBUG) {
226211417b1cfde8f1749905f2d735623af9214148afJeff Brown                        Log.d(TAG, "Route removed: " + route);
226311417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
2264fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_REMOVED, route);
2265c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
2266fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
226711417b1cfde8f1749905f2d735623af9214148afJeff Brown                // Notify provider changed.
226811417b1cfde8f1749905f2d735623af9214148afJeff Brown                if (DEBUG) {
226911417b1cfde8f1749905f2d735623af9214148afJeff Brown                    Log.d(TAG, "Provider changed: " + provider);
227011417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
227111417b1cfde8f1749905f2d735623af9214148afJeff Brown                mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_CHANGED, provider);
2272c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2273c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
2274c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2275e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        private int updateRouteDescriptorAndNotify(RouteInfo route,
2276e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                MediaRouteDescriptor routeDescriptor) {
2277e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            int changes = route.maybeUpdateDescriptor(routeDescriptor);
2278e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            if (changes != 0) {
2279e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if ((changes & RouteInfo.CHANGE_GENERAL) != 0) {
2280e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    if (DEBUG) {
2281e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        Log.d(TAG, "Route changed: " + route);
2282e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    }
2283e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mCallbackHandler.post(
2284e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            CallbackHandler.MSG_ROUTE_CHANGED, route);
2285e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
2286e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if ((changes & RouteInfo.CHANGE_VOLUME) != 0) {
2287e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    if (DEBUG) {
2288e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        Log.d(TAG, "Route volume changed: " + route);
2289e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    }
2290e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mCallbackHandler.post(
2291e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            CallbackHandler.MSG_ROUTE_VOLUME_CHANGED, route);
2292e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
2293e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if ((changes & RouteInfo.CHANGE_PRESENTATION_DISPLAY) != 0) {
2294e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    if (DEBUG) {
2295e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        Log.d(TAG, "Route presentation display changed: "
2296e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                + route);
2297e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    }
2298e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mCallbackHandler.post(CallbackHandler.
2299e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED, route);
2300e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
2301e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            }
2302e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            return changes;
2303e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        }
2304e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo
2305cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        private String assignRouteUniqueId(ProviderInfo provider, String routeDescriptorId) {
2306cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            // Although route descriptor ids are unique within a provider, it's
2307cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            // possible for there to be two providers with the same package name.
2308cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            // Therefore we must dedupe the composite id.
230938e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo            String componentName = provider.getComponentName().flattenToShortString();
231038e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo            String uniqueId = componentName + ":" + routeDescriptorId;
2311cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            if (findRouteByUniqueId(uniqueId) < 0) {
231238e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo                mUniqueIdMap.put(new Pair(componentName, routeDescriptorId), uniqueId);
2313cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                return uniqueId;
2314cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            }
231538e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo            Log.w(TAG, "Either " + routeDescriptorId + " isn't unique in " + componentName
2316480052774a9fb375d68b7a0373a5bd8d733f8dc0Jae Seo                    + " or we're trying to assign a unique ID for an already added route");
2317cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            for (int i = 2; ; i++) {
2318cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                String newUniqueId = String.format(Locale.US, "%s_%d", uniqueId, i);
2319cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                if (findRouteByUniqueId(newUniqueId) < 0) {
232038e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo                    mUniqueIdMap.put(new Pair(componentName, routeDescriptorId), newUniqueId);
2321cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                    return newUniqueId;
2322cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                }
2323cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            }
2324cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        }
2325cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown
2326cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        private int findRouteByUniqueId(String uniqueId) {
2327cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            final int count = mRoutes.size();
2328cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            for (int i = 0; i < count; i++) {
2329cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                if (mRoutes.get(i).mUniqueId.equals(uniqueId)) {
2330cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                    return i;
2331cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                }
2332cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            }
2333cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            return -1;
2334cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        }
2335cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown
233638e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo        private String getUniqueId(ProviderInfo provider, String routeDescriptorId) {
233738e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo            String componentName = provider.getComponentName().flattenToShortString();
233838e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo            return mUniqueIdMap.get(new Pair(componentName, routeDescriptorId));
233938e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo        }
234038e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo
2341567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        private void updateSelectedRouteIfNeeded(boolean selectedRouteDescriptorChanged) {
2342567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            // Update default route.
2343567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            if (mDefaultRoute != null && !isRouteSelectable(mDefaultRoute)) {
234435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                Log.i(TAG, "Clearing the default route because it "
2345567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                        + "is no longer selectable: " + mDefaultRoute);
2346c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mDefaultRoute = null;
2347c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2348c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mDefaultRoute == null && !mRoutes.isEmpty()) {
2349c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                for (RouteInfo route : mRoutes) {
2350c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    if (isSystemDefaultRoute(route) && isRouteSelectable(route)) {
2351c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        mDefaultRoute = route;
2352567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                        Log.i(TAG, "Found default route: " + mDefaultRoute);
2353c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
2354c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
2355c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
2356c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2357567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2358567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            // Update selected route.
2359567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            if (mSelectedRoute != null && !isRouteSelectable(mSelectedRoute)) {
2360567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                Log.i(TAG, "Unselecting the current route because it "
2361567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                        + "is no longer selectable: " + mSelectedRoute);
236294be6100218126ce6a08bf1f56209578500b361fRoboErik                setSelectedRouteInternal(null,
236394be6100218126ce6a08bf1f56209578500b361fRoboErik                        MediaRouter.UNSELECT_REASON_UNKNOWN);
2364567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
2365c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mSelectedRoute == null) {
2366567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                // Choose a new route.
2367567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                // This will have the side-effect of updating the playback info when
2368567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                // the new route is selected.
236994be6100218126ce6a08bf1f56209578500b361fRoboErik                setSelectedRouteInternal(chooseFallbackRoute(),
237094be6100218126ce6a08bf1f56209578500b361fRoboErik                        MediaRouter.UNSELECT_REASON_UNKNOWN);
2371567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            } else if (selectedRouteDescriptorChanged) {
2372567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                // Update the playback info because the properties of the route have changed.
2373567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                updatePlaybackInfoFromSelectedRoute();
2374c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2375c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
2376c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
237735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang        private RouteInfo chooseFallbackRoute() {
237835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            // When the current route is removed or no longer selectable,
237935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            // we want to revert to a live audio route if there is
238035af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            // one (usually Bluetooth A2DP).  Failing that, use
238135af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            // the default route.
238235af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            for (RouteInfo route : mRoutes) {
238335af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                if (route != mDefaultRoute
238435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                        && isSystemLiveAudioOnlyRoute(route)
238535af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                        && isRouteSelectable(route)) {
238635af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                    return route;
238735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                }
238835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            }
238935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            return mDefaultRoute;
239035af7c925d660508fe0892f38eec99a3845630e4Chong Zhang        }
239135af7c925d660508fe0892f38eec99a3845630e4Chong Zhang
239235af7c925d660508fe0892f38eec99a3845630e4Chong Zhang        private boolean isSystemLiveAudioOnlyRoute(RouteInfo route) {
239335af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            return route.getProviderInstance() == mSystemProvider
239435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                    && route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
239535af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                    && !route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO);
239635af7c925d660508fe0892f38eec99a3845630e4Chong Zhang        }
239735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang
2398c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private boolean isRouteSelectable(RouteInfo route) {
2399c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            // This tests whether the route is still valid and enabled.
2400c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            // The route descriptor field is set to null when the route is removed.
2401c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return route.mDescriptor != null && route.mEnabled;
2402c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
2403c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2404c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private boolean isSystemDefaultRoute(RouteInfo route) {
2405fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return route.getProviderInstance() == mSystemProvider
2406c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    && route.mDescriptorId.equals(
2407c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            SystemMediaRouteProvider.DEFAULT_ROUTE_ID);
2408c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
2409c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
241094be6100218126ce6a08bf1f56209578500b361fRoboErik        private void setSelectedRouteInternal(RouteInfo route, int unselectReason) {
2411c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mSelectedRoute != route) {
2412c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (mSelectedRoute != null) {
241311417b1cfde8f1749905f2d735623af9214148afJeff Brown                    if (DEBUG) {
241494be6100218126ce6a08bf1f56209578500b361fRoboErik                        Log.d(TAG, "Route unselected: " + mSelectedRoute + " reason: "
241594be6100218126ce6a08bf1f56209578500b361fRoboErik                                + unselectReason);
241611417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
2417c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_UNSELECTED, mSelectedRoute);
2418c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    if (mSelectedRouteController != null) {
241994be6100218126ce6a08bf1f56209578500b361fRoboErik                        mSelectedRouteController.onUnselect(unselectReason);
2420129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                        mSelectedRouteController.onRelease();
2421c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        mSelectedRouteController = null;
2422c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
24231bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo                    if (mGroupMemberControllers != null) {
24241bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo                        for (RouteController controller : mGroupMemberControllers.values()) {
24251bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo                            controller.onUnselect();
24261bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo                            controller.onRelease();
2427bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang                        }
24281bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo                        mGroupMemberControllers = null;
2429bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang                    }
2430c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
2431c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2432c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mSelectedRoute = route;
2433c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2434c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (mSelectedRoute != null) {
2435fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mSelectedRouteController = route.getProviderInstance().onCreateRouteController(
2436c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            route.mDescriptorId);
2437c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    if (mSelectedRouteController != null) {
2438129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                        mSelectedRouteController.onSelect();
2439c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
244011417b1cfde8f1749905f2d735623af9214148afJeff Brown                    if (DEBUG) {
244111417b1cfde8f1749905f2d735623af9214148afJeff Brown                        Log.d(TAG, "Route selected: " + mSelectedRoute);
244211417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
2443c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_SELECTED, mSelectedRoute);
2444bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang
2445bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang                    if (mSelectedRoute instanceof RouteGroup) {
24461bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo                        mGroupMemberControllers = new HashMap<>();
2447bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang                        RouteGroup group = (RouteGroup) mSelectedRoute;
24481bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo                        for (RouteInfo groupMember : group.getRoutes()) {
24491bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo                            RouteController controller = groupMember.getProviderInstance()
24501bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo                                    .onCreateRouteController(groupMember.mDescriptorId);
2451bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang                            controller.onSelect();
24521bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo                            mGroupMemberControllers.put(groupMember.mDescriptorId, controller);
2453bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang                        }
2454bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang                    }
2455c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
2456567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2457567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                updatePlaybackInfoFromSelectedRoute();
2458c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2459c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
2460c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2461c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
2462c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public RouteInfo getSystemRouteByDescriptorId(String id) {
2463fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int providerIndex = findProviderInfo(mSystemProvider);
2464c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (providerIndex >= 0) {
2465fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ProviderInfo provider = mProviders.get(providerIndex);
2466fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                int routeIndex = provider.findRouteByDescriptorId(id);
2467c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (routeIndex >= 0) {
2468fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return provider.mRoutes.get(routeIndex);
2469c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
2470c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2471c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return null;
2472c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
2473c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2474567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        public void addRemoteControlClient(Object rcc) {
2475567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            int index = findRemoteControlClientRecord(rcc);
2476567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            if (index < 0) {
2477567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                RemoteControlClientRecord record = new RemoteControlClientRecord(rcc);
2478567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mRemoteControlClients.add(record);
2479567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
2480567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
2481567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2482567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        public void removeRemoteControlClient(Object rcc) {
2483567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            int index = findRemoteControlClientRecord(rcc);
2484567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            if (index >= 0) {
2485567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                RemoteControlClientRecord record = mRemoteControlClients.remove(index);
2486567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                record.disconnect();
2487567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
2488567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
2489567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2490bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik        public void setMediaSession(Object session) {
2491bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            if (mMediaSession != null) {
2492bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                mMediaSession.clearVolumeHandling();
2493bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            }
2494bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            if (session == null) {
2495bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                mMediaSession = null;
2496bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            } else {
2497bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                mMediaSession = new MediaSessionRecord(session);
2498bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                updatePlaybackInfoFromSelectedRoute();
2499bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            }
2500bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik        }
2501bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik
25025c9469e010106467791b47b0fa83efda84491a21RoboErik        public void setMediaSessionCompat(final MediaSessionCompat session) {
2503e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            mCompatSession = session;
25045c9469e010106467791b47b0fa83efda84491a21RoboErik            if (android.os.Build.VERSION.SDK_INT >= 21) {
2505d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake                setMediaSession(session != null ? session.getMediaSession() : null);
25065c9469e010106467791b47b0fa83efda84491a21RoboErik            } else if (android.os.Build.VERSION.SDK_INT >= 14) {
25075c9469e010106467791b47b0fa83efda84491a21RoboErik                if (mRccMediaSession != null) {
25085c9469e010106467791b47b0fa83efda84491a21RoboErik                    removeRemoteControlClient(mRccMediaSession.getRemoteControlClient());
25095c9469e010106467791b47b0fa83efda84491a21RoboErik                    mRccMediaSession.removeOnActiveChangeListener(mSessionActiveListener);
25105c9469e010106467791b47b0fa83efda84491a21RoboErik                }
25115c9469e010106467791b47b0fa83efda84491a21RoboErik                mRccMediaSession = session;
2512d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake                if (session != null) {
2513d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake                    session.addOnActiveChangeListener(mSessionActiveListener);
2514d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake                    if (session.isActive()) {
2515d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake                        addRemoteControlClient(session.getRemoteControlClient());
2516d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake                    }
25175c9469e010106467791b47b0fa83efda84491a21RoboErik                }
25185c9469e010106467791b47b0fa83efda84491a21RoboErik            }
25195c9469e010106467791b47b0fa83efda84491a21RoboErik        }
25205c9469e010106467791b47b0fa83efda84491a21RoboErik
252194be6100218126ce6a08bf1f56209578500b361fRoboErik        public MediaSessionCompat.Token getMediaSessionToken() {
252294be6100218126ce6a08bf1f56209578500b361fRoboErik            if (mMediaSession != null) {
252394be6100218126ce6a08bf1f56209578500b361fRoboErik                return mMediaSession.getToken();
2524e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } else if (mCompatSession != null) {
2525e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                return mCompatSession.getSessionToken();
252694be6100218126ce6a08bf1f56209578500b361fRoboErik            }
252794be6100218126ce6a08bf1f56209578500b361fRoboErik            return null;
252894be6100218126ce6a08bf1f56209578500b361fRoboErik        }
252994be6100218126ce6a08bf1f56209578500b361fRoboErik
2530567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        private int findRemoteControlClientRecord(Object rcc) {
2531567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            final int count = mRemoteControlClients.size();
2532567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            for (int i = 0; i < count; i++) {
2533567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                RemoteControlClientRecord record = mRemoteControlClients.get(i);
2534567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                if (record.getRemoteControlClient() == rcc) {
2535567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                    return i;
2536567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                }
2537567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
2538567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            return -1;
2539567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
2540567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2541567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        private void updatePlaybackInfoFromSelectedRoute() {
2542567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            if (mSelectedRoute != null) {
2543567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mPlaybackInfo.volume = mSelectedRoute.getVolume();
2544567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mPlaybackInfo.volumeMax = mSelectedRoute.getVolumeMax();
2545567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mPlaybackInfo.volumeHandling = mSelectedRoute.getVolumeHandling();
2546567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mPlaybackInfo.playbackStream = mSelectedRoute.getPlaybackStream();
2547567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mPlaybackInfo.playbackType = mSelectedRoute.getPlaybackType();
2548567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2549567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                final int count = mRemoteControlClients.size();
2550567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                for (int i = 0; i < count; i++) {
2551567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                    RemoteControlClientRecord record = mRemoteControlClients.get(i);
2552567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                    record.updatePlaybackInfo();
2553567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                }
2554bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                if (mMediaSession != null) {
2555f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                    if (mSelectedRoute == getDefaultRoute()) {
2556f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                        // Local route
2557f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                        mMediaSession.clearVolumeHandling();
2558f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                    } else {
2559f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake                        @VolumeProviderCompat.ControlType int controlType =
2560f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake                                VolumeProviderCompat.VOLUME_CONTROL_FIXED;
2561f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                        if (mPlaybackInfo.volumeHandling
2562f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                                == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE) {
2563f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                            controlType = VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE;
2564f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                        }
2565f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                        mMediaSession.configureVolume(controlType, mPlaybackInfo.volumeMax,
2566f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                                mPlaybackInfo.volume);
2567bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                    }
2568f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                }
2569f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik            } else {
2570f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                if (mMediaSession != null) {
2571f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                    mMediaSession.clearVolumeHandling();
2572bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                }
2573567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
2574567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
2575567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2576c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final class ProviderCallback extends MediaRouteProvider.Callback {
2577c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            @Override
2578c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public void onDescriptorChanged(MediaRouteProvider provider,
257911417b1cfde8f1749905f2d735623af9214148afJeff Brown                    MediaRouteProviderDescriptor descriptor) {
2580c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                updateProviderDescriptor(provider, descriptor);
2581c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2582c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
2583c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2584bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik        private final class MediaSessionRecord {
2585bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            private final MediaSessionCompat mMsCompat;
2586bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik
2587f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake            private @VolumeProviderCompat.ControlType int mControlType;
2588bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            private int mMaxVolume;
2589bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            private VolumeProviderCompat mVpCompat;
2590bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik
2591bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            public MediaSessionRecord(Object mediaSession) {
25925c9469e010106467791b47b0fa83efda84491a21RoboErik                mMsCompat = MediaSessionCompat.obtain(mApplicationContext, mediaSession);
2593bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            }
2594bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik
2595f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake            public void configureVolume(@VolumeProviderCompat.ControlType int controlType,
2596f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake                    int max, int current) {
2597bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                if (mVpCompat != null && controlType == mControlType && max == mMaxVolume) {
2598bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                    // If we haven't changed control type or max just set the
2599bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                    // new current volume
2600bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                    mVpCompat.setCurrentVolume(current);
2601bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                } else {
2602bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                    // Otherwise create a new provider and update
2603bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                    mVpCompat = new VolumeProviderCompat(controlType, max, current) {
2604bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                        @Override
2605a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                        public void onSetVolumeTo(final int volume) {
2606a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                            mCallbackHandler.post(new Runnable() {
2607a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                @Override
2608a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                public void run() {
2609a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                    if (mSelectedRoute != null) {
2610a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                        mSelectedRoute.requestSetVolume(volume);
2611a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                    }
2612a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                }
2613a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                            });
2614bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                        }
2615bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik
2616bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                        @Override
2617a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                        public void onAdjustVolume(final int direction) {
2618a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                            mCallbackHandler.post(new Runnable() {
2619a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                @Override
2620a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                public void run() {
2621a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                    if (mSelectedRoute != null) {
2622a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                        mSelectedRoute.requestUpdateVolume(direction);
2623a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                    }
2624a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                }
2625a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                            });
2626bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                        }
2627bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                    };
2628bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                    mMsCompat.setPlaybackToRemote(mVpCompat);
2629bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                }
2630bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            }
2631bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik
2632bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            public void clearVolumeHandling() {
2633bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                mMsCompat.setPlaybackToLocal(mPlaybackInfo.playbackStream);
2634bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                mVpCompat = null;
2635bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            }
2636bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik
263794be6100218126ce6a08bf1f56209578500b361fRoboErik            public MediaSessionCompat.Token getToken() {
263894be6100218126ce6a08bf1f56209578500b361fRoboErik                return mMsCompat.getSessionToken();
263994be6100218126ce6a08bf1f56209578500b361fRoboErik            }
264094be6100218126ce6a08bf1f56209578500b361fRoboErik
2641bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik        }
2642bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik
2643567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        private final class RemoteControlClientRecord
2644567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                implements RemoteControlClientCompat.VolumeCallback {
2645567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            private final RemoteControlClientCompat mRccCompat;
2646567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            private boolean mDisconnected;
2647567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2648567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            public RemoteControlClientRecord(Object rcc) {
2649567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mRccCompat = RemoteControlClientCompat.obtain(mApplicationContext, rcc);
2650567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mRccCompat.setVolumeCallback(this);
2651567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                updatePlaybackInfo();
2652567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
2653567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2654567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            public Object getRemoteControlClient() {
2655567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                return mRccCompat.getRemoteControlClient();
2656567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
2657567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2658567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            public void disconnect() {
2659567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mDisconnected = true;
2660567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mRccCompat.setVolumeCallback(null);
2661567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
2662567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2663567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            public void updatePlaybackInfo() {
2664567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mRccCompat.setPlaybackInfo(mPlaybackInfo);
2665567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
2666567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2667567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            @Override
2668567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            public void onVolumeSetRequest(int volume) {
2669567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                if (!mDisconnected && mSelectedRoute != null) {
2670567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                    mSelectedRoute.requestSetVolume(volume);
2671567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                }
2672567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
2673567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2674567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            @Override
2675567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            public void onVolumeUpdateRequest(int direction) {
2676567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                if (!mDisconnected && mSelectedRoute != null) {
2677567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                    mSelectedRoute.requestUpdateVolume(direction);
2678567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                }
2679567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
2680567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
2681567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2682c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final class CallbackHandler extends Handler {
26839fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            private final ArrayList<CallbackRecord> mTempCallbackRecords =
26849fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    new ArrayList<CallbackRecord>();
2685c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
268611417b1cfde8f1749905f2d735623af9214148afJeff Brown            private static final int MSG_TYPE_MASK = 0xff00;
268711417b1cfde8f1749905f2d735623af9214148afJeff Brown            private static final int MSG_TYPE_ROUTE = 0x0100;
268811417b1cfde8f1749905f2d735623af9214148afJeff Brown            private static final int MSG_TYPE_PROVIDER = 0x0200;
268911417b1cfde8f1749905f2d735623af9214148afJeff Brown
269011417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_ADDED = MSG_TYPE_ROUTE | 1;
269111417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_REMOVED = MSG_TYPE_ROUTE | 2;
269211417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_CHANGED = MSG_TYPE_ROUTE | 3;
269311417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_VOLUME_CHANGED = MSG_TYPE_ROUTE | 4;
269411417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED = MSG_TYPE_ROUTE | 5;
269511417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_SELECTED = MSG_TYPE_ROUTE | 6;
269611417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_UNSELECTED = MSG_TYPE_ROUTE | 7;
269711417b1cfde8f1749905f2d735623af9214148afJeff Brown
269811417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_PROVIDER_ADDED = MSG_TYPE_PROVIDER | 1;
269911417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_PROVIDER_REMOVED = MSG_TYPE_PROVIDER | 2;
270011417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_PROVIDER_CHANGED = MSG_TYPE_PROVIDER | 3;
2701c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2702fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            public void post(int msg, Object obj) {
2703fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                obtainMessage(msg, obj).sendToTarget();
2704c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2705c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2706c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            @Override
2707c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public void handleMessage(Message msg) {
2708c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                final int what = msg.what;
2709fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                final Object obj = msg.obj;
2710c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2711c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // Synchronize state with the system media router.
2712fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                syncWithSystemProvider(what, obj);
2713c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2714c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // Invoke all registered callbacks.
27159fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                // Build a list of callbacks before invoking them in case callbacks
27169fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                // are added or removed during dispatch.
2717c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                try {
27189fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    for (int i = mRouters.size(); --i >= 0; ) {
27199fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                        MediaRouter router = mRouters.get(i).get();
27209fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                        if (router == null) {
27219fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                            mRouters.remove(i);
27229fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                        } else {
27239fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                            mTempCallbackRecords.addAll(router.mCallbackRecords);
2724c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        }
2725c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
27269fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown
27279fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    final int callbackCount = mTempCallbackRecords.size();
27289fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    for (int i = 0; i < callbackCount; i++) {
27299fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                        invokeCallback(mTempCallbackRecords.get(i), what, obj);
27309fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    }
2731c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                } finally {
27329fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    mTempCallbackRecords.clear();
2733c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
2734c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2735c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2736fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            private void syncWithSystemProvider(int what, Object obj) {
2737c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                switch (what) {
2738c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_ADDED:
2739fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mSystemProvider.onSyncRouteAdded((RouteInfo)obj);
2740c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
2741c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_REMOVED:
2742fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mSystemProvider.onSyncRouteRemoved((RouteInfo)obj);
2743c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
2744c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_CHANGED:
2745fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mSystemProvider.onSyncRouteChanged((RouteInfo)obj);
2746c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
2747c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_SELECTED:
2748fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mSystemProvider.onSyncRouteSelected((RouteInfo)obj);
2749c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
2750c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
2751c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2752c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
27539fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            private void invokeCallback(CallbackRecord record, int what, Object obj) {
27549fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                final MediaRouter router = record.mRouter;
275511417b1cfde8f1749905f2d735623af9214148afJeff Brown                final MediaRouter.Callback callback = record.mCallback;
275611417b1cfde8f1749905f2d735623af9214148afJeff Brown                switch (what & MSG_TYPE_MASK) {
275711417b1cfde8f1749905f2d735623af9214148afJeff Brown                    case MSG_TYPE_ROUTE: {
275811417b1cfde8f1749905f2d735623af9214148afJeff Brown                        final RouteInfo route = (RouteInfo)obj;
275911417b1cfde8f1749905f2d735623af9214148afJeff Brown                        if (!record.filterRouteEvent(route)) {
276011417b1cfde8f1749905f2d735623af9214148afJeff Brown                            break;
276111417b1cfde8f1749905f2d735623af9214148afJeff Brown                        }
276211417b1cfde8f1749905f2d735623af9214148afJeff Brown                        switch (what) {
276311417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_ADDED:
276411417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteAdded(router, route);
276511417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
276611417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_REMOVED:
276711417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteRemoved(router, route);
276811417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
276911417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_CHANGED:
277011417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteChanged(router, route);
277111417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
277211417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_VOLUME_CHANGED:
277311417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteVolumeChanged(router, route);
277411417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
277511417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED:
277611417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRoutePresentationDisplayChanged(router, route);
277711417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
277811417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_SELECTED:
277911417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteSelected(router, route);
278011417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
278111417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_UNSELECTED:
278211417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteUnselected(router, route);
278311417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
278411417b1cfde8f1749905f2d735623af9214148afJeff Brown                        }
2785c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
278611417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
278711417b1cfde8f1749905f2d735623af9214148afJeff Brown                    case MSG_TYPE_PROVIDER: {
278811417b1cfde8f1749905f2d735623af9214148afJeff Brown                        final ProviderInfo provider = (ProviderInfo)obj;
278911417b1cfde8f1749905f2d735623af9214148afJeff Brown                        switch (what) {
279011417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_PROVIDER_ADDED:
279111417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onProviderAdded(router, provider);
279211417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
279311417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_PROVIDER_REMOVED:
279411417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onProviderRemoved(router, provider);
279511417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
279611417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_PROVIDER_CHANGED:
279711417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onProviderChanged(router, provider);
279811417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
279911417b1cfde8f1749905f2d735623af9214148afJeff Brown                        }
280011417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
2801c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
2802c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2803c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
2804c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
2805c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown}
2806