MediaRouter.java revision a97f1edf7624785c41ec0bfec6fd12c2388d9234
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
19c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.ContentResolver;
20c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.Context;
21c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.Intent;
22c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.IntentFilter;
23fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.pm.PackageManager.NameNotFoundException;
24fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.res.Resources;
25c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Bundle;
26c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Handler;
27c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Looper;
28c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Message;
29c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.support.v4.hardware.display.DisplayManagerCompat;
30b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brownimport android.support.v7.media.MediaRouteProvider.ProviderMetadata;
31c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.util.Log;
32c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.view.Display;
33c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
349fcedc160282e6620f409ea46bf6728b35d011ddJeff Brownimport java.lang.ref.WeakReference;
35c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.ArrayList;
36c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.Collections;
37c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.List;
38cb63b6ecac9786891514f241dec71695f09d3efbJeff Brownimport java.util.Locale;
39c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
40c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown/**
41c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter allows applications to control the routing of media channels
42c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * and streams from the current device to external speakers and destination devices.
43c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p>
44c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * A MediaRouter instance is retrieved through {@link #getInstance}.  Applications
45c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * can query the media router about the currently selected route and its capabilities
46c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to determine how to send content to the route's destination.  Applications can
47c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * also {@link RouteInfo#sendControlRequest send control requests} to the route
48c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to ask the route's destination to perform certain remote control functions
49fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * such as playing media.
50c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p>
51c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * See also {@link MediaRouteProvider} for information on how an application
52c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * can publish new media routes to the media router.
53c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p>
54c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The media router API is not thread-safe; all interactions with it must be
55c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * done from the main thread of the process.
56c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p>
57c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */
58c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownpublic final class MediaRouter {
59c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    private static final String TAG = "MediaRouter";
60f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
61c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
62c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    // Maintains global media router state for the process.
63c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    // This field is initialized in MediaRouter.getInstance() before any
64c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    // MediaRouter objects are instantiated so it is guaranteed to be
65c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    // valid whenever any instance method is invoked.
66c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    static GlobalMediaRouter sGlobal;
67c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
68c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    // Context-bound state of the media router.
69c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    final Context mContext;
709fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown    final ArrayList<CallbackRecord> mCallbackRecords = new ArrayList<CallbackRecord>();
7111417b1cfde8f1749905f2d735623af9214148afJeff Brown
7211417b1cfde8f1749905f2d735623af9214148afJeff Brown    /**
7311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Flag for {@link #addCallback}: Actively scan for routes while this callback
7411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * is registered.
7511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * <p>
7611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * When this flag is specified, the media router will actively scan for new
7711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * routes.  Certain routes, such as wifi display routes, may not be discoverable
7811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * except when actively scanning.  This flag is typically used when the route picker
7911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * dialog has been opened by the user to ensure that the route information is
8011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * up to date.
8111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p><p>
8211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Active scanning may consume a significant amount of power and may have intrusive
8311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * effects on wireless connectivity.  Therefore it is important that active scanning
8411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * only be requested when it is actually needed to satisfy a user request to
8511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * discover and select a new route.
86f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * </p><p>
87f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * This flag implies {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} but performing
88f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * active scans is much more expensive than a normal discovery request.
8911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p>
90f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *
91f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * @see #CALLBACK_FLAG_REQUEST_DISCOVERY
9211417b1cfde8f1749905f2d735623af9214148afJeff Brown     */
93f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1 << 0;
9411417b1cfde8f1749905f2d735623af9214148afJeff Brown
9511417b1cfde8f1749905f2d735623af9214148afJeff Brown    /**
9611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Flag for {@link #addCallback}: Do not filter route events.
9711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * <p>
9811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * When this flag is specified, the callback will be invoked for events that affect any
9911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * route event if they do not match the callback's associated media route selector.
10011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p>
10111417b1cfde8f1749905f2d735623af9214148afJeff Brown     */
10211417b1cfde8f1749905f2d735623af9214148afJeff Brown    public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 1 << 1;
10311417b1cfde8f1749905f2d735623af9214148afJeff Brown
10411417b1cfde8f1749905f2d735623af9214148afJeff Brown    /**
105f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * Flag for {@link #addCallback}: Request that route discovery be performed while this
106f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * callback is registered.
107f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * <p>
108f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * When this flag is specified, the media router will try to discover routes.
109f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * Although route discovery is intended to be efficient, checking for new routes may
110f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * result in some network activity and could slowly drain the battery.  Therefore
111f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * applications should only specify {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} when
112f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * they are running in the foreground and would like to provide the user with the
113f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * option of connecting to new routes.
114f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * </p><p>
115f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * Applications should typically add a callback using this flag in the
116f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * {@link android.app.Activity activity's} {@link android.app.Activity#onStart onStart}
117f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * method and remove it in the {@link android.app.Activity#onStop onStop} method.
118f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * The {@link android.support.v7.app.MediaRouteDiscoveryFragment} fragment may
119f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * also be used for this purpose.
120f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * </p>
121f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *
122f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * @see android.support.v7.app.MediaRouteDiscoveryFragment
123f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     */
124f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 1 << 2;
125f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
126f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    /**
12711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Flag for {@link #isRouteAvailable}: Ignore the default route.
12811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * <p>
12911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * This flag is used to determine whether a matching non-default route is available.
13011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * This constraint may be used to decide whether to offer the route chooser dialog
13111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * to the user.  There is no point offering the chooser if there are no
13211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * non-default choices.
13311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p>
13411417b1cfde8f1749905f2d735623af9214148afJeff Brown     */
13511417b1cfde8f1749905f2d735623af9214148afJeff Brown    public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1 << 0;
13611417b1cfde8f1749905f2d735623af9214148afJeff Brown
137c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    MediaRouter(Context context) {
138c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        mContext = context;
139c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
140c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
141c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
1429fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * Gets an instance of the media router service associated with the context.
1439fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * <p>
1449fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * The application is responsible for holding a strong reference to the returned
1459fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * {@link MediaRouter} instance, such as by storing the instance in a field of
1469fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * the {@link android.app.Activity}, to ensure that the media router remains alive
1479fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * as long as the application is using its features.
1489fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * </p><p>
1499fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * In other words, the support library only holds a {@link WeakReference weak reference}
1509fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * to each media router instance.  When there are no remaining strong references to the
1519fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * media router instance, all of its callbacks will be removed and route discovery
1529fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * will no longer be performed on its behalf.
1539fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * </p>
1549fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     *
1559fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * @return The media router instance for the context.  The application must hold
1569fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown     * a strong reference to this object as long as it is in use.
157c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
158c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public static MediaRouter getInstance(Context context) {
159c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (context == null) {
160c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            throw new IllegalArgumentException("context must not be null");
161c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
162c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
163c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
164c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (sGlobal == null) {
165c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            sGlobal = new GlobalMediaRouter(context.getApplicationContext());
166fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            sGlobal.start();
167c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
168c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return sGlobal.getRouter(context);
169c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
170c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
171c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
172fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Gets information about the {@link MediaRouter.RouteInfo routes} currently known to
173fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * this media router.
174c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
175c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public List<RouteInfo> getRoutes() {
176c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
177c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return sGlobal.getRoutes();
178c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
179c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
180c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
181fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Gets information about the {@link MediaRouter.ProviderInfo route providers}
182fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * currently known to this media router.
183fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     */
184fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public List<ProviderInfo> getProviders() {
185fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        checkCallingThread();
186fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        return sGlobal.getProviders();
187fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
188fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
189fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    /**
190c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Gets the default route for playing media content on the system.
191c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
192c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * The system always provides a default route.
193c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
194c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
195c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @return The default route, which is guaranteed to never be null.
196c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
197c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public RouteInfo getDefaultRoute() {
198c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
199c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return sGlobal.getDefaultRoute();
200c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
201c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
202c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
203c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Gets the currently selected route.
204c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
205c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * The application should examine the route's
206fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * {@link RouteInfo#getControlFilters media control intent filters} to assess the
207c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * capabilities of the route before attempting to use it.
208c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
209c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
210c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <h3>Example</h3>
211c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <pre>
212c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * public boolean playMovie() {
213c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     MediaRouter mediaRouter = MediaRouter.getInstance(context);
214c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute();
215c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
216c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     // First try using the remote playback interface, if supported.
217c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
218c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         // The route supports remote playback.
219c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         // Try to send it the Uri of the movie to play.
220c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         Intent intent = new Intent(MediaControlIntent.ACTION_PLAY);
221c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
222c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         intent.setDataAndType("http://example.com/videos/movie.mp4", "video/mp4");
223fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     *         if (route.supportsControlRequest(intent)) {
224fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     *             route.sendControlRequest(intent, null);
225fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     *             return true; // sent the request to play the movie
226c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         }
227c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     }
228c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
229c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     // If remote playback was not possible, then play locally.
230c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     if (route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)) {
231c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         // The route supports live video streaming.
232c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         // Prepare to play content locally in a window or in a presentation.
233c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         return playMovieInWindow();
234c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     }
235c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
236c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     // Neither interface is supported, so we can't play the movie to this route.
237c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     return false;
238c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * }
239c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </pre>
240c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
241c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @return The selected route, which is guaranteed to never be null.
242c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
243fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * @see RouteInfo#getControlFilters
244c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see RouteInfo#supportsControlCategory
245c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see RouteInfo#supportsControlRequest
246c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
247c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public RouteInfo getSelectedRoute() {
248c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
249c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return sGlobal.getSelectedRoute();
250c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
251c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
25328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * Returns the selected route if it matches the specified selector, otherwise
25428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * selects the default route and returns it.
25528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
25628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @param selector The selector to match.
25728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @return The previously selected route if it matched the selector, otherwise the
25828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * newly selected default route which is guaranteed to never be null.
25928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
26011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see MediaRouteSelector
26128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @see RouteInfo#matchesSelector
26228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @see RouteInfo#isDefault
26328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     */
26411417b1cfde8f1749905f2d735623af9214148afJeff Brown    public RouteInfo updateSelectedRoute(MediaRouteSelector selector) {
26528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        if (selector == null) {
26628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            throw new IllegalArgumentException("selector must not be null");
26728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
26828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        checkCallingThread();
26928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
27011417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
27111417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "updateSelectedRoute: " + selector);
27211417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
27328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        RouteInfo route = sGlobal.getSelectedRoute();
27411417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (!route.isDefault() && !route.matchesSelector(selector)) {
27528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            route = sGlobal.getDefaultRoute();
27628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            sGlobal.selectRoute(route);
27728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
27828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        return route;
27928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    }
28028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
28128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    /**
282c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Selects the specified route.
283c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
284c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @param route The route to select.
285c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
286c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public void selectRoute(RouteInfo route) {
287c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (route == null) {
288c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            throw new IllegalArgumentException("route must not be null");
289c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
290c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
291c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
29211417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
29311417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "selectRoute: " + route);
29411417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
295c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        sGlobal.selectRoute(route);
296c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
297c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
298c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
299d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown     * Returns true if there is a route that matches the specified selector.
30011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * <p>
301d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown     * This method returns true if there are any available routes that match the selector
302d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown     * regardless of whether they are enabled or disabled.  If the
303d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown     * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} flag is specified, then
304d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown     * the method will only consider non-default routes.
30511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p>
306c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
30711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param selector The selector to match.
30811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param flags Flags to control the determination of whether a route may be available.
309d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown     * May be zero or {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE}.
31011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @return True if a matching route may be available.
311c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
31211417b1cfde8f1749905f2d735623af9214148afJeff Brown    public boolean isRouteAvailable(MediaRouteSelector selector, int flags) {
31311417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (selector == null) {
31411417b1cfde8f1749905f2d735623af9214148afJeff Brown            throw new IllegalArgumentException("selector must not be null");
315c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
316c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
317c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
31811417b1cfde8f1749905f2d735623af9214148afJeff Brown        return sGlobal.isRouteAvailable(selector, flags);
319c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
320c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
321c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
32211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Registers a callback to discover routes that match the selector and to receive
32311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * events when they change.
32411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * <p>
32511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * This is a convenience method that has the same effect as calling
32611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * {@link #addCallback(MediaRouteSelector, Callback, int)} without flags.
32711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p>
328c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
32911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param selector A route selector that indicates the kinds of routes that the
33011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * callback would like to discover.
33111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param callback The callback to add.
33211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see #removeCallback
333c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
33411417b1cfde8f1749905f2d735623af9214148afJeff Brown    public void addCallback(MediaRouteSelector selector, Callback callback) {
33511417b1cfde8f1749905f2d735623af9214148afJeff Brown        addCallback(selector, callback, 0);
336c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
337c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
338c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
33911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Registers a callback to discover routes that match the selector and to receive
34011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * events when they change.
34128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * <p>
34211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * The selector describes the kinds of routes that the application wants to
34311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * discover.  For example, if the application wants to use
34411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * live audio routes then it should include the
34511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * {@link MediaControlIntent#CATEGORY_LIVE_AUDIO live audio media control intent category}
34611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * in its selector when it adds a callback to the media router.
34711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * The selector may include any number of categories.
34828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * </p><p>
34911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * If the callback has already been registered, then the selector is added to
35011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * the set of selectors being monitored by the callback.
35111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p><p>
35211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * By default, the callback will only be invoked for events that affect routes
35311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * that match the specified selector.  Event filtering may be disabled by specifying
35411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * the {@link #CALLBACK_FLAG_UNFILTERED_EVENTS} flag when the callback is registered.
35528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * </p>
35628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
35728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * <h3>Example</h3>
35828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * <pre>
35911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * public class MyActivity extends Activity {
36011417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     private MediaRouter mRouter;
36111417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     private MediaRouter.Callback mCallback;
36211417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     private MediaRouteSelector mSelector;
36328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
36411417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     protected void onCreate(Bundle savedInstanceState) {
36511417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         super.onCreate(savedInstanceState);
36611417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
36711417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         mRouter = Mediarouter.getInstance(this);
36811417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         mCallback = new MyCallback();
36911417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         mSelector = new MediaRouteSelector.Builder()
37011417b1cfde8f1749905f2d735623af9214148afJeff Brown     *                 .addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
37111417b1cfde8f1749905f2d735623af9214148afJeff Brown     *                 .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
37211417b1cfde8f1749905f2d735623af9214148afJeff Brown     *                 .build();
37328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *     }
37428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
375f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *     // Add the callback on start to tell the media router what kinds of routes
37611417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     // the application is interested in so that it can try to discover suitable ones.
377f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *     public void onStart() {
378f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *         super.onStart();
37911417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
380f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *         mediaRouter.addCallback(mSelector, mCallback,
381f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *                 MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
38211417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
38311417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         MediaRouter.RouteInfo route = mediaRouter.updateSelectedRoute(mSelector);
38411417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         // do something with the route...
38511417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     }
38611417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
387f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *     // Remove the selector on stop to tell the media router that it no longer
38811417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     // needs to invest effort trying to discover routes of these kinds for now.
389f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *     public void onStop() {
390f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     *         super.onStop();
39111417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
39211417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         mediaRouter.removeCallback(mCallback);
39311417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     }
39411417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
39511417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     private final class MyCallback extends MediaRouter.Callback {
39611417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         // Implement callback methods as needed.
39728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *     }
39828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * }
39928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * </pre>
40028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
40111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param selector A route selector that indicates the kinds of routes that the
40211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * callback would like to discover.
40311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param callback The callback to add.
40411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param flags Flags to control the behavior of the callback.
405f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown     * May be zero or a combination of {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and
40611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * {@link #CALLBACK_FLAG_UNFILTERED_EVENTS}.
40711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see #removeCallback
40828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     */
40911417b1cfde8f1749905f2d735623af9214148afJeff Brown    public void addCallback(MediaRouteSelector selector, Callback callback, int flags) {
41028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        if (selector == null) {
41128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            throw new IllegalArgumentException("selector must not be null");
41228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
41311417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (callback == null) {
41411417b1cfde8f1749905f2d735623af9214148afJeff Brown            throw new IllegalArgumentException("callback must not be null");
41511417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
41628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        checkCallingThread();
41728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
41811417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
41911417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "addCallback: selector=" + selector
42011417b1cfde8f1749905f2d735623af9214148afJeff Brown                    + ", callback=" + callback + ", flags=" + Integer.toHexString(flags));
42111417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
42211417b1cfde8f1749905f2d735623af9214148afJeff Brown
42311417b1cfde8f1749905f2d735623af9214148afJeff Brown        CallbackRecord record;
42411417b1cfde8f1749905f2d735623af9214148afJeff Brown        int index = findCallbackRecord(callback);
42511417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (index < 0) {
4269fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            record = new CallbackRecord(this, callback);
42711417b1cfde8f1749905f2d735623af9214148afJeff Brown            mCallbackRecords.add(record);
42811417b1cfde8f1749905f2d735623af9214148afJeff Brown        } else {
42911417b1cfde8f1749905f2d735623af9214148afJeff Brown            record = mCallbackRecords.get(index);
43011417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
43111417b1cfde8f1749905f2d735623af9214148afJeff Brown        boolean updateNeeded = false;
43211417b1cfde8f1749905f2d735623af9214148afJeff Brown        if ((flags & ~record.mFlags) != 0) {
43311417b1cfde8f1749905f2d735623af9214148afJeff Brown            record.mFlags |= flags;
43411417b1cfde8f1749905f2d735623af9214148afJeff Brown            updateNeeded = true;
43511417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
43611417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (!record.mSelector.contains(selector)) {
43711417b1cfde8f1749905f2d735623af9214148afJeff Brown            record.mSelector = new MediaRouteSelector.Builder(record.mSelector)
43811417b1cfde8f1749905f2d735623af9214148afJeff Brown                    .addSelector(selector)
43911417b1cfde8f1749905f2d735623af9214148afJeff Brown                    .build();
44011417b1cfde8f1749905f2d735623af9214148afJeff Brown            updateNeeded = true;
44111417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
44211417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (updateNeeded) {
44311417b1cfde8f1749905f2d735623af9214148afJeff Brown            sGlobal.updateDiscoveryRequest();
44428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
44528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    }
44628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
44728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    /**
44811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Removes the specified callback.  It will no longer receive events about
44911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * changes to media routes.
45028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
45111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param callback The callback to remove.
45211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see #addCallback
45328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     */
45411417b1cfde8f1749905f2d735623af9214148afJeff Brown    public void removeCallback(Callback callback) {
45511417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (callback == null) {
45611417b1cfde8f1749905f2d735623af9214148afJeff Brown            throw new IllegalArgumentException("callback must not be null");
45728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
45828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        checkCallingThread();
45928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
46011417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
46111417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "removeCallback: callback=" + callback);
46211417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
46311417b1cfde8f1749905f2d735623af9214148afJeff Brown
46411417b1cfde8f1749905f2d735623af9214148afJeff Brown        int index = findCallbackRecord(callback);
46511417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (index >= 0) {
46611417b1cfde8f1749905f2d735623af9214148afJeff Brown            mCallbackRecords.remove(index);
46711417b1cfde8f1749905f2d735623af9214148afJeff Brown            sGlobal.updateDiscoveryRequest();
46828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
46928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    }
47028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
47111417b1cfde8f1749905f2d735623af9214148afJeff Brown    private int findCallbackRecord(Callback callback) {
47211417b1cfde8f1749905f2d735623af9214148afJeff Brown        final int count = mCallbackRecords.size();
47311417b1cfde8f1749905f2d735623af9214148afJeff Brown        for (int i = 0; i < count; i++) {
47411417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (mCallbackRecords.get(i).mCallback == callback) {
47511417b1cfde8f1749905f2d735623af9214148afJeff Brown                return i;
47611417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
47711417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
47811417b1cfde8f1749905f2d735623af9214148afJeff Brown        return -1;
47911417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
48011417b1cfde8f1749905f2d735623af9214148afJeff Brown
48128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    /**
4829942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * Registers a media route provider within this application process.
4839942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * <p>
4849942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * The provider will be added to the list of providers that all {@link MediaRouter}
4859942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * instances within this process can use to discover routes.
4869942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * </p>
487c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
488fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * @param providerInstance The media route provider instance to add.
489c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
490c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see MediaRouteProvider
49128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @see #removeCallback
492c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
493fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public void addProvider(MediaRouteProvider providerInstance) {
494fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (providerInstance == null) {
495fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            throw new IllegalArgumentException("providerInstance must not be null");
496c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
497c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
498c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
49911417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
50011417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "addProvider: " + providerInstance);
50111417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
502fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        sGlobal.addProvider(providerInstance);
503c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
504c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
505c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
5069942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * Unregisters a media route provider within this application process.
5079942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * <p>
5089942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * The provider will be removed from the list of providers that all {@link MediaRouter}
5099942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * instances within this process can use to discover routes.
5109942d40d0d952b03b583fe66f434676793697aa2Jeff Brown     * </p>
511c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
512fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * @param providerInstance The media route provider instance to remove.
513c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
514c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see MediaRouteProvider
51528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @see #addCallback
516c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
517fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public void removeProvider(MediaRouteProvider providerInstance) {
518fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (providerInstance == null) {
519fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            throw new IllegalArgumentException("providerInstance must not be null");
520c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
521c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
522c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
52311417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
52411417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "removeProvider: " + providerInstance);
52528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
52611417b1cfde8f1749905f2d735623af9214148afJeff Brown        sGlobal.removeProvider(providerInstance);
52728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    }
52828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
52928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    /**
530567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * Adds a remote control client to enable remote control of the volume
531567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * of the selected route.
532567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * <p>
533567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * The remote control client must have previously been registered with
534567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * the audio manager using the {@link android.media.AudioManager#registerRemoteControlClient
535567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * AudioManager.registerRemoteControlClient} method.
536567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * </p>
537567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     *
538567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * @param remoteControlClient The {@link android.media.RemoteControlClient} to register.
539567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     */
540567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown    public void addRemoteControlClient(Object remoteControlClient) {
541567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        if (remoteControlClient == null) {
542567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            throw new IllegalArgumentException("remoteControlClient must not be null");
543567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
544567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        checkCallingThread();
545567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
546567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        if (DEBUG) {
547567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            Log.d(TAG, "addRemoteControlClient: " + remoteControlClient);
548567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
549567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        sGlobal.addRemoteControlClient(remoteControlClient);
550567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown    }
551567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
552567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown    /**
553567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * Removes a remote control client.
554567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     *
555567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     * @param remoteControlClient The {@link android.media.RemoteControlClient} to register.
556567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown     */
557567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown    public void removeRemoteControlClient(Object remoteControlClient) {
558567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        if (remoteControlClient == null) {
559567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            throw new IllegalArgumentException("remoteControlClient must not be null");
560567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
561567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
562567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        if (DEBUG) {
563567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            Log.d(TAG, "removeRemoteControlClient: " + remoteControlClient);
564567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
565567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        sGlobal.removeRemoteControlClient(remoteControlClient);
566567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown    }
567567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
568567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown    /**
569c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Ensures that calls into the media router are on the correct thread.
570c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * It pays to be a little paranoid when global state invariants are at risk.
571c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
572c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    static void checkCallingThread() {
573c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (Looper.myLooper() != Looper.getMainLooper()) {
574c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            throw new IllegalStateException("The media router service must only be "
575c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + "accessed on the application's main thread.");
576c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
577c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
578c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
579c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    static <T> boolean equal(T a, T b) {
580c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return a == b || (a != null && b != null && a.equals(b));
581c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
582c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
583c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
584c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Provides information about a media route.
585c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
586fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Each media route has a list of {@link MediaControlIntent media control}
587fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * {@link #getControlFilters intent filters} that describe the capabilities of the
588c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * route and the manner in which it is used and controlled.
589c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
590c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
591c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public static final class RouteInfo {
592fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final ProviderInfo mProvider;
593c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final String mDescriptorId;
594cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        private final String mUniqueId;
595c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private String mName;
596d63957d28aaabcec588b8cde12eac16414783aebJeff Brown        private String mDescription;
597c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private boolean mEnabled;
59811417b1cfde8f1749905f2d735623af9214148afJeff Brown        private boolean mConnecting;
599fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final ArrayList<IntentFilter> mControlFilters = new ArrayList<IntentFilter>();
600c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mPlaybackType;
601c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mPlaybackStream;
602c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mVolumeHandling;
603c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mVolume;
604c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mVolumeMax;
605c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private Display mPresentationDisplay;
606c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mPresentationDisplayId = -1;
607c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private Bundle mExtras;
60811417b1cfde8f1749905f2d735623af9214148afJeff Brown        private MediaRouteDescriptor mDescriptor;
609c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
610c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
611c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * The default playback type, "local", indicating the presentation of the media
612c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * is happening on the same device (e.g. a phone, a tablet) as where it is
613c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * controlled from.
614c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
615c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #getPlaybackType
616c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
617c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public static final int PLAYBACK_TYPE_LOCAL = 0;
618c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
619c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
620c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * A playback type indicating the presentation of the media is happening on
621c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * a different device (i.e. the remote device) than where it is controlled from.
622c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
623c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #getPlaybackType
624c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
625c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public static final int PLAYBACK_TYPE_REMOTE = 1;
626c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
627c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
628c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Playback information indicating the playback volume is fixed, i.e. it cannot be
629c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * controlled from this object. An example of fixed playback volume is a remote player,
630c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
631c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * than attenuate at the source.
632c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
633c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #getVolumeHandling
634c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
635c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public static final int PLAYBACK_VOLUME_FIXED = 0;
636c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
637c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
638c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Playback information indicating the playback volume is variable and can be controlled
639c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * from this object.
640c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
641c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #getVolumeHandling
642c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
643c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public static final int PLAYBACK_VOLUME_VARIABLE = 1;
644c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
645c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        static final int CHANGE_GENERAL = 1 << 0;
646c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        static final int CHANGE_VOLUME = 1 << 1;
647c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        static final int CHANGE_PRESENTATION_DISPLAY = 1 << 2;
648c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
649cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        RouteInfo(ProviderInfo provider, String descriptorId, String uniqueId) {
650fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mProvider = provider;
651c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mDescriptorId = descriptorId;
652cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            mUniqueId = uniqueId;
653c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
654c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
655fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
656fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Gets information about the provider of this media route.
657fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
658fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public ProviderInfo getProvider() {
659fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mProvider;
660c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
661c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
662c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
663cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         * Gets the unique id of the route.
664cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         * <p>
665cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         * The route unique id functions as a stable identifier by which the route is known.
666cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         * For example, an application can use this id as a token to remember the
667cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         * selected route across restarts or to communicate its identity to a service.
668cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         * </p>
669cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         *
670cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         * @return The unique id of the route, never null.
671cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown         */
672cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        public String getId() {
673cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            return mUniqueId;
674cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        }
675cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown
676cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        /**
677d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * Gets the user-visible name of the route.
678d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * <p>
679d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * The route name identifies the destination represented by the route.
680d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * It may be a user-supplied name, an alias, or device serial number.
681d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * </p>
682c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
683d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * @return The user-visible name of a media route.  This is the string presented
684c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * to users who may select this as the active route.
685c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
686c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public String getName() {
687c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mName;
688c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
689c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
690c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
691d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * Gets the user-visible description of the route.
692d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * <p>
693d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * The route description describes the kind of destination represented by the route.
694d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * It may be a user-supplied string, a model number or brand of device.
695d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * </p>
696c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
697d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * @return The description of the route, or null if none.
698c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
699d63957d28aaabcec588b8cde12eac16414783aebJeff Brown        public String getDescription() {
700d63957d28aaabcec588b8cde12eac16414783aebJeff Brown            return mDescription;
701c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
702c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
703c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
704c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Returns true if this route is enabled and may be selected.
705c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
70611417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @return True if this route is enabled.
707c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
708c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public boolean isEnabled() {
709c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mEnabled;
710c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
711c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
712c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
71311417b1cfde8f1749905f2d735623af9214148afJeff Brown         * Returns true if the route is in the process of connecting and is not
71411417b1cfde8f1749905f2d735623af9214148afJeff Brown         * yet ready for use.
71511417b1cfde8f1749905f2d735623af9214148afJeff Brown         *
71611417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @return True if this route is in the process of connecting.
71711417b1cfde8f1749905f2d735623af9214148afJeff Brown         */
71811417b1cfde8f1749905f2d735623af9214148afJeff Brown        public boolean isConnecting() {
71911417b1cfde8f1749905f2d735623af9214148afJeff Brown            return mConnecting;
72011417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
72111417b1cfde8f1749905f2d735623af9214148afJeff Brown
72211417b1cfde8f1749905f2d735623af9214148afJeff Brown        /**
723fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Returns true if this route is currently selected.
724c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
72511417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @return True if this route is currently selected.
726fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
727fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @see MediaRouter#getSelectedRoute
728fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
729fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean isSelected() {
730fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
731fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return sGlobal.getSelectedRoute() == this;
732fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
733fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
734fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
735fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Returns true if this route is the default route.
736fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
73711417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @return True if this route is the default route.
738fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
739fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @see MediaRouter#getDefaultRoute
740fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
741fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean isDefault() {
742fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
743fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return sGlobal.getDefaultRoute() == this;
744fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
745fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
746fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
747fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Gets a list of {@link MediaControlIntent media control intent} filters that
748fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * describe the capabilities of this route and the media control actions that
749fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * it supports.
750fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
751fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @return A list of intent filters that specifies the media control intents that
752fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * this route supports.
753c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
754c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent
755c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #supportsControlCategory
756c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #supportsControlRequest
757c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
758fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public List<IntentFilter> getControlFilters() {
759fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mControlFilters;
760c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
761c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
762c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
76328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * Returns true if the route supports at least one of the capabilities
76428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * described by a media route selector.
76528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         *
76628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * @param selector The selector that specifies the capabilities to check.
76728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * @return True if the route supports at least one of the capabilities
76828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * described in the media route selector.
76928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         */
77011417b1cfde8f1749905f2d735623af9214148afJeff Brown        public boolean matchesSelector(MediaRouteSelector selector) {
77128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            if (selector == null) {
77228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown                throw new IllegalArgumentException("selector must not be null");
77328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            }
77428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            checkCallingThread();
77511417b1cfde8f1749905f2d735623af9214148afJeff Brown            return selector.matchesControlFilters(mControlFilters);
77628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
77728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
77828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        /**
779c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Returns true if the route supports the specified
780c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * {@link MediaControlIntent media control} category.
781c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
782c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Media control categories describe the capabilities of this route
783c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * such as whether it supports live audio streaming or remote playback.
784c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
785c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
786c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param category A {@link MediaControlIntent media control} category
787c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO},
788c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO},
789fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined
790c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * media control category.
79128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * @return True if the route supports the specified intent category.
792c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
793c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent
794fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @see #getControlFilters
795c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
796c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public boolean supportsControlCategory(String category) {
797c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (category == null) {
798c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalArgumentException("category must not be null");
799c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
800fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
801c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
802fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int count = mControlFilters.size();
803fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            for (int i = 0; i < count; i++) {
804fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mControlFilters.get(i).hasCategory(category)) {
805fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return true;
806fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
807fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
808fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
809c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
810c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
811c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
812c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Returns true if the route supports the specified
813a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * {@link MediaControlIntent media control} category and action.
814a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * <p>
815a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * Media control actions describe specific requests that an application
816a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * can ask a route to perform.
817a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * </p>
818a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         *
819a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param category A {@link MediaControlIntent media control} category
820a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO},
821a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO},
822a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined
823a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * media control category.
824a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param action A {@link MediaControlIntent media control} action
825a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * such as {@link MediaControlIntent#ACTION_PLAY}.
826a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @return True if the route supports the specified intent action.
827a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         *
828a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @see MediaControlIntent
829a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @see #getControlFilters
830a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         */
831a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        public boolean supportsControlAction(String category, String action) {
832a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            if (category == null) {
833a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                throw new IllegalArgumentException("category must not be null");
834a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            }
835a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            if (action == null) {
836a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                throw new IllegalArgumentException("action must not be null");
837a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            }
838a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            checkCallingThread();
839a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
840a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            int count = mControlFilters.size();
841a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            for (int i = 0; i < count; i++) {
842a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                IntentFilter filter = mControlFilters.get(i);
843a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                if (filter.hasCategory(category) && filter.hasAction(action)) {
844a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    return true;
845a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                }
846a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            }
847a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            return false;
848a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
849a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
850a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        /**
851a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * Returns true if the route supports the specified
852c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * {@link MediaControlIntent media control} request.
853c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
854c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Media control requests are used to request the route to perform
85543f79f79a5117550a7dfedf9c65124afd163ea43Jeff Brown         * actions such as starting remote playback of a media item.
856c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
857c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
858c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param intent A {@link MediaControlIntent media control intent}.
859c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return True if the route can handle the specified intent.
860c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
861c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent
862fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @see #getControlFilters
863c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
864c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public boolean supportsControlRequest(Intent intent) {
865c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (intent == null) {
866c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalArgumentException("intent must not be null");
867c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
868c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            checkCallingThread();
869c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
870c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            ContentResolver contentResolver = sGlobal.getContentResolver();
871fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int count = mControlFilters.size();
872fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            for (int i = 0; i < count; i++) {
873fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mControlFilters.get(i).match(contentResolver, intent, true, TAG) >= 0) {
874fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return true;
875fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
876fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
877fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
878c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
879c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
880c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
881c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Sends a {@link MediaControlIntent media control} request to be performed
882c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * asynchronously by the route's destination.
883c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
884c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Media control requests are used to request the route to perform
88543f79f79a5117550a7dfedf9c65124afd163ea43Jeff Brown         * actions such as starting remote playback of a media item.
886c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p><p>
887fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * This function may only be called on a selected route.  Control requests
888fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * sent to unselected routes will fail.
889c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
890c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
891c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param intent A {@link MediaControlIntent media control intent}.
892c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param callback A {@link ControlRequestCallback} to invoke with the result
893c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * of the request, or null if no result is required.
894c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
895c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent
896c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
897fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void sendControlRequest(Intent intent, ControlRequestCallback callback) {
898c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (intent == null) {
899c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalArgumentException("intent must not be null");
900c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
901c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            checkCallingThread();
902c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
903fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            sGlobal.sendControlRequest(this, intent, callback);
904c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
905c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
906c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
907c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets the type of playback associated with this route.
908c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
909c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The type of playback associated with this route: {@link #PLAYBACK_TYPE_LOCAL}
910c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * or {@link #PLAYBACK_TYPE_REMOTE}.
911c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
912c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getPlaybackType() {
913c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mPlaybackType;
914c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
915c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
916c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
917350ba6e4a1b5ec28721a098e50eaf6a508eb28f0Jeff Brown         * Gets the audio stream over which the playback associated with this route is performed.
918c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
919c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The stream over which the playback associated with this route is performed.
920c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
921c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getPlaybackStream() {
922c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mPlaybackStream;
923c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
924c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
925c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
926c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets information about how volume is handled on the route.
927c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
928c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return How volume is handled on the route: {@link #PLAYBACK_VOLUME_FIXED}
929c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * or {@link #PLAYBACK_VOLUME_VARIABLE}.
930c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
931c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getVolumeHandling() {
932c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mVolumeHandling;
933c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
934c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
935c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
936c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets the current volume for this route. Depending on the route, this may only
937c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * be valid if the route is currently selected.
938c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
939c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The volume at which the playback associated with this route is performed.
940c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
941c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getVolume() {
942c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mVolume;
943c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
944c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
945c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
946c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets the maximum volume at which the playback associated with this route is performed.
947c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
948c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The maximum volume at which the playback associated with
949c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * this route is performed.
950c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
951c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getVolumeMax() {
952c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mVolumeMax;
953c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
954c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
955c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
956c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Requests a volume change for this route asynchronously.
957c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
958c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This function may only be called on a selected route.  It will have
959c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * no effect if the route is currently unselected.
960c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
961c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
962c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param volume The new volume value between 0 and {@link #getVolumeMax}.
963c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
964c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void requestSetVolume(int volume) {
965c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            checkCallingThread();
966c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            sGlobal.requestSetVolume(this, Math.min(mVolumeMax, Math.max(0, volume)));
967c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
968c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
969c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
970c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Requests an incremental volume update for this route asynchronously.
971c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
972c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This function may only be called on a selected route.  It will have
973c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * no effect if the route is currently unselected.
974c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
975c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
976c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param delta The delta to add to the current volume.
977c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
978c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void requestUpdateVolume(int delta) {
979c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            checkCallingThread();
980c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (delta != 0) {
981c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                sGlobal.requestUpdateVolume(this, delta);
982c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
983c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
984c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
985c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
986c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets the {@link Display} that should be used by the application to show
987c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * a {@link android.app.Presentation} on an external display when this route is selected.
988c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Depending on the route, this may only be valid if the route is currently
989c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * selected.
990c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
991c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * The preferred presentation display may change independently of the route
992c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * being selected or unselected.  For example, the presentation display
993c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * of the default system route may change when an external HDMI display is connected
994c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * or disconnected even though the route itself has not changed.
995c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p><p>
996c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This method may return null if there is no external display associated with
997c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * the route or if the display is not ready to show UI yet.
998c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p><p>
999c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * The application should listen for changes to the presentation display
1000c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * using the {@link Callback#onRoutePresentationDisplayChanged} callback and
1001c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * show or dismiss its {@link android.app.Presentation} accordingly when the display
1002c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * becomes available or is removed.
1003c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p><p>
1004c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This method only makes sense for
1005c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO live video} routes.
1006c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
1007c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1008c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The preferred presentation display to use when this route is
1009c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * selected or null if none.
1010c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1011c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent#CATEGORY_LIVE_VIDEO
1012c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see android.app.Presentation
1013c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1014c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public Display getPresentationDisplay() {
1015fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
1016c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mPresentationDisplayId >= 0 && mPresentationDisplay == null) {
1017c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mPresentationDisplay = sGlobal.getDisplay(mPresentationDisplayId);
1018c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1019c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mPresentationDisplay;
1020c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1021c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1022c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1023c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets a collection of extra properties about this route that were supplied
1024c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * by its media route provider, or null if none.
1025c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1026c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public Bundle getExtras() {
1027c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mExtras;
1028c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1029c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1030fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
1031fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Selects this media route.
1032fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1033fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void select() {
1034fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
1035fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            sGlobal.selectRoute(this);
1036fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1037fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1038c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
1039c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public String toString() {
1040cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            return "MediaRouter.RouteInfo{ uniqueId=" + mUniqueId
1041cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                    + ", name=" + mName
1042d63957d28aaabcec588b8cde12eac16414783aebJeff Brown                    + ", description=" + mDescription
1043c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", enabled=" + mEnabled
104411417b1cfde8f1749905f2d735623af9214148afJeff Brown                    + ", connecting=" + mConnecting
1045c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", playbackType=" + mPlaybackType
1046c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", playbackStream=" + mPlaybackStream
1047c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", volumeHandling=" + mVolumeHandling
1048c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", volume=" + mVolume
1049c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", volumeMax=" + mVolumeMax
1050c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", presentationDisplayId=" + mPresentationDisplayId
1051c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", extras=" + mExtras
1052fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    + ", providerPackageName=" + mProvider.getPackageName()
1053fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    + " }";
1054fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1055fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
105611417b1cfde8f1749905f2d735623af9214148afJeff Brown        int updateDescriptor(MediaRouteDescriptor descriptor) {
1057fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int changes = 0;
1058fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mDescriptor != descriptor) {
1059fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mDescriptor = descriptor;
1060fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (descriptor != null) {
1061fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (!equal(mName, descriptor.getName())) {
1062fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mName = descriptor.getName();
1063fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL;
1064fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
1065d63957d28aaabcec588b8cde12eac16414783aebJeff Brown                    if (!equal(mDescription, descriptor.getDescription())) {
1066d63957d28aaabcec588b8cde12eac16414783aebJeff Brown                        mDescription = descriptor.getDescription();
1067fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL;
1068fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
1069fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mEnabled != descriptor.isEnabled()) {
1070fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mEnabled = descriptor.isEnabled();
1071fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL;
1072fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
107311417b1cfde8f1749905f2d735623af9214148afJeff Brown                    if (mConnecting != descriptor.isConnecting()) {
107411417b1cfde8f1749905f2d735623af9214148afJeff Brown                        mConnecting = descriptor.isConnecting();
107511417b1cfde8f1749905f2d735623af9214148afJeff Brown                        changes |= CHANGE_GENERAL;
107611417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
107711417b1cfde8f1749905f2d735623af9214148afJeff Brown                    if (!mControlFilters.equals(descriptor.getControlFilters())) {
1078fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mControlFilters.clear();
107911417b1cfde8f1749905f2d735623af9214148afJeff Brown                        mControlFilters.addAll(descriptor.getControlFilters());
1080fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL;
1081fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
1082fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mPlaybackType != descriptor.getPlaybackType()) {
1083fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mPlaybackType = descriptor.getPlaybackType();
1084fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL;
1085fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
1086fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mPlaybackStream != descriptor.getPlaybackStream()) {
1087fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mPlaybackStream = descriptor.getPlaybackStream();
1088fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL;
1089fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
1090fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mVolumeHandling != descriptor.getVolumeHandling()) {
1091fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mVolumeHandling = descriptor.getVolumeHandling();
1092fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL | CHANGE_VOLUME;
1093fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
1094fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mVolume != descriptor.getVolume()) {
1095fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mVolume = descriptor.getVolume();
1096fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL | CHANGE_VOLUME;
1097fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
1098fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mVolumeMax != descriptor.getVolumeMax()) {
1099fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mVolumeMax = descriptor.getVolumeMax();
1100fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL | CHANGE_VOLUME;
1101fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
1102fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mPresentationDisplayId != descriptor.getPresentationDisplayId()) {
1103fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mPresentationDisplayId = descriptor.getPresentationDisplayId();
1104fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mPresentationDisplay = null;
1105fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY;
1106fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
1107fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (!equal(mExtras, descriptor.getExtras())) {
1108fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mExtras = descriptor.getExtras();
1109fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL;
1110fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
1111fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
1112fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1113fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return changes;
1114fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1115fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1116fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        String getDescriptorId() {
1117fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mDescriptorId;
1118fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1119fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1120fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        MediaRouteProvider getProviderInstance() {
1121fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mProvider.getProviderInstance();
1122fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1123fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
1124fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1125fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    /**
1126fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Provides information about a media route provider.
1127fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * <p>
1128fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * This object may be used to determine which media route provider has
1129fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * published a particular route.
1130fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * </p>
1131fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     */
1132fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public static final class ProviderInfo {
1133fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final MediaRouteProvider mProviderInstance;
1134fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
1135fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1136fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final ProviderMetadata mMetadata;
113711417b1cfde8f1749905f2d735623af9214148afJeff Brown        private MediaRouteProviderDescriptor mDescriptor;
1138fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private Resources mResources;
1139fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private boolean mResourcesNotAvailable;
1140fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1141fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        ProviderInfo(MediaRouteProvider provider) {
1142fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mProviderInstance = provider;
1143fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mMetadata = provider.getMetadata();
1144fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1145fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1146fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
1147fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Gets the provider's underlying {@link MediaRouteProvider} instance.
1148fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1149fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public MediaRouteProvider getProviderInstance() {
1150fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
1151fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mProviderInstance;
1152fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1153fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1154fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
1155fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Gets the package name of the media route provider service.
1156fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1157fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public String getPackageName() {
1158fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mMetadata.getPackageName();
1159fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1160fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1161fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
1162fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Gets the {@link MediaRouter.RouteInfo routes} published by this route provider.
1163fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1164fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public List<RouteInfo> getRoutes() {
1165fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
1166fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mRoutes;
1167fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1168fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1169fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        Resources getResources() {
1170fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mResources == null && !mResourcesNotAvailable) {
1171fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                String packageName = getPackageName();
1172fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Context context = sGlobal.getProviderContext(packageName);
1173fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (context != null) {
1174fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mResources = context.getResources();
1175fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                } else {
1176fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    Log.w(TAG, "Unable to obtain resources for route provider package: "
1177fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            + packageName);
1178fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mResourcesNotAvailable = true;
1179fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
1180fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1181fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mResources;
1182fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1183fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
118411417b1cfde8f1749905f2d735623af9214148afJeff Brown        boolean updateDescriptor(MediaRouteProviderDescriptor descriptor) {
1185fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mDescriptor != descriptor) {
1186fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mDescriptor = descriptor;
1187fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return true;
1188fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1189fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
1190fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1191fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1192fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        int findRouteByDescriptorId(String id) {
1193fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            final int count = mRoutes.size();
1194fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            for (int i = 0; i < count; i++) {
1195fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mRoutes.get(i).mDescriptorId.equals(id)) {
1196fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return i;
1197fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
1198fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1199fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return -1;
1200fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1201fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1202fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
1203fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public String toString() {
1204fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return "MediaRouter.RouteProviderInfo{ packageName=" + getPackageName()
1205c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + " }";
1206c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1207c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
1208c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1209c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
1210c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Interface for receiving events about media routing changes.
1211c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * All methods of this interface will be called from the application's main thread.
1212c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
1213c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * A Callback will only receive events relevant to routes that the callback
121411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * was registered for unless the {@link MediaRouter#CALLBACK_FLAG_UNFILTERED_EVENTS}
121511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * flag was specified in {@link MediaRouter#addCallback(MediaRouteSelector, Callback, int)}.
1216c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
1217c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
121811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see MediaRouter#addCallback(MediaRouteSelector, Callback, int)
1219c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see MediaRouter#removeCallback(Callback)
1220c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
1221c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public static abstract class Callback {
1222c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1223fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when the supplied media route becomes selected as the active route.
1224c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1225fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1226c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that has been selected.
1227c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1228c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteSelected(MediaRouter router, RouteInfo route) {
1229c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1230c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1231c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1232fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when the supplied media route becomes unselected as the active route.
1233c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1234fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1235c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that has been unselected.
1236c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1237c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteUnselected(MediaRouter router, RouteInfo route) {
1238c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1239c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1240c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1241fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route has been added.
1242c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1243fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1244c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that has become available for use.
1245c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1246c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteAdded(MediaRouter router, RouteInfo route) {
1247c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1248c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1249c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1250fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route has been removed.
1251c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1252fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1253c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that has been removed from availability.
1254c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1255c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteRemoved(MediaRouter router, RouteInfo route) {
1256c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1257c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1258c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1259fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a property of the indicated media route has changed.
1260c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1261fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1262c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that was changed.
1263c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1264c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteChanged(MediaRouter router, RouteInfo route) {
1265c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1266c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1267c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1268fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route's volume changes.
1269c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1270fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1271c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route whose volume changed.
1272c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1273c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) {
1274c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1275c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1276c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1277fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route's presentation display changes.
1278c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
1279c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This method is called whenever the route's presentation display becomes
1280fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * available, is removed or has changes to some of its properties (such as its size).
1281c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
1282c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1283fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1284c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route whose presentation display changed.
1285c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1286c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see RouteInfo#getPresentationDisplay()
1287c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1288c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo route) {
1289c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1290fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1291fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
1292fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route provider has been added.
1293fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
1294fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1295fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param provider The provider that has become available for use.
1296fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1297fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void onProviderAdded(MediaRouter router, ProviderInfo provider) {
1298fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1299fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1300fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
1301fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route provider has been removed.
1302fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
1303fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1304fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param provider The provider that has been removed from availability.
1305fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1306fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void onProviderRemoved(MediaRouter router, ProviderInfo provider) {
1307fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
130811417b1cfde8f1749905f2d735623af9214148afJeff Brown
130911417b1cfde8f1749905f2d735623af9214148afJeff Brown        /**
131011417b1cfde8f1749905f2d735623af9214148afJeff Brown         * Called when a property of the indicated media route provider has changed.
131111417b1cfde8f1749905f2d735623af9214148afJeff Brown         *
131211417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @param router The media router reporting the event.
131311417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @param provider The provider that was changed.
131411417b1cfde8f1749905f2d735623af9214148afJeff Brown         */
131511417b1cfde8f1749905f2d735623af9214148afJeff Brown        public void onProviderChanged(MediaRouter router, ProviderInfo provider) {
131611417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
1317c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
1318c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1319c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
1320c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Callback which is invoked with the result of a media control request.
1321c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
1322c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see RouteInfo#sendControlRequest
1323c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
1324fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public static abstract class ControlRequestCallback {
1325c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
13263d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * Called when a media control request succeeds.
13273d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         *
13283d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * @param data Result data, or null if none.
13293d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * Contents depend on the {@link MediaControlIntent media control action}.
1330c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
13313d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown        public void onResult(Bundle data) {
13323d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown        }
1333c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1334c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
13353d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * Called when a media control request fails.
1336c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
13373d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * @param error A localized error message which may be shown to the user, or null
13383d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * if the cause of the error is unclear.
13393d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * @param data Error data, or null if none.
13403d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown         * Contents depend on the {@link MediaControlIntent media control action}.
1341c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
13423d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown        public void onError(String error, Bundle data) {
1343c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1344c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
1345c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
134611417b1cfde8f1749905f2d735623af9214148afJeff Brown    private static final class CallbackRecord {
13479fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown        public final MediaRouter mRouter;
134811417b1cfde8f1749905f2d735623af9214148afJeff Brown        public final Callback mCallback;
134911417b1cfde8f1749905f2d735623af9214148afJeff Brown        public MediaRouteSelector mSelector;
135011417b1cfde8f1749905f2d735623af9214148afJeff Brown        public int mFlags;
135111417b1cfde8f1749905f2d735623af9214148afJeff Brown
13529fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown        public CallbackRecord(MediaRouter router, Callback callback) {
13539fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            mRouter = router;
135411417b1cfde8f1749905f2d735623af9214148afJeff Brown            mCallback = callback;
135511417b1cfde8f1749905f2d735623af9214148afJeff Brown            mSelector = MediaRouteSelector.EMPTY;
135611417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
135711417b1cfde8f1749905f2d735623af9214148afJeff Brown
135811417b1cfde8f1749905f2d735623af9214148afJeff Brown        public boolean filterRouteEvent(RouteInfo route) {
135911417b1cfde8f1749905f2d735623af9214148afJeff Brown            return (mFlags & CALLBACK_FLAG_UNFILTERED_EVENTS) != 0
136011417b1cfde8f1749905f2d735623af9214148afJeff Brown                    || route.matchesSelector(mSelector);
136111417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
136211417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
136311417b1cfde8f1749905f2d735623af9214148afJeff Brown
1364c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
1365c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Global state for the media router.
1366c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
1367c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Media routes and media route providers are global to the process; their
1368c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * state and the bulk of the media router implementation lives here.
1369c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
1370c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
1371c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    private static final class GlobalMediaRouter implements SystemMediaRouteProvider.SyncCallback {
1372c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final Context mApplicationContext;
1373fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final MediaRouter mApplicationRouter;
13749fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown        private final ArrayList<WeakReference<MediaRouter>> mRouters =
13759fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                new ArrayList<WeakReference<MediaRouter>>();
1376c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
1377fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final ArrayList<ProviderInfo> mProviders =
1378fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                new ArrayList<ProviderInfo>();
1379567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        private final ArrayList<RemoteControlClientRecord> mRemoteControlClients =
1380567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                new ArrayList<RemoteControlClientRecord>();
1381567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        private final RemoteControlClientCompat.PlaybackInfo mPlaybackInfo =
1382567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                new RemoteControlClientCompat.PlaybackInfo();
1383c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final ProviderCallback mProviderCallback = new ProviderCallback();
1384c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final CallbackHandler mCallbackHandler = new CallbackHandler();
1385c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final DisplayManagerCompat mDisplayManager;
1386c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final SystemMediaRouteProvider mSystemProvider;
1387c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1388fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private RegisteredMediaRouteProviderWatcher mRegisteredProviderWatcher;
1389c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private RouteInfo mDefaultRoute;
1390c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private RouteInfo mSelectedRoute;
1391c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private MediaRouteProvider.RouteController mSelectedRouteController;
139211417b1cfde8f1749905f2d735623af9214148afJeff Brown        private MediaRouteDiscoveryRequest mDiscoveryRequest;
1393c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1394c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        GlobalMediaRouter(Context applicationContext) {
1395c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mApplicationContext = applicationContext;
1396c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mDisplayManager = DisplayManagerCompat.getInstance(applicationContext);
1397fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mApplicationRouter = getRouter(applicationContext);
1398fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1399fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // Add the system media route provider for interoperating with
1400fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // the framework media router.  This one is special and receives
1401fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // synchronization messages from the media router.
1402c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mSystemProvider = SystemMediaRouteProvider.obtain(applicationContext, this);
1403fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            addProvider(mSystemProvider);
1404fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1405fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1406fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void start() {
1407fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // Start watching for routes published by registered media route
1408fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // provider services.
1409fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mRegisteredProviderWatcher = new RegisteredMediaRouteProviderWatcher(
1410fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mApplicationContext, mApplicationRouter);
1411fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mRegisteredProviderWatcher.start();
1412c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1413c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1414c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public MediaRouter getRouter(Context context) {
14159fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            MediaRouter router;
14169fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            for (int i = mRouters.size(); --i >= 0; ) {
14179fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                router = mRouters.get(i).get();
14189fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                if (router == null) {
14199fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    mRouters.remove(i);
14209fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                } else if (router.mContext == context) {
14219fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    return router;
14229fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                }
1423c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
14249fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            router = new MediaRouter(context);
14259fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            mRouters.add(new WeakReference<MediaRouter>(router));
1426c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return router;
1427c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1428c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1429c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public ContentResolver getContentResolver() {
1430c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mApplicationContext.getContentResolver();
1431c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1432c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1433fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public Context getProviderContext(String packageName) {
1434fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (packageName.equals(SystemMediaRouteProvider.PACKAGE_NAME)) {
1435fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return mApplicationContext;
1436fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1437fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            try {
1438fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return mApplicationContext.createPackageContext(
1439fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        packageName, Context.CONTEXT_RESTRICTED);
1440fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            } catch (NameNotFoundException ex) {
1441fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return null;
1442fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1443fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1444fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1445c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public Display getDisplay(int displayId) {
1446c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mDisplayManager.getDisplay(displayId);
1447c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1448c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1449fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void sendControlRequest(RouteInfo route,
1450c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                Intent intent, ControlRequestCallback callback) {
1451c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (route == mSelectedRoute && mSelectedRouteController != null) {
1452129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                if (mSelectedRouteController.onControlRequest(intent, callback)) {
1453fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return;
1454fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
1455fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1456fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (callback != null) {
14573d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown                callback.onError(null, null);
1458c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1459c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1460c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1461c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void requestSetVolume(RouteInfo route, int volume) {
1462c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (route == mSelectedRoute && mSelectedRouteController != null) {
1463129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                mSelectedRouteController.onSetVolume(volume);
1464c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1465c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1466c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1467c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void requestUpdateVolume(RouteInfo route, int delta) {
1468c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (route == mSelectedRoute && mSelectedRouteController != null) {
1469129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                mSelectedRouteController.onUpdateVolume(delta);
1470c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1471c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1472c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1473c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public List<RouteInfo> getRoutes() {
1474c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mRoutes;
1475c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1476c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1477fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public List<ProviderInfo> getProviders() {
1478fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mProviders;
1479fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1480fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1481c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public RouteInfo getDefaultRoute() {
1482c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mDefaultRoute == null) {
1483c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // This should never happen once the media router has been fully
1484c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // initialized but it is good to check for the error in case there
1485c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // is a bug in provider initialization.
1486c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalStateException("There is no default route.  "
1487c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        + "The media router has not yet been fully initialized.");
1488c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1489c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mDefaultRoute;
1490c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1491c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1492c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public RouteInfo getSelectedRoute() {
1493c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mSelectedRoute == null) {
1494c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // This should never happen once the media router has been fully
1495c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // initialized but it is good to check for the error in case there
1496c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // is a bug in provider initialization.
1497c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalStateException("There is no currently selected route.  "
1498c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        + "The media router has not yet been fully initialized.");
1499c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1500c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mSelectedRoute;
1501c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1502c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1503c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void selectRoute(RouteInfo route) {
1504c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (!mRoutes.contains(route)) {
1505c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                Log.w(TAG, "Ignoring attempt to select removed route: " + route);
1506c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                return;
1507c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1508c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (!route.mEnabled) {
1509c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                Log.w(TAG, "Ignoring attempt to select disabled route: " + route);
1510c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                return;
1511c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1512c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1513c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            setSelectedRouteInternal(route);
1514c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1515c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
151611417b1cfde8f1749905f2d735623af9214148afJeff Brown        public boolean isRouteAvailable(MediaRouteSelector selector, int flags) {
151711417b1cfde8f1749905f2d735623af9214148afJeff Brown            // Check whether any existing routes match the selector.
151811417b1cfde8f1749905f2d735623af9214148afJeff Brown            final int routeCount = mRoutes.size();
151911417b1cfde8f1749905f2d735623af9214148afJeff Brown            for (int i = 0; i < routeCount; i++) {
152011417b1cfde8f1749905f2d735623af9214148afJeff Brown                RouteInfo route = mRoutes.get(i);
152111417b1cfde8f1749905f2d735623af9214148afJeff Brown                if ((flags & AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE) != 0
152211417b1cfde8f1749905f2d735623af9214148afJeff Brown                        && route.isDefault()) {
152311417b1cfde8f1749905f2d735623af9214148afJeff Brown                    continue;
152411417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
152511417b1cfde8f1749905f2d735623af9214148afJeff Brown                if (route.matchesSelector(selector)) {
152611417b1cfde8f1749905f2d735623af9214148afJeff Brown                    return true;
152711417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
152811417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
152911417b1cfde8f1749905f2d735623af9214148afJeff Brown
153011417b1cfde8f1749905f2d735623af9214148afJeff Brown            // It doesn't look like we can find a matching route right now.
153111417b1cfde8f1749905f2d735623af9214148afJeff Brown            return false;
153211417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
153311417b1cfde8f1749905f2d735623af9214148afJeff Brown
153411417b1cfde8f1749905f2d735623af9214148afJeff Brown        public void updateDiscoveryRequest() {
153511417b1cfde8f1749905f2d735623af9214148afJeff Brown            // Combine all of the callback selectors and active scan flags.
1536f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            boolean discover = false;
153711417b1cfde8f1749905f2d735623af9214148afJeff Brown            boolean activeScan = false;
153811417b1cfde8f1749905f2d735623af9214148afJeff Brown            MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder();
15399fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            for (int i = mRouters.size(); --i >= 0; ) {
15409fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                MediaRouter router = mRouters.get(i).get();
15419fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                if (router == null) {
15429fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    mRouters.remove(i);
15439fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                } else {
15449fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    final int count = router.mCallbackRecords.size();
15459fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    for (int j = 0; j < count; j++) {
15469fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                        CallbackRecord callback = router.mCallbackRecords.get(j);
15479fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                        builder.addSelector(callback.mSelector);
1548f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                        if ((callback.mFlags & CALLBACK_FLAG_PERFORM_ACTIVE_SCAN) != 0) {
15499fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                            activeScan = true;
1550f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                            discover = true; // perform active scan implies request discovery
1551f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                        }
1552f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                        if ((callback.mFlags & CALLBACK_FLAG_REQUEST_DISCOVERY) != 0) {
1553f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                            discover = true;
15549fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                        }
155511417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
155611417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
155711417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
1558f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            MediaRouteSelector selector = discover ? builder.build() : MediaRouteSelector.EMPTY;
155911417b1cfde8f1749905f2d735623af9214148afJeff Brown
156011417b1cfde8f1749905f2d735623af9214148afJeff Brown            // Create a new discovery request.
156111417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (mDiscoveryRequest != null
156211417b1cfde8f1749905f2d735623af9214148afJeff Brown                    && mDiscoveryRequest.getSelector().equals(selector)
156311417b1cfde8f1749905f2d735623af9214148afJeff Brown                    && mDiscoveryRequest.isActiveScan() == activeScan) {
156411417b1cfde8f1749905f2d735623af9214148afJeff Brown                return; // no change
156511417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
156611417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (selector.isEmpty() && !activeScan) {
156711417b1cfde8f1749905f2d735623af9214148afJeff Brown                // Discovery is not needed.
156811417b1cfde8f1749905f2d735623af9214148afJeff Brown                if (mDiscoveryRequest == null) {
156911417b1cfde8f1749905f2d735623af9214148afJeff Brown                    return; // no change
157011417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
157111417b1cfde8f1749905f2d735623af9214148afJeff Brown                mDiscoveryRequest = null;
157211417b1cfde8f1749905f2d735623af9214148afJeff Brown            } else {
157311417b1cfde8f1749905f2d735623af9214148afJeff Brown                // Discovery is needed.
157411417b1cfde8f1749905f2d735623af9214148afJeff Brown                mDiscoveryRequest = new MediaRouteDiscoveryRequest(selector, activeScan);
157511417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
157611417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (DEBUG) {
157711417b1cfde8f1749905f2d735623af9214148afJeff Brown                Log.d(TAG, "Updated discovery request: " + mDiscoveryRequest);
157811417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
157911417b1cfde8f1749905f2d735623af9214148afJeff Brown
158011417b1cfde8f1749905f2d735623af9214148afJeff Brown            // Notify providers.
158111417b1cfde8f1749905f2d735623af9214148afJeff Brown            final int providerCount = mProviders.size();
158211417b1cfde8f1749905f2d735623af9214148afJeff Brown            for (int i = 0; i < providerCount; i++) {
158311417b1cfde8f1749905f2d735623af9214148afJeff Brown                mProviders.get(i).mProviderInstance.setDiscoveryRequest(mDiscoveryRequest);
158428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            }
158528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
158628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
1587fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void addProvider(MediaRouteProvider providerInstance) {
1588fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int index = findProviderInfo(providerInstance);
1589c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (index < 0) {
1590c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // 1. Add the provider to the list.
1591fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ProviderInfo provider = new ProviderInfo(providerInstance);
1592fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mProviders.add(provider);
159311417b1cfde8f1749905f2d735623af9214148afJeff Brown                if (DEBUG) {
159411417b1cfde8f1749905f2d735623af9214148afJeff Brown                    Log.d(TAG, "Provider added: " + provider);
159511417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
1596fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_ADDED, provider);
1597c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // 2. Create the provider's contents.
1598fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                updateProviderContents(provider, providerInstance.getDescriptor());
1599c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // 3. Register the provider callback.
160011417b1cfde8f1749905f2d735623af9214148afJeff Brown                providerInstance.setCallback(mProviderCallback);
160111417b1cfde8f1749905f2d735623af9214148afJeff Brown                // 4. Set the discovery request.
160211417b1cfde8f1749905f2d735623af9214148afJeff Brown                providerInstance.setDiscoveryRequest(mDiscoveryRequest);
1603c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1604c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1605c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1606fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void removeProvider(MediaRouteProvider providerInstance) {
1607fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int index = findProviderInfo(providerInstance);
1608c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (index >= 0) {
160911417b1cfde8f1749905f2d735623af9214148afJeff Brown                // 1. Unregister the provider callback.
161011417b1cfde8f1749905f2d735623af9214148afJeff Brown                providerInstance.setCallback(null);
161111417b1cfde8f1749905f2d735623af9214148afJeff Brown                // 2. Clear the discovery request.
161211417b1cfde8f1749905f2d735623af9214148afJeff Brown                providerInstance.setDiscoveryRequest(null);
161328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown                // 3. Delete the provider's contents.
1614fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ProviderInfo provider = mProviders.get(index);
1615fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                updateProviderContents(provider, null);
161628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown                // 4. Remove the provider from the list.
161711417b1cfde8f1749905f2d735623af9214148afJeff Brown                if (DEBUG) {
161811417b1cfde8f1749905f2d735623af9214148afJeff Brown                    Log.d(TAG, "Provider removed: " + provider);
161911417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
1620fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_REMOVED, provider);
1621fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mProviders.remove(index);
1622c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1623c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1624c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1625fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private void updateProviderDescriptor(MediaRouteProvider providerInstance,
162611417b1cfde8f1749905f2d735623af9214148afJeff Brown                MediaRouteProviderDescriptor descriptor) {
1627fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int index = findProviderInfo(providerInstance);
1628c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (index >= 0) {
1629fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                // Update the provider's contents.
1630fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ProviderInfo provider = mProviders.get(index);
1631fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                updateProviderContents(provider, descriptor);
1632c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1633c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1634c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1635fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int findProviderInfo(MediaRouteProvider providerInstance) {
1636fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            final int count = mProviders.size();
1637c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            for (int i = 0; i < count; i++) {
1638fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mProviders.get(i).mProviderInstance == providerInstance) {
1639c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    return i;
1640c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1641c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1642c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return -1;
1643c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1644c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1645fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private void updateProviderContents(ProviderInfo provider,
164611417b1cfde8f1749905f2d735623af9214148afJeff Brown                MediaRouteProviderDescriptor providerDescriptor) {
1647fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (provider.updateDescriptor(providerDescriptor)) {
1648c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // Update all existing routes and reorder them to match
1649c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // the order of their descriptors.
1650c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                int targetIndex = 0;
1651567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                boolean selectedRouteDescriptorChanged = false;
1652c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (providerDescriptor != null) {
1653fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (providerDescriptor.isValid()) {
165411417b1cfde8f1749905f2d735623af9214148afJeff Brown                        final List<MediaRouteDescriptor> routeDescriptors =
165511417b1cfde8f1749905f2d735623af9214148afJeff Brown                                providerDescriptor.getRoutes();
165611417b1cfde8f1749905f2d735623af9214148afJeff Brown                        final int routeCount = routeDescriptors.size();
165711417b1cfde8f1749905f2d735623af9214148afJeff Brown                        for (int i = 0; i < routeCount; i++) {
165811417b1cfde8f1749905f2d735623af9214148afJeff Brown                            final MediaRouteDescriptor routeDescriptor = routeDescriptors.get(i);
1659c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            final String id = routeDescriptor.getId();
1660fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            final int sourceIndex = provider.findRouteByDescriptorId(id);
1661c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            if (sourceIndex < 0) {
1662c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                // 1. Add the route to the list.
1663cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                                String uniqueId = assignRouteUniqueId(provider, id);
1664cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                                RouteInfo route = new RouteInfo(provider, id, uniqueId);
1665fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                provider.mRoutes.add(targetIndex++, route);
1666c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                mRoutes.add(route);
1667c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                // 2. Create the route's contents.
1668c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                route.updateDescriptor(routeDescriptor);
1669fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                // 3. Notify clients about addition.
167011417b1cfde8f1749905f2d735623af9214148afJeff Brown                                if (DEBUG) {
167111417b1cfde8f1749905f2d735623af9214148afJeff Brown                                    Log.d(TAG, "Route added: " + route);
167211417b1cfde8f1749905f2d735623af9214148afJeff Brown                                }
1673c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route);
1674fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            } else if (sourceIndex < targetIndex) {
1675fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                Log.w(TAG, "Ignoring route descriptor with duplicate id: "
1676fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                        + routeDescriptor);
1677c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            } else {
1678c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                // 1. Reorder the route within the list.
1679fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                RouteInfo route = provider.mRoutes.get(sourceIndex);
1680fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                Collections.swap(provider.mRoutes,
1681c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                        sourceIndex, targetIndex++);
1682c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                // 2. Update the route's contents.
1683c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                int changes = route.updateDescriptor(routeDescriptor);
1684567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                // 3. Notify clients about changes.
1685567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                if (changes != 0) {
1686567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                    if ((changes & RouteInfo.CHANGE_GENERAL) != 0) {
1687567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                        if (DEBUG) {
1688567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                            Log.d(TAG, "Route changed: " + route);
1689567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                        }
1690567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                        mCallbackHandler.post(
1691567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                                CallbackHandler.MSG_ROUTE_CHANGED, route);
169211417b1cfde8f1749905f2d735623af9214148afJeff Brown                                    }
1693567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                    if ((changes & RouteInfo.CHANGE_VOLUME) != 0) {
1694567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                        if (DEBUG) {
1695567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                            Log.d(TAG, "Route volume changed: " + route);
1696567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                        }
1697567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                        mCallbackHandler.post(
1698567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                                CallbackHandler.MSG_ROUTE_VOLUME_CHANGED, route);
169911417b1cfde8f1749905f2d735623af9214148afJeff Brown                                    }
1700567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                    if ((changes & RouteInfo.CHANGE_PRESENTATION_DISPLAY) != 0) {
1701567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                        if (DEBUG) {
1702567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                            Log.d(TAG, "Route presentation display changed: "
1703567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                                    + route);
1704567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                        }
1705567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                        mCallbackHandler.post(CallbackHandler.
1706567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                                MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED, route);
1707567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                    }
1708567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                    if (route == mSelectedRoute) {
1709567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                                        selectedRouteDescriptorChanged = true;
171011417b1cfde8f1749905f2d735623af9214148afJeff Brown                                    }
1711c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                }
1712c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            }
1713c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        }
1714fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    } else {
1715fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        Log.w(TAG, "Ignoring invalid provider descriptor: " + providerDescriptor);
1716c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
1717c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1718c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1719c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // Dispose all remaining routes that do not have matching descriptors.
1720fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) {
1721fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    // 1. Delete the route's contents.
1722fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    RouteInfo route = provider.mRoutes.get(i);
1723c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    route.updateDescriptor(null);
1724fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    // 2. Remove the route from the list.
172511417b1cfde8f1749905f2d735623af9214148afJeff Brown                    mRoutes.remove(route);
172635af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                }
172735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang
1728567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                // Update the selected route if needed.
1729567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                updateSelectedRouteIfNeeded(selectedRouteDescriptorChanged);
173035af7c925d660508fe0892f38eec99a3845630e4Chong Zhang
173135af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                // Now notify clients about routes that were removed.
173235af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                // We do this after updating the selected route to ensure
173335af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                // that the framework media router observes the new route
173435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                // selection before the removal since removing the currently
173535af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                // selected route may have side-effects.
173635af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) {
173735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                    RouteInfo route = provider.mRoutes.remove(i);
173811417b1cfde8f1749905f2d735623af9214148afJeff Brown                    if (DEBUG) {
173911417b1cfde8f1749905f2d735623af9214148afJeff Brown                        Log.d(TAG, "Route removed: " + route);
174011417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
1741fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_REMOVED, route);
1742c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1743fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
174411417b1cfde8f1749905f2d735623af9214148afJeff Brown                // Notify provider changed.
174511417b1cfde8f1749905f2d735623af9214148afJeff Brown                if (DEBUG) {
174611417b1cfde8f1749905f2d735623af9214148afJeff Brown                    Log.d(TAG, "Provider changed: " + provider);
174711417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
174811417b1cfde8f1749905f2d735623af9214148afJeff Brown                mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_CHANGED, provider);
1749c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1750c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1751c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1752cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        private String assignRouteUniqueId(ProviderInfo provider, String routeDescriptorId) {
1753cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            // Although route descriptor ids are unique within a provider, it's
1754cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            // possible for there to be two providers with the same package name.
1755cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            // Therefore we must dedupe the composite id.
1756cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            String uniqueId = provider.getPackageName() + ":" + routeDescriptorId;
1757cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            if (findRouteByUniqueId(uniqueId) < 0) {
1758cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                return uniqueId;
1759cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            }
1760cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            for (int i = 2; ; i++) {
1761cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                String newUniqueId = String.format(Locale.US, "%s_%d", uniqueId, i);
1762cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                if (findRouteByUniqueId(newUniqueId) < 0) {
1763cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                    return newUniqueId;
1764cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                }
1765cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            }
1766cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        }
1767cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown
1768cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        private int findRouteByUniqueId(String uniqueId) {
1769cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            final int count = mRoutes.size();
1770cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            for (int i = 0; i < count; i++) {
1771cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                if (mRoutes.get(i).mUniqueId.equals(uniqueId)) {
1772cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                    return i;
1773cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                }
1774cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            }
1775cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            return -1;
1776cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        }
1777cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown
1778567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        private void updateSelectedRouteIfNeeded(boolean selectedRouteDescriptorChanged) {
1779567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            // Update default route.
1780567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            if (mDefaultRoute != null && !isRouteSelectable(mDefaultRoute)) {
178135af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                Log.i(TAG, "Clearing the default route because it "
1782567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                        + "is no longer selectable: " + mDefaultRoute);
1783c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mDefaultRoute = null;
1784c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1785c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mDefaultRoute == null && !mRoutes.isEmpty()) {
1786c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                for (RouteInfo route : mRoutes) {
1787c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    if (isSystemDefaultRoute(route) && isRouteSelectable(route)) {
1788c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        mDefaultRoute = route;
1789567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                        Log.i(TAG, "Found default route: " + mDefaultRoute);
1790c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
1791c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
1792c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1793c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1794567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
1795567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            // Update selected route.
1796567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            if (mSelectedRoute != null && !isRouteSelectable(mSelectedRoute)) {
1797567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                Log.i(TAG, "Unselecting the current route because it "
1798567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                        + "is no longer selectable: " + mSelectedRoute);
1799567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                setSelectedRouteInternal(null);
1800567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
1801c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mSelectedRoute == null) {
1802567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                // Choose a new route.
1803567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                // This will have the side-effect of updating the playback info when
1804567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                // the new route is selected.
180535af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                setSelectedRouteInternal(chooseFallbackRoute());
1806567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            } else if (selectedRouteDescriptorChanged) {
1807567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                // Update the playback info because the properties of the route have changed.
1808567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                updatePlaybackInfoFromSelectedRoute();
1809c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1810c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1811c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
181235af7c925d660508fe0892f38eec99a3845630e4Chong Zhang        private RouteInfo chooseFallbackRoute() {
181335af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            // When the current route is removed or no longer selectable,
181435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            // we want to revert to a live audio route if there is
181535af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            // one (usually Bluetooth A2DP).  Failing that, use
181635af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            // the default route.
181735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            for (RouteInfo route : mRoutes) {
181835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                if (route != mDefaultRoute
181935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                        && isSystemLiveAudioOnlyRoute(route)
182035af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                        && isRouteSelectable(route)) {
182135af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                    return route;
182235af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                }
182335af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            }
182435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            return mDefaultRoute;
182535af7c925d660508fe0892f38eec99a3845630e4Chong Zhang        }
182635af7c925d660508fe0892f38eec99a3845630e4Chong Zhang
182735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang        private boolean isSystemLiveAudioOnlyRoute(RouteInfo route) {
182835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang            return route.getProviderInstance() == mSystemProvider
182935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                    && route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
183035af7c925d660508fe0892f38eec99a3845630e4Chong Zhang                    && !route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO);
183135af7c925d660508fe0892f38eec99a3845630e4Chong Zhang        }
183235af7c925d660508fe0892f38eec99a3845630e4Chong Zhang
1833c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private boolean isRouteSelectable(RouteInfo route) {
1834c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            // This tests whether the route is still valid and enabled.
1835c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            // The route descriptor field is set to null when the route is removed.
1836c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return route.mDescriptor != null && route.mEnabled;
1837c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1838c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1839c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private boolean isSystemDefaultRoute(RouteInfo route) {
1840fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return route.getProviderInstance() == mSystemProvider
1841c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    && route.mDescriptorId.equals(
1842c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            SystemMediaRouteProvider.DEFAULT_ROUTE_ID);
1843c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1844c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1845c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private void setSelectedRouteInternal(RouteInfo route) {
1846c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mSelectedRoute != route) {
1847c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (mSelectedRoute != null) {
184811417b1cfde8f1749905f2d735623af9214148afJeff Brown                    if (DEBUG) {
184911417b1cfde8f1749905f2d735623af9214148afJeff Brown                        Log.d(TAG, "Route unselected: " + mSelectedRoute);
185011417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
1851c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_UNSELECTED, mSelectedRoute);
1852c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    if (mSelectedRouteController != null) {
1853129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                        mSelectedRouteController.onUnselect();
1854129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                        mSelectedRouteController.onRelease();
1855c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        mSelectedRouteController = null;
1856c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
1857c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1858c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1859c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mSelectedRoute = route;
1860c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1861c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (mSelectedRoute != null) {
1862fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mSelectedRouteController = route.getProviderInstance().onCreateRouteController(
1863c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            route.mDescriptorId);
1864c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    if (mSelectedRouteController != null) {
1865129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                        mSelectedRouteController.onSelect();
1866c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
186711417b1cfde8f1749905f2d735623af9214148afJeff Brown                    if (DEBUG) {
186811417b1cfde8f1749905f2d735623af9214148afJeff Brown                        Log.d(TAG, "Route selected: " + mSelectedRoute);
186911417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
1870c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_SELECTED, mSelectedRoute);
1871c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1872567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
1873567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                updatePlaybackInfoFromSelectedRoute();
1874c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1875c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1876c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1877c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
1878c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public RouteInfo getSystemRouteByDescriptorId(String id) {
1879fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int providerIndex = findProviderInfo(mSystemProvider);
1880c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (providerIndex >= 0) {
1881fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ProviderInfo provider = mProviders.get(providerIndex);
1882fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                int routeIndex = provider.findRouteByDescriptorId(id);
1883c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (routeIndex >= 0) {
1884fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return provider.mRoutes.get(routeIndex);
1885c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1886c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1887c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return null;
1888c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1889c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1890567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        public void addRemoteControlClient(Object rcc) {
1891567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            int index = findRemoteControlClientRecord(rcc);
1892567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            if (index < 0) {
1893567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                RemoteControlClientRecord record = new RemoteControlClientRecord(rcc);
1894567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mRemoteControlClients.add(record);
1895567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
1896567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
1897567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
1898567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        public void removeRemoteControlClient(Object rcc) {
1899567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            int index = findRemoteControlClientRecord(rcc);
1900567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            if (index >= 0) {
1901567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                RemoteControlClientRecord record = mRemoteControlClients.remove(index);
1902567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                record.disconnect();
1903567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
1904567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
1905567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
1906567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        private int findRemoteControlClientRecord(Object rcc) {
1907567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            final int count = mRemoteControlClients.size();
1908567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            for (int i = 0; i < count; i++) {
1909567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                RemoteControlClientRecord record = mRemoteControlClients.get(i);
1910567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                if (record.getRemoteControlClient() == rcc) {
1911567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                    return i;
1912567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                }
1913567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
1914567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            return -1;
1915567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
1916567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
1917567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        private void updatePlaybackInfoFromSelectedRoute() {
1918567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            if (mSelectedRoute != null) {
1919567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mPlaybackInfo.volume = mSelectedRoute.getVolume();
1920567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mPlaybackInfo.volumeMax = mSelectedRoute.getVolumeMax();
1921567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mPlaybackInfo.volumeHandling = mSelectedRoute.getVolumeHandling();
1922567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mPlaybackInfo.playbackStream = mSelectedRoute.getPlaybackStream();
1923567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mPlaybackInfo.playbackType = mSelectedRoute.getPlaybackType();
1924567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
1925567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                final int count = mRemoteControlClients.size();
1926567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                for (int i = 0; i < count; i++) {
1927567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                    RemoteControlClientRecord record = mRemoteControlClients.get(i);
1928567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                    record.updatePlaybackInfo();
1929567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                }
1930567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
1931567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
1932567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
1933c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final class ProviderCallback extends MediaRouteProvider.Callback {
1934c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            @Override
1935c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public void onDescriptorChanged(MediaRouteProvider provider,
193611417b1cfde8f1749905f2d735623af9214148afJeff Brown                    MediaRouteProviderDescriptor descriptor) {
1937c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                updateProviderDescriptor(provider, descriptor);
1938c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1939c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1940c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1941567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        private final class RemoteControlClientRecord
1942567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                implements RemoteControlClientCompat.VolumeCallback {
1943567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            private final RemoteControlClientCompat mRccCompat;
1944567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            private boolean mDisconnected;
1945567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
1946567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            public RemoteControlClientRecord(Object rcc) {
1947567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mRccCompat = RemoteControlClientCompat.obtain(mApplicationContext, rcc);
1948567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mRccCompat.setVolumeCallback(this);
1949567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                updatePlaybackInfo();
1950567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
1951567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
1952567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            public Object getRemoteControlClient() {
1953567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                return mRccCompat.getRemoteControlClient();
1954567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
1955567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
1956567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            public void disconnect() {
1957567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mDisconnected = true;
1958567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mRccCompat.setVolumeCallback(null);
1959567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
1960567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
1961567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            public void updatePlaybackInfo() {
1962567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                mRccCompat.setPlaybackInfo(mPlaybackInfo);
1963567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
1964567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
1965567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            @Override
1966567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            public void onVolumeSetRequest(int volume) {
1967567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                if (!mDisconnected && mSelectedRoute != null) {
1968567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                    mSelectedRoute.requestSetVolume(volume);
1969567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                }
1970567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
1971567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
1972567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            @Override
1973567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            public void onVolumeUpdateRequest(int direction) {
1974567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                if (!mDisconnected && mSelectedRoute != null) {
1975567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                    mSelectedRoute.requestUpdateVolume(direction);
1976567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown                }
1977567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown            }
1978567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown        }
1979567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown
1980c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final class CallbackHandler extends Handler {
19819fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            private final ArrayList<CallbackRecord> mTempCallbackRecords =
19829fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    new ArrayList<CallbackRecord>();
1983c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
198411417b1cfde8f1749905f2d735623af9214148afJeff Brown            private static final int MSG_TYPE_MASK = 0xff00;
198511417b1cfde8f1749905f2d735623af9214148afJeff Brown            private static final int MSG_TYPE_ROUTE = 0x0100;
198611417b1cfde8f1749905f2d735623af9214148afJeff Brown            private static final int MSG_TYPE_PROVIDER = 0x0200;
198711417b1cfde8f1749905f2d735623af9214148afJeff Brown
198811417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_ADDED = MSG_TYPE_ROUTE | 1;
198911417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_REMOVED = MSG_TYPE_ROUTE | 2;
199011417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_CHANGED = MSG_TYPE_ROUTE | 3;
199111417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_VOLUME_CHANGED = MSG_TYPE_ROUTE | 4;
199211417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED = MSG_TYPE_ROUTE | 5;
199311417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_SELECTED = MSG_TYPE_ROUTE | 6;
199411417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_UNSELECTED = MSG_TYPE_ROUTE | 7;
199511417b1cfde8f1749905f2d735623af9214148afJeff Brown
199611417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_PROVIDER_ADDED = MSG_TYPE_PROVIDER | 1;
199711417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_PROVIDER_REMOVED = MSG_TYPE_PROVIDER | 2;
199811417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_PROVIDER_CHANGED = MSG_TYPE_PROVIDER | 3;
1999c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2000fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            public void post(int msg, Object obj) {
2001fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                obtainMessage(msg, obj).sendToTarget();
2002c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2003c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2004c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            @Override
2005c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public void handleMessage(Message msg) {
2006c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                final int what = msg.what;
2007fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                final Object obj = msg.obj;
2008c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2009c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // Synchronize state with the system media router.
2010fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                syncWithSystemProvider(what, obj);
2011c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2012c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // Invoke all registered callbacks.
20139fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                // Build a list of callbacks before invoking them in case callbacks
20149fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                // are added or removed during dispatch.
2015c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                try {
20169fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    for (int i = mRouters.size(); --i >= 0; ) {
20179fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                        MediaRouter router = mRouters.get(i).get();
20189fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                        if (router == null) {
20199fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                            mRouters.remove(i);
20209fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                        } else {
20219fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                            mTempCallbackRecords.addAll(router.mCallbackRecords);
2022c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        }
2023c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
20249fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown
20259fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    final int callbackCount = mTempCallbackRecords.size();
20269fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    for (int i = 0; i < callbackCount; i++) {
20279fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                        invokeCallback(mTempCallbackRecords.get(i), what, obj);
20289fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    }
2029c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                } finally {
20309fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                    mTempCallbackRecords.clear();
2031c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
2032c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2033c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
2034fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            private void syncWithSystemProvider(int what, Object obj) {
2035c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                switch (what) {
2036c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_ADDED:
2037fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mSystemProvider.onSyncRouteAdded((RouteInfo)obj);
2038c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
2039c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_REMOVED:
2040fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mSystemProvider.onSyncRouteRemoved((RouteInfo)obj);
2041c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
2042c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_CHANGED:
2043fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mSystemProvider.onSyncRouteChanged((RouteInfo)obj);
2044c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
2045c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_SELECTED:
2046fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mSystemProvider.onSyncRouteSelected((RouteInfo)obj);
2047c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
2048c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
2049c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2050c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
20519fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown            private void invokeCallback(CallbackRecord record, int what, Object obj) {
20529fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown                final MediaRouter router = record.mRouter;
205311417b1cfde8f1749905f2d735623af9214148afJeff Brown                final MediaRouter.Callback callback = record.mCallback;
205411417b1cfde8f1749905f2d735623af9214148afJeff Brown                switch (what & MSG_TYPE_MASK) {
205511417b1cfde8f1749905f2d735623af9214148afJeff Brown                    case MSG_TYPE_ROUTE: {
205611417b1cfde8f1749905f2d735623af9214148afJeff Brown                        final RouteInfo route = (RouteInfo)obj;
205711417b1cfde8f1749905f2d735623af9214148afJeff Brown                        if (!record.filterRouteEvent(route)) {
205811417b1cfde8f1749905f2d735623af9214148afJeff Brown                            break;
205911417b1cfde8f1749905f2d735623af9214148afJeff Brown                        }
206011417b1cfde8f1749905f2d735623af9214148afJeff Brown                        switch (what) {
206111417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_ADDED:
206211417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteAdded(router, route);
206311417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
206411417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_REMOVED:
206511417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteRemoved(router, route);
206611417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
206711417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_CHANGED:
206811417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteChanged(router, route);
206911417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
207011417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_VOLUME_CHANGED:
207111417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteVolumeChanged(router, route);
207211417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
207311417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED:
207411417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRoutePresentationDisplayChanged(router, route);
207511417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
207611417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_SELECTED:
207711417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteSelected(router, route);
207811417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
207911417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_UNSELECTED:
208011417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteUnselected(router, route);
208111417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
208211417b1cfde8f1749905f2d735623af9214148afJeff Brown                        }
2083c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
208411417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
208511417b1cfde8f1749905f2d735623af9214148afJeff Brown                    case MSG_TYPE_PROVIDER: {
208611417b1cfde8f1749905f2d735623af9214148afJeff Brown                        final ProviderInfo provider = (ProviderInfo)obj;
208711417b1cfde8f1749905f2d735623af9214148afJeff Brown                        switch (what) {
208811417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_PROVIDER_ADDED:
208911417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onProviderAdded(router, provider);
209011417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
209111417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_PROVIDER_REMOVED:
209211417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onProviderRemoved(router, provider);
209311417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
209411417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_PROVIDER_CHANGED:
209511417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onProviderChanged(router, provider);
209611417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
209711417b1cfde8f1749905f2d735623af9214148afJeff Brown                        }
209811417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
2099c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
2100c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
2101c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
2102c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
2103c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown}
2104