MediaRouter.java revision e7a694bf5fc4bf417502509a8604fffc7a41d84c
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;
28c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Bundle;
29c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Handler;
30c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Looper;
31c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Message;
32429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport android.support.annotation.IntDef;
33429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport android.support.annotation.NonNull;
34429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport android.support.annotation.Nullable;
35fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brownimport android.support.v4.app.ActivityManagerCompat;
36c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.support.v4.hardware.display.DisplayManagerCompat;
37bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErikimport android.support.v4.media.VolumeProviderCompat;
38bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErikimport android.support.v4.media.session.MediaSessionCompat;
39e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seoimport android.support.v4.util.Pair;
40b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brownimport android.support.v7.media.MediaRouteProvider.ProviderMetadata;
41c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.util.Log;
42c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.view.Display;
43c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
44429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport java.lang.annotation.Retention;
45429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport java.lang.annotation.RetentionPolicy;
469fcedc160282e6620f409ea46bf6728b35d011ddJeff Brownimport java.lang.ref.WeakReference;
47c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.ArrayList;
48c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.Collections;
49c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.List;
50cb63b6ecac9786891514f241dec71695f09d3efbJeff Brownimport java.util.Locale;
51c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
52c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown/**
53c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter allows applications to control the routing of media channels
54c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * and streams from the current device to external speakers and destination devices.
55c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p>
56c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * A MediaRouter instance is retrieved through {@link #getInstance}.  Applications
57c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * can query the media router about the currently selected route and its capabilities
58c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to determine how to send content to the route's destination.  Applications can
59c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * also {@link RouteInfo#sendControlRequest send control requests} to the route
60c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to ask the route's destination to perform certain remote control functions
61fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * such as playing media.
62c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p>
63c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * See also {@link MediaRouteProvider} for information on how an application
64c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * can publish new media routes to the media router.
65c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p>
66c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The media router API is not thread-safe; all interactions with it must be
67c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * done from the main thread of the process.
68c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p>
69c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */
70c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownpublic final class MediaRouter {
71c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    private static final String TAG = "MediaRouter";
72f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
73c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
7494be6100218126ce6a08bf1f56209578500b361fRoboErik    /**
75b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang     * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)}
76b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang     * when the reason the route was unselected is unknown.
7794be6100218126ce6a08bf1f56209578500b361fRoboErik     */
7894be6100218126ce6a08bf1f56209578500b361fRoboErik    public static final int UNSELECT_REASON_UNKNOWN = 0;
7994be6100218126ce6a08bf1f56209578500b361fRoboErik    /**
80b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang     * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)}
81b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang     * when the user pressed the disconnect button to disconnect and keep playing.
8294be6100218126ce6a08bf1f56209578500b361fRoboErik     * <p>
8394be6100218126ce6a08bf1f56209578500b361fRoboErik     *
8494be6100218126ce6a08bf1f56209578500b361fRoboErik     * @see {@link MediaRouteDescriptor#canDisconnectAndKeepPlaying()}.
8594be6100218126ce6a08bf1f56209578500b361fRoboErik     */
8694be6100218126ce6a08bf1f56209578500b361fRoboErik    public static final int UNSELECT_REASON_DISCONNECTED = 1;
8794be6100218126ce6a08bf1f56209578500b361fRoboErik    /**
88b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang     * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)}
89b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang     * when the user pressed the stop casting button.
9094be6100218126ce6a08bf1f56209578500b361fRoboErik     */
9194be6100218126ce6a08bf1f56209578500b361fRoboErik    public static final int UNSELECT_REASON_STOPPED = 2;
9294be6100218126ce6a08bf1f56209578500b361fRoboErik    /**
93b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang     * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)}
94b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang     * when the user selected a different route.
9594be6100218126ce6a08bf1f56209578500b361fRoboErik     */
9694be6100218126ce6a08bf1f56209578500b361fRoboErik    public static final int UNSELECT_REASON_ROUTE_CHANGED = 3;
9794be6100218126ce6a08bf1f56209578500b361fRoboErik
98c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    // Maintains global media router state for the process.
99c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    // This field is initialized in MediaRouter.getInstance() before any
100c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    // MediaRouter objects are instantiated so it is guaranteed to be
101c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    // valid whenever any instance method is invoked.
102c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    static GlobalMediaRouter sGlobal;
103c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
104c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    // Context-bound state of the media router.
105c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    final Context mContext;
1069fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown    final ArrayList<CallbackRecord> mCallbackRecords = new ArrayList<CallbackRecord>();
10711417b1cfde8f1749905f2d735623af9214148afJeff Brown
108429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    /** @hide */
109429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    @IntDef(flag = true,
110429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye            value = {
111429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye                    CALLBACK_FLAG_PERFORM_ACTIVE_SCAN,
112429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye                    CALLBACK_FLAG_REQUEST_DISCOVERY,
113429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye                    CALLBACK_FLAG_UNFILTERED_EVENTS
114429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye            }
115429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    )
116429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    @Retention(RetentionPolicy.SOURCE)
117429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    private @interface CallbackFlags {}
118429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye
11911417b1cfde8f1749905f2d735623af9214148afJeff Brown    /**
12011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Flag for {@link #addCallback}: Actively scan for routes while this callback
12111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * is registered.
12211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * <p>
12311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * When this flag is specified, the media router will actively scan for new
12411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * routes.  Certain routes, such as wifi display routes, may not be discoverable
12511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * except when actively scanning.  This flag is typically used when the route picker
12611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * dialog has been opened by the user to ensure that the route information is
12711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * up to date.
12811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p><p>
12911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Active scanning may consume a significant amount of power and may have intrusive
13011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * effects on wireless connectivity.  Therefore it is important that active scanning
13111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * only be requested when it is actually needed to satisfy a user request to
13211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * discover and select a new route.
133f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * </p><p>
134f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * This flag implies {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} but performing
135f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * active scans is much more expensive than a normal discovery request.
13611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p>
137f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *
138f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * @see #CALLBACK_FLAG_REQUEST_DISCOVERY
13911417b1cfde8f1749905f2d735623af9214148afJeff Brown     */
140f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1 << 0;
14111417b1cfde8f1749905f2d735623af9214148afJeff Brown
14211417b1cfde8f1749905f2d735623af9214148afJeff Brown    /**
14311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Flag for {@link #addCallback}: Do not filter route events.
14411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * <p>
14511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * When this flag is specified, the callback will be invoked for events that affect any
1463efa63d3b896244713e84acbb5945562dce41d77Jeff Brown     * route even if they do not match the callback's filter.
14711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p>
14811417b1cfde8f1749905f2d735623af9214148afJeff Brown     */
14911417b1cfde8f1749905f2d735623af9214148afJeff Brown    public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 1 << 1;
15011417b1cfde8f1749905f2d735623af9214148afJeff Brown
15111417b1cfde8f1749905f2d735623af9214148afJeff Brown    /**
152fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * Flag for {@link #addCallback}: Request passive route discovery while this
153fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * callback is registered, except on {@link ActivityManager#isLowRamDevice low-RAM devices}.
154f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * <p>
155f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * When this flag is specified, the media router will try to discover routes.
156f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * Although route discovery is intended to be efficient, checking for new routes may
157f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * result in some network activity and could slowly drain the battery.  Therefore
158f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * applications should only specify {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} when
159f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * they are running in the foreground and would like to provide the user with the
160f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * option of connecting to new routes.
161f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * </p><p>
162f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * Applications should typically add a callback using this flag in the
163f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * {@link android.app.Activity activity's} {@link android.app.Activity#onStart onStart}
164f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * method and remove it in the {@link android.app.Activity#onStop onStop} method.
165f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * The {@link android.support.v7.app.MediaRouteDiscoveryFragment} fragment may
166f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * also be used for this purpose.
167fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * </p><p class="note">
168fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * On {@link ActivityManager#isLowRamDevice low-RAM devices} this flag
169fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * will be ignored.  Refer to
170fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * {@link #addCallback(MediaRouteSelector, Callback, int) addCallback} for details.
171f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * </p>
172f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *
173f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * @see android.support.v7.app.MediaRouteDiscoveryFragment
174f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     */
175f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 1 << 2;
176f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
177f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    /**
178fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * Flag for {@link #addCallback}: Request passive route discovery while this
179fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * callback is registered, even on {@link ActivityManager#isLowRamDevice low-RAM devices}.
180fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * <p class="note">
181fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * This flag has a significant performance impact on low-RAM devices
182fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * since it may cause many media route providers to be started simultaneously.
183fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * It is much better to use {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} instead to avoid
184fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * performing passive discovery on these devices altogether.  Refer to
185fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * {@link #addCallback(MediaRouteSelector, Callback, int) addCallback} for details.
186fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * </p>
187fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     *
188fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * @see android.support.v7.app.MediaRouteDiscoveryFragment
189fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     */
190fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown    public static final int CALLBACK_FLAG_FORCE_DISCOVERY = 1 << 3;
191fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown
192fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown    /**
19311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Flag for {@link #isRouteAvailable}: Ignore the default route.
19411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * <p>
19511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * This flag is used to determine whether a matching non-default route is available.
19611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * This constraint may be used to decide whether to offer the route chooser dialog
19711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * to the user.  There is no point offering the chooser if there are no
19811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * non-default choices.
19911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p>
20011417b1cfde8f1749905f2d735623af9214148afJeff Brown     */
20111417b1cfde8f1749905f2d735623af9214148afJeff Brown    public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1 << 0;
20211417b1cfde8f1749905f2d735623af9214148afJeff Brown
203fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown    /**
204fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * Flag for {@link #isRouteAvailable}: Require an actual route to be matched.
205fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * <p>
206fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * If this flag is not set, then {@link #isRouteAvailable} will return true
207fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * if it is possible to discover a matching route even if discovery is not in
208fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * progress or if no matching route has yet been found.  This feature is used to
209fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * save resources by removing the need to perform passive route discovery on
210fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * {@link ActivityManager#isLowRamDevice low-RAM devices}.
211fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * </p><p>
212fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * If this flag is set, then {@link #isRouteAvailable} will only return true if
213fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * a matching route has actually been discovered.
214fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * </p>
215fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     */
216fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown    public static final int AVAILABILITY_FLAG_REQUIRE_MATCH = 1 << 1;
217fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown
218c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    MediaRouter(Context context) {
219c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        mContext = context;
220c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
221c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
222c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
2239fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * Gets an instance of the media router service associated with the context.
2249fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * <p>
2259fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * The application is responsible for holding a strong reference to the returned
2269fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * {@link MediaRouter} instance, such as by storing the instance in a field of
2279fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * the {@link android.app.Activity}, to ensure that the media router remains alive
2289fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * as long as the application is using its features.
2299fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * </p><p>
2309fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * In other words, the support library only holds a {@link WeakReference weak reference}
2319fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * to each media router instance.  When there are no remaining strong references to the
2329fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * media router instance, all of its callbacks will be removed and route discovery
2339fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * will no longer be performed on its behalf.
2349fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * </p>
2359fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     *
2369fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * @return The media router instance for the context.  The application must hold
2379fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * a strong reference to this object as long as it is in use.
238c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
239429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    public static MediaRouter getInstance(@NonNull Context context) {
240c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (context == null) {
241c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            throw new IllegalArgumentException("context must not be null");
242c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
243c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
244c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
245c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (sGlobal == null) {
246c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            sGlobal = new GlobalMediaRouter(context.getApplicationContext());
247fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            sGlobal.start();
248c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
249c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return sGlobal.getRouter(context);
250c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
251c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
253fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Gets information about the {@link MediaRouter.RouteInfo routes} currently known to
254fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * this media router.
255c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
256c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public List<RouteInfo> getRoutes() {
257c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
258c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return sGlobal.getRoutes();
259c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
260c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
261c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
262fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Gets information about the {@link MediaRouter.ProviderInfo route providers}
263fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * currently known to this media router.
264fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     */
265fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public List<ProviderInfo> getProviders() {
266fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        checkCallingThread();
267fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        return sGlobal.getProviders();
268fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
269fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
270fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    /**
271c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Gets the default route for playing media content on the system.
272c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
273c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * The system always provides a default route.
274c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
275c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
276c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @return The default route, which is guaranteed to never be null.
277c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
278429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    @NonNull
279c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public RouteInfo getDefaultRoute() {
280c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
281c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return sGlobal.getDefaultRoute();
282c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
283c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
284c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
285c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Gets the currently selected route.
286c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
287c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * The application should examine the route's
288fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * {@link RouteInfo#getControlFilters media control intent filters} to assess the
289c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * capabilities of the route before attempting to use it.
290c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
291c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
292c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <h3>Example</h3>
293c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <pre>
294c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * public boolean playMovie() {
295c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     MediaRouter mediaRouter = MediaRouter.getInstance(context);
296c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute();
297c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
298c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     // First try using the remote playback interface, if supported.
299c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
300c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         // The route supports remote playback.
301c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         // Try to send it the Uri of the movie to play.
302c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         Intent intent = new Intent(MediaControlIntent.ACTION_PLAY);
303c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
304c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         intent.setDataAndType("http://example.com/videos/movie.mp4", "video/mp4");
305fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     *         if (route.supportsControlRequest(intent)) {
306fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     *             route.sendControlRequest(intent, null);
307fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     *             return true; // sent the request to play the movie
308c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         }
309c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     }
310c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
311c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     // If remote playback was not possible, then play locally.
312c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     if (route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)) {
313c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         // The route supports live video streaming.
314c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         // Prepare to play content locally in a window or in a presentation.
315c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         return playMovieInWindow();
316c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     }
317c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
318c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     // Neither interface is supported, so we can't play the movie to this route.
319c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     return false;
320c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * }
321c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </pre>
322c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
323c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @return The selected route, which is guaranteed to never be null.
324c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
325fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * @see RouteInfo#getControlFilters
326c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see RouteInfo#supportsControlCategory
327c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see RouteInfo#supportsControlRequest
328c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
329429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    @NonNull
330c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public RouteInfo getSelectedRoute() {
331c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
332c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return sGlobal.getSelectedRoute();
333c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
334c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
335c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
33628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * Returns the selected route if it matches the specified selector, otherwise
33728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * selects the default route and returns it.
33828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
33928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @param selector The selector to match.
34028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @return The previously selected route if it matched the selector, otherwise the
34128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * newly selected default route which is guaranteed to never be null.
34228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
34311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see MediaRouteSelector
34428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @see RouteInfo#matchesSelector
34528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @see RouteInfo#isDefault
34628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     */
347429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    @NonNull
348429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    public RouteInfo updateSelectedRoute(@NonNull MediaRouteSelector selector) {
34928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        if (selector == null) {
35028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            throw new IllegalArgumentException("selector must not be null");
35128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
35228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        checkCallingThread();
35328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
35411417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
35511417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "updateSelectedRoute: " + selector);
35611417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
35728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        RouteInfo route = sGlobal.getSelectedRoute();
35811417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (!route.isDefault() && !route.matchesSelector(selector)) {
35928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            route = sGlobal.getDefaultRoute();
36028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            sGlobal.selectRoute(route);
36128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
36228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        return route;
36328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    }
36428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
36528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    /**
366c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Selects the specified route.
367c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
368c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @param route The route to select.
369c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
370429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    public void selectRoute(@NonNull RouteInfo route) {
371c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (route == null) {
372c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            throw new IllegalArgumentException("route must not be null");
373c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
374c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
375c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
37611417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
37711417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "selectRoute: " + route);
37811417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
379c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        sGlobal.selectRoute(route);
380c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
381c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
382c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
38394be6100218126ce6a08bf1f56209578500b361fRoboErik     * Unselects the current round and selects the default route instead.
38494be6100218126ce6a08bf1f56209578500b361fRoboErik     * <p>
38594be6100218126ce6a08bf1f56209578500b361fRoboErik     * The reason given must be one of:
38694be6100218126ce6a08bf1f56209578500b361fRoboErik     * <ul>
38794be6100218126ce6a08bf1f56209578500b361fRoboErik     * <li>{@link MediaRouter#UNSELECT_REASON_UNKNOWN}</li>
38894be6100218126ce6a08bf1f56209578500b361fRoboErik     * <li>{@link MediaRouter#UNSELECT_REASON_DISCONNECTED}</li>
38994be6100218126ce6a08bf1f56209578500b361fRoboErik     * <li>{@link MediaRouter#UNSELECT_REASON_STOPPED}</li>
39094be6100218126ce6a08bf1f56209578500b361fRoboErik     * <li>{@link MediaRouter#UNSELECT_REASON_ROUTE_CHANGED}</li>
39194be6100218126ce6a08bf1f56209578500b361fRoboErik     * </ul>
39294be6100218126ce6a08bf1f56209578500b361fRoboErik     *
39394be6100218126ce6a08bf1f56209578500b361fRoboErik     * @param reason The reason for disconnecting the current route.
39494be6100218126ce6a08bf1f56209578500b361fRoboErik     */
39594be6100218126ce6a08bf1f56209578500b361fRoboErik    public void unselect(int reason) {
39694be6100218126ce6a08bf1f56209578500b361fRoboErik        if (reason < MediaRouter.UNSELECT_REASON_UNKNOWN ||
39794be6100218126ce6a08bf1f56209578500b361fRoboErik                reason > MediaRouter.UNSELECT_REASON_ROUTE_CHANGED) {
39894be6100218126ce6a08bf1f56209578500b361fRoboErik            throw new IllegalArgumentException("Unsupported reason to unselect route");
39994be6100218126ce6a08bf1f56209578500b361fRoboErik        }
40094be6100218126ce6a08bf1f56209578500b361fRoboErik        checkCallingThread();
40194be6100218126ce6a08bf1f56209578500b361fRoboErik
40294be6100218126ce6a08bf1f56209578500b361fRoboErik        sGlobal.selectRoute(getDefaultRoute(), reason);
40394be6100218126ce6a08bf1f56209578500b361fRoboErik    }
40494be6100218126ce6a08bf1f56209578500b361fRoboErik
40594be6100218126ce6a08bf1f56209578500b361fRoboErik    /**
406d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown     * Returns true if there is a route that matches the specified selector.
40711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * <p>
40894be6100218126ce6a08bf1f56209578500b361fRoboErik     * This method returns true if there are any available routes that match the
40994be6100218126ce6a08bf1f56209578500b361fRoboErik     * selector regardless of whether they are enabled or disabled. If the
410d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown     * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} flag is specified, then
411d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown     * the method will only consider non-default routes.
41294be6100218126ce6a08bf1f56209578500b361fRoboErik     * </p>
41394be6100218126ce6a08bf1f56209578500b361fRoboErik     * <p class="note">
414fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * On {@link ActivityManager#isLowRamDevice low-RAM devices} this method
415fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * will return true if it is possible to discover a matching route even if
416fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * discovery is not in progress or if no matching route has yet been found.
417fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * Use {@link #AVAILABILITY_FLAG_REQUIRE_MATCH} to require an actual match.
41811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p>
419c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
42011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param selector The selector to match.
42194be6100218126ce6a08bf1f56209578500b361fRoboErik     * @param flags Flags to control the determination of whether a route may be
42294be6100218126ce6a08bf1f56209578500b361fRoboErik     *            available. May be zero or some combination of
42394be6100218126ce6a08bf1f56209578500b361fRoboErik     *            {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} and
42494be6100218126ce6a08bf1f56209578500b361fRoboErik     *            {@link #AVAILABILITY_FLAG_REQUIRE_MATCH}.
42511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @return True if a matching route may be available.
426c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
427429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    public boolean isRouteAvailable(@NonNull MediaRouteSelector selector, int flags) {
42811417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (selector == null) {
42911417b1cfde8f1749905f2d735623af9214148afJeff Brown            throw new IllegalArgumentException("selector must not be null");
430c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
431c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
432c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
43311417b1cfde8f1749905f2d735623af9214148afJeff Brown        return sGlobal.isRouteAvailable(selector, flags);
434c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
435c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
436c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
43711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Registers a callback to discover routes that match the selector and to receive
43811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * events when they change.
43911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * <p>
44011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * This is a convenience method that has the same effect as calling
44111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * {@link #addCallback(MediaRouteSelector, Callback, int)} without flags.
44211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p>
443c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
44411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param selector A route selector that indicates the kinds of routes that the
44511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * callback would like to discover.
44611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param callback The callback to add.
44711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see #removeCallback
448c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
44911417b1cfde8f1749905f2d735623af9214148afJeff Brown    public void addCallback(MediaRouteSelector selector, Callback callback) {
45011417b1cfde8f1749905f2d735623af9214148afJeff Brown        addCallback(selector, callback, 0);
451c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
452c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
453c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
45411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Registers a callback to discover routes that match the selector and to receive
45511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * events when they change.
45628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * <p>
45711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * The selector describes the kinds of routes that the application wants to
45811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * discover.  For example, if the application wants to use
45911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * live audio routes then it should include the
46011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * {@link MediaControlIntent#CATEGORY_LIVE_AUDIO live audio media control intent category}
46111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * in its selector when it adds a callback to the media router.
46211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * The selector may include any number of categories.
46328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * </p><p>
46411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * If the callback has already been registered, then the selector is added to
46511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * the set of selectors being monitored by the callback.
46611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p><p>
46711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * By default, the callback will only be invoked for events that affect routes
46811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * that match the specified selector.  Event filtering may be disabled by specifying
46911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * the {@link #CALLBACK_FLAG_UNFILTERED_EVENTS} flag when the callback is registered.
470fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * </p><p>
471fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * Applications should use the {@link #isRouteAvailable} method to determine
472fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * whether is it possible to discover a route with the desired capabilities
473fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * and therefore whether the media route button should be shown to the user.
474fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * </p><p>
475fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * The {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} flag should be used while the application
476fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * is in the foreground to request that passive discovery be performed if there are
477fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * sufficient resources to allow continuous passive discovery.
478fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * On {@link ActivityManager#isLowRamDevice low-RAM devices} this flag will be
479fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * ignored to conserve resources.
480fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * </p><p>
481fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * The {@link #CALLBACK_FLAG_FORCE_DISCOVERY} flag should be used when
482fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * passive discovery absolutely must be performed, even on low-RAM devices.
483fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * This flag has a significant performance impact on low-RAM devices
484fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * since it may cause many media route providers to be started simultaneously.
485fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * It is much better to use {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} instead to avoid
486fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * performing passive discovery on these devices altogether.
487fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * </p><p>
488fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * The {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} flag should be used when the
489fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * media route chooser dialog is showing to confirm the presence of available
490fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * routes that the user may connect to.  This flag may use substantially more
491fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown     * power.
49228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * </p>
49328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
49428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * <h3>Example</h3>
49528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * <pre>
49611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * public class MyActivity extends Activity {
49711417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     private MediaRouter mRouter;
49811417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     private MediaRouter.Callback mCallback;
49911417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     private MediaRouteSelector mSelector;
50028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
50111417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     protected void onCreate(Bundle savedInstanceState) {
50211417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         super.onCreate(savedInstanceState);
50311417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
50411417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         mRouter = Mediarouter.getInstance(this);
50511417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         mCallback = new MyCallback();
50611417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         mSelector = new MediaRouteSelector.Builder()
50711417b1cfde8f1749905f2d735623af9214148afJeff Brown     *                 .addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
50811417b1cfde8f1749905f2d735623af9214148afJeff Brown     *                 .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
50911417b1cfde8f1749905f2d735623af9214148afJeff Brown     *                 .build();
51028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *     }
51128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
512f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *     // Add the callback on start to tell the media router what kinds of routes
51311417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     // the application is interested in so that it can try to discover suitable ones.
514f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *     public void onStart() {
515f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *         super.onStart();
51611417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
517f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *         mediaRouter.addCallback(mSelector, mCallback,
518f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *                 MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
51911417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
52011417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         MediaRouter.RouteInfo route = mediaRouter.updateSelectedRoute(mSelector);
52111417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         // do something with the route...
52211417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     }
52311417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
524f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *     // Remove the selector on stop to tell the media router that it no longer
52511417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     // needs to invest effort trying to discover routes of these kinds for now.
526f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *     public void onStop() {
527f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *         super.onStop();
52811417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
52911417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         mediaRouter.removeCallback(mCallback);
53011417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     }
53111417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
53211417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     private final class MyCallback extends MediaRouter.Callback {
53311417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         // Implement callback methods as needed.
53428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *     }
53528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * }
53628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * </pre>
53728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
53811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param selector A route selector that indicates the kinds of routes that the
53911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * callback would like to discover.
54011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param callback The callback to add.
54111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param flags Flags to control the behavior of the callback.
542f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * May be zero or a combination of {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and
54311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * {@link #CALLBACK_FLAG_UNFILTERED_EVENTS}.
54411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see #removeCallback
54528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     */
546429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    public void addCallback(@NonNull MediaRouteSelector selector, @NonNull Callback callback,
547429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye            @CallbackFlags int flags) {
54828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        if (selector == null) {
54928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            throw new IllegalArgumentException("selector must not be null");
55028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
55111417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (callback == null) {
55211417b1cfde8f1749905f2d735623af9214148afJeff Brown            throw new IllegalArgumentException("callback must not be null");
55311417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
55428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        checkCallingThread();
55528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
55611417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
55711417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "addCallback: selector=" + selector
55811417b1cfde8f1749905f2d735623af9214148afJeff Brown                    + ", callback=" + callback + ", flags=" + Integer.toHexString(flags));
55911417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
56011417b1cfde8f1749905f2d735623af9214148afJeff Brown
56111417b1cfde8f1749905f2d735623af9214148afJeff Brown        CallbackRecord record;
56211417b1cfde8f1749905f2d735623af9214148afJeff Brown        int index = findCallbackRecord(callback);
56311417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (index < 0) {
5649fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            record = new CallbackRecord(this, callback);
56511417b1cfde8f1749905f2d735623af9214148afJeff Brown            mCallbackRecords.add(record);
56611417b1cfde8f1749905f2d735623af9214148afJeff Brown        } else {
56711417b1cfde8f1749905f2d735623af9214148afJeff Brown            record = mCallbackRecords.get(index);
56811417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
56911417b1cfde8f1749905f2d735623af9214148afJeff Brown        boolean updateNeeded = false;
57011417b1cfde8f1749905f2d735623af9214148afJeff Brown        if ((flags & ~record.mFlags) != 0) {
57111417b1cfde8f1749905f2d735623af9214148afJeff Brown            record.mFlags |= flags;
57211417b1cfde8f1749905f2d735623af9214148afJeff Brown            updateNeeded = true;
57311417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
57411417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (!record.mSelector.contains(selector)) {
57511417b1cfde8f1749905f2d735623af9214148afJeff Brown            record.mSelector = new MediaRouteSelector.Builder(record.mSelector)
57611417b1cfde8f1749905f2d735623af9214148afJeff Brown                    .addSelector(selector)
57711417b1cfde8f1749905f2d735623af9214148afJeff Brown                    .build();
57811417b1cfde8f1749905f2d735623af9214148afJeff Brown            updateNeeded = true;
57911417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
58011417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (updateNeeded) {
58111417b1cfde8f1749905f2d735623af9214148afJeff Brown            sGlobal.updateDiscoveryRequest();
58228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
58328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    }
58428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
58528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    /**
58611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Removes the specified callback.  It will no longer receive events about
58711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * changes to media routes.
58828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
58911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param callback The callback to remove.
59011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see #addCallback
59128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     */
592429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    public void removeCallback(@NonNull Callback callback) {
59311417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (callback == null) {
59411417b1cfde8f1749905f2d735623af9214148afJeff Brown            throw new IllegalArgumentException("callback must not be null");
59528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
59628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        checkCallingThread();
59728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
59811417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
59911417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "removeCallback: callback=" + callback);
60011417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
60111417b1cfde8f1749905f2d735623af9214148afJeff Brown
60211417b1cfde8f1749905f2d735623af9214148afJeff Brown        int index = findCallbackRecord(callback);
60311417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (index >= 0) {
60411417b1cfde8f1749905f2d735623af9214148afJeff Brown            mCallbackRecords.remove(index);
60511417b1cfde8f1749905f2d735623af9214148afJeff Brown            sGlobal.updateDiscoveryRequest();
60628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
60728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    }
60828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
60911417b1cfde8f1749905f2d735623af9214148afJeff Brown    private int findCallbackRecord(Callback callback) {
61011417b1cfde8f1749905f2d735623af9214148afJeff Brown        final int count = mCallbackRecords.size();
61111417b1cfde8f1749905f2d735623af9214148afJeff Brown        for (int i = 0; i < count; i++) {
61211417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (mCallbackRecords.get(i).mCallback == callback) {
61311417b1cfde8f1749905f2d735623af9214148afJeff Brown                return i;
61411417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
61511417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
61611417b1cfde8f1749905f2d735623af9214148afJeff Brown        return -1;
61711417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
61811417b1cfde8f1749905f2d735623af9214148afJeff Brown
61928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    /**
6209942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * Registers a media route provider within this application process.
6219942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * <p>
6229942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * The provider will be added to the list of providers that all {@link MediaRouter}
6239942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * instances within this process can use to discover routes.
6249942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * </p>
625c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
626fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * @param providerInstance The media route provider instance to add.
627c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
628c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see MediaRouteProvider
62928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @see #removeCallback
630c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
631429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    public void addProvider(@NonNull MediaRouteProvider providerInstance) {
632fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (providerInstance == null) {
633fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            throw new IllegalArgumentException("providerInstance must not be null");
634c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
635c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
636c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
63711417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
63811417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "addProvider: " + providerInstance);
63911417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
640fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        sGlobal.addProvider(providerInstance);
641c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
642c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
643c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
6449942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * Unregisters a media route provider within this application process.
6459942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * <p>
6469942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * The provider will be removed from the list of providers that all {@link MediaRouter}
6479942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * instances within this process can use to discover routes.
6489942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * </p>
649c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
650fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * @param providerInstance The media route provider instance to remove.
651c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
652c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see MediaRouteProvider
65328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @see #addCallback
654c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
655429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    public void removeProvider(@NonNull MediaRouteProvider providerInstance) {
656fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (providerInstance == null) {
657fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            throw new IllegalArgumentException("providerInstance must not be null");
658c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
659c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
660c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
66111417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
66211417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "removeProvider: " + providerInstance);
66328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
66411417b1cfde8f1749905f2d735623af9214148afJeff Brown        sGlobal.removeProvider(providerInstance);
66528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    }
66628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
66728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    /**
668567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * Adds a remote control client to enable remote control of the volume
669567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * of the selected route.
670567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * <p>
671567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * The remote control client must have previously been registered with
672567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * the audio manager using the {@link android.media.AudioManager#registerRemoteControlClient
673567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * AudioManager.registerRemoteControlClient} method.
674567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * </p>
675567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     *
676567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * @param remoteControlClient The {@link android.media.RemoteControlClient} to register.
677567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     */
678429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    public void addRemoteControlClient(@NonNull Object remoteControlClient) {
679567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        if (remoteControlClient == null) {
680567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            throw new IllegalArgumentException("remoteControlClient must not be null");
681567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
682567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        checkCallingThread();
683567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
684567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        if (DEBUG) {
685567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            Log.d(TAG, "addRemoteControlClient: " + remoteControlClient);
686567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
687567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        sGlobal.addRemoteControlClient(remoteControlClient);
688567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown    }
689567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
690567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown    /**
691567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * Removes a remote control client.
692567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     *
693bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik     * @param remoteControlClient The {@link android.media.RemoteControlClient}
694bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik     *            to unregister.
695567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     */
696429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye    public void removeRemoteControlClient(@NonNull Object remoteControlClient) {
697567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        if (remoteControlClient == null) {
698567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            throw new IllegalArgumentException("remoteControlClient must not be null");
699567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
700567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
701567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        if (DEBUG) {
702567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            Log.d(TAG, "removeRemoteControlClient: " + remoteControlClient);
703567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
704567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        sGlobal.removeRemoteControlClient(remoteControlClient);
705567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown    }
706567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
707567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown    /**
708bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik     * Sets the media session to enable remote control of the volume of the
709bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik     * selected route. This should be used instead of
710bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik     * {@link #addRemoteControlClient} when using media sessions. Set the
711bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik     * session to null to clear it.
712bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik     *
713bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik     * @param mediaSession The {@link android.media.session.MediaSession} to
714bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik     *            use.
715bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik     */
716bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik    public void setMediaSession(Object mediaSession) {
717bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik        if (DEBUG) {
718bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            Log.d(TAG, "addMediaSession: " + mediaSession);
719bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik        }
720bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik        sGlobal.setMediaSession(mediaSession);
721bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik    }
722bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik
723bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik    /**
72494be6100218126ce6a08bf1f56209578500b361fRoboErik     * Sets a compat media session to enable remote control of the volume of the
72594be6100218126ce6a08bf1f56209578500b361fRoboErik     * selected route. This should be used instead of
72694be6100218126ce6a08bf1f56209578500b361fRoboErik     * {@link #addRemoteControlClient} when using {@link MediaSessionCompat}.
72794be6100218126ce6a08bf1f56209578500b361fRoboErik     * Set the session to null to clear it.
72894be6100218126ce6a08bf1f56209578500b361fRoboErik     *
72994be6100218126ce6a08bf1f56209578500b361fRoboErik     * @param mediaSession
73094be6100218126ce6a08bf1f56209578500b361fRoboErik     */
73194be6100218126ce6a08bf1f56209578500b361fRoboErik    public void setMediaSessionCompat(MediaSessionCompat mediaSession) {
73294be6100218126ce6a08bf1f56209578500b361fRoboErik        if (DEBUG) {
73394be6100218126ce6a08bf1f56209578500b361fRoboErik            Log.d(TAG, "addMediaSessionCompat: " + mediaSession);
73494be6100218126ce6a08bf1f56209578500b361fRoboErik        }
7355c9469e010106467791b47b0fa83efda84491a21RoboErik        sGlobal.setMediaSessionCompat(mediaSession);
73694be6100218126ce6a08bf1f56209578500b361fRoboErik    }
73794be6100218126ce6a08bf1f56209578500b361fRoboErik
73894be6100218126ce6a08bf1f56209578500b361fRoboErik    public MediaSessionCompat.Token getMediaSessionToken() {
73994be6100218126ce6a08bf1f56209578500b361fRoboErik        return sGlobal.getMediaSessionToken();
74094be6100218126ce6a08bf1f56209578500b361fRoboErik    }
74194be6100218126ce6a08bf1f56209578500b361fRoboErik
74294be6100218126ce6a08bf1f56209578500b361fRoboErik    /**
743c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Ensures that calls into the media router are on the correct thread.
744c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * It pays to be a little paranoid when global state invariants are at risk.
745c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
746c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    static void checkCallingThread() {
747c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (Looper.myLooper() != Looper.getMainLooper()) {
748c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            throw new IllegalStateException("The media router service must only be "
749c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + "accessed on the application's main thread.");
750c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
751c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
752c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
753c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    static <T> boolean equal(T a, T b) {
754c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return a == b || (a != null && b != null && a.equals(b));
755c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
756c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
757c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
758c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Provides information about a media route.
759c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
760fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Each media route has a list of {@link MediaControlIntent media control}
761fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * {@link #getControlFilters intent filters} that describe the capabilities of the
762c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * route and the manner in which it is used and controlled.
763c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
764c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
765e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo    public static class RouteInfo {
766fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final ProviderInfo mProvider;
767c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final String mDescriptorId;
768cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        private final String mUniqueId;
769c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private String mName;
770d63957d28aaabcec588b8cde12eac16414783aebJeff Brown        private String mDescription;
771c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private boolean mEnabled;
77211417b1cfde8f1749905f2d735623af9214148afJeff Brown        private boolean mConnecting;
77394be6100218126ce6a08bf1f56209578500b361fRoboErik        private boolean mCanDisconnect;
774e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        private final ArrayList<IntentFilter> mControlFilters = new ArrayList<>();
775c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mPlaybackType;
776c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mPlaybackStream;
777c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mVolumeHandling;
778c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mVolume;
779c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mVolumeMax;
780c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private Display mPresentationDisplay;
781c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mPresentationDisplayId = -1;
782c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private Bundle mExtras;
78394be6100218126ce6a08bf1f56209578500b361fRoboErik        private IntentSender mSettingsIntent;
784e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        MediaRouteDescriptor mDescriptor;
785c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
786429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        /** @hide */
787429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        @IntDef({PLAYBACK_TYPE_LOCAL,PLAYBACK_TYPE_REMOTE})
788429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        @Retention(RetentionPolicy.SOURCE)
789429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        private @interface PlaybackType {}
790429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye
791c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
792c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * The default playback type, "local", indicating the presentation of the media
793c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * is happening on the same device (e.g. a phone, a tablet) as where it is
794c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * controlled from.
795c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
796c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #getPlaybackType
797c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
798c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public static final int PLAYBACK_TYPE_LOCAL = 0;
799c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
800c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
801c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * A playback type indicating the presentation of the media is happening on
802c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * a different device (i.e. the remote device) than where it is controlled from.
803c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
804c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #getPlaybackType
805c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
806c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public static final int PLAYBACK_TYPE_REMOTE = 1;
807c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
808429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        /** @hide */
809429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        @IntDef({PLAYBACK_VOLUME_FIXED,PLAYBACK_VOLUME_VARIABLE})
810429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        @Retention(RetentionPolicy.SOURCE)
811429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        private @interface PlaybackVolume {}
812429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye
813c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
814c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Playback information indicating the playback volume is fixed, i.e. it cannot be
815c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * controlled from this object. An example of fixed playback volume is a remote player,
816c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
817c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * than attenuate at the source.
818c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
819c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #getVolumeHandling
820c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
821c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public static final int PLAYBACK_VOLUME_FIXED = 0;
822c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
823c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
824c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Playback information indicating the playback volume is variable and can be controlled
825c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * from this object.
826c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
827c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #getVolumeHandling
828c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
829c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public static final int PLAYBACK_VOLUME_VARIABLE = 1;
830c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
831c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        static final int CHANGE_GENERAL = 1 << 0;
832c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        static final int CHANGE_VOLUME = 1 << 1;
833c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        static final int CHANGE_PRESENTATION_DISPLAY = 1 << 2;
834c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
835cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        RouteInfo(ProviderInfo provider, String descriptorId, String uniqueId) {
836fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mProvider = provider;
837c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mDescriptorId = descriptorId;
838cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            mUniqueId = uniqueId;
839c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
840c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
841fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
842fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Gets information about the provider of this media route.
843fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
844fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public ProviderInfo getProvider() {
845fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mProvider;
846c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
847c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
848c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
849cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         * Gets the unique id of the route.
850cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         * <p>
851cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         * The route unique id functions as a stable identifier by which the route is known.
852cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         * For example, an application can use this id as a token to remember the
853cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         * selected route across restarts or to communicate its identity to a service.
854cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         * </p>
855cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         *
856cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         * @return The unique id of the route, never null.
857cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         */
858429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        @NonNull
859cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        public String getId() {
860cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            return mUniqueId;
861cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        }
862cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown
863cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        /**
864d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * Gets the user-visible name of the route.
865d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * <p>
866d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * The route name identifies the destination represented by the route.
867d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * It may be a user-supplied name, an alias, or device serial number.
868d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * </p>
869c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
870d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * @return The user-visible name of a media route.  This is the string presented
871c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * to users who may select this as the active route.
872c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
873c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public String getName() {
874c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mName;
875c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
876c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
877c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
878d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * Gets the user-visible description of the route.
879d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * <p>
880d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * The route description describes the kind of destination represented by the route.
881d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * It may be a user-supplied string, a model number or brand of device.
882d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * </p>
883c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
884d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * @return The description of the route, or null if none.
885c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
886429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        @Nullable
887d63957d28aaabcec588b8cde12eac16414783aebJeff Brown        public String getDescription() {
888d63957d28aaabcec588b8cde12eac16414783aebJeff Brown            return mDescription;
889c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
890c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
891c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
892c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Returns true if this route is enabled and may be selected.
893c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
89411417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @return True if this route is enabled.
895c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
896c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public boolean isEnabled() {
897c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mEnabled;
898c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
899c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
900c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
90111417b1cfde8f1749905f2d735623af9214148afJeff Brown         * Returns true if the route is in the process of connecting and is not
90211417b1cfde8f1749905f2d735623af9214148afJeff Brown         * yet ready for use.
90311417b1cfde8f1749905f2d735623af9214148afJeff Brown         *
90411417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @return True if this route is in the process of connecting.
90511417b1cfde8f1749905f2d735623af9214148afJeff Brown         */
90611417b1cfde8f1749905f2d735623af9214148afJeff Brown        public boolean isConnecting() {
90711417b1cfde8f1749905f2d735623af9214148afJeff Brown            return mConnecting;
90811417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
90911417b1cfde8f1749905f2d735623af9214148afJeff Brown
91011417b1cfde8f1749905f2d735623af9214148afJeff Brown        /**
911fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Returns true if this route is currently selected.
912c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
91311417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @return True if this route is currently selected.
914fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
915fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @see MediaRouter#getSelectedRoute
916fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
917fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean isSelected() {
918fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
919fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return sGlobal.getSelectedRoute() == this;
920fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
921fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
922fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
923fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Returns true if this route is the default route.
924fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
92511417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @return True if this route is the default route.
926fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
927fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @see MediaRouter#getDefaultRoute
928fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
929fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean isDefault() {
930fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
931fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return sGlobal.getDefaultRoute() == this;
932fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
933fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
934fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
935fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Gets a list of {@link MediaControlIntent media control intent} filters that
936fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * describe the capabilities of this route and the media control actions that
937fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * it supports.
938fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
939fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @return A list of intent filters that specifies the media control intents that
940fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * this route supports.
941c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
942c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent
943c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #supportsControlCategory
944c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #supportsControlRequest
945c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
946fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public List<IntentFilter> getControlFilters() {
947fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mControlFilters;
948c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
949c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
950c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
95128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * Returns true if the route supports at least one of the capabilities
95228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * described by a media route selector.
95328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         *
95428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * @param selector The selector that specifies the capabilities to check.
95528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * @return True if the route supports at least one of the capabilities
95628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * described in the media route selector.
95728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         */
958429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        public boolean matchesSelector(@NonNull MediaRouteSelector selector) {
95928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            if (selector == null) {
96028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown                throw new IllegalArgumentException("selector must not be null");
96128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            }
96228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            checkCallingThread();
96311417b1cfde8f1749905f2d735623af9214148afJeff Brown            return selector.matchesControlFilters(mControlFilters);
96428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
96528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
96628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        /**
967c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Returns true if the route supports the specified
968c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * {@link MediaControlIntent media control} category.
969c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
970c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Media control categories describe the capabilities of this route
971c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * such as whether it supports live audio streaming or remote playback.
972c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
973c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
974c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param category A {@link MediaControlIntent media control} category
975c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO},
976c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO},
977fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined
978c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * media control category.
97928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * @return True if the route supports the specified intent category.
980c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
981c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent
982fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @see #getControlFilters
983c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
984429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        public boolean supportsControlCategory(@NonNull String category) {
985c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (category == null) {
986c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalArgumentException("category must not be null");
987c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
988fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
989c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
990fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int count = mControlFilters.size();
991fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            for (int i = 0; i < count; i++) {
992fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mControlFilters.get(i).hasCategory(category)) {
993fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return true;
994fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
995fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
996fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
997c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
998c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
999c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1000c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Returns true if the route supports the specified
1001a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * {@link MediaControlIntent media control} category and action.
1002a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * <p>
1003a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * Media control actions describe specific requests that an application
1004a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * can ask a route to perform.
1005a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * </p>
1006a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         *
1007a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param category A {@link MediaControlIntent media control} category
1008a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO},
1009a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO},
1010a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined
1011a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * media control category.
1012a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param action A {@link MediaControlIntent media control} action
1013a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * such as {@link MediaControlIntent#ACTION_PLAY}.
1014a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @return True if the route supports the specified intent action.
1015a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         *
1016a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @see MediaControlIntent
1017a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @see #getControlFilters
1018a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         */
1019429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        public boolean supportsControlAction(@NonNull String category, @NonNull String action) {
1020a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            if (category == null) {
1021a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                throw new IllegalArgumentException("category must not be null");
1022a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            }
1023a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            if (action == null) {
1024a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                throw new IllegalArgumentException("action must not be null");
1025a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            }
1026a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            checkCallingThread();
1027a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
1028a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            int count = mControlFilters.size();
1029a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            for (int i = 0; i < count; i++) {
1030a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                IntentFilter filter = mControlFilters.get(i);
1031a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                if (filter.hasCategory(category) && filter.hasAction(action)) {
1032a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    return true;
1033a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                }
1034a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            }
1035a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            return false;
1036a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
1037a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
1038a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        /**
1039a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * Returns true if the route supports the specified
1040c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * {@link MediaControlIntent media control} request.
1041c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
1042c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Media control requests are used to request the route to perform
104343f79f79a5117550a7dfedf9c65124afd163ea43Jeff Brown         * actions such as starting remote playback of a media item.
1044c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
1045c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1046c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param intent A {@link MediaControlIntent media control intent}.
1047c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return True if the route can handle the specified intent.
1048c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1049c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent
1050fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @see #getControlFilters
1051c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1052429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        public boolean supportsControlRequest(@NonNull Intent intent) {
1053c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (intent == null) {
1054c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalArgumentException("intent must not be null");
1055c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1056c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            checkCallingThread();
1057c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1058c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            ContentResolver contentResolver = sGlobal.getContentResolver();
1059fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int count = mControlFilters.size();
1060fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            for (int i = 0; i < count; i++) {
1061fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mControlFilters.get(i).match(contentResolver, intent, true, TAG) >= 0) {
1062fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return true;
1063fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
1064fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1065fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
1066c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1067c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1068c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1069c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Sends a {@link MediaControlIntent media control} request to be performed
1070c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * asynchronously by the route's destination.
1071c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
1072c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Media control requests are used to request the route to perform
107343f79f79a5117550a7dfedf9c65124afd163ea43Jeff Brown         * actions such as starting remote playback of a media item.
1074c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p><p>
1075fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * This function may only be called on a selected route.  Control requests
1076fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * sent to unselected routes will fail.
1077c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
1078c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1079c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param intent A {@link MediaControlIntent media control intent}.
1080c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param callback A {@link ControlRequestCallback} to invoke with the result
1081c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * of the request, or null if no result is required.
1082c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1083c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent
1084c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1085429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        public void sendControlRequest(@NonNull Intent intent,
1086429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye                @Nullable ControlRequestCallback callback) {
1087c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (intent == null) {
1088c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalArgumentException("intent must not be null");
1089c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1090c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            checkCallingThread();
1091c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1092fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            sGlobal.sendControlRequest(this, intent, callback);
1093c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1094c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1095c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1096c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets the type of playback associated with this route.
1097c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1098c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The type of playback associated with this route: {@link #PLAYBACK_TYPE_LOCAL}
1099c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * or {@link #PLAYBACK_TYPE_REMOTE}.
1100c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1101429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        @PlaybackType
1102c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getPlaybackType() {
1103c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mPlaybackType;
1104c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1105c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1106c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1107350ba6e4a1b5ec28721a098e50eaf6a508eb28f0Jeff Brown         * Gets the audio stream over which the playback associated with this route is performed.
1108c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1109c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The stream over which the playback associated with this route is performed.
1110c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1111c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getPlaybackStream() {
1112c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mPlaybackStream;
1113c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1114c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1115c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1116c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets information about how volume is handled on the route.
1117c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1118c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return How volume is handled on the route: {@link #PLAYBACK_VOLUME_FIXED}
1119c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * or {@link #PLAYBACK_VOLUME_VARIABLE}.
1120c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1121429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        @PlaybackVolume
1122c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getVolumeHandling() {
1123c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mVolumeHandling;
1124c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1125c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1126c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1127c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets the current volume for this route. Depending on the route, this may only
1128c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * be valid if the route is currently selected.
1129c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1130c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The volume at which the playback associated with this route is performed.
1131c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1132c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getVolume() {
1133c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mVolume;
1134c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1135c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1136c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1137c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets the maximum volume at which the playback associated with this route is performed.
1138c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1139c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The maximum volume at which the playback associated with
1140c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * this route is performed.
1141c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1142c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getVolumeMax() {
1143c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mVolumeMax;
1144c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1145c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1146c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
114794be6100218126ce6a08bf1f56209578500b361fRoboErik         * Gets whether this route supports disconnecting without interrupting
114894be6100218126ce6a08bf1f56209578500b361fRoboErik         * playback.
114994be6100218126ce6a08bf1f56209578500b361fRoboErik         *
115094be6100218126ce6a08bf1f56209578500b361fRoboErik         * @return True if this route can disconnect without stopping playback,
115194be6100218126ce6a08bf1f56209578500b361fRoboErik         *         false otherwise.
115294be6100218126ce6a08bf1f56209578500b361fRoboErik         */
115394be6100218126ce6a08bf1f56209578500b361fRoboErik        public boolean canDisconnect() {
115494be6100218126ce6a08bf1f56209578500b361fRoboErik            return mCanDisconnect;
115594be6100218126ce6a08bf1f56209578500b361fRoboErik        }
115694be6100218126ce6a08bf1f56209578500b361fRoboErik
115794be6100218126ce6a08bf1f56209578500b361fRoboErik        /**
1158c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Requests a volume change for this route asynchronously.
1159c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
1160c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This function may only be called on a selected route.  It will have
1161c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * no effect if the route is currently unselected.
1162c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
1163c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1164c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param volume The new volume value between 0 and {@link #getVolumeMax}.
1165c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1166c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void requestSetVolume(int volume) {
1167c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            checkCallingThread();
1168c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            sGlobal.requestSetVolume(this, Math.min(mVolumeMax, Math.max(0, volume)));
1169c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1170c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1171c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1172c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Requests an incremental volume update for this route asynchronously.
1173c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
1174c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This function may only be called on a selected route.  It will have
1175c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * no effect if the route is currently unselected.
1176c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
1177c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1178c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param delta The delta to add to the current volume.
1179c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1180c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void requestUpdateVolume(int delta) {
1181c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            checkCallingThread();
1182c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (delta != 0) {
1183c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                sGlobal.requestUpdateVolume(this, delta);
1184c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1185c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1186c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1187c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1188c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets the {@link Display} that should be used by the application to show
1189c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * a {@link android.app.Presentation} on an external display when this route is selected.
1190c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Depending on the route, this may only be valid if the route is currently
1191c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * selected.
1192c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
1193c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * The preferred presentation display may change independently of the route
1194c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * being selected or unselected.  For example, the presentation display
1195c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * of the default system route may change when an external HDMI display is connected
1196c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * or disconnected even though the route itself has not changed.
1197c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p><p>
1198c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This method may return null if there is no external display associated with
1199c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * the route or if the display is not ready to show UI yet.
1200c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p><p>
1201c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * The application should listen for changes to the presentation display
1202c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * using the {@link Callback#onRoutePresentationDisplayChanged} callback and
1203c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * show or dismiss its {@link android.app.Presentation} accordingly when the display
1204c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * becomes available or is removed.
1205c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p><p>
1206c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This method only makes sense for
1207c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO live video} routes.
1208c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
1209c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1210c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The preferred presentation display to use when this route is
1211c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * selected or null if none.
1212c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1213c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent#CATEGORY_LIVE_VIDEO
1214c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see android.app.Presentation
1215c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1216429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        @Nullable
1217c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public Display getPresentationDisplay() {
1218fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
1219c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mPresentationDisplayId >= 0 && mPresentationDisplay == null) {
1220c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mPresentationDisplay = sGlobal.getDisplay(mPresentationDisplayId);
1221c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1222c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mPresentationDisplay;
1223c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1224c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1225c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1226c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets a collection of extra properties about this route that were supplied
1227c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * by its media route provider, or null if none.
1228c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1229429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye        @Nullable
1230c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public Bundle getExtras() {
1231c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mExtras;
1232c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1233c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1234fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
123594be6100218126ce6a08bf1f56209578500b361fRoboErik         * Gets an intent sender for launching a settings activity for this
123694be6100218126ce6a08bf1f56209578500b361fRoboErik         * route.
123794be6100218126ce6a08bf1f56209578500b361fRoboErik         */
123894be6100218126ce6a08bf1f56209578500b361fRoboErik        @Nullable
123994be6100218126ce6a08bf1f56209578500b361fRoboErik        public IntentSender getSettingsIntent() {
124094be6100218126ce6a08bf1f56209578500b361fRoboErik            return mSettingsIntent;
124194be6100218126ce6a08bf1f56209578500b361fRoboErik        }
124294be6100218126ce6a08bf1f56209578500b361fRoboErik
124394be6100218126ce6a08bf1f56209578500b361fRoboErik        /**
1244fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Selects this media route.
1245fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1246fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void select() {
1247fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
1248fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            sGlobal.selectRoute(this);
1249fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1250fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1251c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
1252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public String toString() {
1253cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            return "MediaRouter.RouteInfo{ uniqueId=" + mUniqueId
1254cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                    + ", name=" + mName
1255d63957d28aaabcec588b8cde12eac16414783aebJeff Brown                    + ", description=" + mDescription
1256c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", enabled=" + mEnabled
125711417b1cfde8f1749905f2d735623af9214148afJeff Brown                    + ", connecting=" + mConnecting
125894be6100218126ce6a08bf1f56209578500b361fRoboErik                    + ", canDisconnect=" + mCanDisconnect
1259c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", playbackType=" + mPlaybackType
1260c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", playbackStream=" + mPlaybackStream
1261c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", volumeHandling=" + mVolumeHandling
1262c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", volume=" + mVolume
1263c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", volumeMax=" + mVolumeMax
1264c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", presentationDisplayId=" + mPresentationDisplayId
1265c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", extras=" + mExtras
126694be6100218126ce6a08bf1f56209578500b361fRoboErik                    + ", settingsIntent=" + mSettingsIntent
1267fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    + ", providerPackageName=" + mProvider.getPackageName()
1268fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    + " }";
1269fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1270fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1271e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        int maybeUpdateDescriptor(MediaRouteDescriptor descriptor) {
1272fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int changes = 0;
1273fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mDescriptor != descriptor) {
1274e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                changes = updateDescriptor(descriptor);
1275e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            }
1276e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            return changes;
1277e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        }
1278e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo
1279e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        int updateDescriptor(MediaRouteDescriptor descriptor) {
1280e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            int changes = 0;
1281e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            mDescriptor = descriptor;
1282e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            if (descriptor != null) {
1283e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (!equal(mName, descriptor.getName())) {
1284e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mName = descriptor.getName();
1285e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL;
1286e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1287e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (!equal(mDescription, descriptor.getDescription())) {
1288e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mDescription = descriptor.getDescription();
1289e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL;
1290e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1291e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (mEnabled != descriptor.isEnabled()) {
1292e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mEnabled = descriptor.isEnabled();
1293e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL;
1294e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1295e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (mConnecting != descriptor.isConnecting()) {
1296e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mConnecting = descriptor.isConnecting();
1297e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL;
1298e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1299e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (!mControlFilters.equals(descriptor.getControlFilters())) {
1300e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mControlFilters.clear();
1301e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mControlFilters.addAll(descriptor.getControlFilters());
1302e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL;
1303e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1304e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (mPlaybackType != descriptor.getPlaybackType()) {
1305e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mPlaybackType = descriptor.getPlaybackType();
1306e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL;
1307e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1308e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (mPlaybackStream != descriptor.getPlaybackStream()) {
1309e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mPlaybackStream = descriptor.getPlaybackStream();
1310e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL;
1311e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1312e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (mVolumeHandling != descriptor.getVolumeHandling()) {
1313e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mVolumeHandling = descriptor.getVolumeHandling();
1314e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL | CHANGE_VOLUME;
1315e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1316e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (mVolume != descriptor.getVolume()) {
1317e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mVolume = descriptor.getVolume();
1318e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL | CHANGE_VOLUME;
1319e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1320e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (mVolumeMax != descriptor.getVolumeMax()) {
1321e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mVolumeMax = descriptor.getVolumeMax();
1322e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL | CHANGE_VOLUME;
1323e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1324e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (mPresentationDisplayId != descriptor.getPresentationDisplayId()) {
1325e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mPresentationDisplayId = descriptor.getPresentationDisplayId();
1326e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mPresentationDisplay = null;
1327e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY;
1328e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1329e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (!equal(mExtras, descriptor.getExtras())) {
1330e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mExtras = descriptor.getExtras();
1331e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL;
1332e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1333e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (!equal(mSettingsIntent, descriptor.getSettingsActivity())) {
1334e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mSettingsIntent = descriptor.getSettingsActivity();
1335e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL;
1336e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1337e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (mCanDisconnect != descriptor.canDisconnectAndKeepPlaying()) {
1338e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mCanDisconnect = descriptor.canDisconnectAndKeepPlaying();
1339e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY;
1340fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
1341fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1342fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return changes;
1343fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1344fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1345fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        String getDescriptorId() {
1346fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mDescriptorId;
1347fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1348fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
134914b631fc79b2fc69f392ab8d2f096b0e86b9bea2Jae Seo        /** @hide */
135014b631fc79b2fc69f392ab8d2f096b0e86b9bea2Jae Seo        public MediaRouteProvider getProviderInstance() {
1351fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mProvider.getProviderInstance();
1352fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1353fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
1354fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1355fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    /**
1356e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo     * Information about a route that consists of multiple other routes in a group.
1357e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo     * @hide
1358e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo     * STOPSHIP: Unhide or remove.
1359e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo     */
1360e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo    public static class RouteGroup extends RouteInfo {
1361e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        private List<RouteInfo> mRoutes = new ArrayList<>();
1362e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo
1363e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        RouteGroup(ProviderInfo provider, String descriptorId, String uniqueId) {
1364e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            super(provider, descriptorId, uniqueId);
1365e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        }
1366e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo
1367e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        /**
1368e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo         * @return The number of routes in this group
1369e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo         */
1370e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        public int getRouteCount() {
1371e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            return mRoutes.size();
1372e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        }
1373e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo
1374e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        /**
1375e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo         * Returns the route in this group at the specified index
1376e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo         *
1377e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo         * @param index Index to fetch
1378e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo         * @return The route at index
1379e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo         */
1380e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        public RouteInfo getRouteAt(int index) {
1381e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            return mRoutes.get(index);
1382e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        }
1383e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo
1384e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        @Override
1385e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        public String toString() {
1386e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            StringBuilder sb = new StringBuilder(super.toString());
1387e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            sb.append('[');
1388e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            final int count = mRoutes.size();
1389e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            for (int i = 0; i < count; i++) {
1390e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (i > 0) sb.append(", ");
1391e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                sb.append(mRoutes.get(i));
1392e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            }
1393e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            sb.append(']');
1394e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            return sb.toString();
1395e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        }
1396e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo
1397e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        @Override
1398e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        int maybeUpdateDescriptor(MediaRouteDescriptor descriptor) {
1399e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            boolean changed = false;
1400e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            if (mDescriptor != descriptor) {
1401e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                mDescriptor = descriptor;
1402e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (descriptor != null) {
1403e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    List<String> childIds = descriptor.getChildIds();
1404e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    List<RouteInfo> routes = new ArrayList<>();
1405e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    changed = childIds.size() != mRoutes.size();
1406e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    for (String childId : childIds) {
1407e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        RouteInfo child = sGlobal.getRoute(childId);
1408e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        if (child != null) {
1409e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            routes.add(child);
1410e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            if (!changed && !mRoutes.contains(child)) {
1411e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                changed = true;
1412e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            }
1413e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        }
1414e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    }
1415e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    if (changed) {
1416e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        mRoutes = routes;
1417e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    }
1418e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1419e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            }
1420e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            return (changed ? CHANGE_GENERAL : 0) | super.updateDescriptor(descriptor);
1421e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        }
1422e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo    }
1423e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo
1424e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo    /**
1425fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Provides information about a media route provider.
1426fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * <p>
1427fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * This object may be used to determine which media route provider has
1428fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * published a particular route.
1429fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * </p>
1430fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     */
1431fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public static final class ProviderInfo {
1432fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final MediaRouteProvider mProviderInstance;
1433e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        private final List<RouteInfo> mRoutes = new ArrayList<>();
1434fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1435fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final ProviderMetadata mMetadata;
143611417b1cfde8f1749905f2d735623af9214148afJeff Brown        private MediaRouteProviderDescriptor mDescriptor;
1437fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private Resources mResources;
1438fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private boolean mResourcesNotAvailable;
1439fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1440fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        ProviderInfo(MediaRouteProvider provider) {
1441fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mProviderInstance = provider;
1442fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mMetadata = provider.getMetadata();
1443fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1444fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1445fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
1446fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Gets the provider's underlying {@link MediaRouteProvider} instance.
1447fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1448fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public MediaRouteProvider getProviderInstance() {
1449fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
1450fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mProviderInstance;
1451fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1452fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1453fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
1454adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown         * Gets the package name of the media route provider.
1455fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1456fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public String getPackageName() {
1457fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mMetadata.getPackageName();
1458fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1459fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1460fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
1461adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown         * Gets the component name of the media route provider.
1462adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown         */
1463adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown        public ComponentName getComponentName() {
1464adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown            return mMetadata.getComponentName();
1465adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown        }
1466adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown
1467adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown        /**
1468fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Gets the {@link MediaRouter.RouteInfo routes} published by this route provider.
1469fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1470fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public List<RouteInfo> getRoutes() {
1471fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
1472fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mRoutes;
1473fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1474fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1475fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        Resources getResources() {
1476fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mResources == null && !mResourcesNotAvailable) {
1477fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                String packageName = getPackageName();
1478fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Context context = sGlobal.getProviderContext(packageName);
1479fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (context != null) {
1480fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mResources = context.getResources();
1481fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                } else {
1482fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    Log.w(TAG, "Unable to obtain resources for route provider package: "
1483fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            + packageName);
1484fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mResourcesNotAvailable = true;
1485fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
1486fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1487fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mResources;
1488fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1489fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
149011417b1cfde8f1749905f2d735623af9214148afJeff Brown        boolean updateDescriptor(MediaRouteProviderDescriptor descriptor) {
1491fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mDescriptor != descriptor) {
1492fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mDescriptor = descriptor;
1493fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return true;
1494fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1495fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
1496fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1497fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1498fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        int findRouteByDescriptorId(String id) {
1499fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            final int count = mRoutes.size();
1500fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            for (int i = 0; i < count; i++) {
1501fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mRoutes.get(i).mDescriptorId.equals(id)) {
1502fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return i;
1503fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
1504fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1505fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return -1;
1506fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1507fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1508fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
1509fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public String toString() {
1510fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return "MediaRouter.RouteProviderInfo{ packageName=" + getPackageName()
1511c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + " }";
1512c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1513c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
1514c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1515c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
1516c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Interface for receiving events about media routing changes.
1517c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * All methods of this interface will be called from the application's main thread.
1518c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
1519c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * A Callback will only receive events relevant to routes that the callback
152011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * was registered for unless the {@link MediaRouter#CALLBACK_FLAG_UNFILTERED_EVENTS}
152111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * flag was specified in {@link MediaRouter#addCallback(MediaRouteSelector, Callback, int)}.
1522c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
1523c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
152411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see MediaRouter#addCallback(MediaRouteSelector, Callback, int)
1525c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see MediaRouter#removeCallback(Callback)
1526c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
1527c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public static abstract class Callback {
1528c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1529fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when the supplied media route becomes selected as the active route.
1530c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1531fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1532c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that has been selected.
1533c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1534c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteSelected(MediaRouter router, RouteInfo route) {
1535c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1536c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1537c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1538fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when the supplied media route becomes unselected as the active route.
1539c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1540fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1541c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that has been unselected.
1542c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1543c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteUnselected(MediaRouter router, RouteInfo route) {
1544c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1545c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1546c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1547fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route has been added.
1548c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1549fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1550c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that has become available for use.
1551c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1552c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteAdded(MediaRouter router, RouteInfo route) {
1553c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1554c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1555c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1556fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route has been removed.
1557c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1558fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1559c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that has been removed from availability.
1560c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1561c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteRemoved(MediaRouter router, RouteInfo route) {
1562c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1563c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1564c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1565fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a property of the indicated media route has changed.
1566c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1567fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1568c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that was changed.
1569c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1570c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteChanged(MediaRouter router, RouteInfo route) {
1571c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1572c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1573c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1574fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route's volume changes.
1575c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1576fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1577c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route whose volume changed.
1578c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1579c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) {
1580c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1581c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1582c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1583fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route's presentation display changes.
1584c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
1585c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This method is called whenever the route's presentation display becomes
1586fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * available, is removed or has changes to some of its properties (such as its size).
1587c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
1588c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1589fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1590c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route whose presentation display changed.
1591c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1592c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see RouteInfo#getPresentationDisplay()
1593c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1594c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo route) {
1595c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1596fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1597fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
1598fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route provider has been added.
1599fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
1600fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1601fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param provider The provider that has become available for use.
1602fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1603fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void onProviderAdded(MediaRouter router, ProviderInfo provider) {
1604fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1605fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1606fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
1607fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route provider has been removed.
1608fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
1609fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1610fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param provider The provider that has been removed from availability.
1611fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1612fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void onProviderRemoved(MediaRouter router, ProviderInfo provider) {
1613fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
161411417b1cfde8f1749905f2d735623af9214148afJeff Brown
161511417b1cfde8f1749905f2d735623af9214148afJeff Brown        /**
161611417b1cfde8f1749905f2d735623af9214148afJeff Brown         * Called when a property of the indicated media route provider has changed.
161711417b1cfde8f1749905f2d735623af9214148afJeff Brown         *
161811417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @param router The media router reporting the event.
161911417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @param provider The provider that was changed.
162011417b1cfde8f1749905f2d735623af9214148afJeff Brown         */
162111417b1cfde8f1749905f2d735623af9214148afJeff Brown        public void onProviderChanged(MediaRouter router, ProviderInfo provider) {
162211417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
1623c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
1624c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1625c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
1626c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Callback which is invoked with the result of a media control request.
1627c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
1628c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see RouteInfo#sendControlRequest
1629c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
1630fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public static abstract class ControlRequestCallback {
1631c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
16323d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * Called when a media control request succeeds.
16333d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         *
16343d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * @param data Result data, or null if none.
16353d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * Contents depend on the {@link MediaControlIntent media control action}.
1636c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
16373d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown        public void onResult(Bundle data) {
16383d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown        }
1639c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1640c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
16413d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * Called when a media control request fails.
1642c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
16433d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * @param error A localized error message which may be shown to the user, or null
16443d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * if the cause of the error is unclear.
16453d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * @param data Error data, or null if none.
16463d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * Contents depend on the {@link MediaControlIntent media control action}.
1647c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
16483d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown        public void onError(String error, Bundle data) {
1649c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1650c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
1651c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
165211417b1cfde8f1749905f2d735623af9214148afJeff Brown    private static final class CallbackRecord {
16539fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown        public final MediaRouter mRouter;
165411417b1cfde8f1749905f2d735623af9214148afJeff Brown        public final Callback mCallback;
165511417b1cfde8f1749905f2d735623af9214148afJeff Brown        public MediaRouteSelector mSelector;
165611417b1cfde8f1749905f2d735623af9214148afJeff Brown        public int mFlags;
165711417b1cfde8f1749905f2d735623af9214148afJeff Brown
16589fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown        public CallbackRecord(MediaRouter router, Callback callback) {
16599fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            mRouter = router;
166011417b1cfde8f1749905f2d735623af9214148afJeff Brown            mCallback = callback;
166111417b1cfde8f1749905f2d735623af9214148afJeff Brown            mSelector = MediaRouteSelector.EMPTY;
166211417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
166311417b1cfde8f1749905f2d735623af9214148afJeff Brown
166411417b1cfde8f1749905f2d735623af9214148afJeff Brown        public boolean filterRouteEvent(RouteInfo route) {
166511417b1cfde8f1749905f2d735623af9214148afJeff Brown            return (mFlags & CALLBACK_FLAG_UNFILTERED_EVENTS) != 0
166611417b1cfde8f1749905f2d735623af9214148afJeff Brown                    || route.matchesSelector(mSelector);
166711417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
166811417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
166911417b1cfde8f1749905f2d735623af9214148afJeff Brown
1670c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
1671c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Global state for the media router.
1672c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
1673c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Media routes and media route providers are global to the process; their
1674c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * state and the bulk of the media router implementation lives here.
1675c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
1676c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
16773efa63d3b896244713e84acbb5945562dce41d77Jeff Brown    private static final class GlobalMediaRouter
16783efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            implements SystemMediaRouteProvider.SyncCallback,
16793efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            RegisteredMediaRouteProviderWatcher.Callback {
1680c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final Context mApplicationContext;
16819fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown        private final ArrayList<WeakReference<MediaRouter>> mRouters =
16829fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                new ArrayList<WeakReference<MediaRouter>>();
1683c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
1684fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final ArrayList<ProviderInfo> mProviders =
1685fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                new ArrayList<ProviderInfo>();
1686567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        private final ArrayList<RemoteControlClientRecord> mRemoteControlClients =
1687567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                new ArrayList<RemoteControlClientRecord>();
1688567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        private final RemoteControlClientCompat.PlaybackInfo mPlaybackInfo =
1689567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                new RemoteControlClientCompat.PlaybackInfo();
1690c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final ProviderCallback mProviderCallback = new ProviderCallback();
1691c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final CallbackHandler mCallbackHandler = new CallbackHandler();
1692c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final DisplayManagerCompat mDisplayManager;
1693c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final SystemMediaRouteProvider mSystemProvider;
1694fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown        private final boolean mLowRam;
1695c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1696fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private RegisteredMediaRouteProviderWatcher mRegisteredProviderWatcher;
1697c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private RouteInfo mDefaultRoute;
1698c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private RouteInfo mSelectedRoute;
1699c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private MediaRouteProvider.RouteController mSelectedRouteController;
170011417b1cfde8f1749905f2d735623af9214148afJeff Brown        private MediaRouteDiscoveryRequest mDiscoveryRequest;
1701bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik        private MediaSessionRecord mMediaSession;
17025c9469e010106467791b47b0fa83efda84491a21RoboErik        private MediaSessionCompat mRccMediaSession;
1703e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        private MediaSessionCompat mCompatSession;
17045c9469e010106467791b47b0fa83efda84491a21RoboErik        private MediaSessionCompat.OnActiveChangeListener mSessionActiveListener =
17055c9469e010106467791b47b0fa83efda84491a21RoboErik                new MediaSessionCompat.OnActiveChangeListener() {
17065c9469e010106467791b47b0fa83efda84491a21RoboErik            @Override
17075c9469e010106467791b47b0fa83efda84491a21RoboErik            public void onActiveChanged() {
17085c9469e010106467791b47b0fa83efda84491a21RoboErik                if(mRccMediaSession != null) {
17095c9469e010106467791b47b0fa83efda84491a21RoboErik                    if (mRccMediaSession.isActive()) {
17105c9469e010106467791b47b0fa83efda84491a21RoboErik                        addRemoteControlClient(mRccMediaSession.getRemoteControlClient());
17115c9469e010106467791b47b0fa83efda84491a21RoboErik                    } else {
17125c9469e010106467791b47b0fa83efda84491a21RoboErik                        removeRemoteControlClient(mRccMediaSession.getRemoteControlClient());
17135c9469e010106467791b47b0fa83efda84491a21RoboErik                    }
17145c9469e010106467791b47b0fa83efda84491a21RoboErik                }
17155c9469e010106467791b47b0fa83efda84491a21RoboErik            }
17165c9469e010106467791b47b0fa83efda84491a21RoboErik        };
1717c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1718c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        GlobalMediaRouter(Context applicationContext) {
1719c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mApplicationContext = applicationContext;
1720c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mDisplayManager = DisplayManagerCompat.getInstance(applicationContext);
1721fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown            mLowRam = ActivityManagerCompat.isLowRamDevice(
1722fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                    (ActivityManager)applicationContext.getSystemService(
1723fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                            Context.ACTIVITY_SERVICE));
1724fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1725fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // Add the system media route provider for interoperating with
1726fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // the framework media router.  This one is special and receives
1727fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // synchronization messages from the media router.
1728c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mSystemProvider = SystemMediaRouteProvider.obtain(applicationContext, this);
1729fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            addProvider(mSystemProvider);
1730fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1731fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1732fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void start() {
1733fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // Start watching for routes published by registered media route
1734fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // provider services.
1735fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mRegisteredProviderWatcher = new RegisteredMediaRouteProviderWatcher(
17363efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                    mApplicationContext, this);
1737fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mRegisteredProviderWatcher.start();
1738c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1739c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1740c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public MediaRouter getRouter(Context context) {
17419fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            MediaRouter router;
17429fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            for (int i = mRouters.size(); --i >= 0; ) {
17439fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                router = mRouters.get(i).get();
17449fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                if (router == null) {
17459fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    mRouters.remove(i);
17469fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                } else if (router.mContext == context) {
17479fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    return router;
17489fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                }
1749c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
17509fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            router = new MediaRouter(context);
17519fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            mRouters.add(new WeakReference<MediaRouter>(router));
1752c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return router;
1753c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1754c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1755c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public ContentResolver getContentResolver() {
1756c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mApplicationContext.getContentResolver();
1757c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1758c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1759fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public Context getProviderContext(String packageName) {
1760fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (packageName.equals(SystemMediaRouteProvider.PACKAGE_NAME)) {
1761fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return mApplicationContext;
1762fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1763fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            try {
1764fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return mApplicationContext.createPackageContext(
1765fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        packageName, Context.CONTEXT_RESTRICTED);
1766fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            } catch (NameNotFoundException ex) {
1767fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return null;
1768fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1769fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1770fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1771c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public Display getDisplay(int displayId) {
1772c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mDisplayManager.getDisplay(displayId);
1773c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1774c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1775fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void sendControlRequest(RouteInfo route,
1776c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                Intent intent, ControlRequestCallback callback) {
1777c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (route == mSelectedRoute && mSelectedRouteController != null) {
1778129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                if (mSelectedRouteController.onControlRequest(intent, callback)) {
1779fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return;
1780fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
1781fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1782fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (callback != null) {
17833d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown                callback.onError(null, null);
1784c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1785c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1786c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1787c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void requestSetVolume(RouteInfo route, int volume) {
1788c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (route == mSelectedRoute && mSelectedRouteController != null) {
1789129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                mSelectedRouteController.onSetVolume(volume);
1790c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1791c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1792c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1793c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void requestUpdateVolume(RouteInfo route, int delta) {
1794c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (route == mSelectedRoute && mSelectedRouteController != null) {
1795129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                mSelectedRouteController.onUpdateVolume(delta);
1796c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1797c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1798c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1799e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        public RouteInfo getRoute(String uniqueId) {
1800e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            for (RouteInfo info : mRoutes) {
1801e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if (info.mUniqueId.equals(uniqueId)) {
1802e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    return info;
1803e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
1804e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            }
1805e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            return null;
1806e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        }
1807e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo
1808c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public List<RouteInfo> getRoutes() {
1809c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mRoutes;
1810c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1811c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1812fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public List<ProviderInfo> getProviders() {
1813fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mProviders;
1814fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1815fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1816c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public RouteInfo getDefaultRoute() {
1817c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mDefaultRoute == null) {
1818c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // This should never happen once the media router has been fully
1819c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // initialized but it is good to check for the error in case there
1820c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // is a bug in provider initialization.
1821c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalStateException("There is no default route.  "
1822c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        + "The media router has not yet been fully initialized.");
1823c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1824c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mDefaultRoute;
1825c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1826c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1827c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public RouteInfo getSelectedRoute() {
1828c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mSelectedRoute == null) {
1829c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // This should never happen once the media router has been fully
1830c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // initialized but it is good to check for the error in case there
1831c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // is a bug in provider initialization.
1832c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalStateException("There is no currently selected route.  "
1833c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        + "The media router has not yet been fully initialized.");
1834c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1835c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mSelectedRoute;
1836c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1837c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1838c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void selectRoute(RouteInfo route) {
183994be6100218126ce6a08bf1f56209578500b361fRoboErik            selectRoute(route, MediaRouter.UNSELECT_REASON_ROUTE_CHANGED);
184094be6100218126ce6a08bf1f56209578500b361fRoboErik        }
184194be6100218126ce6a08bf1f56209578500b361fRoboErik
184294be6100218126ce6a08bf1f56209578500b361fRoboErik        public void selectRoute(RouteInfo route, int unselectReason) {
1843c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (!mRoutes.contains(route)) {
1844c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                Log.w(TAG, "Ignoring attempt to select removed route: " + route);
1845c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                return;
1846c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1847c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (!route.mEnabled) {
1848c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                Log.w(TAG, "Ignoring attempt to select disabled route: " + route);
1849c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                return;
1850c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1851c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
185294be6100218126ce6a08bf1f56209578500b361fRoboErik            setSelectedRouteInternal(route, unselectReason);
1853c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1854c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
185511417b1cfde8f1749905f2d735623af9214148afJeff Brown        public boolean isRouteAvailable(MediaRouteSelector selector, int flags) {
1856fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown            if (selector.isEmpty()) {
1857fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                return false;
1858fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown            }
1859fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown
1860fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown            // On low-RAM devices, do not rely on actual discovery results unless asked to.
1861fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown            if ((flags & AVAILABILITY_FLAG_REQUIRE_MATCH) == 0 && mLowRam) {
1862fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                return true;
1863fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown            }
1864fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown
186511417b1cfde8f1749905f2d735623af9214148afJeff Brown            // Check whether any existing routes match the selector.
186611417b1cfde8f1749905f2d735623af9214148afJeff Brown            final int routeCount = mRoutes.size();
186711417b1cfde8f1749905f2d735623af9214148afJeff Brown            for (int i = 0; i < routeCount; i++) {
186811417b1cfde8f1749905f2d735623af9214148afJeff Brown                RouteInfo route = mRoutes.get(i);
186911417b1cfde8f1749905f2d735623af9214148afJeff Brown                if ((flags & AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE) != 0
187011417b1cfde8f1749905f2d735623af9214148afJeff Brown                        && route.isDefault()) {
187111417b1cfde8f1749905f2d735623af9214148afJeff Brown                    continue;
187211417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
187311417b1cfde8f1749905f2d735623af9214148afJeff Brown                if (route.matchesSelector(selector)) {
187411417b1cfde8f1749905f2d735623af9214148afJeff Brown                    return true;
187511417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
187611417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
187711417b1cfde8f1749905f2d735623af9214148afJeff Brown
187811417b1cfde8f1749905f2d735623af9214148afJeff Brown            // It doesn't look like we can find a matching route right now.
187911417b1cfde8f1749905f2d735623af9214148afJeff Brown            return false;
188011417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
188111417b1cfde8f1749905f2d735623af9214148afJeff Brown
188211417b1cfde8f1749905f2d735623af9214148afJeff Brown        public void updateDiscoveryRequest() {
188311417b1cfde8f1749905f2d735623af9214148afJeff Brown            // Combine all of the callback selectors and active scan flags.
1884f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            boolean discover = false;
188511417b1cfde8f1749905f2d735623af9214148afJeff Brown            boolean activeScan = false;
188611417b1cfde8f1749905f2d735623af9214148afJeff Brown            MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder();
18879fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            for (int i = mRouters.size(); --i >= 0; ) {
18889fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                MediaRouter router = mRouters.get(i).get();
18899fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                if (router == null) {
18909fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    mRouters.remove(i);
18919fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                } else {
18929fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    final int count = router.mCallbackRecords.size();
18939fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    for (int j = 0; j < count; j++) {
18949fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                        CallbackRecord callback = router.mCallbackRecords.get(j);
18959fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                        builder.addSelector(callback.mSelector);
1896f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                        if ((callback.mFlags & CALLBACK_FLAG_PERFORM_ACTIVE_SCAN) != 0) {
18979fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                            activeScan = true;
1898f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                            discover = true; // perform active scan implies request discovery
1899f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                        }
1900f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                        if ((callback.mFlags & CALLBACK_FLAG_REQUEST_DISCOVERY) != 0) {
1901fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                            if (!mLowRam) {
1902fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                                discover = true;
1903fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                            }
1904fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                        }
1905fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                        if ((callback.mFlags & CALLBACK_FLAG_FORCE_DISCOVERY) != 0) {
1906f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                            discover = true;
19079fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                        }
190811417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
190911417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
191011417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
1911f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            MediaRouteSelector selector = discover ? builder.build() : MediaRouteSelector.EMPTY;
191211417b1cfde8f1749905f2d735623af9214148afJeff Brown
191311417b1cfde8f1749905f2d735623af9214148afJeff Brown            // Create a new discovery request.
191411417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (mDiscoveryRequest != null
191511417b1cfde8f1749905f2d735623af9214148afJeff Brown                    && mDiscoveryRequest.getSelector().equals(selector)
191611417b1cfde8f1749905f2d735623af9214148afJeff Brown                    && mDiscoveryRequest.isActiveScan() == activeScan) {
191711417b1cfde8f1749905f2d735623af9214148afJeff Brown                return; // no change
191811417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
191911417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (selector.isEmpty() && !activeScan) {
192011417b1cfde8f1749905f2d735623af9214148afJeff Brown                // Discovery is not needed.
192111417b1cfde8f1749905f2d735623af9214148afJeff Brown                if (mDiscoveryRequest == null) {
192211417b1cfde8f1749905f2d735623af9214148afJeff Brown                    return; // no change
192311417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
192411417b1cfde8f1749905f2d735623af9214148afJeff Brown                mDiscoveryRequest = null;
192511417b1cfde8f1749905f2d735623af9214148afJeff Brown            } else {
192611417b1cfde8f1749905f2d735623af9214148afJeff Brown                // Discovery is needed.
192711417b1cfde8f1749905f2d735623af9214148afJeff Brown                mDiscoveryRequest = new MediaRouteDiscoveryRequest(selector, activeScan);
192811417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
192911417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (DEBUG) {
193011417b1cfde8f1749905f2d735623af9214148afJeff Brown                Log.d(TAG, "Updated discovery request: " + mDiscoveryRequest);
193111417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
1932fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown            if (discover && !activeScan && mLowRam) {
1933fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                Log.i(TAG, "Forcing passive route discovery on a low-RAM device, "
1934fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                        + "system performance may be affected.  Please consider using "
1935fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                        + "CALLBACK_FLAG_REQUEST_DISCOVERY instead of "
1936fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown                        + "CALLBACK_FLAG_FORCE_DISCOVERY.");
1937fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown            }
193811417b1cfde8f1749905f2d735623af9214148afJeff Brown
193911417b1cfde8f1749905f2d735623af9214148afJeff Brown            // Notify providers.
194011417b1cfde8f1749905f2d735623af9214148afJeff Brown            final int providerCount = mProviders.size();
194111417b1cfde8f1749905f2d735623af9214148afJeff Brown            for (int i = 0; i < providerCount; i++) {
194211417b1cfde8f1749905f2d735623af9214148afJeff Brown                mProviders.get(i).mProviderInstance.setDiscoveryRequest(mDiscoveryRequest);
194328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            }
194428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
194528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
19463efa63d3b896244713e84acbb5945562dce41d77Jeff Brown        @Override
1947fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void addProvider(MediaRouteProvider providerInstance) {
1948fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int index = findProviderInfo(providerInstance);
1949c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (index < 0) {
1950c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // 1. Add the provider to the list.
1951fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ProviderInfo provider = new ProviderInfo(providerInstance);
1952fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mProviders.add(provider);
195311417b1cfde8f1749905f2d735623af9214148afJeff Brown                if (DEBUG) {
195411417b1cfde8f1749905f2d735623af9214148afJeff Brown                    Log.d(TAG, "Provider added: " + provider);
195511417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
1956fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_ADDED, provider);
1957c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // 2. Create the provider's contents.
1958fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                updateProviderContents(provider, providerInstance.getDescriptor());
1959c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // 3. Register the provider callback.
196011417b1cfde8f1749905f2d735623af9214148afJeff Brown                providerInstance.setCallback(mProviderCallback);
196111417b1cfde8f1749905f2d735623af9214148afJeff Brown                // 4. Set the discovery request.
196211417b1cfde8f1749905f2d735623af9214148afJeff Brown                providerInstance.setDiscoveryRequest(mDiscoveryRequest);
1963c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1964c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1965c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
19663efa63d3b896244713e84acbb5945562dce41d77Jeff Brown        @Override
1967fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void removeProvider(MediaRouteProvider providerInstance) {
1968fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int index = findProviderInfo(providerInstance);
1969c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (index >= 0) {
197011417b1cfde8f1749905f2d735623af9214148afJeff Brown                // 1. Unregister the provider callback.
197111417b1cfde8f1749905f2d735623af9214148afJeff Brown                providerInstance.setCallback(null);
197211417b1cfde8f1749905f2d735623af9214148afJeff Brown                // 2. Clear the discovery request.
197311417b1cfde8f1749905f2d735623af9214148afJeff Brown                providerInstance.setDiscoveryRequest(null);
197428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown                // 3. Delete the provider's contents.
1975fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ProviderInfo provider = mProviders.get(index);
1976fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                updateProviderContents(provider, null);
197728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown                // 4. Remove the provider from the list.
197811417b1cfde8f1749905f2d735623af9214148afJeff Brown                if (DEBUG) {
197911417b1cfde8f1749905f2d735623af9214148afJeff Brown                    Log.d(TAG, "Provider removed: " + provider);
198011417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
1981fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_REMOVED, provider);
1982fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mProviders.remove(index);
1983c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1984c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1985c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1986fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private void updateProviderDescriptor(MediaRouteProvider providerInstance,
198711417b1cfde8f1749905f2d735623af9214148afJeff Brown                MediaRouteProviderDescriptor descriptor) {
1988fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int index = findProviderInfo(providerInstance);
1989c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (index >= 0) {
1990fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                // Update the provider's contents.
1991fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ProviderInfo provider = mProviders.get(index);
1992fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                updateProviderContents(provider, descriptor);
1993c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1994c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1995c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1996fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int findProviderInfo(MediaRouteProvider providerInstance) {
1997fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            final int count = mProviders.size();
1998c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            for (int i = 0; i < count; i++) {
1999fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mProviders.get(i).mProviderInstance == providerInstance) {
2000c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    return i;
2001c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
2002c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2003c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return -1;
2004c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
2005c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2006fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private void updateProviderContents(ProviderInfo provider,
200711417b1cfde8f1749905f2d735623af9214148afJeff Brown                MediaRouteProviderDescriptor providerDescriptor) {
2008fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (provider.updateDescriptor(providerDescriptor)) {
2009c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // Update all existing routes and reorder them to match
2010c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // the order of their descriptors.
2011c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                int targetIndex = 0;
2012567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                boolean selectedRouteDescriptorChanged = false;
2013c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (providerDescriptor != null) {
2014fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (providerDescriptor.isValid()) {
201511417b1cfde8f1749905f2d735623af9214148afJeff Brown                        final List<MediaRouteDescriptor> routeDescriptors =
201611417b1cfde8f1749905f2d735623af9214148afJeff Brown                                providerDescriptor.getRoutes();
201711417b1cfde8f1749905f2d735623af9214148afJeff Brown                        final int routeCount = routeDescriptors.size();
2018e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        // Updating route group's contents requires all member routes' information.
2019e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        // Add the groups to the lists and update them later.
2020e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        List<Pair<RouteInfo, MediaRouteDescriptor>> addedGroups = new ArrayList<>();
2021e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        List<Pair<RouteInfo, MediaRouteDescriptor>> updatedGroups =
2022e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                new ArrayList<>();
202311417b1cfde8f1749905f2d735623af9214148afJeff Brown                        for (int i = 0; i < routeCount; i++) {
202411417b1cfde8f1749905f2d735623af9214148afJeff Brown                            final MediaRouteDescriptor routeDescriptor = routeDescriptors.get(i);
2025c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            final String id = routeDescriptor.getId();
2026fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            final int sourceIndex = provider.findRouteByDescriptorId(id);
2027c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            if (sourceIndex < 0) {
2028c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                // 1. Add the route to the list.
2029cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                                String uniqueId = assignRouteUniqueId(provider, id);
2030e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                boolean isGroup = routeDescriptor.getChildIds() != null;
2031e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                RouteInfo route = isGroup ? new RouteGroup(provider, id, uniqueId) :
2032e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                        new RouteInfo(provider, id, uniqueId);
2033fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                provider.mRoutes.add(targetIndex++, route);
2034c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                mRoutes.add(route);
2035c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                // 2. Create the route's contents.
2036e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                if (isGroup) {
2037e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                    addedGroups.add(new Pair(route, routeDescriptor));
2038e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                } else {
2039e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                    route.maybeUpdateDescriptor(routeDescriptor);
2040e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                    // 3. Notify clients about addition.
2041e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                    if (DEBUG) {
2042e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                        Log.d(TAG, "Route added: " + route);
2043e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                    }
2044e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route);
204511417b1cfde8f1749905f2d735623af9214148afJeff Brown                                }
2046e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo
2047fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            } else if (sourceIndex < targetIndex) {
2048fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                Log.w(TAG, "Ignoring route descriptor with duplicate id: "
2049fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                        + routeDescriptor);
2050c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            } else {
2051c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                // 1. Reorder the route within the list.
2052fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                RouteInfo route = provider.mRoutes.get(sourceIndex);
2053fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                Collections.swap(provider.mRoutes,
2054c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                        sourceIndex, targetIndex++);
2055c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                // 2. Update the route's contents.
2056e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                if (route instanceof RouteGroup) {
2057e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                    updatedGroups.add(new Pair(route, routeDescriptor));
2058e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                } else {
2059e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                    // 3. Notify clients about changes.
2060e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                    if (updateRouteDescriptorAndNotify(route, routeDescriptor)
2061e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                            != 0) {
2062e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                        if (route == mSelectedRoute) {
2063e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                            selectedRouteDescriptorChanged = true;
2064567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                        }
206511417b1cfde8f1749905f2d735623af9214148afJeff Brown                                    }
2066c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                }
2067c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            }
2068c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        }
2069e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        // Update the new and/or existing groups.
2070e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        for (Pair<RouteInfo, MediaRouteDescriptor> pair : addedGroups) {
2071e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            RouteInfo route = pair.first;
2072e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            route.maybeUpdateDescriptor(pair.second);
2073e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            if (DEBUG) {
2074e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                Log.d(TAG, "Route added: " + route);
2075e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            }
2076e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route);
2077e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        }
2078e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        for (Pair<RouteInfo, MediaRouteDescriptor> pair : updatedGroups) {
2079e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            RouteInfo route = pair.first;
2080e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            if (updateRouteDescriptorAndNotify(route, pair.second) != 0) {
2081e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                if (route == mSelectedRoute) {
2082e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                    selectedRouteDescriptorChanged = true;
2083e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                }
2084e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            }
2085e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        }
2086fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    } else {
2087fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        Log.w(TAG, "Ignoring invalid provider descriptor: " + providerDescriptor);
2088c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
2089c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
2090c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2091c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // Dispose all remaining routes that do not have matching descriptors.
2092fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) {
2093fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    // 1. Delete the route's contents.
2094fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    RouteInfo route = provider.mRoutes.get(i);
2095e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    route.maybeUpdateDescriptor(null);
2096fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    // 2. Remove the route from the list.
209711417b1cfde8f1749905f2d735623af9214148afJeff Brown                    mRoutes.remove(route);
209835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                }
209935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang
2100567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                // Update the selected route if needed.
2101567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                updateSelectedRouteIfNeeded(selectedRouteDescriptorChanged);
210235af7c925d660508fe0892f38eec99a3845630e4Chong Zhang
210335af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                // Now notify clients about routes that were removed.
210435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                // We do this after updating the selected route to ensure
210535af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                // that the framework media router observes the new route
210635af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                // selection before the removal since removing the currently
210735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                // selected route may have side-effects.
210835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) {
210935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                    RouteInfo route = provider.mRoutes.remove(i);
211011417b1cfde8f1749905f2d735623af9214148afJeff Brown                    if (DEBUG) {
211111417b1cfde8f1749905f2d735623af9214148afJeff Brown                        Log.d(TAG, "Route removed: " + route);
211211417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
2113fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_REMOVED, route);
2114c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
2115fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
211611417b1cfde8f1749905f2d735623af9214148afJeff Brown                // Notify provider changed.
211711417b1cfde8f1749905f2d735623af9214148afJeff Brown                if (DEBUG) {
211811417b1cfde8f1749905f2d735623af9214148afJeff Brown                    Log.d(TAG, "Provider changed: " + provider);
211911417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
212011417b1cfde8f1749905f2d735623af9214148afJeff Brown                mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_CHANGED, provider);
2121c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2122c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
2123c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2124e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        private int updateRouteDescriptorAndNotify(RouteInfo route,
2125e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                MediaRouteDescriptor routeDescriptor) {
2126e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            int changes = route.maybeUpdateDescriptor(routeDescriptor);
2127e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            if (changes != 0) {
2128e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if ((changes & RouteInfo.CHANGE_GENERAL) != 0) {
2129e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    if (DEBUG) {
2130e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        Log.d(TAG, "Route changed: " + route);
2131e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    }
2132e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mCallbackHandler.post(
2133e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            CallbackHandler.MSG_ROUTE_CHANGED, route);
2134e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
2135e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if ((changes & RouteInfo.CHANGE_VOLUME) != 0) {
2136e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    if (DEBUG) {
2137e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        Log.d(TAG, "Route volume changed: " + route);
2138e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    }
2139e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mCallbackHandler.post(
2140e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            CallbackHandler.MSG_ROUTE_VOLUME_CHANGED, route);
2141e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
2142e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                if ((changes & RouteInfo.CHANGE_PRESENTATION_DISPLAY) != 0) {
2143e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    if (DEBUG) {
2144e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                        Log.d(TAG, "Route presentation display changed: "
2145e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                                + route);
2146e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    }
2147e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                    mCallbackHandler.post(CallbackHandler.
2148e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                            MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED, route);
2149e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo                }
2150e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            }
2151e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo            return changes;
2152e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo        }
2153e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo
2154cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        private String assignRouteUniqueId(ProviderInfo provider, String routeDescriptorId) {
2155cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            // Although route descriptor ids are unique within a provider, it's
2156cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            // possible for there to be two providers with the same package name.
2157cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            // Therefore we must dedupe the composite id.
2158adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown            String uniqueId = provider.getComponentName().flattenToShortString()
2159adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown                    + ":" + routeDescriptorId;
2160cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            if (findRouteByUniqueId(uniqueId) < 0) {
2161cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                return uniqueId;
2162cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            }
2163cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            for (int i = 2; ; i++) {
2164cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                String newUniqueId = String.format(Locale.US, "%s_%d", uniqueId, i);
2165cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                if (findRouteByUniqueId(newUniqueId) < 0) {
2166cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                    return newUniqueId;
2167cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                }
2168cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            }
2169cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        }
2170cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown
2171cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        private int findRouteByUniqueId(String uniqueId) {
2172cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            final int count = mRoutes.size();
2173cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            for (int i = 0; i < count; i++) {
2174cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                if (mRoutes.get(i).mUniqueId.equals(uniqueId)) {
2175cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                    return i;
2176cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                }
2177cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            }
2178cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            return -1;
2179cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        }
2180cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown
2181567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        private void updateSelectedRouteIfNeeded(boolean selectedRouteDescriptorChanged) {
2182567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            // Update default route.
2183567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            if (mDefaultRoute != null && !isRouteSelectable(mDefaultRoute)) {
218435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                Log.i(TAG, "Clearing the default route because it "
2185567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                        + "is no longer selectable: " + mDefaultRoute);
2186c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mDefaultRoute = null;
2187c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2188c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mDefaultRoute == null && !mRoutes.isEmpty()) {
2189c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                for (RouteInfo route : mRoutes) {
2190c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    if (isSystemDefaultRoute(route) && isRouteSelectable(route)) {
2191c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        mDefaultRoute = route;
2192567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                        Log.i(TAG, "Found default route: " + mDefaultRoute);
2193c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
2194c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
2195c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
2196c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2197567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2198567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            // Update selected route.
2199567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            if (mSelectedRoute != null && !isRouteSelectable(mSelectedRoute)) {
2200567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                Log.i(TAG, "Unselecting the current route because it "
2201567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                        + "is no longer selectable: " + mSelectedRoute);
220294be6100218126ce6a08bf1f56209578500b361fRoboErik                setSelectedRouteInternal(null,
220394be6100218126ce6a08bf1f56209578500b361fRoboErik                        MediaRouter.UNSELECT_REASON_UNKNOWN);
2204567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
2205c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mSelectedRoute == null) {
2206567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                // Choose a new route.
2207567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                // This will have the side-effect of updating the playback info when
2208567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                // the new route is selected.
220994be6100218126ce6a08bf1f56209578500b361fRoboErik                setSelectedRouteInternal(chooseFallbackRoute(),
221094be6100218126ce6a08bf1f56209578500b361fRoboErik                        MediaRouter.UNSELECT_REASON_UNKNOWN);
2211567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            } else if (selectedRouteDescriptorChanged) {
2212567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                // Update the playback info because the properties of the route have changed.
2213567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                updatePlaybackInfoFromSelectedRoute();
2214c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2215c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
2216c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
221735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang        private RouteInfo chooseFallbackRoute() {
221835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            // When the current route is removed or no longer selectable,
221935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            // we want to revert to a live audio route if there is
222035af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            // one (usually Bluetooth A2DP).  Failing that, use
222135af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            // the default route.
222235af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            for (RouteInfo route : mRoutes) {
222335af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                if (route != mDefaultRoute
222435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                        && isSystemLiveAudioOnlyRoute(route)
222535af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                        && isRouteSelectable(route)) {
222635af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                    return route;
222735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                }
222835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            }
222935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            return mDefaultRoute;
223035af7c925d660508fe0892f38eec99a3845630e4Chong Zhang        }
223135af7c925d660508fe0892f38eec99a3845630e4Chong Zhang
223235af7c925d660508fe0892f38eec99a3845630e4Chong Zhang        private boolean isSystemLiveAudioOnlyRoute(RouteInfo route) {
223335af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            return route.getProviderInstance() == mSystemProvider
223435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                    && route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
223535af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                    && !route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO);
223635af7c925d660508fe0892f38eec99a3845630e4Chong Zhang        }
223735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang
2238c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private boolean isRouteSelectable(RouteInfo route) {
2239c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            // This tests whether the route is still valid and enabled.
2240c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            // The route descriptor field is set to null when the route is removed.
2241c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return route.mDescriptor != null && route.mEnabled;
2242c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
2243c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2244c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private boolean isSystemDefaultRoute(RouteInfo route) {
2245fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return route.getProviderInstance() == mSystemProvider
2246c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    && route.mDescriptorId.equals(
2247c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            SystemMediaRouteProvider.DEFAULT_ROUTE_ID);
2248c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
2249c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
225094be6100218126ce6a08bf1f56209578500b361fRoboErik        private void setSelectedRouteInternal(RouteInfo route, int unselectReason) {
2251c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mSelectedRoute != route) {
2252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (mSelectedRoute != null) {
225311417b1cfde8f1749905f2d735623af9214148afJeff Brown                    if (DEBUG) {
225494be6100218126ce6a08bf1f56209578500b361fRoboErik                        Log.d(TAG, "Route unselected: " + mSelectedRoute + " reason: "
225594be6100218126ce6a08bf1f56209578500b361fRoboErik                                + unselectReason);
225611417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
2257c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_UNSELECTED, mSelectedRoute);
2258c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    if (mSelectedRouteController != null) {
225994be6100218126ce6a08bf1f56209578500b361fRoboErik                        mSelectedRouteController.onUnselect(unselectReason);
2260129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                        mSelectedRouteController.onRelease();
2261c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        mSelectedRouteController = null;
2262c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
2263c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
2264c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2265c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mSelectedRoute = route;
2266c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2267c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (mSelectedRoute != null) {
2268fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mSelectedRouteController = route.getProviderInstance().onCreateRouteController(
2269c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            route.mDescriptorId);
2270c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    if (mSelectedRouteController != null) {
2271129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                        mSelectedRouteController.onSelect();
2272c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
227311417b1cfde8f1749905f2d735623af9214148afJeff Brown                    if (DEBUG) {
227411417b1cfde8f1749905f2d735623af9214148afJeff Brown                        Log.d(TAG, "Route selected: " + mSelectedRoute);
227511417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
2276c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_SELECTED, mSelectedRoute);
2277c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
2278567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2279567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                updatePlaybackInfoFromSelectedRoute();
2280c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2281c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
2282c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2283c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
2284c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public RouteInfo getSystemRouteByDescriptorId(String id) {
2285fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int providerIndex = findProviderInfo(mSystemProvider);
2286c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (providerIndex >= 0) {
2287fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ProviderInfo provider = mProviders.get(providerIndex);
2288fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                int routeIndex = provider.findRouteByDescriptorId(id);
2289c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (routeIndex >= 0) {
2290fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return provider.mRoutes.get(routeIndex);
2291c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
2292c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2293c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return null;
2294c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
2295c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2296567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        public void addRemoteControlClient(Object rcc) {
2297567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            int index = findRemoteControlClientRecord(rcc);
2298567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            if (index < 0) {
2299567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                RemoteControlClientRecord record = new RemoteControlClientRecord(rcc);
2300567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mRemoteControlClients.add(record);
2301567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
2302567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
2303567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2304567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        public void removeRemoteControlClient(Object rcc) {
2305567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            int index = findRemoteControlClientRecord(rcc);
2306567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            if (index >= 0) {
2307567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                RemoteControlClientRecord record = mRemoteControlClients.remove(index);
2308567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                record.disconnect();
2309567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
2310567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
2311567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2312bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik        public void setMediaSession(Object session) {
2313bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            if (mMediaSession != null) {
2314bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                mMediaSession.clearVolumeHandling();
2315bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            }
2316bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            if (session == null) {
2317bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                mMediaSession = null;
2318bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            } else {
2319bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                mMediaSession = new MediaSessionRecord(session);
2320bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                updatePlaybackInfoFromSelectedRoute();
2321bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            }
2322bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik        }
2323bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik
23245c9469e010106467791b47b0fa83efda84491a21RoboErik        public void setMediaSessionCompat(final MediaSessionCompat session) {
2325e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            mCompatSession = session;
23265c9469e010106467791b47b0fa83efda84491a21RoboErik            if (android.os.Build.VERSION.SDK_INT >= 21) {
2327d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake                setMediaSession(session != null ? session.getMediaSession() : null);
23285c9469e010106467791b47b0fa83efda84491a21RoboErik            } else if (android.os.Build.VERSION.SDK_INT >= 14) {
23295c9469e010106467791b47b0fa83efda84491a21RoboErik                if (mRccMediaSession != null) {
23305c9469e010106467791b47b0fa83efda84491a21RoboErik                    removeRemoteControlClient(mRccMediaSession.getRemoteControlClient());
23315c9469e010106467791b47b0fa83efda84491a21RoboErik                    mRccMediaSession.removeOnActiveChangeListener(mSessionActiveListener);
23325c9469e010106467791b47b0fa83efda84491a21RoboErik                }
23335c9469e010106467791b47b0fa83efda84491a21RoboErik                mRccMediaSession = session;
2334d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake                if (session != null) {
2335d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake                    session.addOnActiveChangeListener(mSessionActiveListener);
2336d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake                    if (session.isActive()) {
2337d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake                        addRemoteControlClient(session.getRemoteControlClient());
2338d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake                    }
23395c9469e010106467791b47b0fa83efda84491a21RoboErik                }
23405c9469e010106467791b47b0fa83efda84491a21RoboErik            }
23415c9469e010106467791b47b0fa83efda84491a21RoboErik        }
23425c9469e010106467791b47b0fa83efda84491a21RoboErik
234394be6100218126ce6a08bf1f56209578500b361fRoboErik        public MediaSessionCompat.Token getMediaSessionToken() {
234494be6100218126ce6a08bf1f56209578500b361fRoboErik            if (mMediaSession != null) {
234594be6100218126ce6a08bf1f56209578500b361fRoboErik                return mMediaSession.getToken();
2346e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } else if (mCompatSession != null) {
2347e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                return mCompatSession.getSessionToken();
234894be6100218126ce6a08bf1f56209578500b361fRoboErik            }
234994be6100218126ce6a08bf1f56209578500b361fRoboErik            return null;
235094be6100218126ce6a08bf1f56209578500b361fRoboErik        }
235194be6100218126ce6a08bf1f56209578500b361fRoboErik
2352567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        private int findRemoteControlClientRecord(Object rcc) {
2353567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            final int count = mRemoteControlClients.size();
2354567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            for (int i = 0; i < count; i++) {
2355567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                RemoteControlClientRecord record = mRemoteControlClients.get(i);
2356567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                if (record.getRemoteControlClient() == rcc) {
2357567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                    return i;
2358567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                }
2359567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
2360567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            return -1;
2361567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
2362567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2363567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        private void updatePlaybackInfoFromSelectedRoute() {
2364567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            if (mSelectedRoute != null) {
2365567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mPlaybackInfo.volume = mSelectedRoute.getVolume();
2366567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mPlaybackInfo.volumeMax = mSelectedRoute.getVolumeMax();
2367567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mPlaybackInfo.volumeHandling = mSelectedRoute.getVolumeHandling();
2368567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mPlaybackInfo.playbackStream = mSelectedRoute.getPlaybackStream();
2369567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mPlaybackInfo.playbackType = mSelectedRoute.getPlaybackType();
2370567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2371567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                final int count = mRemoteControlClients.size();
2372567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                for (int i = 0; i < count; i++) {
2373567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                    RemoteControlClientRecord record = mRemoteControlClients.get(i);
2374567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                    record.updatePlaybackInfo();
2375567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                }
2376bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                if (mMediaSession != null) {
2377f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                    if (mSelectedRoute == getDefaultRoute()) {
2378f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                        // Local route
2379f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                        mMediaSession.clearVolumeHandling();
2380f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                    } else {
2381f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake                        @VolumeProviderCompat.ControlType int controlType =
2382f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake                                VolumeProviderCompat.VOLUME_CONTROL_FIXED;
2383f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                        if (mPlaybackInfo.volumeHandling
2384f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                                == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE) {
2385f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                            controlType = VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE;
2386f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                        }
2387f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                        mMediaSession.configureVolume(controlType, mPlaybackInfo.volumeMax,
2388f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                                mPlaybackInfo.volume);
2389bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                    }
2390f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                }
2391f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik            } else {
2392f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                if (mMediaSession != null) {
2393f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik                    mMediaSession.clearVolumeHandling();
2394bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                }
2395567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
2396567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
2397567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2398c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final class ProviderCallback extends MediaRouteProvider.Callback {
2399c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            @Override
2400c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public void onDescriptorChanged(MediaRouteProvider provider,
240111417b1cfde8f1749905f2d735623af9214148afJeff Brown                    MediaRouteProviderDescriptor descriptor) {
2402c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                updateProviderDescriptor(provider, descriptor);
2403c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2404c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
2405c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2406bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik        private final class MediaSessionRecord {
2407bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            private final MediaSessionCompat mMsCompat;
2408bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik
2409f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake            private @VolumeProviderCompat.ControlType int mControlType;
2410bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            private int mMaxVolume;
2411bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            private VolumeProviderCompat mVpCompat;
2412bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik
2413bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            public MediaSessionRecord(Object mediaSession) {
24145c9469e010106467791b47b0fa83efda84491a21RoboErik                mMsCompat = MediaSessionCompat.obtain(mApplicationContext, mediaSession);
2415bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            }
2416bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik
2417f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake            public void configureVolume(@VolumeProviderCompat.ControlType int controlType,
2418f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake                    int max, int current) {
2419bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                if (mVpCompat != null && controlType == mControlType && max == mMaxVolume) {
2420bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                    // If we haven't changed control type or max just set the
2421bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                    // new current volume
2422bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                    mVpCompat.setCurrentVolume(current);
2423bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                } else {
2424bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                    // Otherwise create a new provider and update
2425bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                    mVpCompat = new VolumeProviderCompat(controlType, max, current) {
2426bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                        @Override
2427a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                        public void onSetVolumeTo(final int volume) {
2428a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                            mCallbackHandler.post(new Runnable() {
2429a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                @Override
2430a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                public void run() {
2431a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                    if (mSelectedRoute != null) {
2432a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                        mSelectedRoute.requestSetVolume(volume);
2433a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                    }
2434a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                }
2435a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                            });
2436bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                        }
2437bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik
2438bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                        @Override
2439a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                        public void onAdjustVolume(final int direction) {
2440a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                            mCallbackHandler.post(new Runnable() {
2441a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                @Override
2442a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                public void run() {
2443a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                    if (mSelectedRoute != null) {
2444a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                        mSelectedRoute.requestUpdateVolume(direction);
2445a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                    }
2446a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                                }
2447a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik                            });
2448bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                        }
2449bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                    };
2450bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                    mMsCompat.setPlaybackToRemote(mVpCompat);
2451bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                }
2452bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            }
2453bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik
2454bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            public void clearVolumeHandling() {
2455bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                mMsCompat.setPlaybackToLocal(mPlaybackInfo.playbackStream);
2456bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik                mVpCompat = null;
2457bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik            }
2458bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik
245994be6100218126ce6a08bf1f56209578500b361fRoboErik            public MediaSessionCompat.Token getToken() {
246094be6100218126ce6a08bf1f56209578500b361fRoboErik                return mMsCompat.getSessionToken();
246194be6100218126ce6a08bf1f56209578500b361fRoboErik            }
246294be6100218126ce6a08bf1f56209578500b361fRoboErik
2463bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik        }
2464bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik
2465567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        private final class RemoteControlClientRecord
2466567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                implements RemoteControlClientCompat.VolumeCallback {
2467567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            private final RemoteControlClientCompat mRccCompat;
2468567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            private boolean mDisconnected;
2469567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2470567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            public RemoteControlClientRecord(Object rcc) {
2471567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mRccCompat = RemoteControlClientCompat.obtain(mApplicationContext, rcc);
2472567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mRccCompat.setVolumeCallback(this);
2473567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                updatePlaybackInfo();
2474567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
2475567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2476567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            public Object getRemoteControlClient() {
2477567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                return mRccCompat.getRemoteControlClient();
2478567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
2479567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2480567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            public void disconnect() {
2481567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mDisconnected = true;
2482567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mRccCompat.setVolumeCallback(null);
2483567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
2484567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2485567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            public void updatePlaybackInfo() {
2486567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mRccCompat.setPlaybackInfo(mPlaybackInfo);
2487567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
2488567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2489567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            @Override
2490567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            public void onVolumeSetRequest(int volume) {
2491567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                if (!mDisconnected && mSelectedRoute != null) {
2492567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                    mSelectedRoute.requestSetVolume(volume);
2493567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                }
2494567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
2495567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2496567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            @Override
2497567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            public void onVolumeUpdateRequest(int direction) {
2498567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                if (!mDisconnected && mSelectedRoute != null) {
2499567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                    mSelectedRoute.requestUpdateVolume(direction);
2500567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                }
2501567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
2502567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
2503567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
2504c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final class CallbackHandler extends Handler {
25059fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            private final ArrayList<CallbackRecord> mTempCallbackRecords =
25069fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    new ArrayList<CallbackRecord>();
2507c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
250811417b1cfde8f1749905f2d735623af9214148afJeff Brown            private static final int MSG_TYPE_MASK = 0xff00;
250911417b1cfde8f1749905f2d735623af9214148afJeff Brown            private static final int MSG_TYPE_ROUTE = 0x0100;
251011417b1cfde8f1749905f2d735623af9214148afJeff Brown            private static final int MSG_TYPE_PROVIDER = 0x0200;
251111417b1cfde8f1749905f2d735623af9214148afJeff Brown
251211417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_ADDED = MSG_TYPE_ROUTE | 1;
251311417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_REMOVED = MSG_TYPE_ROUTE | 2;
251411417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_CHANGED = MSG_TYPE_ROUTE | 3;
251511417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_VOLUME_CHANGED = MSG_TYPE_ROUTE | 4;
251611417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED = MSG_TYPE_ROUTE | 5;
251711417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_SELECTED = MSG_TYPE_ROUTE | 6;
251811417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_UNSELECTED = MSG_TYPE_ROUTE | 7;
251911417b1cfde8f1749905f2d735623af9214148afJeff Brown
252011417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_PROVIDER_ADDED = MSG_TYPE_PROVIDER | 1;
252111417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_PROVIDER_REMOVED = MSG_TYPE_PROVIDER | 2;
252211417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_PROVIDER_CHANGED = MSG_TYPE_PROVIDER | 3;
2523c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2524fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            public void post(int msg, Object obj) {
2525fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                obtainMessage(msg, obj).sendToTarget();
2526c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2527c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2528c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            @Override
2529c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public void handleMessage(Message msg) {
2530c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                final int what = msg.what;
2531fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                final Object obj = msg.obj;
2532c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2533c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // Synchronize state with the system media router.
2534fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                syncWithSystemProvider(what, obj);
2535c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2536c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // Invoke all registered callbacks.
25379fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                // Build a list of callbacks before invoking them in case callbacks
25389fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                // are added or removed during dispatch.
2539c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                try {
25409fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    for (int i = mRouters.size(); --i >= 0; ) {
25419fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                        MediaRouter router = mRouters.get(i).get();
25429fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                        if (router == null) {
25439fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                            mRouters.remove(i);
25449fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                        } else {
25459fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                            mTempCallbackRecords.addAll(router.mCallbackRecords);
2546c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        }
2547c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
25489fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown
25499fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    final int callbackCount = mTempCallbackRecords.size();
25509fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    for (int i = 0; i < callbackCount; i++) {
25519fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                        invokeCallback(mTempCallbackRecords.get(i), what, obj);
25529fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    }
2553c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                } finally {
25549fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    mTempCallbackRecords.clear();
2555c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
2556c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2557c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2558fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            private void syncWithSystemProvider(int what, Object obj) {
2559c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                switch (what) {
2560c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_ADDED:
2561fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mSystemProvider.onSyncRouteAdded((RouteInfo)obj);
2562c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
2563c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_REMOVED:
2564fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mSystemProvider.onSyncRouteRemoved((RouteInfo)obj);
2565c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
2566c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_CHANGED:
2567fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mSystemProvider.onSyncRouteChanged((RouteInfo)obj);
2568c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
2569c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_SELECTED:
2570fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mSystemProvider.onSyncRouteSelected((RouteInfo)obj);
2571c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
2572c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
2573c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2574c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
25759fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            private void invokeCallback(CallbackRecord record, int what, Object obj) {
25769fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                final MediaRouter router = record.mRouter;
257711417b1cfde8f1749905f2d735623af9214148afJeff Brown                final MediaRouter.Callback callback = record.mCallback;
257811417b1cfde8f1749905f2d735623af9214148afJeff Brown                switch (what & MSG_TYPE_MASK) {
257911417b1cfde8f1749905f2d735623af9214148afJeff Brown                    case MSG_TYPE_ROUTE: {
258011417b1cfde8f1749905f2d735623af9214148afJeff Brown                        final RouteInfo route = (RouteInfo)obj;
258111417b1cfde8f1749905f2d735623af9214148afJeff Brown                        if (!record.filterRouteEvent(route)) {
258211417b1cfde8f1749905f2d735623af9214148afJeff Brown                            break;
258311417b1cfde8f1749905f2d735623af9214148afJeff Brown                        }
258411417b1cfde8f1749905f2d735623af9214148afJeff Brown                        switch (what) {
258511417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_ADDED:
258611417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteAdded(router, route);
258711417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
258811417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_REMOVED:
258911417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteRemoved(router, route);
259011417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
259111417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_CHANGED:
259211417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteChanged(router, route);
259311417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
259411417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_VOLUME_CHANGED:
259511417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteVolumeChanged(router, route);
259611417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
259711417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED:
259811417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRoutePresentationDisplayChanged(router, route);
259911417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
260011417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_SELECTED:
260111417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteSelected(router, route);
260211417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
260311417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_UNSELECTED:
260411417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteUnselected(router, route);
260511417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
260611417b1cfde8f1749905f2d735623af9214148afJeff Brown                        }
2607c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
260811417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
260911417b1cfde8f1749905f2d735623af9214148afJeff Brown                    case MSG_TYPE_PROVIDER: {
261011417b1cfde8f1749905f2d735623af9214148afJeff Brown                        final ProviderInfo provider = (ProviderInfo)obj;
261111417b1cfde8f1749905f2d735623af9214148afJeff Brown                        switch (what) {
261211417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_PROVIDER_ADDED:
261311417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onProviderAdded(router, provider);
261411417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
261511417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_PROVIDER_REMOVED:
261611417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onProviderRemoved(router, provider);
261711417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
261811417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_PROVIDER_CHANGED:
261911417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onProviderChanged(router, provider);
262011417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
262111417b1cfde8f1749905f2d735623af9214148afJeff Brown                        }
262211417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
2623c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
2624c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2625c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
2626c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
2627c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown}
2628