MediaRouter.java revision d63957d28aaabcec588b8cde12eac16414783aeb
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
34c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.ArrayList;
35c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.Collections;
36c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.List;
37c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.WeakHashMap;
38c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.concurrent.CopyOnWriteArrayList;
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";
6011417b1cfde8f1749905f2d735623af9214148afJeff Brown    private static final boolean DEBUG = false;
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;
7011417b1cfde8f1749905f2d735623af9214148afJeff Brown    final CopyOnWriteArrayList<CallbackRecord> mCallbackRecords =
7111417b1cfde8f1749905f2d735623af9214148afJeff Brown            new CopyOnWriteArrayList<CallbackRecord>();
7211417b1cfde8f1749905f2d735623af9214148afJeff Brown
7311417b1cfde8f1749905f2d735623af9214148afJeff Brown    /**
7411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Flag for {@link #addCallback}: Actively scan for routes while this callback
7511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * is registered.
7611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * <p>
7711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * When this flag is specified, the media router will actively scan for new
7811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * routes.  Certain routes, such as wifi display routes, may not be discoverable
7911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * except when actively scanning.  This flag is typically used when the route picker
8011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * dialog has been opened by the user to ensure that the route information is
8111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * up to date.
8211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p><p>
8311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Active scanning may consume a significant amount of power and may have intrusive
8411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * effects on wireless connectivity.  Therefore it is important that active scanning
8511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * only be requested when it is actually needed to satisfy a user request to
8611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * discover and select a new route.
8711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p>
8811417b1cfde8f1749905f2d735623af9214148afJeff Brown     */
8911417b1cfde8f1749905f2d735623af9214148afJeff Brown    public static final int CALLBACK_FLAG_ACTIVE_SCAN = 1 << 0;
9011417b1cfde8f1749905f2d735623af9214148afJeff Brown
9111417b1cfde8f1749905f2d735623af9214148afJeff Brown    /**
9211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Flag for {@link #addCallback}: Do not filter route events.
9311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * <p>
9411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * When this flag is specified, the callback will be invoked for events that affect any
9511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * route event if they do not match the callback's associated media route selector.
9611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p>
9711417b1cfde8f1749905f2d735623af9214148afJeff Brown     */
9811417b1cfde8f1749905f2d735623af9214148afJeff Brown    public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 1 << 1;
9911417b1cfde8f1749905f2d735623af9214148afJeff Brown
10011417b1cfde8f1749905f2d735623af9214148afJeff Brown    /**
10111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Flag for {@link #isRouteAvailable}: Ignore the default route.
10211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * <p>
10311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * This flag is used to determine whether a matching non-default route is available.
10411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * This constraint may be used to decide whether to offer the route chooser dialog
10511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * to the user.  There is no point offering the chooser if there are no
10611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * non-default choices.
10711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p>
10811417b1cfde8f1749905f2d735623af9214148afJeff Brown     */
10911417b1cfde8f1749905f2d735623af9214148afJeff Brown    public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1 << 0;
11011417b1cfde8f1749905f2d735623af9214148afJeff Brown
11111417b1cfde8f1749905f2d735623af9214148afJeff Brown    /**
11211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Flag for {@link #isRouteAvailable}: Consider whether matching routes
11311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * might be discovered if an active scan were performed.
11411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * <p>
11511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * If no existing routes match the route selector, then this flag is used to
11611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * determine whether to consider whether any route providers that require active
11711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * scans might discover matching routes if an active scan were actually performed.
11811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p><p>
11911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * This flag may be used to decide whether to offer the route chooser dialog to the user.
12011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * When the dialog is opened, an active scan will be performed which may cause
12111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * additional routes to be discovered by any providers that require active scans.
12211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p>
12311417b1cfde8f1749905f2d735623af9214148afJeff Brown     */
12411417b1cfde8f1749905f2d735623af9214148afJeff Brown    public static final int AVAILABILITY_FLAG_CONSIDER_ACTIVE_SCAN = 1 << 1;
125c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
126c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    MediaRouter(Context context) {
127c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        mContext = context;
128c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
129c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
130c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
131c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Gets an instance of the media router service from the context.
132c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
133c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public static MediaRouter getInstance(Context context) {
134c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (context == null) {
135c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            throw new IllegalArgumentException("context must not be null");
136c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
137c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
138c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
139c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (sGlobal == null) {
140c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            sGlobal = new GlobalMediaRouter(context.getApplicationContext());
141fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            sGlobal.start();
142c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
143c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return sGlobal.getRouter(context);
144c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
145c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
146c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
147fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Gets information about the {@link MediaRouter.RouteInfo routes} currently known to
148fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * this media router.
149c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
150c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public List<RouteInfo> getRoutes() {
151c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
152c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return sGlobal.getRoutes();
153c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
154c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
155c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
156fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Gets information about the {@link MediaRouter.ProviderInfo route providers}
157fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * currently known to this media router.
158fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     */
159fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public List<ProviderInfo> getProviders() {
160fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        checkCallingThread();
161fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        return sGlobal.getProviders();
162fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
163fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
164fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    /**
165c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Gets the default route for playing media content on the system.
166c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
167c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * The system always provides a default route.
168c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
169c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
170c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @return The default route, which is guaranteed to never be null.
171c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
172c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public RouteInfo getDefaultRoute() {
173c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
174c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return sGlobal.getDefaultRoute();
175c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
176c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
177c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
178c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Gets the currently selected route.
179c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
180c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * The application should examine the route's
181fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * {@link RouteInfo#getControlFilters media control intent filters} to assess the
182c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * capabilities of the route before attempting to use it.
183c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
184c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
185c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <h3>Example</h3>
186c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <pre>
187c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * public boolean playMovie() {
188c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     MediaRouter mediaRouter = MediaRouter.getInstance(context);
189c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute();
190c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
191c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     // First try using the remote playback interface, if supported.
192c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
193c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         // The route supports remote playback.
194c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         // Try to send it the Uri of the movie to play.
195c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         Intent intent = new Intent(MediaControlIntent.ACTION_PLAY);
196c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
197c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         intent.setDataAndType("http://example.com/videos/movie.mp4", "video/mp4");
198fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     *         if (route.supportsControlRequest(intent)) {
199fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     *             route.sendControlRequest(intent, null);
200fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     *             return true; // sent the request to play the movie
201c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         }
202c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     }
203c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
204c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     // If remote playback was not possible, then play locally.
205c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     if (route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)) {
206c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         // The route supports live video streaming.
207c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         // Prepare to play content locally in a window or in a presentation.
208c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         return playMovieInWindow();
209c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     }
210c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
211c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     // Neither interface is supported, so we can't play the movie to this route.
212c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     return false;
213c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * }
214c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </pre>
215c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
216c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @return The selected route, which is guaranteed to never be null.
217c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
218fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * @see RouteInfo#getControlFilters
219c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see RouteInfo#supportsControlCategory
220c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see RouteInfo#supportsControlRequest
221c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
222c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public RouteInfo getSelectedRoute() {
223c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
224c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return sGlobal.getSelectedRoute();
225c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
226c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
227c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
22828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * Returns the selected route if it matches the specified selector, otherwise
22928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * selects the default route and returns it.
23028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
23128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @param selector The selector to match.
23228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @return The previously selected route if it matched the selector, otherwise the
23328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * newly selected default route which is guaranteed to never be null.
23428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
23511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see MediaRouteSelector
23628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @see RouteInfo#matchesSelector
23728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @see RouteInfo#isDefault
23828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     */
23911417b1cfde8f1749905f2d735623af9214148afJeff Brown    public RouteInfo updateSelectedRoute(MediaRouteSelector selector) {
24028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        if (selector == null) {
24128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            throw new IllegalArgumentException("selector must not be null");
24228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
24328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        checkCallingThread();
24428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
24511417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
24611417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "updateSelectedRoute: " + selector);
24711417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
24828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        RouteInfo route = sGlobal.getSelectedRoute();
24911417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (!route.isDefault() && !route.matchesSelector(selector)) {
25028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            route = sGlobal.getDefaultRoute();
25128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            sGlobal.selectRoute(route);
25228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
25328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        return route;
25428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    }
25528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
25628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    /**
257c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Selects the specified route.
258c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
259c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @param route The route to select.
260c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
261c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public void selectRoute(RouteInfo route) {
262c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (route == null) {
263c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            throw new IllegalArgumentException("route must not be null");
264c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
265c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
266c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
26711417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
26811417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "selectRoute: " + route);
26911417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
270c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        sGlobal.selectRoute(route);
271c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
272c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
273c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
27411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Returns true if there is a route that matches the specified selector
27511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * or, depending on the specified availability flags, if it is possible to discover one.
27611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * <p>
27711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * This method first considers whether there are any available
27811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * routes that match the selector regardless of whether they are enabled or
27911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * disabled.  If not and the {@link #AVAILABILITY_FLAG_CONSIDER_ACTIVE_SCAN} flag
28011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * was specifies, then it considers whether any of the route providers
28111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * could discover a matching route if an active scan were performed.
28211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p>
283c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
28411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param selector The selector to match.
28511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param flags Flags to control the determination of whether a route may be available.
28611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * May be zero or a combination of
28711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} and
28811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * {@link #AVAILABILITY_FLAG_CONSIDER_ACTIVE_SCAN}.
28911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @return True if a matching route may be available.
290c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
29111417b1cfde8f1749905f2d735623af9214148afJeff Brown    public boolean isRouteAvailable(MediaRouteSelector selector, int flags) {
29211417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (selector == null) {
29311417b1cfde8f1749905f2d735623af9214148afJeff Brown            throw new IllegalArgumentException("selector must not be null");
294c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
295c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
296c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
29711417b1cfde8f1749905f2d735623af9214148afJeff Brown        return sGlobal.isRouteAvailable(selector, flags);
298c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
299c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
300c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
30111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Registers a callback to discover routes that match the selector and to receive
30211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * events when they change.
30311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * <p>
30411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * This is a convenience method that has the same effect as calling
30511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * {@link #addCallback(MediaRouteSelector, Callback, int)} without flags.
30611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p>
307c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
30811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param selector A route selector that indicates the kinds of routes that the
30911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * callback would like to discover.
31011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param callback The callback to add.
31111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see #removeCallback
312c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
31311417b1cfde8f1749905f2d735623af9214148afJeff Brown    public void addCallback(MediaRouteSelector selector, Callback callback) {
31411417b1cfde8f1749905f2d735623af9214148afJeff Brown        addCallback(selector, callback, 0);
315c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
316c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
317c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
31811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Registers a callback to discover routes that match the selector and to receive
31911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * events when they change.
32028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * <p>
32111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * The selector describes the kinds of routes that the application wants to
32211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * discover.  For example, if the application wants to use
32311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * live audio routes then it should include the
32411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * {@link MediaControlIntent#CATEGORY_LIVE_AUDIO live audio media control intent category}
32511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * in its selector when it adds a callback to the media router.
32611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * The selector may include any number of categories.
32728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * </p><p>
32811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * If the callback has already been registered, then the selector is added to
32911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * the set of selectors being monitored by the callback.
33011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p><p>
33111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * By default, the callback will only be invoked for events that affect routes
33211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * that match the specified selector.  Event filtering may be disabled by specifying
33311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * the {@link #CALLBACK_FLAG_UNFILTERED_EVENTS} flag when the callback is registered.
33428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * </p>
33528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
33628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * <h3>Example</h3>
33728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * <pre>
33811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * public class MyActivity extends Activity {
33911417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     private MediaRouter mRouter;
34011417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     private MediaRouter.Callback mCallback;
34111417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     private MediaRouteSelector mSelector;
34228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
34311417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     protected void onCreate(Bundle savedInstanceState) {
34411417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         super.onCreate(savedInstanceState);
34511417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
34611417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         mRouter = Mediarouter.getInstance(this);
34711417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         mCallback = new MyCallback();
34811417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         mSelector = new MediaRouteSelector.Builder()
34911417b1cfde8f1749905f2d735623af9214148afJeff Brown     *                 .addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
35011417b1cfde8f1749905f2d735623af9214148afJeff Brown     *                 .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
35111417b1cfde8f1749905f2d735623af9214148afJeff Brown     *                 .build();
35228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *     }
35328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
35411417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     // Add the callback on resume to tell the media router what kinds of routes
35511417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     // the application is interested in so that it can try to discover suitable ones.
35611417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     public void onResume() {
35711417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         super.onResume();
35811417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
35911417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         mediaRouter.addCallback(mSelector, mCallback);
36011417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
36111417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         MediaRouter.RouteInfo route = mediaRouter.updateSelectedRoute(mSelector);
36211417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         // do something with the route...
36311417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     }
36411417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
36511417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     // Remove the selector on pause to tell the media router that it no longer
36611417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     // needs to invest effort trying to discover routes of these kinds for now.
36711417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     public void onPause() {
36811417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         super.onPause();
36911417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
37011417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         mediaRouter.removeCallback(mCallback);
37111417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     }
37211417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
37311417b1cfde8f1749905f2d735623af9214148afJeff Brown     *     private final class MyCallback extends MediaRouter.Callback {
37411417b1cfde8f1749905f2d735623af9214148afJeff Brown     *         // Implement callback methods as needed.
37528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *     }
37628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * }
37728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * </pre>
37828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
37911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param selector A route selector that indicates the kinds of routes that the
38011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * callback would like to discover.
38111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param callback The callback to add.
38211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param flags Flags to control the behavior of the callback.
38311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * May be zero or a combination of {@link #CALLBACK_FLAG_ACTIVE_SCAN} and
38411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * {@link #CALLBACK_FLAG_UNFILTERED_EVENTS}.
38511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see #removeCallback
38628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     */
38711417b1cfde8f1749905f2d735623af9214148afJeff Brown    public void addCallback(MediaRouteSelector selector, Callback callback, int flags) {
38828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        if (selector == null) {
38928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            throw new IllegalArgumentException("selector must not be null");
39028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
39111417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (callback == null) {
39211417b1cfde8f1749905f2d735623af9214148afJeff Brown            throw new IllegalArgumentException("callback must not be null");
39311417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
39428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        checkCallingThread();
39528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
39611417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
39711417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "addCallback: selector=" + selector
39811417b1cfde8f1749905f2d735623af9214148afJeff Brown                    + ", callback=" + callback + ", flags=" + Integer.toHexString(flags));
39911417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
40011417b1cfde8f1749905f2d735623af9214148afJeff Brown
40111417b1cfde8f1749905f2d735623af9214148afJeff Brown        CallbackRecord record;
40211417b1cfde8f1749905f2d735623af9214148afJeff Brown        int index = findCallbackRecord(callback);
40311417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (index < 0) {
40411417b1cfde8f1749905f2d735623af9214148afJeff Brown            record = new CallbackRecord(callback);
40511417b1cfde8f1749905f2d735623af9214148afJeff Brown            mCallbackRecords.add(record);
40611417b1cfde8f1749905f2d735623af9214148afJeff Brown        } else {
40711417b1cfde8f1749905f2d735623af9214148afJeff Brown            record = mCallbackRecords.get(index);
40811417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
40911417b1cfde8f1749905f2d735623af9214148afJeff Brown        boolean updateNeeded = false;
41011417b1cfde8f1749905f2d735623af9214148afJeff Brown        if ((flags & ~record.mFlags) != 0) {
41111417b1cfde8f1749905f2d735623af9214148afJeff Brown            record.mFlags |= flags;
41211417b1cfde8f1749905f2d735623af9214148afJeff Brown            updateNeeded = true;
41311417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
41411417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (!record.mSelector.contains(selector)) {
41511417b1cfde8f1749905f2d735623af9214148afJeff Brown            record.mSelector = new MediaRouteSelector.Builder(record.mSelector)
41611417b1cfde8f1749905f2d735623af9214148afJeff Brown                    .addSelector(selector)
41711417b1cfde8f1749905f2d735623af9214148afJeff Brown                    .build();
41811417b1cfde8f1749905f2d735623af9214148afJeff Brown            updateNeeded = true;
41911417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
42011417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (updateNeeded) {
42111417b1cfde8f1749905f2d735623af9214148afJeff Brown            sGlobal.updateDiscoveryRequest();
42228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
42328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    }
42428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
42528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    /**
42611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Removes the specified callback.  It will no longer receive events about
42711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * changes to media routes.
42828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     *
42911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param callback The callback to remove.
43011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see #addCallback
43128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     */
43211417b1cfde8f1749905f2d735623af9214148afJeff Brown    public void removeCallback(Callback callback) {
43311417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (callback == null) {
43411417b1cfde8f1749905f2d735623af9214148afJeff Brown            throw new IllegalArgumentException("callback must not be null");
43528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
43628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        checkCallingThread();
43728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
43811417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
43911417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "removeCallback: callback=" + callback);
44011417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
44111417b1cfde8f1749905f2d735623af9214148afJeff Brown
44211417b1cfde8f1749905f2d735623af9214148afJeff Brown        int index = findCallbackRecord(callback);
44311417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (index >= 0) {
44411417b1cfde8f1749905f2d735623af9214148afJeff Brown            mCallbackRecords.remove(index);
44511417b1cfde8f1749905f2d735623af9214148afJeff Brown            sGlobal.updateDiscoveryRequest();
44628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
44728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    }
44828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
44911417b1cfde8f1749905f2d735623af9214148afJeff Brown    private int findCallbackRecord(Callback callback) {
45011417b1cfde8f1749905f2d735623af9214148afJeff Brown        final int count = mCallbackRecords.size();
45111417b1cfde8f1749905f2d735623af9214148afJeff Brown        for (int i = 0; i < count; i++) {
45211417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (mCallbackRecords.get(i).mCallback == callback) {
45311417b1cfde8f1749905f2d735623af9214148afJeff Brown                return i;
45411417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
45511417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
45611417b1cfde8f1749905f2d735623af9214148afJeff Brown        return -1;
45711417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
45811417b1cfde8f1749905f2d735623af9214148afJeff Brown
45928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    /**
460c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Registers a media route provider globally for this application process.
461c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
462fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * @param providerInstance The media route provider instance to add.
463c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
464c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see MediaRouteProvider
46528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @see #removeCallback
466c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
467fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public void addProvider(MediaRouteProvider providerInstance) {
468fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (providerInstance == null) {
469fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            throw new IllegalArgumentException("providerInstance must not be null");
470c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
471c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
472c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
47311417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
47411417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "addProvider: " + providerInstance);
47511417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
476fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        sGlobal.addProvider(providerInstance);
477c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
478c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
479c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
480c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Unregisters a media route provider globally for this application process.
481c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
482fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * @param providerInstance The media route provider instance to remove.
483c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
484c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see MediaRouteProvider
48528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * @see #addCallback
486c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
487fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public void removeProvider(MediaRouteProvider providerInstance) {
488fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (providerInstance == null) {
489fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            throw new IllegalArgumentException("providerInstance must not be null");
490c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
491c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
492c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
49311417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (DEBUG) {
49411417b1cfde8f1749905f2d735623af9214148afJeff Brown            Log.d(TAG, "removeProvider: " + providerInstance);
49528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
49611417b1cfde8f1749905f2d735623af9214148afJeff Brown        sGlobal.removeProvider(providerInstance);
49728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    }
49828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
49928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    /**
500c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Ensures that calls into the media router are on the correct thread.
501c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * It pays to be a little paranoid when global state invariants are at risk.
502c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
503c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    static void checkCallingThread() {
504c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (Looper.myLooper() != Looper.getMainLooper()) {
505c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            throw new IllegalStateException("The media router service must only be "
506c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + "accessed on the application's main thread.");
507c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
508c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
509c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
510c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    static <T> boolean equal(T a, T b) {
511c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return a == b || (a != null && b != null && a.equals(b));
512c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
513c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
514c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
515c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Provides information about a media route.
516c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
517fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Each media route has a list of {@link MediaControlIntent media control}
518fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * {@link #getControlFilters intent filters} that describe the capabilities of the
519c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * route and the manner in which it is used and controlled.
520c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
521c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
522c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public static final class RouteInfo {
523fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final ProviderInfo mProvider;
524c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final String mDescriptorId;
525c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private String mName;
526d63957d28aaabcec588b8cde12eac16414783aebJeff Brown        private String mDescription;
527c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private boolean mEnabled;
52811417b1cfde8f1749905f2d735623af9214148afJeff Brown        private boolean mConnecting;
529fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final ArrayList<IntentFilter> mControlFilters = new ArrayList<IntentFilter>();
530c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mPlaybackType;
531c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mPlaybackStream;
532c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mVolumeHandling;
533c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mVolume;
534c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mVolumeMax;
535c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private Display mPresentationDisplay;
536c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mPresentationDisplayId = -1;
537c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private Bundle mExtras;
53811417b1cfde8f1749905f2d735623af9214148afJeff Brown        private MediaRouteDescriptor mDescriptor;
539c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
540c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
541c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * The default playback type, "local", indicating the presentation of the media
542c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * is happening on the same device (e.g. a phone, a tablet) as where it is
543c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * controlled from.
544c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
545c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #getPlaybackType
546c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
547c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public static final int PLAYBACK_TYPE_LOCAL = 0;
548c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
549c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
550c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * A playback type indicating the presentation of the media is happening on
551c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * a different device (i.e. the remote device) than where it is controlled from.
552c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
553c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #getPlaybackType
554c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
555c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public static final int PLAYBACK_TYPE_REMOTE = 1;
556c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
557c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
558c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Playback information indicating the playback volume is fixed, i.e. it cannot be
559c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * controlled from this object. An example of fixed playback volume is a remote player,
560c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
561c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * than attenuate at the source.
562c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
563c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #getVolumeHandling
564c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
565c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public static final int PLAYBACK_VOLUME_FIXED = 0;
566c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
567c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
568c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Playback information indicating the playback volume is variable and can be controlled
569c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * from this object.
570c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
571c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #getVolumeHandling
572c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
573c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public static final int PLAYBACK_VOLUME_VARIABLE = 1;
574c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
575c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        static final int CHANGE_GENERAL = 1 << 0;
576c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        static final int CHANGE_VOLUME = 1 << 1;
577c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        static final int CHANGE_PRESENTATION_DISPLAY = 1 << 2;
578c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
579fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        RouteInfo(ProviderInfo provider, String descriptorId) {
580fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mProvider = provider;
581c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mDescriptorId = descriptorId;
582c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
583c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
584fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
585fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Gets information about the provider of this media route.
586fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
587fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public ProviderInfo getProvider() {
588fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mProvider;
589c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
590c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
591c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
592d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * Gets the user-visible name of the route.
593d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * <p>
594d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * The route name identifies the destination represented by the route.
595d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * It may be a user-supplied name, an alias, or device serial number.
596d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * </p>
597c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
598d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * @return The user-visible name of a media route.  This is the string presented
599c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * to users who may select this as the active route.
600c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
601c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public String getName() {
602c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mName;
603c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
604c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
605c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
606d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * Gets the user-visible description of the route.
607d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * <p>
608d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * The route description describes the kind of destination represented by the route.
609d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * It may be a user-supplied string, a model number or brand of device.
610d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * </p>
611c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
612d63957d28aaabcec588b8cde12eac16414783aebJeff Brown         * @return The description of the route, or null if none.
613c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
614d63957d28aaabcec588b8cde12eac16414783aebJeff Brown        public String getDescription() {
615d63957d28aaabcec588b8cde12eac16414783aebJeff Brown            return mDescription;
616c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
617c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
618c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
619c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Returns true if this route is enabled and may be selected.
620c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
62111417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @return True if this route is enabled.
622c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
623c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public boolean isEnabled() {
624c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mEnabled;
625c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
626c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
627c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
62811417b1cfde8f1749905f2d735623af9214148afJeff Brown         * Returns true if the route is in the process of connecting and is not
62911417b1cfde8f1749905f2d735623af9214148afJeff Brown         * yet ready for use.
63011417b1cfde8f1749905f2d735623af9214148afJeff Brown         *
63111417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @return True if this route is in the process of connecting.
63211417b1cfde8f1749905f2d735623af9214148afJeff Brown         */
63311417b1cfde8f1749905f2d735623af9214148afJeff Brown        public boolean isConnecting() {
63411417b1cfde8f1749905f2d735623af9214148afJeff Brown            return mConnecting;
63511417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
63611417b1cfde8f1749905f2d735623af9214148afJeff Brown
63711417b1cfde8f1749905f2d735623af9214148afJeff Brown        /**
638fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Returns true if this route is currently selected.
639c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
64011417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @return True if this route is currently selected.
641fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
642fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @see MediaRouter#getSelectedRoute
643fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
644fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean isSelected() {
645fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
646fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return sGlobal.getSelectedRoute() == this;
647fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
648fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
649fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
650fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Returns true if this route is the default route.
651fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
65211417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @return True if this route is the default route.
653fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
654fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @see MediaRouter#getDefaultRoute
655fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
656fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean isDefault() {
657fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
658fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return sGlobal.getDefaultRoute() == this;
659fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
660fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
661fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
662fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Gets a list of {@link MediaControlIntent media control intent} filters that
663fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * describe the capabilities of this route and the media control actions that
664fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * it supports.
665fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
666fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @return A list of intent filters that specifies the media control intents that
667fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * this route supports.
668c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
669c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent
670c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #supportsControlCategory
671c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #supportsControlRequest
672c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
673fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public List<IntentFilter> getControlFilters() {
674fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mControlFilters;
675c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
676c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
677c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
67828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * Returns true if the route supports at least one of the capabilities
67928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * described by a media route selector.
68028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         *
68128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * @param selector The selector that specifies the capabilities to check.
68228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * @return True if the route supports at least one of the capabilities
68328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * described in the media route selector.
68428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         */
68511417b1cfde8f1749905f2d735623af9214148afJeff Brown        public boolean matchesSelector(MediaRouteSelector selector) {
68628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            if (selector == null) {
68728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown                throw new IllegalArgumentException("selector must not be null");
68828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            }
68928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            checkCallingThread();
69011417b1cfde8f1749905f2d735623af9214148afJeff Brown            return selector.matchesControlFilters(mControlFilters);
69128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
69228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
69328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        /**
694c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Returns true if the route supports the specified
695c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * {@link MediaControlIntent media control} category.
696c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
697c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Media control categories describe the capabilities of this route
698c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * such as whether it supports live audio streaming or remote playback.
699c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
700c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
701c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param category A {@link MediaControlIntent media control} category
702c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO},
703c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO},
704fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined
705c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * media control category.
70628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * @return True if the route supports the specified intent category.
707c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
708c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent
709fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @see #getControlFilters
710c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
711c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public boolean supportsControlCategory(String category) {
712c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (category == null) {
713c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalArgumentException("category must not be null");
714c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
715fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
716c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
717fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int count = mControlFilters.size();
718fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            for (int i = 0; i < count; i++) {
719fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mControlFilters.get(i).hasCategory(category)) {
720fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return true;
721fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
722fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
723fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
724c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
725c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
726c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
727c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Returns true if the route supports the specified
728c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * {@link MediaControlIntent media control} request.
729c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
730c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Media control requests are used to request the route to perform
73143f79f79a5117550a7dfedf9c65124afd163ea43Jeff Brown         * actions such as starting remote playback of a media item.
732c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
733c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
734c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param intent A {@link MediaControlIntent media control intent}.
735c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return True if the route can handle the specified intent.
736c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
737c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent
738fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @see #getControlFilters
739c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
740c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public boolean supportsControlRequest(Intent intent) {
741c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (intent == null) {
742c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalArgumentException("intent must not be null");
743c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
744c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            checkCallingThread();
745c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
746c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            ContentResolver contentResolver = sGlobal.getContentResolver();
747fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int count = mControlFilters.size();
748fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            for (int i = 0; i < count; i++) {
749fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mControlFilters.get(i).match(contentResolver, intent, true, TAG) >= 0) {
750fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return true;
751fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
752fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
753fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
754c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
755c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
756c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
757c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Sends a {@link MediaControlIntent media control} request to be performed
758c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * asynchronously by the route's destination.
759c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
760c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Media control requests are used to request the route to perform
76143f79f79a5117550a7dfedf9c65124afd163ea43Jeff Brown         * actions such as starting remote playback of a media item.
762c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p><p>
763fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * This function may only be called on a selected route.  Control requests
764fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * sent to unselected routes will fail.
765c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
766c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
767c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param intent A {@link MediaControlIntent media control intent}.
768c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param callback A {@link ControlRequestCallback} to invoke with the result
769c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * of the request, or null if no result is required.
770c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
771c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent
772c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
773fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void sendControlRequest(Intent intent, ControlRequestCallback callback) {
774c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (intent == null) {
775c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalArgumentException("intent must not be null");
776c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
777c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            checkCallingThread();
778c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
779fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            sGlobal.sendControlRequest(this, intent, callback);
780c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
781c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
782c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
783c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets the type of playback associated with this route.
784c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
785c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The type of playback associated with this route: {@link #PLAYBACK_TYPE_LOCAL}
786c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * or {@link #PLAYBACK_TYPE_REMOTE}.
787c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
788c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getPlaybackType() {
789c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mPlaybackType;
790c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
791c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
792c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
793350ba6e4a1b5ec28721a098e50eaf6a508eb28f0Jeff Brown         * Gets the audio stream over which the playback associated with this route is performed.
794c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
795c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The stream over which the playback associated with this route is performed.
796c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
797c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getPlaybackStream() {
798c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mPlaybackStream;
799c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
800c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
801c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
802c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets information about how volume is handled on the route.
803c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
804c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return How volume is handled on the route: {@link #PLAYBACK_VOLUME_FIXED}
805c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * or {@link #PLAYBACK_VOLUME_VARIABLE}.
806c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
807c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getVolumeHandling() {
808c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mVolumeHandling;
809c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
810c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
811c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
812c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets the current volume for this route. Depending on the route, this may only
813c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * be valid if the route is currently selected.
814c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
815c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The volume at which the playback associated with this route is performed.
816c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
817c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getVolume() {
818c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mVolume;
819c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
820c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
821c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
822c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets the maximum volume at which the playback associated with this route is performed.
823c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
824c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The maximum volume at which the playback associated with
825c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * this route is performed.
826c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
827c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getVolumeMax() {
828c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mVolumeMax;
829c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
830c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
831c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
832c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Requests a volume change for this route asynchronously.
833c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
834c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This function may only be called on a selected route.  It will have
835c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * no effect if the route is currently unselected.
836c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
837c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
838c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param volume The new volume value between 0 and {@link #getVolumeMax}.
839c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
840c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void requestSetVolume(int volume) {
841c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            checkCallingThread();
842c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            sGlobal.requestSetVolume(this, Math.min(mVolumeMax, Math.max(0, volume)));
843c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
844c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
845c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
846c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Requests an incremental volume update for this route asynchronously.
847c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
848c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This function may only be called on a selected route.  It will have
849c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * no effect if the route is currently unselected.
850c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
851c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
852c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param delta The delta to add to the current volume.
853c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
854c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void requestUpdateVolume(int delta) {
855c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            checkCallingThread();
856c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (delta != 0) {
857c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                sGlobal.requestUpdateVolume(this, delta);
858c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
859c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
860c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
861c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
862c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets the {@link Display} that should be used by the application to show
863c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * a {@link android.app.Presentation} on an external display when this route is selected.
864c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Depending on the route, this may only be valid if the route is currently
865c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * selected.
866c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
867c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * The preferred presentation display may change independently of the route
868c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * being selected or unselected.  For example, the presentation display
869c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * of the default system route may change when an external HDMI display is connected
870c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * or disconnected even though the route itself has not changed.
871c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p><p>
872c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This method may return null if there is no external display associated with
873c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * the route or if the display is not ready to show UI yet.
874c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p><p>
875c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * The application should listen for changes to the presentation display
876c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * using the {@link Callback#onRoutePresentationDisplayChanged} callback and
877c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * show or dismiss its {@link android.app.Presentation} accordingly when the display
878c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * becomes available or is removed.
879c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p><p>
880c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This method only makes sense for
881c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO live video} routes.
882c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
883c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
884c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The preferred presentation display to use when this route is
885c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * selected or null if none.
886c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
887c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent#CATEGORY_LIVE_VIDEO
888c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see android.app.Presentation
889c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
890c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public Display getPresentationDisplay() {
891fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
892c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mPresentationDisplayId >= 0 && mPresentationDisplay == null) {
893c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mPresentationDisplay = sGlobal.getDisplay(mPresentationDisplayId);
894c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
895c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mPresentationDisplay;
896c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
897c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
898c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
899c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets a collection of extra properties about this route that were supplied
900c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * by its media route provider, or null if none.
901c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
902c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public Bundle getExtras() {
903c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mExtras;
904c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
905c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
906fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
907fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Selects this media route.
908fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
909fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void select() {
910fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
911fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            sGlobal.selectRoute(this);
912fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
913fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
914c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
915c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public String toString() {
916c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return "MediaRouter.RouteInfo{ name=" + mName
917d63957d28aaabcec588b8cde12eac16414783aebJeff Brown                    + ", description=" + mDescription
918c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", enabled=" + mEnabled
91911417b1cfde8f1749905f2d735623af9214148afJeff Brown                    + ", connecting=" + mConnecting
920c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", playbackType=" + mPlaybackType
921c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", playbackStream=" + mPlaybackStream
922c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", volumeHandling=" + mVolumeHandling
923c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", volume=" + mVolume
924c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", volumeMax=" + mVolumeMax
925c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", presentationDisplayId=" + mPresentationDisplayId
926c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", extras=" + mExtras
927fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    + ", providerPackageName=" + mProvider.getPackageName()
928fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    + " }";
929fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
930fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
93111417b1cfde8f1749905f2d735623af9214148afJeff Brown        int updateDescriptor(MediaRouteDescriptor descriptor) {
932fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int changes = 0;
933fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mDescriptor != descriptor) {
934fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mDescriptor = descriptor;
935fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (descriptor != null) {
936fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (!equal(mName, descriptor.getName())) {
937fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mName = descriptor.getName();
938fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL;
939fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
940d63957d28aaabcec588b8cde12eac16414783aebJeff Brown                    if (!equal(mDescription, descriptor.getDescription())) {
941d63957d28aaabcec588b8cde12eac16414783aebJeff Brown                        mDescription = descriptor.getDescription();
942fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL;
943fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
944fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mEnabled != descriptor.isEnabled()) {
945fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mEnabled = descriptor.isEnabled();
946fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL;
947fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
94811417b1cfde8f1749905f2d735623af9214148afJeff Brown                    if (mConnecting != descriptor.isConnecting()) {
94911417b1cfde8f1749905f2d735623af9214148afJeff Brown                        mConnecting = descriptor.isConnecting();
95011417b1cfde8f1749905f2d735623af9214148afJeff Brown                        changes |= CHANGE_GENERAL;
95111417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
95211417b1cfde8f1749905f2d735623af9214148afJeff Brown                    if (!mControlFilters.equals(descriptor.getControlFilters())) {
953fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mControlFilters.clear();
95411417b1cfde8f1749905f2d735623af9214148afJeff Brown                        mControlFilters.addAll(descriptor.getControlFilters());
955fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL;
956fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
957fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mPlaybackType != descriptor.getPlaybackType()) {
958fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mPlaybackType = descriptor.getPlaybackType();
959fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL;
960fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
961fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mPlaybackStream != descriptor.getPlaybackStream()) {
962fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mPlaybackStream = descriptor.getPlaybackStream();
963fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL;
964fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
965fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mVolumeHandling != descriptor.getVolumeHandling()) {
966fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mVolumeHandling = descriptor.getVolumeHandling();
967fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL | CHANGE_VOLUME;
968fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
969fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mVolume != descriptor.getVolume()) {
970fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mVolume = descriptor.getVolume();
971fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL | CHANGE_VOLUME;
972fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
973fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mVolumeMax != descriptor.getVolumeMax()) {
974fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mVolumeMax = descriptor.getVolumeMax();
975fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL | CHANGE_VOLUME;
976fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
977fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mPresentationDisplayId != descriptor.getPresentationDisplayId()) {
978fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mPresentationDisplayId = descriptor.getPresentationDisplayId();
979fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mPresentationDisplay = null;
980fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY;
981fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
982fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (!equal(mExtras, descriptor.getExtras())) {
983fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mExtras = descriptor.getExtras();
984fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL;
985fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
986fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
987fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
988fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return changes;
989fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
990fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
991fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        String getDescriptorId() {
992fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mDescriptorId;
993fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
994fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
995fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        MediaRouteProvider getProviderInstance() {
996fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mProvider.getProviderInstance();
997fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
998fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
999fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1000fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    /**
1001fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Provides information about a media route provider.
1002fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * <p>
1003fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * This object may be used to determine which media route provider has
1004fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * published a particular route.
1005fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * </p>
1006fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     */
1007fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public static final class ProviderInfo {
1008fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final MediaRouteProvider mProviderInstance;
1009fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
101011417b1cfde8f1749905f2d735623af9214148afJeff Brown        private final ArrayList<IntentFilter> mDiscoverableControlFilters =
101111417b1cfde8f1749905f2d735623af9214148afJeff Brown                new ArrayList<IntentFilter>();
1012fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1013fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final ProviderMetadata mMetadata;
101411417b1cfde8f1749905f2d735623af9214148afJeff Brown        private MediaRouteProviderDescriptor mDescriptor;
1015fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private Resources mResources;
1016fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private boolean mResourcesNotAvailable;
1017fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1018fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        ProviderInfo(MediaRouteProvider provider) {
1019fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mProviderInstance = provider;
1020fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mMetadata = provider.getMetadata();
1021fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1022fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1023fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
1024fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Gets the provider's underlying {@link MediaRouteProvider} instance.
1025fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1026fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public MediaRouteProvider getProviderInstance() {
1027fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
1028fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mProviderInstance;
1029fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1030fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1031fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
1032fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Gets the package name of the media route provider service.
1033fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1034fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public String getPackageName() {
1035fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mMetadata.getPackageName();
1036fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1037fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1038fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
1039fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Gets the {@link MediaRouter.RouteInfo routes} published by this route provider.
1040fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1041fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public List<RouteInfo> getRoutes() {
1042fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
1043fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mRoutes;
1044fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1045fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
104628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        /**
104728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         * Returns true if the provider requires active scans to discover routes.
104811417b1cfde8f1749905f2d735623af9214148afJeff Brown         * <p>
104911417b1cfde8f1749905f2d735623af9214148afJeff Brown         * To provide the best user experience, a media route provider should passively
105011417b1cfde8f1749905f2d735623af9214148afJeff Brown         * discover and publish changes to route descriptors in the background.
105111417b1cfde8f1749905f2d735623af9214148afJeff Brown         * However, for some providers, scanning for routes may use a significant
105211417b1cfde8f1749905f2d735623af9214148afJeff Brown         * amount of power or may interfere with wireless network connectivity.
105311417b1cfde8f1749905f2d735623af9214148afJeff Brown         * If this is the case, then the provider will indicate that it requires
105411417b1cfde8f1749905f2d735623af9214148afJeff Brown         * active scans to discover routes by setting this flag.  Active scans
105511417b1cfde8f1749905f2d735623af9214148afJeff Brown         * will be performed when the user opens the route chooser dialog.
105611417b1cfde8f1749905f2d735623af9214148afJeff Brown         * </p>
105728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown         */
105828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        public boolean isActiveScanRequired() {
105928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            checkCallingThread();
106028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            return mDescriptor != null && mDescriptor.isActiveScanRequired();
106128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
106228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
106311417b1cfde8f1749905f2d735623af9214148afJeff Brown        /**
106411417b1cfde8f1749905f2d735623af9214148afJeff Brown         * Gets a list of {@link MediaControlIntent media route control filters} that
106511417b1cfde8f1749905f2d735623af9214148afJeff Brown         * describe the union of capabilities of all routes that this provider can
106611417b1cfde8f1749905f2d735623af9214148afJeff Brown         * possibly discover.
106711417b1cfde8f1749905f2d735623af9214148afJeff Brown         * <p>
106811417b1cfde8f1749905f2d735623af9214148afJeff Brown         * Because a route provider may not know what to look for until an
106911417b1cfde8f1749905f2d735623af9214148afJeff Brown         * application actually asks for it, the contents of the discoverable control
107011417b1cfde8f1749905f2d735623af9214148afJeff Brown         * filter list may change depending on the route selectors that applications have
107111417b1cfde8f1749905f2d735623af9214148afJeff Brown         * actually specified when {@link MediaRouter#addCallback registering callbacks}
107211417b1cfde8f1749905f2d735623af9214148afJeff Brown         * on the media router to discover routes.
107311417b1cfde8f1749905f2d735623af9214148afJeff Brown         * </p>
107411417b1cfde8f1749905f2d735623af9214148afJeff Brown         */
107511417b1cfde8f1749905f2d735623af9214148afJeff Brown        public List<IntentFilter> getDiscoverableControlFilters() {
107611417b1cfde8f1749905f2d735623af9214148afJeff Brown            checkCallingThread();
107711417b1cfde8f1749905f2d735623af9214148afJeff Brown            return mDiscoverableControlFilters;
107811417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
107911417b1cfde8f1749905f2d735623af9214148afJeff Brown
1080fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        Resources getResources() {
1081fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mResources == null && !mResourcesNotAvailable) {
1082fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                String packageName = getPackageName();
1083fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Context context = sGlobal.getProviderContext(packageName);
1084fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (context != null) {
1085fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mResources = context.getResources();
1086fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                } else {
1087fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    Log.w(TAG, "Unable to obtain resources for route provider package: "
1088fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            + packageName);
1089fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mResourcesNotAvailable = true;
1090fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
1091fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1092fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mResources;
1093fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1094fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
109511417b1cfde8f1749905f2d735623af9214148afJeff Brown        boolean updateDescriptor(MediaRouteProviderDescriptor descriptor) {
1096fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mDescriptor != descriptor) {
1097fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mDescriptor = descriptor;
109811417b1cfde8f1749905f2d735623af9214148afJeff Brown
109911417b1cfde8f1749905f2d735623af9214148afJeff Brown                if (!mDiscoverableControlFilters.equals(
110011417b1cfde8f1749905f2d735623af9214148afJeff Brown                        descriptor.getDiscoverableControlFilters())) {
110111417b1cfde8f1749905f2d735623af9214148afJeff Brown                    mDiscoverableControlFilters.clear();
110211417b1cfde8f1749905f2d735623af9214148afJeff Brown                    mDiscoverableControlFilters.addAll(descriptor.getDiscoverableControlFilters());
110311417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
1104fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return true;
1105fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1106fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
1107fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1108fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1109fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        int findRouteByDescriptorId(String id) {
1110fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            final int count = mRoutes.size();
1111fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            for (int i = 0; i < count; i++) {
1112fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mRoutes.get(i).mDescriptorId.equals(id)) {
1113fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return i;
1114fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
1115fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1116fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return -1;
1117fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1118fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1119fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
1120fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public String toString() {
1121fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return "MediaRouter.RouteProviderInfo{ packageName=" + getPackageName()
112211417b1cfde8f1749905f2d735623af9214148afJeff Brown                    + ", isActiveScanRequired=" + isActiveScanRequired()
1123c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + " }";
1124c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1125c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
1126c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1127c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
1128c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Interface for receiving events about media routing changes.
1129c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * All methods of this interface will be called from the application's main thread.
1130c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
1131c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * A Callback will only receive events relevant to routes that the callback
113211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * was registered for unless the {@link MediaRouter#CALLBACK_FLAG_UNFILTERED_EVENTS}
113311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * flag was specified in {@link MediaRouter#addCallback(MediaRouteSelector, Callback, int)}.
1134c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
1135c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
113611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see MediaRouter#addCallback(MediaRouteSelector, Callback, int)
1137c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see MediaRouter#removeCallback(Callback)
1138c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
1139c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public static abstract class Callback {
1140c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1141fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when the supplied media route becomes selected as the active route.
1142c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1143fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1144c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that has been selected.
1145c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1146c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteSelected(MediaRouter router, RouteInfo route) {
1147c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1148c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1149c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1150fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when the supplied media route becomes unselected as the active route.
1151c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1152fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1153c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that has been unselected.
1154c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1155c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteUnselected(MediaRouter router, RouteInfo route) {
1156c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1157c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1158c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1159fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route has been added.
1160c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1161fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1162c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that has become available for use.
1163c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1164c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteAdded(MediaRouter router, RouteInfo route) {
1165c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1166c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1167c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1168fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route has been removed.
1169c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1170fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1171c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that has been removed from availability.
1172c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1173c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteRemoved(MediaRouter router, RouteInfo route) {
1174c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1175c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1176c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1177fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a property of the indicated media route has changed.
1178c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1179fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1180c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that was changed.
1181c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1182c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteChanged(MediaRouter router, RouteInfo route) {
1183c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1184c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1185c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1186fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route's volume changes.
1187c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1188fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1189c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route whose volume changed.
1190c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1191c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) {
1192c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1193c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1194c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1195fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route's presentation display changes.
1196c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
1197c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This method is called whenever the route's presentation display becomes
1198fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * available, is removed or has changes to some of its properties (such as its size).
1199c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
1200c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1201fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1202c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route whose presentation display changed.
1203c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1204c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see RouteInfo#getPresentationDisplay()
1205c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1206c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo route) {
1207c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1208fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1209fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
1210fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route provider has been added.
1211fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
1212fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1213fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param provider The provider that has become available for use.
1214fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1215fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void onProviderAdded(MediaRouter router, ProviderInfo provider) {
1216fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1217fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1218fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
1219fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route provider has been removed.
1220fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
1221fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
1222fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param provider The provider that has been removed from availability.
1223fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
1224fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void onProviderRemoved(MediaRouter router, ProviderInfo provider) {
1225fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
122611417b1cfde8f1749905f2d735623af9214148afJeff Brown
122711417b1cfde8f1749905f2d735623af9214148afJeff Brown        /**
122811417b1cfde8f1749905f2d735623af9214148afJeff Brown         * Called when a property of the indicated media route provider has changed.
122911417b1cfde8f1749905f2d735623af9214148afJeff Brown         *
123011417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @param router The media router reporting the event.
123111417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @param provider The provider that was changed.
123211417b1cfde8f1749905f2d735623af9214148afJeff Brown         */
123311417b1cfde8f1749905f2d735623af9214148afJeff Brown        public void onProviderChanged(MediaRouter router, ProviderInfo provider) {
123411417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
1235c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
1236c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1237c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
1238c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Callback which is invoked with the result of a media control request.
1239c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
1240c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see RouteInfo#sendControlRequest
1241c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
1242fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public static abstract class ControlRequestCallback {
1243c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1244fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Result code: The media control action succeeded.
1245c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1246fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public static final int REQUEST_SUCCEEDED = 0;
1247c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1248c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1249c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Result code: The media control action failed.
1250c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1251c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public static final int REQUEST_FAILED = -1;
1252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1253c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
1254c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Called with the result of the media control request.
1255c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
1256fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param result The result code: {@link #REQUEST_SUCCEEDED}, or {@link #REQUEST_FAILED}.
1257c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param data Additional result data.  Contents depend on the media control action.
1258c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
1259c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onResult(int result, Bundle data) {
1260c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1261c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
1262c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
126311417b1cfde8f1749905f2d735623af9214148afJeff Brown    private static final class CallbackRecord {
126411417b1cfde8f1749905f2d735623af9214148afJeff Brown        public final Callback mCallback;
126511417b1cfde8f1749905f2d735623af9214148afJeff Brown        public MediaRouteSelector mSelector;
126611417b1cfde8f1749905f2d735623af9214148afJeff Brown        public int mFlags;
126711417b1cfde8f1749905f2d735623af9214148afJeff Brown
126811417b1cfde8f1749905f2d735623af9214148afJeff Brown        public CallbackRecord(Callback callback) {
126911417b1cfde8f1749905f2d735623af9214148afJeff Brown            mCallback = callback;
127011417b1cfde8f1749905f2d735623af9214148afJeff Brown            mSelector = MediaRouteSelector.EMPTY;
127111417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
127211417b1cfde8f1749905f2d735623af9214148afJeff Brown
127311417b1cfde8f1749905f2d735623af9214148afJeff Brown        public boolean filterRouteEvent(RouteInfo route) {
127411417b1cfde8f1749905f2d735623af9214148afJeff Brown            return (mFlags & CALLBACK_FLAG_UNFILTERED_EVENTS) != 0
127511417b1cfde8f1749905f2d735623af9214148afJeff Brown                    || route.matchesSelector(mSelector);
127611417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
127711417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
127811417b1cfde8f1749905f2d735623af9214148afJeff Brown
1279c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
1280c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Global state for the media router.
1281c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
1282c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Media routes and media route providers are global to the process; their
1283c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * state and the bulk of the media router implementation lives here.
1284c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
1285c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
1286c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    private static final class GlobalMediaRouter implements SystemMediaRouteProvider.SyncCallback {
1287c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final Context mApplicationContext;
1288fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final MediaRouter mApplicationRouter;
1289c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final WeakHashMap<Context, MediaRouter> mRouters =
1290c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                new WeakHashMap<Context, MediaRouter>();
1291c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
1292fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final ArrayList<ProviderInfo> mProviders =
1293fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                new ArrayList<ProviderInfo>();
1294c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final ProviderCallback mProviderCallback = new ProviderCallback();
1295c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final CallbackHandler mCallbackHandler = new CallbackHandler();
1296c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final DisplayManagerCompat mDisplayManager;
1297c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final SystemMediaRouteProvider mSystemProvider;
1298c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1299fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private RegisteredMediaRouteProviderWatcher mRegisteredProviderWatcher;
1300c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private RouteInfo mDefaultRoute;
1301c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private RouteInfo mSelectedRoute;
1302c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private MediaRouteProvider.RouteController mSelectedRouteController;
130311417b1cfde8f1749905f2d735623af9214148afJeff Brown        private MediaRouteDiscoveryRequest mDiscoveryRequest;
1304c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1305c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        GlobalMediaRouter(Context applicationContext) {
1306c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mApplicationContext = applicationContext;
1307c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mDisplayManager = DisplayManagerCompat.getInstance(applicationContext);
1308fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mApplicationRouter = getRouter(applicationContext);
1309fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1310fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // Add the system media route provider for interoperating with
1311fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // the framework media router.  This one is special and receives
1312fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // synchronization messages from the media router.
1313c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mSystemProvider = SystemMediaRouteProvider.obtain(applicationContext, this);
1314fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            addProvider(mSystemProvider);
1315fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1316fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1317fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void start() {
1318fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // Start watching for routes published by registered media route
1319fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // provider services.
1320fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mRegisteredProviderWatcher = new RegisteredMediaRouteProviderWatcher(
1321fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mApplicationContext, mApplicationRouter);
1322fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mRegisteredProviderWatcher.start();
1323c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1324c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1325c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public MediaRouter getRouter(Context context) {
1326c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            MediaRouter router = mRouters.get(context);
1327c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (router == null) {
1328c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                router = new MediaRouter(context);
1329c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mRouters.put(context, router);
1330c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1331c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return router;
1332c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1333c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1334c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public ContentResolver getContentResolver() {
1335c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mApplicationContext.getContentResolver();
1336c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1337c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1338fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public Context getProviderContext(String packageName) {
1339fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (packageName.equals(SystemMediaRouteProvider.PACKAGE_NAME)) {
1340fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return mApplicationContext;
1341fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1342fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            try {
1343fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return mApplicationContext.createPackageContext(
1344fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        packageName, Context.CONTEXT_RESTRICTED);
1345fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            } catch (NameNotFoundException ex) {
1346fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return null;
1347fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1348fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1349fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1350c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public Display getDisplay(int displayId) {
1351c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mDisplayManager.getDisplay(displayId);
1352c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1353c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1354fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void sendControlRequest(RouteInfo route,
1355c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                Intent intent, ControlRequestCallback callback) {
1356c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (route == mSelectedRoute && mSelectedRouteController != null) {
1357129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                if (mSelectedRouteController.onControlRequest(intent, callback)) {
1358fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return;
1359fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
1360fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1361fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (callback != null) {
1362fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                callback.onResult(ControlRequestCallback.REQUEST_FAILED, null);
1363c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1364c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1365c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1366c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void requestSetVolume(RouteInfo route, int volume) {
1367c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (route == mSelectedRoute && mSelectedRouteController != null) {
1368129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                mSelectedRouteController.onSetVolume(volume);
1369c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1370c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1371c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1372c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void requestUpdateVolume(RouteInfo route, int delta) {
1373c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (route == mSelectedRoute && mSelectedRouteController != null) {
1374129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                mSelectedRouteController.onUpdateVolume(delta);
1375c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1376c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1377c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1378c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public List<RouteInfo> getRoutes() {
1379c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mRoutes;
1380c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1381c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1382fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public List<ProviderInfo> getProviders() {
1383fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mProviders;
1384fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1385fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1386c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public RouteInfo getDefaultRoute() {
1387c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mDefaultRoute == null) {
1388c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // This should never happen once the media router has been fully
1389c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // initialized but it is good to check for the error in case there
1390c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // is a bug in provider initialization.
1391c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalStateException("There is no default route.  "
1392c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        + "The media router has not yet been fully initialized.");
1393c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1394c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mDefaultRoute;
1395c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1396c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1397c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public RouteInfo getSelectedRoute() {
1398c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mSelectedRoute == null) {
1399c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // This should never happen once the media router has been fully
1400c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // initialized but it is good to check for the error in case there
1401c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // is a bug in provider initialization.
1402c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalStateException("There is no currently selected route.  "
1403c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        + "The media router has not yet been fully initialized.");
1404c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1405c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mSelectedRoute;
1406c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1407c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1408c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void selectRoute(RouteInfo route) {
1409c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (!mRoutes.contains(route)) {
1410c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                Log.w(TAG, "Ignoring attempt to select removed route: " + route);
1411c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                return;
1412c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1413c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (!route.mEnabled) {
1414c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                Log.w(TAG, "Ignoring attempt to select disabled route: " + route);
1415c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                return;
1416c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1417c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1418c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            setSelectedRouteInternal(route);
1419c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1420c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
142111417b1cfde8f1749905f2d735623af9214148afJeff Brown        public boolean isRouteAvailable(MediaRouteSelector selector, int flags) {
142211417b1cfde8f1749905f2d735623af9214148afJeff Brown            // Check whether any existing routes match the selector.
142311417b1cfde8f1749905f2d735623af9214148afJeff Brown            final int routeCount = mRoutes.size();
142411417b1cfde8f1749905f2d735623af9214148afJeff Brown            for (int i = 0; i < routeCount; i++) {
142511417b1cfde8f1749905f2d735623af9214148afJeff Brown                RouteInfo route = mRoutes.get(i);
142611417b1cfde8f1749905f2d735623af9214148afJeff Brown                if ((flags & AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE) != 0
142711417b1cfde8f1749905f2d735623af9214148afJeff Brown                        && route.isDefault()) {
142811417b1cfde8f1749905f2d735623af9214148afJeff Brown                    continue;
142911417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
143011417b1cfde8f1749905f2d735623af9214148afJeff Brown                if (route.matchesSelector(selector)) {
143111417b1cfde8f1749905f2d735623af9214148afJeff Brown                    return true;
143211417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
143311417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
143411417b1cfde8f1749905f2d735623af9214148afJeff Brown
143511417b1cfde8f1749905f2d735623af9214148afJeff Brown            // Check whether any provider could possibly discover a matching route
143611417b1cfde8f1749905f2d735623af9214148afJeff Brown            // if a required active scan were performed.
143711417b1cfde8f1749905f2d735623af9214148afJeff Brown            if ((flags & AVAILABILITY_FLAG_CONSIDER_ACTIVE_SCAN) != 0) {
143811417b1cfde8f1749905f2d735623af9214148afJeff Brown                final int providerCount = mProviders.size();
143911417b1cfde8f1749905f2d735623af9214148afJeff Brown                for (int i = 0; i < providerCount; i++) {
144011417b1cfde8f1749905f2d735623af9214148afJeff Brown                    ProviderInfo provider = mProviders.get(i);
144111417b1cfde8f1749905f2d735623af9214148afJeff Brown                    if (provider.isActiveScanRequired() && selector.matchesControlFilters(
144211417b1cfde8f1749905f2d735623af9214148afJeff Brown                            provider.getDiscoverableControlFilters())) {
144311417b1cfde8f1749905f2d735623af9214148afJeff Brown                        return true;
144411417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
144511417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
144611417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
144711417b1cfde8f1749905f2d735623af9214148afJeff Brown
144811417b1cfde8f1749905f2d735623af9214148afJeff Brown            // It doesn't look like we can find a matching route right now.
144911417b1cfde8f1749905f2d735623af9214148afJeff Brown            return false;
145011417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
145111417b1cfde8f1749905f2d735623af9214148afJeff Brown
145211417b1cfde8f1749905f2d735623af9214148afJeff Brown        public void updateDiscoveryRequest() {
145311417b1cfde8f1749905f2d735623af9214148afJeff Brown            // Combine all of the callback selectors and active scan flags.
145411417b1cfde8f1749905f2d735623af9214148afJeff Brown            boolean activeScan = false;
145511417b1cfde8f1749905f2d735623af9214148afJeff Brown            MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder();
145628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            for (MediaRouter router : mRouters.values()) {
145711417b1cfde8f1749905f2d735623af9214148afJeff Brown                final int count = router.mCallbackRecords.size();
145811417b1cfde8f1749905f2d735623af9214148afJeff Brown                for (int i = 0; i < count; i++) {
145911417b1cfde8f1749905f2d735623af9214148afJeff Brown                    CallbackRecord callback = router.mCallbackRecords.get(i);
146011417b1cfde8f1749905f2d735623af9214148afJeff Brown                    builder.addSelector(callback.mSelector);
146111417b1cfde8f1749905f2d735623af9214148afJeff Brown                    if ((callback.mFlags & CALLBACK_FLAG_ACTIVE_SCAN) != 0) {
146211417b1cfde8f1749905f2d735623af9214148afJeff Brown                        activeScan = true;
146311417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
146411417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
146511417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
146611417b1cfde8f1749905f2d735623af9214148afJeff Brown            MediaRouteSelector selector = builder.build();
146711417b1cfde8f1749905f2d735623af9214148afJeff Brown
146811417b1cfde8f1749905f2d735623af9214148afJeff Brown            // Create a new discovery request.
146911417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (mDiscoveryRequest != null
147011417b1cfde8f1749905f2d735623af9214148afJeff Brown                    && mDiscoveryRequest.getSelector().equals(selector)
147111417b1cfde8f1749905f2d735623af9214148afJeff Brown                    && mDiscoveryRequest.isActiveScan() == activeScan) {
147211417b1cfde8f1749905f2d735623af9214148afJeff Brown                return; // no change
147311417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
147411417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (selector.isEmpty() && !activeScan) {
147511417b1cfde8f1749905f2d735623af9214148afJeff Brown                // Discovery is not needed.
147611417b1cfde8f1749905f2d735623af9214148afJeff Brown                if (mDiscoveryRequest == null) {
147711417b1cfde8f1749905f2d735623af9214148afJeff Brown                    return; // no change
147811417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
147911417b1cfde8f1749905f2d735623af9214148afJeff Brown                mDiscoveryRequest = null;
148011417b1cfde8f1749905f2d735623af9214148afJeff Brown            } else {
148111417b1cfde8f1749905f2d735623af9214148afJeff Brown                // Discovery is needed.
148211417b1cfde8f1749905f2d735623af9214148afJeff Brown                mDiscoveryRequest = new MediaRouteDiscoveryRequest(selector, activeScan);
148311417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
148411417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (DEBUG) {
148511417b1cfde8f1749905f2d735623af9214148afJeff Brown                Log.d(TAG, "Updated discovery request: " + mDiscoveryRequest);
148611417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
148711417b1cfde8f1749905f2d735623af9214148afJeff Brown
148811417b1cfde8f1749905f2d735623af9214148afJeff Brown            // Notify providers.
148911417b1cfde8f1749905f2d735623af9214148afJeff Brown            final int providerCount = mProviders.size();
149011417b1cfde8f1749905f2d735623af9214148afJeff Brown            for (int i = 0; i < providerCount; i++) {
149111417b1cfde8f1749905f2d735623af9214148afJeff Brown                mProviders.get(i).mProviderInstance.setDiscoveryRequest(mDiscoveryRequest);
149228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            }
149328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
149428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
1495fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void addProvider(MediaRouteProvider providerInstance) {
1496fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int index = findProviderInfo(providerInstance);
1497c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (index < 0) {
1498c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // 1. Add the provider to the list.
1499fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ProviderInfo provider = new ProviderInfo(providerInstance);
1500fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mProviders.add(provider);
150111417b1cfde8f1749905f2d735623af9214148afJeff Brown                if (DEBUG) {
150211417b1cfde8f1749905f2d735623af9214148afJeff Brown                    Log.d(TAG, "Provider added: " + provider);
150311417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
1504fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_ADDED, provider);
1505c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // 2. Create the provider's contents.
1506fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                updateProviderContents(provider, providerInstance.getDescriptor());
1507c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // 3. Register the provider callback.
150811417b1cfde8f1749905f2d735623af9214148afJeff Brown                providerInstance.setCallback(mProviderCallback);
150911417b1cfde8f1749905f2d735623af9214148afJeff Brown                // 4. Set the discovery request.
151011417b1cfde8f1749905f2d735623af9214148afJeff Brown                providerInstance.setDiscoveryRequest(mDiscoveryRequest);
1511c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1512c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1513c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1514fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void removeProvider(MediaRouteProvider providerInstance) {
1515fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int index = findProviderInfo(providerInstance);
1516c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (index >= 0) {
151711417b1cfde8f1749905f2d735623af9214148afJeff Brown                // 1. Unregister the provider callback.
151811417b1cfde8f1749905f2d735623af9214148afJeff Brown                providerInstance.setCallback(null);
151911417b1cfde8f1749905f2d735623af9214148afJeff Brown                // 2. Clear the discovery request.
152011417b1cfde8f1749905f2d735623af9214148afJeff Brown                providerInstance.setDiscoveryRequest(null);
152128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown                // 3. Delete the provider's contents.
1522fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ProviderInfo provider = mProviders.get(index);
1523fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                updateProviderContents(provider, null);
152428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown                // 4. Remove the provider from the list.
152511417b1cfde8f1749905f2d735623af9214148afJeff Brown                if (DEBUG) {
152611417b1cfde8f1749905f2d735623af9214148afJeff Brown                    Log.d(TAG, "Provider removed: " + provider);
152711417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
1528fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_REMOVED, provider);
1529fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mProviders.remove(index);
1530c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1531c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1532c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1533fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private void updateProviderDescriptor(MediaRouteProvider providerInstance,
153411417b1cfde8f1749905f2d735623af9214148afJeff Brown                MediaRouteProviderDescriptor descriptor) {
1535fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int index = findProviderInfo(providerInstance);
1536c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (index >= 0) {
1537fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                // Update the provider's contents.
1538fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ProviderInfo provider = mProviders.get(index);
1539fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                updateProviderContents(provider, descriptor);
1540c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1541c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1542c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1543fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int findProviderInfo(MediaRouteProvider providerInstance) {
1544fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            final int count = mProviders.size();
1545c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            for (int i = 0; i < count; i++) {
1546fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mProviders.get(i).mProviderInstance == providerInstance) {
1547c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    return i;
1548c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1549c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1550c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return -1;
1551c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1552c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1553fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private void updateProviderContents(ProviderInfo provider,
155411417b1cfde8f1749905f2d735623af9214148afJeff Brown                MediaRouteProviderDescriptor providerDescriptor) {
1555fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (provider.updateDescriptor(providerDescriptor)) {
1556c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // Update all existing routes and reorder them to match
1557c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // the order of their descriptors.
1558c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                int targetIndex = 0;
1559c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (providerDescriptor != null) {
1560fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (providerDescriptor.isValid()) {
156111417b1cfde8f1749905f2d735623af9214148afJeff Brown                        final List<MediaRouteDescriptor> routeDescriptors =
156211417b1cfde8f1749905f2d735623af9214148afJeff Brown                                providerDescriptor.getRoutes();
156311417b1cfde8f1749905f2d735623af9214148afJeff Brown                        final int routeCount = routeDescriptors.size();
156411417b1cfde8f1749905f2d735623af9214148afJeff Brown                        for (int i = 0; i < routeCount; i++) {
156511417b1cfde8f1749905f2d735623af9214148afJeff Brown                            final MediaRouteDescriptor routeDescriptor = routeDescriptors.get(i);
1566c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            final String id = routeDescriptor.getId();
1567fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            final int sourceIndex = provider.findRouteByDescriptorId(id);
1568c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            if (sourceIndex < 0) {
1569c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                // 1. Add the route to the list.
1570fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                RouteInfo route = new RouteInfo(provider, id);
1571fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                provider.mRoutes.add(targetIndex++, route);
1572c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                mRoutes.add(route);
1573c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                // 2. Create the route's contents.
1574c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                route.updateDescriptor(routeDescriptor);
1575fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                // 3. Notify clients about addition.
157611417b1cfde8f1749905f2d735623af9214148afJeff Brown                                if (DEBUG) {
157711417b1cfde8f1749905f2d735623af9214148afJeff Brown                                    Log.d(TAG, "Route added: " + route);
157811417b1cfde8f1749905f2d735623af9214148afJeff Brown                                }
1579c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route);
1580fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            } else if (sourceIndex < targetIndex) {
1581fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                Log.w(TAG, "Ignoring route descriptor with duplicate id: "
1582fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                        + routeDescriptor);
1583c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            } else {
1584c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                // 1. Reorder the route within the list.
1585fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                RouteInfo route = provider.mRoutes.get(sourceIndex);
1586fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                Collections.swap(provider.mRoutes,
1587c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                        sourceIndex, targetIndex++);
1588c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                // 2. Update the route's contents.
1589c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                int changes = route.updateDescriptor(routeDescriptor);
1590fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                // 3. Unselect route if needed before notifying about changes.
1591fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                unselectRouteIfNeeded(route);
1592fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                // 4. Notify clients about changes.
1593c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                if ((changes & RouteInfo.CHANGE_GENERAL) != 0) {
159411417b1cfde8f1749905f2d735623af9214148afJeff Brown                                    if (DEBUG) {
159511417b1cfde8f1749905f2d735623af9214148afJeff Brown                                        Log.d(TAG, "Route changed: " + route);
159611417b1cfde8f1749905f2d735623af9214148afJeff Brown                                    }
1597c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                    mCallbackHandler.post(
1598c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                            CallbackHandler.MSG_ROUTE_CHANGED, route);
1599c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                }
1600c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                if ((changes & RouteInfo.CHANGE_VOLUME) != 0) {
160111417b1cfde8f1749905f2d735623af9214148afJeff Brown                                    if (DEBUG) {
160211417b1cfde8f1749905f2d735623af9214148afJeff Brown                                        Log.d(TAG, "Route volume changed: " + route);
160311417b1cfde8f1749905f2d735623af9214148afJeff Brown                                    }
1604c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                    mCallbackHandler.post(
1605c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                            CallbackHandler.MSG_ROUTE_VOLUME_CHANGED, route);
1606c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                }
1607c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                if ((changes & RouteInfo.CHANGE_PRESENTATION_DISPLAY) != 0) {
160811417b1cfde8f1749905f2d735623af9214148afJeff Brown                                    if (DEBUG) {
160911417b1cfde8f1749905f2d735623af9214148afJeff Brown                                        Log.d(TAG, "Route presentation display changed: "
161011417b1cfde8f1749905f2d735623af9214148afJeff Brown                                                + route);
161111417b1cfde8f1749905f2d735623af9214148afJeff Brown                                    }
1612fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                    mCallbackHandler.post(CallbackHandler.
1613fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                            MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED, route);
1614c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                }
1615c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            }
1616c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        }
1617fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    } else {
1618fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        Log.w(TAG, "Ignoring invalid provider descriptor: " + providerDescriptor);
1619c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
1620c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1621c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1622c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // Dispose all remaining routes that do not have matching descriptors.
1623fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) {
1624fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    // 1. Delete the route's contents.
1625fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    RouteInfo route = provider.mRoutes.get(i);
1626c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    route.updateDescriptor(null);
1627fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    // 2. Remove the route from the list.
162811417b1cfde8f1749905f2d735623af9214148afJeff Brown                    mRoutes.remove(route);
1629fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    provider.mRoutes.remove(i);
1630fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    // 3. Unselect route if needed before notifying about removal.
1631fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    unselectRouteIfNeeded(route);
1632fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    // 4. Notify clients about removal.
163311417b1cfde8f1749905f2d735623af9214148afJeff Brown                    if (DEBUG) {
163411417b1cfde8f1749905f2d735623af9214148afJeff Brown                        Log.d(TAG, "Route removed: " + route);
163511417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
1636fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_REMOVED, route);
1637c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1638fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
163911417b1cfde8f1749905f2d735623af9214148afJeff Brown                // Notify provider changed.
164011417b1cfde8f1749905f2d735623af9214148afJeff Brown                if (DEBUG) {
164111417b1cfde8f1749905f2d735623af9214148afJeff Brown                    Log.d(TAG, "Provider changed: " + provider);
164211417b1cfde8f1749905f2d735623af9214148afJeff Brown                }
164311417b1cfde8f1749905f2d735623af9214148afJeff Brown                mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_CHANGED, provider);
164411417b1cfde8f1749905f2d735623af9214148afJeff Brown
1645fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                // Choose a new selected route if needed.
1646fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                selectRouteIfNeeded();
1647c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1648c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1649c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1650fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private void unselectRouteIfNeeded(RouteInfo route) {
1651fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mDefaultRoute == route && !isRouteSelectable(route)) {
1652c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                Log.i(TAG, "Choosing a new default route because the current one "
1653fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        + "is no longer selectable: " + route);
1654c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mDefaultRoute = null;
1655c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1656fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mSelectedRoute == route && !isRouteSelectable(route)) {
1657fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Log.i(TAG, "Choosing a new selected route because the current one "
1658fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        + "is no longer selectable: " + route);
1659fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                setSelectedRouteInternal(null);
1660fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1661fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1662fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1663fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private void selectRouteIfNeeded() {
1664c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mDefaultRoute == null && !mRoutes.isEmpty()) {
1665c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                for (RouteInfo route : mRoutes) {
1666c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    if (isSystemDefaultRoute(route) && isRouteSelectable(route)) {
1667c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        mDefaultRoute = route;
1668c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
1669c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
1670c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1671c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1672c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mSelectedRoute == null) {
1673c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                setSelectedRouteInternal(mDefaultRoute);
1674c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1675c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1676c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1677c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private boolean isRouteSelectable(RouteInfo route) {
1678c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            // This tests whether the route is still valid and enabled.
1679c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            // The route descriptor field is set to null when the route is removed.
1680c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return route.mDescriptor != null && route.mEnabled;
1681c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1682c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1683c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private boolean isSystemDefaultRoute(RouteInfo route) {
1684fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return route.getProviderInstance() == mSystemProvider
1685c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    && route.mDescriptorId.equals(
1686c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            SystemMediaRouteProvider.DEFAULT_ROUTE_ID);
1687c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1688c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1689c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private void setSelectedRouteInternal(RouteInfo route) {
1690c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mSelectedRoute != route) {
1691c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (mSelectedRoute != null) {
169211417b1cfde8f1749905f2d735623af9214148afJeff Brown                    if (DEBUG) {
169311417b1cfde8f1749905f2d735623af9214148afJeff Brown                        Log.d(TAG, "Route unselected: " + mSelectedRoute);
169411417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
1695c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_UNSELECTED, mSelectedRoute);
1696c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    if (mSelectedRouteController != null) {
1697129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                        mSelectedRouteController.onUnselect();
1698129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                        mSelectedRouteController.onRelease();
1699c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        mSelectedRouteController = null;
1700c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
1701c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1702c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1703c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mSelectedRoute = route;
1704c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1705c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (mSelectedRoute != null) {
1706fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mSelectedRouteController = route.getProviderInstance().onCreateRouteController(
1707c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            route.mDescriptorId);
1708c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    if (mSelectedRouteController != null) {
1709129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                        mSelectedRouteController.onSelect();
1710c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
171111417b1cfde8f1749905f2d735623af9214148afJeff Brown                    if (DEBUG) {
171211417b1cfde8f1749905f2d735623af9214148afJeff Brown                        Log.d(TAG, "Route selected: " + mSelectedRoute);
171311417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
1714c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_SELECTED, mSelectedRoute);
1715c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1716c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1717c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1718c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1719c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
1720c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public RouteInfo getSystemRouteByDescriptorId(String id) {
1721fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int providerIndex = findProviderInfo(mSystemProvider);
1722c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (providerIndex >= 0) {
1723fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ProviderInfo provider = mProviders.get(providerIndex);
1724fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                int routeIndex = provider.findRouteByDescriptorId(id);
1725c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (routeIndex >= 0) {
1726fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return provider.mRoutes.get(routeIndex);
1727c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1728c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1729c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return null;
1730c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1731c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1732c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final class ProviderCallback extends MediaRouteProvider.Callback {
1733c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            @Override
1734c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public void onDescriptorChanged(MediaRouteProvider provider,
173511417b1cfde8f1749905f2d735623af9214148afJeff Brown                    MediaRouteProviderDescriptor descriptor) {
1736c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                updateProviderDescriptor(provider, descriptor);
1737c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1738c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1739c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1740c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final class CallbackHandler extends Handler {
1741c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            private final ArrayList<MediaRouter> mTempMediaRouters =
1742c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    new ArrayList<MediaRouter>();
1743c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
174411417b1cfde8f1749905f2d735623af9214148afJeff Brown            private static final int MSG_TYPE_MASK = 0xff00;
174511417b1cfde8f1749905f2d735623af9214148afJeff Brown            private static final int MSG_TYPE_ROUTE = 0x0100;
174611417b1cfde8f1749905f2d735623af9214148afJeff Brown            private static final int MSG_TYPE_PROVIDER = 0x0200;
174711417b1cfde8f1749905f2d735623af9214148afJeff Brown
174811417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_ADDED = MSG_TYPE_ROUTE | 1;
174911417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_REMOVED = MSG_TYPE_ROUTE | 2;
175011417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_CHANGED = MSG_TYPE_ROUTE | 3;
175111417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_VOLUME_CHANGED = MSG_TYPE_ROUTE | 4;
175211417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED = MSG_TYPE_ROUTE | 5;
175311417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_SELECTED = MSG_TYPE_ROUTE | 6;
175411417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_ROUTE_UNSELECTED = MSG_TYPE_ROUTE | 7;
175511417b1cfde8f1749905f2d735623af9214148afJeff Brown
175611417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_PROVIDER_ADDED = MSG_TYPE_PROVIDER | 1;
175711417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_PROVIDER_REMOVED = MSG_TYPE_PROVIDER | 2;
175811417b1cfde8f1749905f2d735623af9214148afJeff Brown            public static final int MSG_PROVIDER_CHANGED = MSG_TYPE_PROVIDER | 3;
1759c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1760fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            public void post(int msg, Object obj) {
1761fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                obtainMessage(msg, obj).sendToTarget();
1762c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1763c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1764c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            @Override
1765c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public void handleMessage(Message msg) {
1766c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                final int what = msg.what;
1767fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                final Object obj = msg.obj;
1768c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1769c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // Synchronize state with the system media router.
1770fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                syncWithSystemProvider(what, obj);
1771c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1772c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // Invoke all registered callbacks.
1773c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mTempMediaRouters.addAll(mRouters.values());
1774c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                try {
1775c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    final int routerCount = mTempMediaRouters.size();
1776c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    for (int i = 0; i < routerCount; i++) {
1777c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        final MediaRouter router = mTempMediaRouters.get(i);
177811417b1cfde8f1749905f2d735623af9214148afJeff Brown                        if (!router.mCallbackRecords.isEmpty()) {
177911417b1cfde8f1749905f2d735623af9214148afJeff Brown                            for (CallbackRecord record : router.mCallbackRecords) {
178011417b1cfde8f1749905f2d735623af9214148afJeff Brown                                invokeCallback(router, record, what, obj);
1781c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            }
1782c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        }
1783c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
1784c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                } finally {
1785c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    mTempMediaRouters.clear();
1786c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1787c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1788c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1789fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            private void syncWithSystemProvider(int what, Object obj) {
1790c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                switch (what) {
1791c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_ADDED:
1792fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mSystemProvider.onSyncRouteAdded((RouteInfo)obj);
1793c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
1794c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_REMOVED:
1795fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mSystemProvider.onSyncRouteRemoved((RouteInfo)obj);
1796c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
1797c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_CHANGED:
1798fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mSystemProvider.onSyncRouteChanged((RouteInfo)obj);
1799c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
1800c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_SELECTED:
1801fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mSystemProvider.onSyncRouteSelected((RouteInfo)obj);
1802c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
1803c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1804c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1805c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
180611417b1cfde8f1749905f2d735623af9214148afJeff Brown            private void invokeCallback(MediaRouter router, CallbackRecord record,
1807fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    int what, Object obj) {
180811417b1cfde8f1749905f2d735623af9214148afJeff Brown                final MediaRouter.Callback callback = record.mCallback;
180911417b1cfde8f1749905f2d735623af9214148afJeff Brown                switch (what & MSG_TYPE_MASK) {
181011417b1cfde8f1749905f2d735623af9214148afJeff Brown                    case MSG_TYPE_ROUTE: {
181111417b1cfde8f1749905f2d735623af9214148afJeff Brown                        final RouteInfo route = (RouteInfo)obj;
181211417b1cfde8f1749905f2d735623af9214148afJeff Brown                        if (!record.filterRouteEvent(route)) {
181311417b1cfde8f1749905f2d735623af9214148afJeff Brown                            break;
181411417b1cfde8f1749905f2d735623af9214148afJeff Brown                        }
181511417b1cfde8f1749905f2d735623af9214148afJeff Brown                        switch (what) {
181611417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_ADDED:
181711417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteAdded(router, route);
181811417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
181911417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_REMOVED:
182011417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteRemoved(router, route);
182111417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
182211417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_CHANGED:
182311417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteChanged(router, route);
182411417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
182511417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_VOLUME_CHANGED:
182611417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteVolumeChanged(router, route);
182711417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
182811417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED:
182911417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRoutePresentationDisplayChanged(router, route);
183011417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
183111417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_SELECTED:
183211417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteSelected(router, route);
183311417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
183411417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_ROUTE_UNSELECTED:
183511417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onRouteUnselected(router, route);
183611417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
183711417b1cfde8f1749905f2d735623af9214148afJeff Brown                        }
1838c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
183911417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
184011417b1cfde8f1749905f2d735623af9214148afJeff Brown                    case MSG_TYPE_PROVIDER: {
184111417b1cfde8f1749905f2d735623af9214148afJeff Brown                        final ProviderInfo provider = (ProviderInfo)obj;
184211417b1cfde8f1749905f2d735623af9214148afJeff Brown                        switch (what) {
184311417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_PROVIDER_ADDED:
184411417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onProviderAdded(router, provider);
184511417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
184611417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_PROVIDER_REMOVED:
184711417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onProviderRemoved(router, provider);
184811417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
184911417b1cfde8f1749905f2d735623af9214148afJeff Brown                            case MSG_PROVIDER_CHANGED:
185011417b1cfde8f1749905f2d735623af9214148afJeff Brown                                callback.onProviderChanged(router, provider);
185111417b1cfde8f1749905f2d735623af9214148afJeff Brown                                break;
185211417b1cfde8f1749905f2d735623af9214148afJeff Brown                        }
185311417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
1854c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1855c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1856c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1857c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
1858c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown}
1859