MediaRouter.java revision 43f79f79a5117550a7dfedf9c65124afd163ea43
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.graphics.drawable.Drawable;
26c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Bundle;
27c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Handler;
28c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Looper;
29c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Message;
30c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.support.v4.hardware.display.DisplayManagerCompat;
31b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brownimport android.support.v7.media.MediaRouteProvider.RouteDescriptor;
32b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brownimport android.support.v7.media.MediaRouteProvider.ProviderDescriptor;
33b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brownimport android.support.v7.media.MediaRouteProvider.ProviderMetadata;
34c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.util.Log;
35c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.view.Display;
36c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
37c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.ArrayList;
38c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.Collections;
39c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.List;
40c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.WeakHashMap;
41c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.concurrent.CopyOnWriteArrayList;
42c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
43c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown/**
44c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter allows applications to control the routing of media channels
45c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * and streams from the current device to external speakers and destination devices.
46c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p>
47c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * A MediaRouter instance is retrieved through {@link #getInstance}.  Applications
48c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * can query the media router about the currently selected route and its capabilities
49c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to determine how to send content to the route's destination.  Applications can
50c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * also {@link RouteInfo#sendControlRequest send control requests} to the route
51c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to ask the route's destination to perform certain remote control functions
52fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * such as playing media.
53c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p>
54c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * See also {@link MediaRouteProvider} for information on how an application
55c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * can publish new media routes to the media router.
56c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p>
57c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The media router API is not thread-safe; all interactions with it must be
58c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * done from the main thread of the process.
59c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p>
60c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */
61c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownpublic final class MediaRouter {
62c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    private static final String TAG = "MediaRouter";
63c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
64c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    // Maintains global media router state for the process.
65c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    // This field is initialized in MediaRouter.getInstance() before any
66c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    // MediaRouter objects are instantiated so it is guaranteed to be
67c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    // valid whenever any instance method is invoked.
68c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    static GlobalMediaRouter sGlobal;
69c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
70c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    // Context-bound state of the media router.
71c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    final Context mContext;
72c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    final CopyOnWriteArrayList<Callback> mCallbacks = new CopyOnWriteArrayList<Callback>();
73c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
74c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    MediaRouter(Context context) {
75c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        mContext = context;
76c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
77c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
78c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
79c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Gets an instance of the media router service from the context.
80c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
81c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public static MediaRouter getInstance(Context context) {
82c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (context == null) {
83c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            throw new IllegalArgumentException("context must not be null");
84c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
85c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
86c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
87c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (sGlobal == null) {
88c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            sGlobal = new GlobalMediaRouter(context.getApplicationContext());
89fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            sGlobal.start();
90c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
91c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return sGlobal.getRouter(context);
92c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
93c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
94c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
95fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Gets information about the {@link MediaRouter.RouteInfo routes} currently known to
96fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * this media router.
97c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
98c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public List<RouteInfo> getRoutes() {
99c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
100c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return sGlobal.getRoutes();
101c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
102c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
103c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
104fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Gets information about the {@link MediaRouter.ProviderInfo route providers}
105fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * currently known to this media router.
106fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     */
107fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public List<ProviderInfo> getProviders() {
108fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        checkCallingThread();
109fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        return sGlobal.getProviders();
110fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
111fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
112fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    /**
113c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Gets the default route for playing media content on the system.
114c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
115c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * The system always provides a default route.
116c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
117c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
118c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @return The default route, which is guaranteed to never be null.
119c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
120c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public RouteInfo getDefaultRoute() {
121c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
122c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return sGlobal.getDefaultRoute();
123c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
124c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
125c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
126c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Gets the currently selected route.
127c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
128c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * The application should examine the route's
129fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * {@link RouteInfo#getControlFilters media control intent filters} to assess the
130c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * capabilities of the route before attempting to use it.
131c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
132c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
133c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <h3>Example</h3>
134c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <pre>
135c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * public boolean playMovie() {
136c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     MediaRouter mediaRouter = MediaRouter.getInstance(context);
137c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute();
138c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
139c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     // First try using the remote playback interface, if supported.
140c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
141c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         // The route supports remote playback.
142c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         // Try to send it the Uri of the movie to play.
143c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         Intent intent = new Intent(MediaControlIntent.ACTION_PLAY);
144c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
145c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         intent.setDataAndType("http://example.com/videos/movie.mp4", "video/mp4");
146fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     *         if (route.supportsControlRequest(intent)) {
147fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     *             route.sendControlRequest(intent, null);
148fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     *             return true; // sent the request to play the movie
149c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         }
150c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     }
151c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
152c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     // If remote playback was not possible, then play locally.
153c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     if (route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)) {
154c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         // The route supports live video streaming.
155c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         // Prepare to play content locally in a window or in a presentation.
156c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *         return playMovieInWindow();
157c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     }
158c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
159c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     // Neither interface is supported, so we can't play the movie to this route.
160c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *     return false;
161c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * }
162c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </pre>
163c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
164c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @return The selected route, which is guaranteed to never be null.
165c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
166fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * @see RouteInfo#getControlFilters
167c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see RouteInfo#supportsControlCategory
168c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see RouteInfo#supportsControlRequest
169c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
170c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public RouteInfo getSelectedRoute() {
171c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
172c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return sGlobal.getSelectedRoute();
173c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
174c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
175c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
176c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Selects the specified route.
177c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
178c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @param route The route to select.
179c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
180c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public void selectRoute(RouteInfo route) {
181c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (route == null) {
182c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            throw new IllegalArgumentException("route must not be null");
183c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
184c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
185c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
186c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        sGlobal.selectRoute(route);
187c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
188c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
189c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
190c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Adds a callback to listen to changes to media routes.
191c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
192c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @param callback The callback to add.
193c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
194c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public void addCallback(Callback callback) {
195c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (callback == null) {
196c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            throw new IllegalArgumentException("callback must not be null");
197c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
198c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
199c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
200c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (!mCallbacks.contains(callback)) {
201c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mCallbacks.add(callback);
202c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
203c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
204c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
205c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
206c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Removes the specified callback.  It will no longer receive information about
207c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * changes to media routes.
208c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
209c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @param callback The callback to remove.
210c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
211c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public void removeCallback(Callback callback) {
212c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (callback == null) {
213c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            throw new IllegalArgumentException("callback must not be null");
214c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
215c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
216c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
217c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        mCallbacks.remove(callback);
218c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
219c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
220c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
221c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Registers a media route provider globally for this application process.
222c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
223fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * @param providerInstance The media route provider instance to add.
224c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
225c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see MediaRouteProvider
226c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
227fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public void addProvider(MediaRouteProvider providerInstance) {
228fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (providerInstance == null) {
229fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            throw new IllegalArgumentException("providerInstance must not be null");
230c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
231c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
232c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
233fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        sGlobal.addProvider(providerInstance);
234c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
235c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
236c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
237c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Unregisters a media route provider globally for this application process.
238c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
239fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * @param providerInstance The media route provider instance to remove.
240c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
241c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see MediaRouteProvider
242c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
243fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public void removeProvider(MediaRouteProvider providerInstance) {
244fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (providerInstance == null) {
245fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            throw new IllegalArgumentException("providerInstance must not be null");
246c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
247c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        checkCallingThread();
248c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
249fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        sGlobal.removeProvider(providerInstance);
250c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
251c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
253c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Ensures that calls into the media router are on the correct thread.
254c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * It pays to be a little paranoid when global state invariants are at risk.
255c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
256c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    static void checkCallingThread() {
257c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (Looper.myLooper() != Looper.getMainLooper()) {
258c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            throw new IllegalStateException("The media router service must only be "
259c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + "accessed on the application's main thread.");
260c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
261c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
262c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
263c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    static <T> boolean equal(T a, T b) {
264c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return a == b || (a != null && b != null && a.equals(b));
265c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
266c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
267c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
268c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Provides information about a media route.
269c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
270fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Each media route has a list of {@link MediaControlIntent media control}
271fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * {@link #getControlFilters intent filters} that describe the capabilities of the
272c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * route and the manner in which it is used and controlled.
273c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
274c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
275c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public static final class RouteInfo {
276fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final ProviderInfo mProvider;
277c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final String mDescriptorId;
278c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private String mName;
279c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private String mStatus;
280c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private Drawable mIconDrawable;
281fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int mIconResource;
282c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private boolean mEnabled;
283fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final ArrayList<IntentFilter> mControlFilters = new ArrayList<IntentFilter>();
284c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mPlaybackType;
285c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mPlaybackStream;
286c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mVolumeHandling;
287c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mVolume;
288c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mVolumeMax;
289c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private Display mPresentationDisplay;
290c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private int mPresentationDisplayId = -1;
291c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private Bundle mExtras;
292c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private RouteDescriptor mDescriptor;
293c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
294c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
295c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * The default playback type, "local", indicating the presentation of the media
296c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * is happening on the same device (e.g. a phone, a tablet) as where it is
297c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * controlled from.
298c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
299c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #getPlaybackType
300c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
301c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public static final int PLAYBACK_TYPE_LOCAL = 0;
302c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
303c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
304c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * A playback type indicating the presentation of the media is happening on
305c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * a different device (i.e. the remote device) than where it is controlled from.
306c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
307c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #getPlaybackType
308c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
309c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public static final int PLAYBACK_TYPE_REMOTE = 1;
310c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
311c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
312c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Playback information indicating the playback volume is fixed, i.e. it cannot be
313c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * controlled from this object. An example of fixed playback volume is a remote player,
314c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
315c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * than attenuate at the source.
316c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
317c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #getVolumeHandling
318c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
319c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public static final int PLAYBACK_VOLUME_FIXED = 0;
320c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
321c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
322c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Playback information indicating the playback volume is variable and can be controlled
323c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * from this object.
324c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
325c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #getVolumeHandling
326c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
327c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public static final int PLAYBACK_VOLUME_VARIABLE = 1;
328c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
329c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        static final int CHANGE_GENERAL = 1 << 0;
330c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        static final int CHANGE_VOLUME = 1 << 1;
331c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        static final int CHANGE_PRESENTATION_DISPLAY = 1 << 2;
332c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
333fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        RouteInfo(ProviderInfo provider, String descriptorId) {
334fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mProvider = provider;
335c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mDescriptorId = descriptorId;
336c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
337c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
338fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
339fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Gets information about the provider of this media route.
340fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
341fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public ProviderInfo getProvider() {
342fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mProvider;
343c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
344c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
345c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
346c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets the name of this route.
347c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
348c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The user-friendly name of a media route. This is the string presented
349c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * to users who may select this as the active route.
350c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
351c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public String getName() {
352c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mName;
353c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
354c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
355c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
356c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets the status of this route.
357c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
358c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The user-friendly status for a media route. This may include a description
359c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * of the currently playing media, if available.
360c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
361c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public String getStatus() {
362c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mStatus;
363c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
364c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
365c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
366c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Get the icon representing this route.
367c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This icon will be used in picker UIs if available.
368c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
369fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @return The icon representing this route or null if no icon is available.
370c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
371c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public Drawable getIconDrawable() {
372fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
373fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mIconDrawable == null) {
374fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mIconResource != 0) {
375fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    Resources resources = mProvider.getResources();
376fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (resources != null) {
377fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        try {
378fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            mIconDrawable = resources.getDrawable(mIconResource);
379fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        } catch (Resources.NotFoundException ex) {
380fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            Log.w(TAG, "Unable to load media route icon drawable resource "
381fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                    + "from provider.", ex);
382fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        }
383fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
384fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
385fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
386c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mIconDrawable;
387c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
388c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
389c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
390c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Returns true if this route is enabled and may be selected.
391c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
392c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return true if this route is enabled and may be selected.
393c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
394c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public boolean isEnabled() {
395c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mEnabled;
396c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
397c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
398c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
399fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Returns true if this route is currently selected.
400c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
401fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @return true if this route is currently selected.
402fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
403fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @see MediaRouter#getSelectedRoute
404fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
405fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean isSelected() {
406fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
407fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return sGlobal.getSelectedRoute() == this;
408fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
409fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
410fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
411fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Returns true if this route is the default route.
412fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
413fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @return true if this route is the default route.
414fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
415fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @see MediaRouter#getDefaultRoute
416fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
417fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean isDefault() {
418fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
419fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return sGlobal.getDefaultRoute() == this;
420fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
421fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
422fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
423fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Gets a list of {@link MediaControlIntent media control intent} filters that
424fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * describe the capabilities of this route and the media control actions that
425fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * it supports.
426fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
427fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @return A list of intent filters that specifies the media control intents that
428fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * this route supports.
429c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
430c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent
431c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #supportsControlCategory
432c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see #supportsControlRequest
433c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
434fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public List<IntentFilter> getControlFilters() {
435fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mControlFilters;
436c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
437c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
438c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
439c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Returns true if the route supports the specified
440c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * {@link MediaControlIntent media control} category.
441c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
442c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Media control categories describe the capabilities of this route
443c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * such as whether it supports live audio streaming or remote playback.
444c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
445c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
446c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param category A {@link MediaControlIntent media control} category
447c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO},
448c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO},
449fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined
450c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * media control category.
451c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
452c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent
453fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @see #getControlFilters
454c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
455c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public boolean supportsControlCategory(String category) {
456c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (category == null) {
457c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalArgumentException("category must not be null");
458c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
459fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
460c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
461fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int count = mControlFilters.size();
462fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            for (int i = 0; i < count; i++) {
463fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mControlFilters.get(i).hasCategory(category)) {
464fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return true;
465fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
466fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
467fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
468c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
469c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
470c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
471c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Returns true if the route supports the specified
472c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * {@link MediaControlIntent media control} request.
473c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
474c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Media control requests are used to request the route to perform
47543f79f79a5117550a7dfedf9c65124afd163ea43Jeff Brown         * actions such as starting remote playback of a media item.
476c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
477c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
478c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param intent A {@link MediaControlIntent media control intent}.
479c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return True if the route can handle the specified intent.
480c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
481c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent
482fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @see #getControlFilters
483c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
484c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public boolean supportsControlRequest(Intent intent) {
485c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (intent == null) {
486c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalArgumentException("intent must not be null");
487c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
488c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            checkCallingThread();
489c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
490c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            ContentResolver contentResolver = sGlobal.getContentResolver();
491fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int count = mControlFilters.size();
492fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            for (int i = 0; i < count; i++) {
493fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mControlFilters.get(i).match(contentResolver, intent, true, TAG) >= 0) {
494fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return true;
495fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
496fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
497fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
498c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
499c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
500c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
501c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Sends a {@link MediaControlIntent media control} request to be performed
502c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * asynchronously by the route's destination.
503c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
504c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Media control requests are used to request the route to perform
50543f79f79a5117550a7dfedf9c65124afd163ea43Jeff Brown         * actions such as starting remote playback of a media item.
506c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p><p>
507fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * This function may only be called on a selected route.  Control requests
508fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * sent to unselected routes will fail.
509c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
510c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
511c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param intent A {@link MediaControlIntent media control intent}.
512c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param callback A {@link ControlRequestCallback} to invoke with the result
513c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * of the request, or null if no result is required.
514c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
515c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent
516c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
517fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void sendControlRequest(Intent intent, ControlRequestCallback callback) {
518c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (intent == null) {
519c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalArgumentException("intent must not be null");
520c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
521c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            checkCallingThread();
522c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
523fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            sGlobal.sendControlRequest(this, intent, callback);
524c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
525c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
526c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
527c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets the type of playback associated with this route.
528c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
529c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The type of playback associated with this route: {@link #PLAYBACK_TYPE_LOCAL}
530c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * or {@link #PLAYBACK_TYPE_REMOTE}.
531c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
532c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getPlaybackType() {
533c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mPlaybackType;
534c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
535c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
536c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
537350ba6e4a1b5ec28721a098e50eaf6a508eb28f0Jeff Brown         * Gets the audio stream over which the playback associated with this route is performed.
538c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
539c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The stream over which the playback associated with this route is performed.
540c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
541c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getPlaybackStream() {
542c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mPlaybackStream;
543c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
544c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
545c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
546c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets information about how volume is handled on the route.
547c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
548c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return How volume is handled on the route: {@link #PLAYBACK_VOLUME_FIXED}
549c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * or {@link #PLAYBACK_VOLUME_VARIABLE}.
550c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
551c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getVolumeHandling() {
552c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mVolumeHandling;
553c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
554c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
555c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
556c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets the current volume for this route. Depending on the route, this may only
557c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * be valid if the route is currently selected.
558c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
559c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The volume at which the playback associated with this route is performed.
560c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
561c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getVolume() {
562c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mVolume;
563c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
564c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
565c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
566c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets the maximum volume at which the playback associated with this route is performed.
567c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
568c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The maximum volume at which the playback associated with
569c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * this route is performed.
570c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
571c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public int getVolumeMax() {
572c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mVolumeMax;
573c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
574c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
575c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
576c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Requests a volume change for this route asynchronously.
577c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
578c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This function may only be called on a selected route.  It will have
579c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * no effect if the route is currently unselected.
580c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
581c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
582c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param volume The new volume value between 0 and {@link #getVolumeMax}.
583c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
584c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void requestSetVolume(int volume) {
585c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            checkCallingThread();
586c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            sGlobal.requestSetVolume(this, Math.min(mVolumeMax, Math.max(0, volume)));
587c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
588c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
589c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
590c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Requests an incremental volume update for this route asynchronously.
591c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
592c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This function may only be called on a selected route.  It will have
593c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * no effect if the route is currently unselected.
594c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
595c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
596c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param delta The delta to add to the current volume.
597c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
598c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void requestUpdateVolume(int delta) {
599c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            checkCallingThread();
600c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (delta != 0) {
601c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                sGlobal.requestUpdateVolume(this, delta);
602c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
603c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
604c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
605c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
606c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets the {@link Display} that should be used by the application to show
607c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * a {@link android.app.Presentation} on an external display when this route is selected.
608c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Depending on the route, this may only be valid if the route is currently
609c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * selected.
610c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
611c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * The preferred presentation display may change independently of the route
612c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * being selected or unselected.  For example, the presentation display
613c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * of the default system route may change when an external HDMI display is connected
614c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * or disconnected even though the route itself has not changed.
615c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p><p>
616c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This method may return null if there is no external display associated with
617c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * the route or if the display is not ready to show UI yet.
618c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p><p>
619c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * The application should listen for changes to the presentation display
620c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * using the {@link Callback#onRoutePresentationDisplayChanged} callback and
621c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * show or dismiss its {@link android.app.Presentation} accordingly when the display
622c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * becomes available or is removed.
623c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p><p>
624c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This method only makes sense for
625c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO live video} routes.
626c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
627c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
628c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @return The preferred presentation display to use when this route is
629c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * selected or null if none.
630c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
631c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent#CATEGORY_LIVE_VIDEO
632c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see android.app.Presentation
633c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
634c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public Display getPresentationDisplay() {
635fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
636c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mPresentationDisplayId >= 0 && mPresentationDisplay == null) {
637c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mPresentationDisplay = sGlobal.getDisplay(mPresentationDisplayId);
638c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
639c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mPresentationDisplay;
640c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
641c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
642c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
643c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Gets a collection of extra properties about this route that were supplied
644c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * by its media route provider, or null if none.
645c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
646c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public Bundle getExtras() {
647c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mExtras;
648c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
649c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
650fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
651fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Selects this media route.
652fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
653fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void select() {
654fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
655fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            sGlobal.selectRoute(this);
656fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
657fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
658c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
659c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public String toString() {
660c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return "MediaRouter.RouteInfo{ name=" + mName
661c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", status=" + mStatus
662c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", enabled=" + mEnabled
663c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", playbackType=" + mPlaybackType
664c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", playbackStream=" + mPlaybackStream
665c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", volumeHandling=" + mVolumeHandling
666c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", volume=" + mVolume
667c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", volumeMax=" + mVolumeMax
668c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", presentationDisplayId=" + mPresentationDisplayId
669c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + ", extras=" + mExtras
670fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    + ", providerPackageName=" + mProvider.getPackageName()
671fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    + " }";
672fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
673fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
674fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        int updateDescriptor(RouteDescriptor descriptor) {
675fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int changes = 0;
676fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mDescriptor != descriptor) {
677fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mDescriptor = descriptor;
678fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (descriptor != null) {
679fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (!equal(mName, descriptor.getName())) {
680fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mName = descriptor.getName();
681fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL;
682fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
683fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (!equal(mStatus, descriptor.getStatus())) {
684fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mStatus = descriptor.getStatus();
685fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL;
686fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
687fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mIconResource != descriptor.getIconResource()) {
688fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mIconResource = descriptor.getIconResource();
689fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mIconDrawable = null;
690fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL;
691fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
692fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mIconResource == 0
693fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            && mIconDrawable != descriptor.getIconDrawable()) {
694fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mIconDrawable = descriptor.getIconDrawable();
695fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL;
696fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
697fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mEnabled != descriptor.isEnabled()) {
698fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mEnabled = descriptor.isEnabled();
699fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL;
700fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
701fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    IntentFilter[] descriptorControlFilters = descriptor.getControlFilters();
702fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (!hasSameControlFilters(descriptorControlFilters)) {
703fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mControlFilters.clear();
704fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        for (IntentFilter f : descriptorControlFilters) {
705fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            mControlFilters.add(f);
706fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        }
707fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL;
708fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
709fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mPlaybackType != descriptor.getPlaybackType()) {
710fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mPlaybackType = descriptor.getPlaybackType();
711fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL;
712fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
713fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mPlaybackStream != descriptor.getPlaybackStream()) {
714fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mPlaybackStream = descriptor.getPlaybackStream();
715fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL;
716fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
717fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mVolumeHandling != descriptor.getVolumeHandling()) {
718fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mVolumeHandling = descriptor.getVolumeHandling();
719fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL | CHANGE_VOLUME;
720fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
721fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mVolume != descriptor.getVolume()) {
722fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mVolume = descriptor.getVolume();
723fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL | CHANGE_VOLUME;
724fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
725fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mVolumeMax != descriptor.getVolumeMax()) {
726fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mVolumeMax = descriptor.getVolumeMax();
727fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL | CHANGE_VOLUME;
728fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
729fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mPresentationDisplayId != descriptor.getPresentationDisplayId()) {
730fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mPresentationDisplayId = descriptor.getPresentationDisplayId();
731fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mPresentationDisplay = null;
732fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY;
733fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
734fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (!equal(mExtras, descriptor.getExtras())) {
735fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mExtras = descriptor.getExtras();
736fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        changes |= CHANGE_GENERAL;
737fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
738fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
739fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
740fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return changes;
741fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
742fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
743fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        boolean hasSameControlFilters(IntentFilter[] controlFilters) {
744fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            final int count = mControlFilters.size();
745fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (count != controlFilters.length) {
746fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return false;
747fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
748fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            for (int i = 0; i < count; i++) {
749fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (!mControlFilters.get(i).equals(controlFilters[i])) {
750fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return false;
751fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
752fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
753fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return true;
754fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
755fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
756fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        String getDescriptorId() {
757fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mDescriptorId;
758fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
759fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
760fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        MediaRouteProvider getProviderInstance() {
761fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mProvider.getProviderInstance();
762fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
763fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
764fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
765fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    /**
766fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Provides information about a media route provider.
767fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * <p>
768fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * This object may be used to determine which media route provider has
769fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * published a particular route.
770fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * </p>
771fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     */
772fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public static final class ProviderInfo {
773fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final MediaRouteProvider mProviderInstance;
774fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
775fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
776fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final ProviderMetadata mMetadata;
777fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private ProviderDescriptor mDescriptor;
778fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private Resources mResources;
779fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private boolean mResourcesNotAvailable;
780fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
781fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        ProviderInfo(MediaRouteProvider provider) {
782fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mProviderInstance = provider;
783fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mMetadata = provider.getMetadata();
784fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
785fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
786fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
787fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Gets the provider's underlying {@link MediaRouteProvider} instance.
788fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
789fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public MediaRouteProvider getProviderInstance() {
790fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
791fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mProviderInstance;
792fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
793fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
794fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
795fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Gets the package name of the media route provider service.
796fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
797fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public String getPackageName() {
798fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mMetadata.getPackageName();
799fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
800fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
801fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
802fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Gets the {@link MediaRouter.RouteInfo routes} published by this route provider.
803fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
804fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public List<RouteInfo> getRoutes() {
805fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            checkCallingThread();
806fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mRoutes;
807fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
808fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
809fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        Resources getResources() {
810fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mResources == null && !mResourcesNotAvailable) {
811fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                String packageName = getPackageName();
812fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Context context = sGlobal.getProviderContext(packageName);
813fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (context != null) {
814fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mResources = context.getResources();
815fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                } else {
816fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    Log.w(TAG, "Unable to obtain resources for route provider package: "
817fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            + packageName);
818fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mResourcesNotAvailable = true;
819fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
820fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
821fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mResources;
822fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
823fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
824fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        boolean updateDescriptor(ProviderDescriptor descriptor) {
825fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mDescriptor != descriptor) {
826fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mDescriptor = descriptor;
827fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return true;
828fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
829fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
830fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
831fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
832fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        int findRouteByDescriptorId(String id) {
833fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            final int count = mRoutes.size();
834fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            for (int i = 0; i < count; i++) {
835fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mRoutes.get(i).mDescriptorId.equals(id)) {
836fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return i;
837fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
838fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
839fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return -1;
840fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
841fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
842fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
843fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public String toString() {
844fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return "MediaRouter.RouteProviderInfo{ packageName=" + getPackageName()
845c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    + " }";
846c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
847c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
848c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
849c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
850c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Interface for receiving events about media routing changes.
851c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * All methods of this interface will be called from the application's main thread.
852c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
853c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * A Callback will only receive events relevant to routes that the callback
854c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * was registered for.
855c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
856c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
857c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see MediaRouter#addCallback(Callback)
858c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see MediaRouter#removeCallback(Callback)
859c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
860c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public static abstract class Callback {
861c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
862fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when the supplied media route becomes selected as the active route.
863c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
864fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
865c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that has been selected.
866c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
867c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteSelected(MediaRouter router, RouteInfo route) {
868c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
869c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
870c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
871fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when the supplied media route becomes unselected as the active route.
872c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
873fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
874c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that has been unselected.
875c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
876c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteUnselected(MediaRouter router, RouteInfo route) {
877c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
878c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
879c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
880fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route has been added.
881c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
882fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
883c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that has become available for use.
884c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
885c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteAdded(MediaRouter router, RouteInfo route) {
886c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
887c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
888c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
889fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route has been removed.
890c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
891fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
892c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that has been removed from availability.
893c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
894c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteRemoved(MediaRouter router, RouteInfo route) {
895c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
896c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
897c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
898fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a property of the indicated media route has changed.
899c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
900fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
901c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route that was changed.
902c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
903c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteChanged(MediaRouter router, RouteInfo route) {
904c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
905c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
906c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
907fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route's volume changes.
908c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
909fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
910c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route whose volume changed.
911c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
912c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) {
913c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
914c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
915c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
916fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route's presentation display changes.
917c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * <p>
918c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * This method is called whenever the route's presentation display becomes
919fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * available, is removed or has changes to some of its properties (such as its size).
920c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * </p>
921c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
922fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
923c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param route The route whose presentation display changed.
924c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
925c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see RouteInfo#getPresentationDisplay()
926c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
927c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo route) {
928c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
929fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
930fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
931fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route provider has been added.
932fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
933fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
934fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param provider The provider that has become available for use.
935fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
936fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void onProviderAdded(MediaRouter router, ProviderInfo provider) {
937fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
938fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
939fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        /**
940fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Called when a media route provider has been removed.
941fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         *
942fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param router The media router reporting the event.
943fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param provider The provider that has been removed from availability.
944fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         */
945fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void onProviderRemoved(MediaRouter router, ProviderInfo provider) {
946fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
947c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
948c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
949c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
950c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Callback which is invoked with the result of a media control request.
951c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
952c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @see RouteInfo#sendControlRequest
953c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
954fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public static abstract class ControlRequestCallback {
955c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
956fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * Result code: The media control action succeeded.
957c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
958fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public static final int REQUEST_SUCCEEDED = 0;
959c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
960c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
961c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Result code: The media control action failed.
962c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
963c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public static final int REQUEST_FAILED = -1;
964c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
965c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
966c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Called with the result of the media control request.
967c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
968fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @param result The result code: {@link #REQUEST_SUCCEEDED}, or {@link #REQUEST_FAILED}.
969c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param data Additional result data.  Contents depend on the media control action.
970c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
971c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onResult(int result, Bundle data) {
972c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
973c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
974c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
975c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
976c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Global state for the media router.
977c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
978c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Media routes and media route providers are global to the process; their
979c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * state and the bulk of the media router implementation lives here.
980c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
981c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
982c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    private static final class GlobalMediaRouter implements SystemMediaRouteProvider.SyncCallback {
983c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final Context mApplicationContext;
984fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final MediaRouter mApplicationRouter;
985c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final WeakHashMap<Context, MediaRouter> mRouters =
986c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                new WeakHashMap<Context, MediaRouter>();
987c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
988fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final ArrayList<ProviderInfo> mProviders =
989fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                new ArrayList<ProviderInfo>();
990c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final ProviderCallback mProviderCallback = new ProviderCallback();
991c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final CallbackHandler mCallbackHandler = new CallbackHandler();
992c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final DisplayManagerCompat mDisplayManager;
993c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final SystemMediaRouteProvider mSystemProvider;
994c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
995fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private RegisteredMediaRouteProviderWatcher mRegisteredProviderWatcher;
996c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private RouteInfo mDefaultRoute;
997c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private RouteInfo mSelectedRoute;
998c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private MediaRouteProvider.RouteController mSelectedRouteController;
999c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1000c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        GlobalMediaRouter(Context applicationContext) {
1001c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mApplicationContext = applicationContext;
1002c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mDisplayManager = DisplayManagerCompat.getInstance(applicationContext);
1003fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mApplicationRouter = getRouter(applicationContext);
1004fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1005fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // Add the system media route provider for interoperating with
1006fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // the framework media router.  This one is special and receives
1007fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // synchronization messages from the media router.
1008c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mSystemProvider = SystemMediaRouteProvider.obtain(applicationContext, this);
1009fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            addProvider(mSystemProvider);
1010fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1011fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1012fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void start() {
1013fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // Start watching for routes published by registered media route
1014fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // provider services.
1015fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mRegisteredProviderWatcher = new RegisteredMediaRouteProviderWatcher(
1016fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mApplicationContext, mApplicationRouter);
1017fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mRegisteredProviderWatcher.start();
1018c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1019c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1020c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public MediaRouter getRouter(Context context) {
1021c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            MediaRouter router = mRouters.get(context);
1022c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (router == null) {
1023c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                router = new MediaRouter(context);
1024c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mRouters.put(context, router);
1025c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1026c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return router;
1027c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1028c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1029c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public ContentResolver getContentResolver() {
1030c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mApplicationContext.getContentResolver();
1031c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1032c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1033fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public Context getProviderContext(String packageName) {
1034fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (packageName.equals(SystemMediaRouteProvider.PACKAGE_NAME)) {
1035fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return mApplicationContext;
1036fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1037fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            try {
1038fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return mApplicationContext.createPackageContext(
1039fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        packageName, Context.CONTEXT_RESTRICTED);
1040fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            } catch (NameNotFoundException ex) {
1041fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return null;
1042fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1043fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1044fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1045c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public Display getDisplay(int displayId) {
1046c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mDisplayManager.getDisplay(displayId);
1047c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1048c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1049fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void sendControlRequest(RouteInfo route,
1050c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                Intent intent, ControlRequestCallback callback) {
1051c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (route == mSelectedRoute && mSelectedRouteController != null) {
1052129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                if (mSelectedRouteController.onControlRequest(intent, callback)) {
1053fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return;
1054fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
1055fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1056fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (callback != null) {
1057fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                callback.onResult(ControlRequestCallback.REQUEST_FAILED, null);
1058c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1059c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1060c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1061c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void requestSetVolume(RouteInfo route, int volume) {
1062c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (route == mSelectedRoute && mSelectedRouteController != null) {
1063129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                mSelectedRouteController.onSetVolume(volume);
1064c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1065c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1066c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1067c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void requestUpdateVolume(RouteInfo route, int delta) {
1068c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (route == mSelectedRoute && mSelectedRouteController != null) {
1069129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                mSelectedRouteController.onUpdateVolume(delta);
1070c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1071c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1072c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1073c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public List<RouteInfo> getRoutes() {
1074c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mRoutes;
1075c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1076c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1077fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public List<ProviderInfo> getProviders() {
1078fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return mProviders;
1079fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1080fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1081c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public RouteInfo getDefaultRoute() {
1082c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mDefaultRoute == null) {
1083c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // This should never happen once the media router has been fully
1084c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // initialized but it is good to check for the error in case there
1085c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // is a bug in provider initialization.
1086c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalStateException("There is no default route.  "
1087c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        + "The media router has not yet been fully initialized.");
1088c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1089c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mDefaultRoute;
1090c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1091c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1092c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public RouteInfo getSelectedRoute() {
1093c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mSelectedRoute == null) {
1094c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // This should never happen once the media router has been fully
1095c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // initialized but it is good to check for the error in case there
1096c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // is a bug in provider initialization.
1097c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                throw new IllegalStateException("There is no currently selected route.  "
1098c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        + "The media router has not yet been fully initialized.");
1099c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1100c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return mSelectedRoute;
1101c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1102c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1103c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void selectRoute(RouteInfo route) {
1104c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (!mRoutes.contains(route)) {
1105c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                Log.w(TAG, "Ignoring attempt to select removed route: " + route);
1106c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                return;
1107c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1108c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (!route.mEnabled) {
1109c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                Log.w(TAG, "Ignoring attempt to select disabled route: " + route);
1110c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                return;
1111c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1112c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1113c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            setSelectedRouteInternal(route);
1114c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1115c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1116fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void addProvider(MediaRouteProvider providerInstance) {
1117fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int index = findProviderInfo(providerInstance);
1118c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (index < 0) {
1119c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // 1. Add the provider to the list.
1120fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ProviderInfo provider = new ProviderInfo(providerInstance);
1121fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mProviders.add(provider);
1122fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_ADDED, provider);
1123c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // 2. Create the provider's contents.
1124fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                updateProviderContents(provider, providerInstance.getDescriptor());
1125c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // 3. Register the provider callback.
1126fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                providerInstance.addCallback(mProviderCallback);
1127c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1128c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1129c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1130fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void removeProvider(MediaRouteProvider providerInstance) {
1131fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int index = findProviderInfo(providerInstance);
1132c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (index >= 0) {
1133c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // 1. Unregister the provider callback.
1134fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                providerInstance.removeCallback(mProviderCallback);
1135c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // 2. Delete the provider's contents.
1136fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ProviderInfo provider = mProviders.get(index);
1137fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                updateProviderContents(provider, null);
1138c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // 3. Remove the provider from the list.
1139fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_REMOVED, provider);
1140fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mProviders.remove(index);
1141c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1142c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1143c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1144fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private void updateProviderDescriptor(MediaRouteProvider providerInstance,
1145fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ProviderDescriptor descriptor) {
1146fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int index = findProviderInfo(providerInstance);
1147c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (index >= 0) {
1148fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                // Update the provider's contents.
1149fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ProviderInfo provider = mProviders.get(index);
1150fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                updateProviderContents(provider, descriptor);
1151c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1152c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1153c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1154fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int findProviderInfo(MediaRouteProvider providerInstance) {
1155fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            final int count = mProviders.size();
1156c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            for (int i = 0; i < count; i++) {
1157fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mProviders.get(i).mProviderInstance == providerInstance) {
1158c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    return i;
1159c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1160c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1161c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return -1;
1162c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1163c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1164fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private void updateProviderContents(ProviderInfo provider,
1165fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ProviderDescriptor providerDescriptor) {
1166fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (provider.updateDescriptor(providerDescriptor)) {
1167c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // Update all existing routes and reorder them to match
1168c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // the order of their descriptors.
1169c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                int targetIndex = 0;
1170c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (providerDescriptor != null) {
1171fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (providerDescriptor.isValid()) {
1172fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        final RouteDescriptor[] routeDescriptors = providerDescriptor.getRoutes();
1173fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        for (int i = 0; i < routeDescriptors.length; i++) {
1174fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            final RouteDescriptor routeDescriptor = routeDescriptors[i];
1175c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            final String id = routeDescriptor.getId();
1176fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            final int sourceIndex = provider.findRouteByDescriptorId(id);
1177c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            if (sourceIndex < 0) {
1178c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                // 1. Add the route to the list.
1179fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                RouteInfo route = new RouteInfo(provider, id);
1180fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                provider.mRoutes.add(targetIndex++, route);
1181c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                mRoutes.add(route);
1182c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                // 2. Create the route's contents.
1183c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                route.updateDescriptor(routeDescriptor);
1184fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                // 3. Notify clients about addition.
1185c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route);
1186fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            } else if (sourceIndex < targetIndex) {
1187fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                Log.w(TAG, "Ignoring route descriptor with duplicate id: "
1188fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                        + routeDescriptor);
1189c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            } else {
1190c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                // 1. Reorder the route within the list.
1191fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                RouteInfo route = provider.mRoutes.get(sourceIndex);
1192fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                Collections.swap(provider.mRoutes,
1193c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                        sourceIndex, targetIndex++);
1194c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                // 2. Update the route's contents.
1195c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                int changes = route.updateDescriptor(routeDescriptor);
1196fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                // 3. Unselect route if needed before notifying about changes.
1197fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                unselectRouteIfNeeded(route);
1198fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                // 4. Notify clients about changes.
1199c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                if ((changes & RouteInfo.CHANGE_GENERAL) != 0) {
1200c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                    mCallbackHandler.post(
1201c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                            CallbackHandler.MSG_ROUTE_CHANGED, route);
1202c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                }
1203c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                if ((changes & RouteInfo.CHANGE_VOLUME) != 0) {
1204c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                    mCallbackHandler.post(
1205c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                            CallbackHandler.MSG_ROUTE_VOLUME_CHANGED, route);
1206c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                }
1207c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                if ((changes & RouteInfo.CHANGE_PRESENTATION_DISPLAY) != 0) {
1208fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                    mCallbackHandler.post(CallbackHandler.
1209fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                            MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED, route);
1210c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                                }
1211c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            }
1212c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        }
1213fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    } else {
1214fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        Log.w(TAG, "Ignoring invalid provider descriptor: " + providerDescriptor);
1215c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
1216c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1217c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1218c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // Dispose all remaining routes that do not have matching descriptors.
1219fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) {
1220fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    // 1. Delete the route's contents.
1221fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    RouteInfo route = provider.mRoutes.get(i);
1222c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    route.updateDescriptor(null);
1223fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    // 2. Remove the route from the list.
1224fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mRoutes.remove(provider);
1225fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    provider.mRoutes.remove(i);
1226fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    // 3. Unselect route if needed before notifying about removal.
1227fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    unselectRouteIfNeeded(route);
1228fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    // 4. Notify clients about removal.
1229fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_REMOVED, route);
1230c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1231fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1232fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                // Choose a new selected route if needed.
1233fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                selectRouteIfNeeded();
1234c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1235c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1236c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1237fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private void unselectRouteIfNeeded(RouteInfo route) {
1238fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mDefaultRoute == route && !isRouteSelectable(route)) {
1239c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                Log.i(TAG, "Choosing a new default route because the current one "
1240fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        + "is no longer selectable: " + route);
1241c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mDefaultRoute = null;
1242c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1243fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mSelectedRoute == route && !isRouteSelectable(route)) {
1244fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Log.i(TAG, "Choosing a new selected route because the current one "
1245fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        + "is no longer selectable: " + route);
1246fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                setSelectedRouteInternal(null);
1247fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
1248fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1249fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
1250fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private void selectRouteIfNeeded() {
1251c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mDefaultRoute == null && !mRoutes.isEmpty()) {
1252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                for (RouteInfo route : mRoutes) {
1253c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    if (isSystemDefaultRoute(route) && isRouteSelectable(route)) {
1254c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        mDefaultRoute = route;
1255c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
1256c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
1257c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1258c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1259c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mSelectedRoute == null) {
1260c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                setSelectedRouteInternal(mDefaultRoute);
1261c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1262c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1263c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1264c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private boolean isRouteSelectable(RouteInfo route) {
1265c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            // This tests whether the route is still valid and enabled.
1266c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            // The route descriptor field is set to null when the route is removed.
1267c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return route.mDescriptor != null && route.mEnabled;
1268c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1269c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1270c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private boolean isSystemDefaultRoute(RouteInfo route) {
1271fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return route.getProviderInstance() == mSystemProvider
1272c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    && route.mDescriptorId.equals(
1273c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            SystemMediaRouteProvider.DEFAULT_ROUTE_ID);
1274c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1275c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1276c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private void setSelectedRouteInternal(RouteInfo route) {
1277c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (mSelectedRoute != route) {
1278c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (mSelectedRoute != null) {
1279c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_UNSELECTED, mSelectedRoute);
1280c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    if (mSelectedRouteController != null) {
1281129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                        mSelectedRouteController.onUnselect();
1282129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                        mSelectedRouteController.onRelease();
1283c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        mSelectedRouteController = null;
1284c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
1285c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1286c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1287c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mSelectedRoute = route;
1288c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1289c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (mSelectedRoute != null) {
1290fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mSelectedRouteController = route.getProviderInstance().onCreateRouteController(
1291c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            route.mDescriptorId);
1292c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    if (mSelectedRouteController != null) {
1293129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown                        mSelectedRouteController.onSelect();
1294c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
1295c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_SELECTED, mSelectedRoute);
1296c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1297c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1298c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1299c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1300c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
1301c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public RouteInfo getSystemRouteByDescriptorId(String id) {
1302fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int providerIndex = findProviderInfo(mSystemProvider);
1303c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (providerIndex >= 0) {
1304fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ProviderInfo provider = mProviders.get(providerIndex);
1305fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                int routeIndex = provider.findRouteByDescriptorId(id);
1306c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (routeIndex >= 0) {
1307fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return provider.mRoutes.get(routeIndex);
1308c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1309c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1310c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return null;
1311c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1312c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1313c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final class ProviderCallback extends MediaRouteProvider.Callback {
1314c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            @Override
1315c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public void onDescriptorChanged(MediaRouteProvider provider,
1316fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    ProviderDescriptor descriptor) {
1317c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                updateProviderDescriptor(provider, descriptor);
1318c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1319c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1320c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1321c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final class CallbackHandler extends Handler {
1322c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            private final ArrayList<MediaRouter> mTempMediaRouters =
1323c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    new ArrayList<MediaRouter>();
1324c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1325c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public static final int MSG_ROUTE_ADDED = 1;
1326c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public static final int MSG_ROUTE_REMOVED = 2;
1327c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public static final int MSG_ROUTE_CHANGED = 3;
1328c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public static final int MSG_ROUTE_VOLUME_CHANGED = 4;
1329c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public static final int MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED = 5;
1330c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public static final int MSG_ROUTE_SELECTED = 6;
1331c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public static final int MSG_ROUTE_UNSELECTED = 7;
1332fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            public static final int MSG_PROVIDER_ADDED = 8;
1333fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            public static final int MSG_PROVIDER_REMOVED = 9;
1334c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1335fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            public void post(int msg, Object obj) {
1336fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                obtainMessage(msg, obj).sendToTarget();
1337c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1338c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1339c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            @Override
1340c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public void handleMessage(Message msg) {
1341c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                final int what = msg.what;
1342fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                final Object obj = msg.obj;
1343c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1344c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // Synchronize state with the system media router.
1345fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                syncWithSystemProvider(what, obj);
1346c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1347c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // Invoke all registered callbacks.
1348c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mTempMediaRouters.addAll(mRouters.values());
1349c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                try {
1350c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    final int routerCount = mTempMediaRouters.size();
1351c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    for (int i = 0; i < routerCount; i++) {
1352c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        final MediaRouter router = mTempMediaRouters.get(i);
1353c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        if (!router.mCallbacks.isEmpty()) {
1354c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            for (MediaRouter.Callback callback : router.mCallbacks) {
1355fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                invokeCallback(router, callback, what, obj);
1356c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            }
1357c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        }
1358c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
1359c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                } finally {
1360c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    mTempMediaRouters.clear();
1361c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1362c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1363c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1364fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            private void syncWithSystemProvider(int what, Object obj) {
1365c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                switch (what) {
1366c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_ADDED:
1367fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mSystemProvider.onSyncRouteAdded((RouteInfo)obj);
1368c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
1369c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_REMOVED:
1370fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mSystemProvider.onSyncRouteRemoved((RouteInfo)obj);
1371c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
1372c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_CHANGED:
1373fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mSystemProvider.onSyncRouteChanged((RouteInfo)obj);
1374c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
1375c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_SELECTED:
1376fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        mSystemProvider.onSyncRouteSelected((RouteInfo)obj);
1377c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
1378c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1379c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1380c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
1381c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            private void invokeCallback(MediaRouter router, MediaRouter.Callback callback,
1382fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    int what, Object obj) {
1383c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                switch (what) {
1384c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_ADDED:
1385fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        callback.onRouteAdded(router, (RouteInfo)obj);
1386c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
1387c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_REMOVED:
1388fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        callback.onRouteRemoved(router, (RouteInfo)obj);
1389c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
1390c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_CHANGED:
1391fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        callback.onRouteChanged(router, (RouteInfo)obj);
1392c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
1393c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_VOLUME_CHANGED:
1394fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        callback.onRouteVolumeChanged(router, (RouteInfo)obj);
1395c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
1396c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED:
1397fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        callback.onRoutePresentationDisplayChanged(router, (RouteInfo)obj);
1398c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
1399c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_SELECTED:
1400fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        callback.onRouteSelected(router, (RouteInfo)obj);
1401c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
1402c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    case MSG_ROUTE_UNSELECTED:
1403fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        callback.onRouteUnselected(router, (RouteInfo)obj);
1404fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        break;
1405fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    case MSG_PROVIDER_ADDED:
1406fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        callback.onProviderAdded(router, (ProviderInfo)obj);
1407fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        break;
1408fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    case MSG_PROVIDER_REMOVED:
1409fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        callback.onProviderRemoved(router, (ProviderInfo)obj);
1410c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        break;
1411c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
1412c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
1413c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
1414c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
1415c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown}
1416