1c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown/* 2c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Copyright (C) 2013 The Android Open Source Project 3c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 4c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Licensed under the Apache License, Version 2.0 (the "License"); 5c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * you may not use this file except in compliance with the License. 6c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * You may obtain a copy of the License at 7c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 8c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * http://www.apache.org/licenses/LICENSE-2.0 9c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 10c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Unless required by applicable law or agreed to in writing, software 11c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * distributed under the License is distributed on an "AS IS" BASIS, 12c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * See the License for the specific language governing permissions and 14c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * limitations under the License. 15c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 16c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 17b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brownpackage android.support.v7.media; 18c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 19c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.ContentResolver; 20c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.Context; 21c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.Intent; 22c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.IntentFilter; 23fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.pm.PackageManager.NameNotFoundException; 24fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.res.Resources; 25c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Bundle; 26c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Handler; 27c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Looper; 28c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Message; 29c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.support.v4.hardware.display.DisplayManagerCompat; 30b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brownimport android.support.v7.media.MediaRouteProvider.ProviderMetadata; 31c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.util.Log; 32c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.view.Display; 33c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 349fcedc160282e6620f409ea46bf6728b35d011ddJeff Brownimport java.lang.ref.WeakReference; 35c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.ArrayList; 36c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.Collections; 37c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.List; 38cb63b6ecac9786891514f241dec71695f09d3efbJeff Brownimport java.util.Locale; 39c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 40c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown/** 41c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter allows applications to control the routing of media channels 42c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * and streams from the current device to external speakers and destination devices. 43c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 44c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * A MediaRouter instance is retrieved through {@link #getInstance}. Applications 45c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * can query the media router about the currently selected route and its capabilities 46c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to determine how to send content to the route's destination. Applications can 47c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * also {@link RouteInfo#sendControlRequest send control requests} to the route 48c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to ask the route's destination to perform certain remote control functions 49fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * such as playing media. 50c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 51c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * See also {@link MediaRouteProvider} for information on how an application 52c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * can publish new media routes to the media router. 53c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 54c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The media router API is not thread-safe; all interactions with it must be 55c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * done from the main thread of the process. 56c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 57c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 58c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownpublic final class MediaRouter { 59c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private static final String TAG = "MediaRouter"; 60f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 61c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 62c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Maintains global media router state for the process. 63c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This field is initialized in MediaRouter.getInstance() before any 64c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // MediaRouter objects are instantiated so it is guaranteed to be 65c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // valid whenever any instance method is invoked. 66c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static GlobalMediaRouter sGlobal; 67c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 68c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Context-bound state of the media router. 69c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final Context mContext; 709fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown final ArrayList<CallbackRecord> mCallbackRecords = new ArrayList<CallbackRecord>(); 7111417b1cfde8f1749905f2d735623af9214148afJeff Brown 7211417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 7311417b1cfde8f1749905f2d735623af9214148afJeff Brown * Flag for {@link #addCallback}: Actively scan for routes while this callback 7411417b1cfde8f1749905f2d735623af9214148afJeff Brown * is registered. 7511417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 7611417b1cfde8f1749905f2d735623af9214148afJeff Brown * When this flag is specified, the media router will actively scan for new 7711417b1cfde8f1749905f2d735623af9214148afJeff Brown * routes. Certain routes, such as wifi display routes, may not be discoverable 7811417b1cfde8f1749905f2d735623af9214148afJeff Brown * except when actively scanning. This flag is typically used when the route picker 7911417b1cfde8f1749905f2d735623af9214148afJeff Brown * dialog has been opened by the user to ensure that the route information is 8011417b1cfde8f1749905f2d735623af9214148afJeff Brown * up to date. 8111417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p><p> 8211417b1cfde8f1749905f2d735623af9214148afJeff Brown * Active scanning may consume a significant amount of power and may have intrusive 8311417b1cfde8f1749905f2d735623af9214148afJeff Brown * effects on wireless connectivity. Therefore it is important that active scanning 8411417b1cfde8f1749905f2d735623af9214148afJeff Brown * only be requested when it is actually needed to satisfy a user request to 8511417b1cfde8f1749905f2d735623af9214148afJeff Brown * discover and select a new route. 86f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * </p><p> 87f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * This flag implies {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} but performing 88f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * active scans is much more expensive than a normal discovery request. 8911417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 90f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * 91f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * @see #CALLBACK_FLAG_REQUEST_DISCOVERY 9211417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 93f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1 << 0; 9411417b1cfde8f1749905f2d735623af9214148afJeff Brown 9511417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 9611417b1cfde8f1749905f2d735623af9214148afJeff Brown * Flag for {@link #addCallback}: Do not filter route events. 9711417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 9811417b1cfde8f1749905f2d735623af9214148afJeff Brown * When this flag is specified, the callback will be invoked for events that affect any 9911417b1cfde8f1749905f2d735623af9214148afJeff Brown * route event if they do not match the callback's associated media route selector. 10011417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 10111417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 10211417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 1 << 1; 10311417b1cfde8f1749905f2d735623af9214148afJeff Brown 10411417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 105f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * Flag for {@link #addCallback}: Request that route discovery be performed while this 106f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * callback is registered. 107f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * <p> 108f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * When this flag is specified, the media router will try to discover routes. 109f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * Although route discovery is intended to be efficient, checking for new routes may 110f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * result in some network activity and could slowly drain the battery. Therefore 111f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * applications should only specify {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} when 112f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * they are running in the foreground and would like to provide the user with the 113f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * option of connecting to new routes. 114f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * </p><p> 115f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * Applications should typically add a callback using this flag in the 116f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * {@link android.app.Activity activity's} {@link android.app.Activity#onStart onStart} 117f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * method and remove it in the {@link android.app.Activity#onStop onStop} method. 118f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * The {@link android.support.v7.app.MediaRouteDiscoveryFragment} fragment may 119f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * also be used for this purpose. 120f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * </p> 121f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * 122f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * @see android.support.v7.app.MediaRouteDiscoveryFragment 123f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown */ 124f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 1 << 2; 125f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown 126f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown /** 12711417b1cfde8f1749905f2d735623af9214148afJeff Brown * Flag for {@link #isRouteAvailable}: Ignore the default route. 12811417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 12911417b1cfde8f1749905f2d735623af9214148afJeff Brown * This flag is used to determine whether a matching non-default route is available. 13011417b1cfde8f1749905f2d735623af9214148afJeff Brown * This constraint may be used to decide whether to offer the route chooser dialog 13111417b1cfde8f1749905f2d735623af9214148afJeff Brown * to the user. There is no point offering the chooser if there are no 13211417b1cfde8f1749905f2d735623af9214148afJeff Brown * non-default choices. 13311417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 13411417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 13511417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1 << 0; 13611417b1cfde8f1749905f2d735623af9214148afJeff Brown 137c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouter(Context context) { 138c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mContext = context; 139c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 140c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 141c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1429fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * Gets an instance of the media router service associated with the context. 1439fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * <p> 1449fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * The application is responsible for holding a strong reference to the returned 1459fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * {@link MediaRouter} instance, such as by storing the instance in a field of 1469fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * the {@link android.app.Activity}, to ensure that the media router remains alive 1479fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * as long as the application is using its features. 1489fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * </p><p> 1499fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * In other words, the support library only holds a {@link WeakReference weak reference} 1509fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * to each media router instance. When there are no remaining strong references to the 1519fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * media router instance, all of its callbacks will be removed and route discovery 1529fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * will no longer be performed on its behalf. 1539fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * </p> 1549fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * 1559fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * @return The media router instance for the context. The application must hold 1569fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * a strong reference to this object as long as it is in use. 157c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 158c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static MediaRouter getInstance(Context context) { 159c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (context == null) { 160c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("context must not be null"); 161c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 162c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 163c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 164c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (sGlobal == null) { 165c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal = new GlobalMediaRouter(context.getApplicationContext()); 166fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown sGlobal.start(); 167c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 168c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getRouter(context); 169c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 170c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 171c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 172fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets information about the {@link MediaRouter.RouteInfo routes} currently known to 173fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * this media router. 174c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 175c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public List<RouteInfo> getRoutes() { 176c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 177c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getRoutes(); 178c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 179c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 180c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 181fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets information about the {@link MediaRouter.ProviderInfo route providers} 182fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * currently known to this media router. 183fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 184fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public List<ProviderInfo> getProviders() { 185fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 186fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return sGlobal.getProviders(); 187fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 188fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 189fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 190c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the default route for playing media content on the system. 191c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 192c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The system always provides a default route. 193c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 194c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 195c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The default route, which is guaranteed to never be null. 196c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 197c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getDefaultRoute() { 198c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 199c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getDefaultRoute(); 200c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 201c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 202c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 203c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the currently selected route. 204c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 205c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The application should examine the route's 206fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * {@link RouteInfo#getControlFilters media control intent filters} to assess the 207c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * capabilities of the route before attempting to use it. 208c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 209c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 210c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <h3>Example</h3> 211c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <pre> 212c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * public boolean playMovie() { 213c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter mediaRouter = MediaRouter.getInstance(context); 214c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute(); 215c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 216c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // First try using the remote playback interface, if supported. 217c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) { 218c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // The route supports remote playback. 219c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // Try to send it the Uri of the movie to play. 220c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Intent intent = new Intent(MediaControlIntent.ACTION_PLAY); 221c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); 222c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * intent.setDataAndType("http://example.com/videos/movie.mp4", "video/mp4"); 223fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * if (route.supportsControlRequest(intent)) { 224fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * route.sendControlRequest(intent, null); 225fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * return true; // sent the request to play the movie 226c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 227c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 228c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 229c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // If remote playback was not possible, then play locally. 230c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * if (route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)) { 231c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // The route supports live video streaming. 232c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // Prepare to play content locally in a window or in a presentation. 233c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * return playMovieInWindow(); 234c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 235c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 236c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // Neither interface is supported, so we can't play the movie to this route. 237c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * return false; 238c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 239c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </pre> 240c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 241c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The selected route, which is guaranteed to never be null. 242c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 243fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see RouteInfo#getControlFilters 244c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#supportsControlCategory 245c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#supportsControlRequest 246c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 247c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getSelectedRoute() { 248c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 249c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getSelectedRoute(); 250c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 251c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 25328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * Returns the selected route if it matches the specified selector, otherwise 25428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * selects the default route and returns it. 25528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 25628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @param selector The selector to match. 25728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @return The previously selected route if it matched the selector, otherwise the 25828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * newly selected default route which is guaranteed to never be null. 25928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 26011417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see MediaRouteSelector 26128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @see RouteInfo#matchesSelector 26228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @see RouteInfo#isDefault 26328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown */ 26411417b1cfde8f1749905f2d735623af9214148afJeff Brown public RouteInfo updateSelectedRoute(MediaRouteSelector selector) { 26528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown if (selector == null) { 26628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown throw new IllegalArgumentException("selector must not be null"); 26728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 26828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown checkCallingThread(); 26928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 27011417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 27111417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "updateSelectedRoute: " + selector); 27211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 27328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown RouteInfo route = sGlobal.getSelectedRoute(); 27411417b1cfde8f1749905f2d735623af9214148afJeff Brown if (!route.isDefault() && !route.matchesSelector(selector)) { 27528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown route = sGlobal.getDefaultRoute(); 27628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown sGlobal.selectRoute(route); 27728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 27828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown return route; 27928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 28028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 28128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 282c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Selects the specified route. 283c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 284c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route to select. 285c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 286c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void selectRoute(RouteInfo route) { 287c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == null) { 288c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("route must not be null"); 289c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 290c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 291c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 29211417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 29311417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "selectRoute: " + route); 29411417b1cfde8f1749905f2d735623af9214148afJeff Brown } 295c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal.selectRoute(route); 296c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 297c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 298c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 299d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown * Returns true if there is a route that matches the specified selector. 30011417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 301d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown * This method returns true if there are any available routes that match the selector 302d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown * regardless of whether they are enabled or disabled. If the 303d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} flag is specified, then 304d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown * the method will only consider non-default routes. 30511417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 306c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 30711417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param selector The selector to match. 30811417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param flags Flags to control the determination of whether a route may be available. 309d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown * May be zero or {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE}. 31011417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if a matching route may be available. 311c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 31211417b1cfde8f1749905f2d735623af9214148afJeff Brown public boolean isRouteAvailable(MediaRouteSelector selector, int flags) { 31311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (selector == null) { 31411417b1cfde8f1749905f2d735623af9214148afJeff Brown throw new IllegalArgumentException("selector must not be null"); 315c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 316c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 317c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 31811417b1cfde8f1749905f2d735623af9214148afJeff Brown return sGlobal.isRouteAvailable(selector, flags); 319c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 320c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 321c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 32211417b1cfde8f1749905f2d735623af9214148afJeff Brown * Registers a callback to discover routes that match the selector and to receive 32311417b1cfde8f1749905f2d735623af9214148afJeff Brown * events when they change. 32411417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 32511417b1cfde8f1749905f2d735623af9214148afJeff Brown * This is a convenience method that has the same effect as calling 32611417b1cfde8f1749905f2d735623af9214148afJeff Brown * {@link #addCallback(MediaRouteSelector, Callback, int)} without flags. 32711417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 328c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 32911417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param selector A route selector that indicates the kinds of routes that the 33011417b1cfde8f1749905f2d735623af9214148afJeff Brown * callback would like to discover. 33111417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param callback The callback to add. 33211417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see #removeCallback 333c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 33411417b1cfde8f1749905f2d735623af9214148afJeff Brown public void addCallback(MediaRouteSelector selector, Callback callback) { 33511417b1cfde8f1749905f2d735623af9214148afJeff Brown addCallback(selector, callback, 0); 336c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 337c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 338c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 33911417b1cfde8f1749905f2d735623af9214148afJeff Brown * Registers a callback to discover routes that match the selector and to receive 34011417b1cfde8f1749905f2d735623af9214148afJeff Brown * events when they change. 34128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * <p> 34211417b1cfde8f1749905f2d735623af9214148afJeff Brown * The selector describes the kinds of routes that the application wants to 34311417b1cfde8f1749905f2d735623af9214148afJeff Brown * discover. For example, if the application wants to use 34411417b1cfde8f1749905f2d735623af9214148afJeff Brown * live audio routes then it should include the 34511417b1cfde8f1749905f2d735623af9214148afJeff Brown * {@link MediaControlIntent#CATEGORY_LIVE_AUDIO live audio media control intent category} 34611417b1cfde8f1749905f2d735623af9214148afJeff Brown * in its selector when it adds a callback to the media router. 34711417b1cfde8f1749905f2d735623af9214148afJeff Brown * The selector may include any number of categories. 34828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * </p><p> 34911417b1cfde8f1749905f2d735623af9214148afJeff Brown * If the callback has already been registered, then the selector is added to 35011417b1cfde8f1749905f2d735623af9214148afJeff Brown * the set of selectors being monitored by the callback. 35111417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p><p> 35211417b1cfde8f1749905f2d735623af9214148afJeff Brown * By default, the callback will only be invoked for events that affect routes 35311417b1cfde8f1749905f2d735623af9214148afJeff Brown * that match the specified selector. Event filtering may be disabled by specifying 35411417b1cfde8f1749905f2d735623af9214148afJeff Brown * the {@link #CALLBACK_FLAG_UNFILTERED_EVENTS} flag when the callback is registered. 35528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * </p> 35628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 35728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * <h3>Example</h3> 35828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * <pre> 35911417b1cfde8f1749905f2d735623af9214148afJeff Brown * public class MyActivity extends Activity { 36011417b1cfde8f1749905f2d735623af9214148afJeff Brown * private MediaRouter mRouter; 36111417b1cfde8f1749905f2d735623af9214148afJeff Brown * private MediaRouter.Callback mCallback; 36211417b1cfde8f1749905f2d735623af9214148afJeff Brown * private MediaRouteSelector mSelector; 36328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 36411417b1cfde8f1749905f2d735623af9214148afJeff Brown * protected void onCreate(Bundle savedInstanceState) { 36511417b1cfde8f1749905f2d735623af9214148afJeff Brown * super.onCreate(savedInstanceState); 36611417b1cfde8f1749905f2d735623af9214148afJeff Brown * 36711417b1cfde8f1749905f2d735623af9214148afJeff Brown * mRouter = Mediarouter.getInstance(this); 36811417b1cfde8f1749905f2d735623af9214148afJeff Brown * mCallback = new MyCallback(); 36911417b1cfde8f1749905f2d735623af9214148afJeff Brown * mSelector = new MediaRouteSelector.Builder() 37011417b1cfde8f1749905f2d735623af9214148afJeff Brown * .addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO) 37111417b1cfde8f1749905f2d735623af9214148afJeff Brown * .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK) 37211417b1cfde8f1749905f2d735623af9214148afJeff Brown * .build(); 37328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * } 37428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 375f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * // Add the callback on start to tell the media router what kinds of routes 37611417b1cfde8f1749905f2d735623af9214148afJeff Brown * // the application is interested in so that it can try to discover suitable ones. 377f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * public void onStart() { 378f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * super.onStart(); 37911417b1cfde8f1749905f2d735623af9214148afJeff Brown * 380f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * mediaRouter.addCallback(mSelector, mCallback, 381f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY); 38211417b1cfde8f1749905f2d735623af9214148afJeff Brown * 38311417b1cfde8f1749905f2d735623af9214148afJeff Brown * MediaRouter.RouteInfo route = mediaRouter.updateSelectedRoute(mSelector); 38411417b1cfde8f1749905f2d735623af9214148afJeff Brown * // do something with the route... 38511417b1cfde8f1749905f2d735623af9214148afJeff Brown * } 38611417b1cfde8f1749905f2d735623af9214148afJeff Brown * 387f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * // Remove the selector on stop to tell the media router that it no longer 38811417b1cfde8f1749905f2d735623af9214148afJeff Brown * // needs to invest effort trying to discover routes of these kinds for now. 389f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * public void onStop() { 390f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * super.onStop(); 39111417b1cfde8f1749905f2d735623af9214148afJeff Brown * 39211417b1cfde8f1749905f2d735623af9214148afJeff Brown * mediaRouter.removeCallback(mCallback); 39311417b1cfde8f1749905f2d735623af9214148afJeff Brown * } 39411417b1cfde8f1749905f2d735623af9214148afJeff Brown * 39511417b1cfde8f1749905f2d735623af9214148afJeff Brown * private final class MyCallback extends MediaRouter.Callback { 39611417b1cfde8f1749905f2d735623af9214148afJeff Brown * // Implement callback methods as needed. 39728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * } 39828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * } 39928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * </pre> 40028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 40111417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param selector A route selector that indicates the kinds of routes that the 40211417b1cfde8f1749905f2d735623af9214148afJeff Brown * callback would like to discover. 40311417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param callback The callback to add. 40411417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param flags Flags to control the behavior of the callback. 405f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * May be zero or a combination of {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and 40611417b1cfde8f1749905f2d735623af9214148afJeff Brown * {@link #CALLBACK_FLAG_UNFILTERED_EVENTS}. 40711417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see #removeCallback 40828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown */ 40911417b1cfde8f1749905f2d735623af9214148afJeff Brown public void addCallback(MediaRouteSelector selector, Callback callback, int flags) { 41028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown if (selector == null) { 41128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown throw new IllegalArgumentException("selector must not be null"); 41228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 41311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (callback == null) { 41411417b1cfde8f1749905f2d735623af9214148afJeff Brown throw new IllegalArgumentException("callback must not be null"); 41511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 41628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown checkCallingThread(); 41728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 41811417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 41911417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "addCallback: selector=" + selector 42011417b1cfde8f1749905f2d735623af9214148afJeff Brown + ", callback=" + callback + ", flags=" + Integer.toHexString(flags)); 42111417b1cfde8f1749905f2d735623af9214148afJeff Brown } 42211417b1cfde8f1749905f2d735623af9214148afJeff Brown 42311417b1cfde8f1749905f2d735623af9214148afJeff Brown CallbackRecord record; 42411417b1cfde8f1749905f2d735623af9214148afJeff Brown int index = findCallbackRecord(callback); 42511417b1cfde8f1749905f2d735623af9214148afJeff Brown if (index < 0) { 4269fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown record = new CallbackRecord(this, callback); 42711417b1cfde8f1749905f2d735623af9214148afJeff Brown mCallbackRecords.add(record); 42811417b1cfde8f1749905f2d735623af9214148afJeff Brown } else { 42911417b1cfde8f1749905f2d735623af9214148afJeff Brown record = mCallbackRecords.get(index); 43011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 43111417b1cfde8f1749905f2d735623af9214148afJeff Brown boolean updateNeeded = false; 43211417b1cfde8f1749905f2d735623af9214148afJeff Brown if ((flags & ~record.mFlags) != 0) { 43311417b1cfde8f1749905f2d735623af9214148afJeff Brown record.mFlags |= flags; 43411417b1cfde8f1749905f2d735623af9214148afJeff Brown updateNeeded = true; 43511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 43611417b1cfde8f1749905f2d735623af9214148afJeff Brown if (!record.mSelector.contains(selector)) { 43711417b1cfde8f1749905f2d735623af9214148afJeff Brown record.mSelector = new MediaRouteSelector.Builder(record.mSelector) 43811417b1cfde8f1749905f2d735623af9214148afJeff Brown .addSelector(selector) 43911417b1cfde8f1749905f2d735623af9214148afJeff Brown .build(); 44011417b1cfde8f1749905f2d735623af9214148afJeff Brown updateNeeded = true; 44111417b1cfde8f1749905f2d735623af9214148afJeff Brown } 44211417b1cfde8f1749905f2d735623af9214148afJeff Brown if (updateNeeded) { 44311417b1cfde8f1749905f2d735623af9214148afJeff Brown sGlobal.updateDiscoveryRequest(); 44428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 44528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 44628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 44728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 44811417b1cfde8f1749905f2d735623af9214148afJeff Brown * Removes the specified callback. It will no longer receive events about 44911417b1cfde8f1749905f2d735623af9214148afJeff Brown * changes to media routes. 45028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 45111417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param callback The callback to remove. 45211417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see #addCallback 45328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown */ 45411417b1cfde8f1749905f2d735623af9214148afJeff Brown public void removeCallback(Callback callback) { 45511417b1cfde8f1749905f2d735623af9214148afJeff Brown if (callback == null) { 45611417b1cfde8f1749905f2d735623af9214148afJeff Brown throw new IllegalArgumentException("callback must not be null"); 45728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 45828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown checkCallingThread(); 45928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 46011417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 46111417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "removeCallback: callback=" + callback); 46211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 46311417b1cfde8f1749905f2d735623af9214148afJeff Brown 46411417b1cfde8f1749905f2d735623af9214148afJeff Brown int index = findCallbackRecord(callback); 46511417b1cfde8f1749905f2d735623af9214148afJeff Brown if (index >= 0) { 46611417b1cfde8f1749905f2d735623af9214148afJeff Brown mCallbackRecords.remove(index); 46711417b1cfde8f1749905f2d735623af9214148afJeff Brown sGlobal.updateDiscoveryRequest(); 46828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 46928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 47028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 47111417b1cfde8f1749905f2d735623af9214148afJeff Brown private int findCallbackRecord(Callback callback) { 47211417b1cfde8f1749905f2d735623af9214148afJeff Brown final int count = mCallbackRecords.size(); 47311417b1cfde8f1749905f2d735623af9214148afJeff Brown for (int i = 0; i < count; i++) { 47411417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mCallbackRecords.get(i).mCallback == callback) { 47511417b1cfde8f1749905f2d735623af9214148afJeff Brown return i; 47611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 47711417b1cfde8f1749905f2d735623af9214148afJeff Brown } 47811417b1cfde8f1749905f2d735623af9214148afJeff Brown return -1; 47911417b1cfde8f1749905f2d735623af9214148afJeff Brown } 48011417b1cfde8f1749905f2d735623af9214148afJeff Brown 48128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 4829942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * Registers a media route provider within this application process. 4839942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * <p> 4849942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * The provider will be added to the list of providers that all {@link MediaRouter} 4859942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * instances within this process can use to discover routes. 4869942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * </p> 487c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 488fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param providerInstance The media route provider instance to add. 489c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 490c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaRouteProvider 49128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @see #removeCallback 492c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 493fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void addProvider(MediaRouteProvider providerInstance) { 494fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (providerInstance == null) { 495fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown throw new IllegalArgumentException("providerInstance must not be null"); 496c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 497c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 498c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 49911417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 50011417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "addProvider: " + providerInstance); 50111417b1cfde8f1749905f2d735623af9214148afJeff Brown } 502fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown sGlobal.addProvider(providerInstance); 503c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 504c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 505c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 5069942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * Unregisters a media route provider within this application process. 5079942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * <p> 5089942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * The provider will be removed from the list of providers that all {@link MediaRouter} 5099942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * instances within this process can use to discover routes. 5109942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * </p> 511c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 512fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param providerInstance The media route provider instance to remove. 513c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 514c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaRouteProvider 51528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @see #addCallback 516c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 517fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void removeProvider(MediaRouteProvider providerInstance) { 518fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (providerInstance == null) { 519fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown throw new IllegalArgumentException("providerInstance must not be null"); 520c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 521c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 522c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 52311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 52411417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "removeProvider: " + providerInstance); 52528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 52611417b1cfde8f1749905f2d735623af9214148afJeff Brown sGlobal.removeProvider(providerInstance); 52728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 52828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 52928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 530c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Ensures that calls into the media router are on the correct thread. 531c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * It pays to be a little paranoid when global state invariants are at risk. 532c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 533c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static void checkCallingThread() { 534c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (Looper.myLooper() != Looper.getMainLooper()) { 535c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalStateException("The media router service must only be " 536c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + "accessed on the application's main thread."); 537c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 538c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 539c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 540c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static <T> boolean equal(T a, T b) { 541c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return a == b || (a != null && b != null && a.equals(b)); 542c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 543c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 544c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 545c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Provides information about a media route. 546c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 547fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Each media route has a list of {@link MediaControlIntent media control} 548fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * {@link #getControlFilters intent filters} that describe the capabilities of the 549c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * route and the manner in which it is used and controlled. 550c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 551c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 552c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final class RouteInfo { 553fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private final ProviderInfo mProvider; 554c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final String mDescriptorId; 555cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown private final String mUniqueId; 556c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private String mName; 557d63957d28aaabcec588b8cde12eac16414783aebJeff Brown private String mDescription; 558c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private boolean mEnabled; 55911417b1cfde8f1749905f2d735623af9214148afJeff Brown private boolean mConnecting; 560fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private final ArrayList<IntentFilter> mControlFilters = new ArrayList<IntentFilter>(); 561c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mPlaybackType; 562c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mPlaybackStream; 563c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mVolumeHandling; 564c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mVolume; 565c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mVolumeMax; 566c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private Display mPresentationDisplay; 567c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mPresentationDisplayId = -1; 568c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private Bundle mExtras; 56911417b1cfde8f1749905f2d735623af9214148afJeff Brown private MediaRouteDescriptor mDescriptor; 570c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 571c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 572c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The default playback type, "local", indicating the presentation of the media 573c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * is happening on the same device (e.g. a phone, a tablet) as where it is 574c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * controlled from. 575c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 576c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getPlaybackType 577c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 578c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_TYPE_LOCAL = 0; 579c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 580c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 581c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * A playback type indicating the presentation of the media is happening on 582c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * a different device (i.e. the remote device) than where it is controlled from. 583c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 584c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getPlaybackType 585c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 586c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_TYPE_REMOTE = 1; 587c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 588c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 589c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Playback information indicating the playback volume is fixed, i.e. it cannot be 590c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * controlled from this object. An example of fixed playback volume is a remote player, 591c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather 592c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * than attenuate at the source. 593c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 594c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getVolumeHandling 595c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 596c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_VOLUME_FIXED = 0; 597c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 598c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 599c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Playback information indicating the playback volume is variable and can be controlled 600c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * from this object. 601c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 602c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getVolumeHandling 603c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 604c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_VOLUME_VARIABLE = 1; 605c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 606c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static final int CHANGE_GENERAL = 1 << 0; 607c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static final int CHANGE_VOLUME = 1 << 1; 608c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static final int CHANGE_PRESENTATION_DISPLAY = 1 << 2; 609c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 610cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown RouteInfo(ProviderInfo provider, String descriptorId, String uniqueId) { 611fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mProvider = provider; 612c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDescriptorId = descriptorId; 613cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown mUniqueId = uniqueId; 614c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 615c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 616fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 617fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets information about the provider of this media route. 618fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 619fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public ProviderInfo getProvider() { 620fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mProvider; 621c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 622c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 623c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 624cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * Gets the unique id of the route. 625cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * <p> 626cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * The route unique id functions as a stable identifier by which the route is known. 627cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * For example, an application can use this id as a token to remember the 628cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * selected route across restarts or to communicate its identity to a service. 629cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * </p> 630cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * 631cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * @return The unique id of the route, never null. 632cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown */ 633cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown public String getId() { 634cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return mUniqueId; 635cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 636cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown 637cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown /** 638d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * Gets the user-visible name of the route. 639d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * <p> 640d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * The route name identifies the destination represented by the route. 641d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * It may be a user-supplied name, an alias, or device serial number. 642d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * </p> 643c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 644d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * @return The user-visible name of a media route. This is the string presented 645c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to users who may select this as the active route. 646c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 647c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public String getName() { 648c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mName; 649c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 650c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 651c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 652d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * Gets the user-visible description of the route. 653d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * <p> 654d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * The route description describes the kind of destination represented by the route. 655d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * It may be a user-supplied string, a model number or brand of device. 656d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * </p> 657c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 658d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * @return The description of the route, or null if none. 659c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 660d63957d28aaabcec588b8cde12eac16414783aebJeff Brown public String getDescription() { 661d63957d28aaabcec588b8cde12eac16414783aebJeff Brown return mDescription; 662c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 663c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 664c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 665c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Returns true if this route is enabled and may be selected. 666c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 66711417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if this route is enabled. 668c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 669c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public boolean isEnabled() { 670c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mEnabled; 671c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 672c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 673c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 67411417b1cfde8f1749905f2d735623af9214148afJeff Brown * Returns true if the route is in the process of connecting and is not 67511417b1cfde8f1749905f2d735623af9214148afJeff Brown * yet ready for use. 67611417b1cfde8f1749905f2d735623af9214148afJeff Brown * 67711417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if this route is in the process of connecting. 67811417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 67911417b1cfde8f1749905f2d735623af9214148afJeff Brown public boolean isConnecting() { 68011417b1cfde8f1749905f2d735623af9214148afJeff Brown return mConnecting; 68111417b1cfde8f1749905f2d735623af9214148afJeff Brown } 68211417b1cfde8f1749905f2d735623af9214148afJeff Brown 68311417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 684fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Returns true if this route is currently selected. 685c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 68611417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if this route is currently selected. 687fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 688fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see MediaRouter#getSelectedRoute 689fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 690fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public boolean isSelected() { 691fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 692fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return sGlobal.getSelectedRoute() == this; 693fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 694fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 695fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 696fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Returns true if this route is the default route. 697fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 69811417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if this route is the default route. 699fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 700fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see MediaRouter#getDefaultRoute 701fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 702fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public boolean isDefault() { 703fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 704fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return sGlobal.getDefaultRoute() == this; 705fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 706fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 707fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 708fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets a list of {@link MediaControlIntent media control intent} filters that 709fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * describe the capabilities of this route and the media control actions that 710fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * it supports. 711fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 712fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @return A list of intent filters that specifies the media control intents that 713fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * this route supports. 714c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 715c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 716c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #supportsControlCategory 717c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #supportsControlRequest 718c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 719fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public List<IntentFilter> getControlFilters() { 720fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mControlFilters; 721c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 722c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 723c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 72428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * Returns true if the route supports at least one of the capabilities 72528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * described by a media route selector. 72628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 72728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @param selector The selector that specifies the capabilities to check. 72828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @return True if the route supports at least one of the capabilities 72928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * described in the media route selector. 73028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown */ 73111417b1cfde8f1749905f2d735623af9214148afJeff Brown public boolean matchesSelector(MediaRouteSelector selector) { 73228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown if (selector == null) { 73328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown throw new IllegalArgumentException("selector must not be null"); 73428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 73528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown checkCallingThread(); 73611417b1cfde8f1749905f2d735623af9214148afJeff Brown return selector.matchesControlFilters(mControlFilters); 73728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 73828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 73928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 740c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Returns true if the route supports the specified 741c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent media control} category. 742c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 743c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media control categories describe the capabilities of this route 744c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * such as whether it supports live audio streaming or remote playback. 745c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 746c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 747c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param category A {@link MediaControlIntent media control} category 748c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO}, 749c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO}, 750fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined 751c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * media control category. 75228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @return True if the route supports the specified intent category. 753c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 754c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 755fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see #getControlFilters 756c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 757c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public boolean supportsControlCategory(String category) { 758c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (category == null) { 759c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("category must not be null"); 760c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 761fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 762c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 763fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int count = mControlFilters.size(); 764fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown for (int i = 0; i < count; i++) { 765fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mControlFilters.get(i).hasCategory(category)) { 766fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return true; 767fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 768fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 769fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return false; 770c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 771c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 772c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 773c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Returns true if the route supports the specified 774c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent media control} request. 775c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 776c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media control requests are used to request the route to perform 77743f79f79a5117550a7dfedf9c65124afd163ea43Jeff Brown * actions such as starting remote playback of a media item. 778c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 779c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 780c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param intent A {@link MediaControlIntent media control intent}. 781c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return True if the route can handle the specified intent. 782c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 783c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 784fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see #getControlFilters 785c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 786c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public boolean supportsControlRequest(Intent intent) { 787c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (intent == null) { 788c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("intent must not be null"); 789c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 790c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 791c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 792c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown ContentResolver contentResolver = sGlobal.getContentResolver(); 793fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int count = mControlFilters.size(); 794fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown for (int i = 0; i < count; i++) { 795fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mControlFilters.get(i).match(contentResolver, intent, true, TAG) >= 0) { 796fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return true; 797fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 798fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 799fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return false; 800c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 801c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 802c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 803c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Sends a {@link MediaControlIntent media control} request to be performed 804c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * asynchronously by the route's destination. 805c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 806c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media control requests are used to request the route to perform 80743f79f79a5117550a7dfedf9c65124afd163ea43Jeff Brown * actions such as starting remote playback of a media item. 808c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 809fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * This function may only be called on a selected route. Control requests 810fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * sent to unselected routes will fail. 811c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 812c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 813c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param intent A {@link MediaControlIntent media control intent}. 814c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param callback A {@link ControlRequestCallback} to invoke with the result 815c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * of the request, or null if no result is required. 816c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 817c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 818c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 819fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void sendControlRequest(Intent intent, ControlRequestCallback callback) { 820c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (intent == null) { 821c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("intent must not be null"); 822c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 823c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 824c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 825fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown sGlobal.sendControlRequest(this, intent, callback); 826c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 827c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 828c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 829c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the type of playback associated with this route. 830c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 831c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The type of playback associated with this route: {@link #PLAYBACK_TYPE_LOCAL} 832c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * or {@link #PLAYBACK_TYPE_REMOTE}. 833c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 834c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getPlaybackType() { 835c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mPlaybackType; 836c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 837c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 838c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 839350ba6e4a1b5ec28721a098e50eaf6a508eb28f0Jeff Brown * Gets the audio stream over which the playback associated with this route is performed. 840c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 841c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The stream over which the playback associated with this route is performed. 842c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 843c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getPlaybackStream() { 844c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mPlaybackStream; 845c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 846c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 847c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 848c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets information about how volume is handled on the route. 849c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 850c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return How volume is handled on the route: {@link #PLAYBACK_VOLUME_FIXED} 851c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * or {@link #PLAYBACK_VOLUME_VARIABLE}. 852c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 853c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getVolumeHandling() { 854c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mVolumeHandling; 855c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 856c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 857c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 858c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the current volume for this route. Depending on the route, this may only 859c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * be valid if the route is currently selected. 860c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 861c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The volume at which the playback associated with this route is performed. 862c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 863c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getVolume() { 864c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mVolume; 865c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 866c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 867c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 868c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the maximum volume at which the playback associated with this route is performed. 869c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 870c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The maximum volume at which the playback associated with 871c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * this route is performed. 872c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 873c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getVolumeMax() { 874c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mVolumeMax; 875c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 876c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 877c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 878c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Requests a volume change for this route asynchronously. 879c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 880c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This function may only be called on a selected route. It will have 881c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * no effect if the route is currently unselected. 882c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 883c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 884c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param volume The new volume value between 0 and {@link #getVolumeMax}. 885c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 886c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestSetVolume(int volume) { 887c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 888c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal.requestSetVolume(this, Math.min(mVolumeMax, Math.max(0, volume))); 889c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 890c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 891c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 892c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Requests an incremental volume update for this route asynchronously. 893c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 894c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This function may only be called on a selected route. It will have 895c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * no effect if the route is currently unselected. 896c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 897c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 898c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param delta The delta to add to the current volume. 899c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 900c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestUpdateVolume(int delta) { 901c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 902c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (delta != 0) { 903c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal.requestUpdateVolume(this, delta); 904c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 905c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 906c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 907c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 908c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the {@link Display} that should be used by the application to show 909c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * a {@link android.app.Presentation} on an external display when this route is selected. 910c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Depending on the route, this may only be valid if the route is currently 911c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * selected. 912c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 913c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The preferred presentation display may change independently of the route 914c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * being selected or unselected. For example, the presentation display 915c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * of the default system route may change when an external HDMI display is connected 916c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * or disconnected even though the route itself has not changed. 917c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 918c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This method may return null if there is no external display associated with 919c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * the route or if the display is not ready to show UI yet. 920c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 921c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The application should listen for changes to the presentation display 922c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * using the {@link Callback#onRoutePresentationDisplayChanged} callback and 923c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * show or dismiss its {@link android.app.Presentation} accordingly when the display 924c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * becomes available or is removed. 925c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 926c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This method only makes sense for 927c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO live video} routes. 928c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 929c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 930c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The preferred presentation display to use when this route is 931c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * selected or null if none. 932c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 933c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent#CATEGORY_LIVE_VIDEO 934c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see android.app.Presentation 935c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 936c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public Display getPresentationDisplay() { 937fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 938c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mPresentationDisplayId >= 0 && mPresentationDisplay == null) { 939c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mPresentationDisplay = sGlobal.getDisplay(mPresentationDisplayId); 940c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 941c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mPresentationDisplay; 942c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 943c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 944c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 945c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets a collection of extra properties about this route that were supplied 946c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * by its media route provider, or null if none. 947c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 948c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public Bundle getExtras() { 949c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mExtras; 950c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 951c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 952fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 953fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Selects this media route. 954fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 955fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void select() { 956fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 957fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown sGlobal.selectRoute(this); 958fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 959fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 960c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 961c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public String toString() { 962cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return "MediaRouter.RouteInfo{ uniqueId=" + mUniqueId 963cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown + ", name=" + mName 964d63957d28aaabcec588b8cde12eac16414783aebJeff Brown + ", description=" + mDescription 965c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", enabled=" + mEnabled 96611417b1cfde8f1749905f2d735623af9214148afJeff Brown + ", connecting=" + mConnecting 967c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", playbackType=" + mPlaybackType 968c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", playbackStream=" + mPlaybackStream 969c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", volumeHandling=" + mVolumeHandling 970c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", volume=" + mVolume 971c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", volumeMax=" + mVolumeMax 972c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", presentationDisplayId=" + mPresentationDisplayId 973c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", extras=" + mExtras 974fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown + ", providerPackageName=" + mProvider.getPackageName() 975fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown + " }"; 976fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 977fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 97811417b1cfde8f1749905f2d735623af9214148afJeff Brown int updateDescriptor(MediaRouteDescriptor descriptor) { 979fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int changes = 0; 980fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mDescriptor != descriptor) { 981fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mDescriptor = descriptor; 982fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (descriptor != null) { 983fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (!equal(mName, descriptor.getName())) { 984fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mName = descriptor.getName(); 985fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown changes |= CHANGE_GENERAL; 986fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 987d63957d28aaabcec588b8cde12eac16414783aebJeff Brown if (!equal(mDescription, descriptor.getDescription())) { 988d63957d28aaabcec588b8cde12eac16414783aebJeff Brown mDescription = descriptor.getDescription(); 989fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown changes |= CHANGE_GENERAL; 990fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 991fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mEnabled != descriptor.isEnabled()) { 992fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mEnabled = descriptor.isEnabled(); 993fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown changes |= CHANGE_GENERAL; 994fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 99511417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mConnecting != descriptor.isConnecting()) { 99611417b1cfde8f1749905f2d735623af9214148afJeff Brown mConnecting = descriptor.isConnecting(); 99711417b1cfde8f1749905f2d735623af9214148afJeff Brown changes |= CHANGE_GENERAL; 99811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 99911417b1cfde8f1749905f2d735623af9214148afJeff Brown if (!mControlFilters.equals(descriptor.getControlFilters())) { 1000fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mControlFilters.clear(); 100111417b1cfde8f1749905f2d735623af9214148afJeff Brown mControlFilters.addAll(descriptor.getControlFilters()); 1002fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown changes |= CHANGE_GENERAL; 1003fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1004fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mPlaybackType != descriptor.getPlaybackType()) { 1005fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mPlaybackType = descriptor.getPlaybackType(); 1006fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown changes |= CHANGE_GENERAL; 1007fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1008fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mPlaybackStream != descriptor.getPlaybackStream()) { 1009fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mPlaybackStream = descriptor.getPlaybackStream(); 1010fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown changes |= CHANGE_GENERAL; 1011fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1012fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mVolumeHandling != descriptor.getVolumeHandling()) { 1013fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mVolumeHandling = descriptor.getVolumeHandling(); 1014fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown changes |= CHANGE_GENERAL | CHANGE_VOLUME; 1015fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1016fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mVolume != descriptor.getVolume()) { 1017fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mVolume = descriptor.getVolume(); 1018fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown changes |= CHANGE_GENERAL | CHANGE_VOLUME; 1019fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1020fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mVolumeMax != descriptor.getVolumeMax()) { 1021fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mVolumeMax = descriptor.getVolumeMax(); 1022fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown changes |= CHANGE_GENERAL | CHANGE_VOLUME; 1023fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1024fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mPresentationDisplayId != descriptor.getPresentationDisplayId()) { 1025fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mPresentationDisplayId = descriptor.getPresentationDisplayId(); 1026fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mPresentationDisplay = null; 1027fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY; 1028fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1029fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (!equal(mExtras, descriptor.getExtras())) { 1030fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mExtras = descriptor.getExtras(); 1031fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown changes |= CHANGE_GENERAL; 1032fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1033fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1034fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1035fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return changes; 1036fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1037fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1038fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown String getDescriptorId() { 1039fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mDescriptorId; 1040fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1041fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1042fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown MediaRouteProvider getProviderInstance() { 1043fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mProvider.getProviderInstance(); 1044fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1045fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1046fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1047fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1048fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Provides information about a media route provider. 1049fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * <p> 1050fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * This object may be used to determine which media route provider has 1051fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * published a particular route. 1052fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * </p> 1053fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1054fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public static final class ProviderInfo { 1055fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private final MediaRouteProvider mProviderInstance; 1056fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>(); 1057fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1058fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private final ProviderMetadata mMetadata; 105911417b1cfde8f1749905f2d735623af9214148afJeff Brown private MediaRouteProviderDescriptor mDescriptor; 1060fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private Resources mResources; 1061fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private boolean mResourcesNotAvailable; 1062fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1063fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo(MediaRouteProvider provider) { 1064fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mProviderInstance = provider; 1065fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mMetadata = provider.getMetadata(); 1066fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1067fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1068fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1069fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets the provider's underlying {@link MediaRouteProvider} instance. 1070fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1071fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public MediaRouteProvider getProviderInstance() { 1072fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1073fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mProviderInstance; 1074fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1075fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1076fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1077fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets the package name of the media route provider service. 1078fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1079fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public String getPackageName() { 1080fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mMetadata.getPackageName(); 1081fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1082fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1083fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1084fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets the {@link MediaRouter.RouteInfo routes} published by this route provider. 1085fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1086fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public List<RouteInfo> getRoutes() { 1087fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1088fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mRoutes; 1089fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1090fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1091fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Resources getResources() { 1092fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mResources == null && !mResourcesNotAvailable) { 1093fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown String packageName = getPackageName(); 1094fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Context context = sGlobal.getProviderContext(packageName); 1095fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (context != null) { 1096fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mResources = context.getResources(); 1097fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } else { 1098fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Log.w(TAG, "Unable to obtain resources for route provider package: " 1099fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown + packageName); 1100fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mResourcesNotAvailable = true; 1101fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1102fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1103fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mResources; 1104fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1105fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 110611417b1cfde8f1749905f2d735623af9214148afJeff Brown boolean updateDescriptor(MediaRouteProviderDescriptor descriptor) { 1107fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mDescriptor != descriptor) { 1108fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mDescriptor = descriptor; 1109fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return true; 1110fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1111fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return false; 1112fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1113fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1114fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int findRouteByDescriptorId(String id) { 1115fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown final int count = mRoutes.size(); 1116fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown for (int i = 0; i < count; i++) { 1117fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mRoutes.get(i).mDescriptorId.equals(id)) { 1118fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return i; 1119fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1120fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1121fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return -1; 1122fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1123fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1124fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown @Override 1125fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public String toString() { 1126fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return "MediaRouter.RouteProviderInfo{ packageName=" + getPackageName() 1127c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + " }"; 1128c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1129c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1130c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1131c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1132c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Interface for receiving events about media routing changes. 1133c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * All methods of this interface will be called from the application's main thread. 1134c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1135c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * A Callback will only receive events relevant to routes that the callback 113611417b1cfde8f1749905f2d735623af9214148afJeff Brown * was registered for unless the {@link MediaRouter#CALLBACK_FLAG_UNFILTERED_EVENTS} 113711417b1cfde8f1749905f2d735623af9214148afJeff Brown * flag was specified in {@link MediaRouter#addCallback(MediaRouteSelector, Callback, int)}. 1138c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1139c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 114011417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see MediaRouter#addCallback(MediaRouteSelector, Callback, int) 1141c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaRouter#removeCallback(Callback) 1142c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1143c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static abstract class Callback { 1144c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1145fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when the supplied media route becomes selected as the active route. 1146c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1147fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1148c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has been selected. 1149c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1150c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteSelected(MediaRouter router, RouteInfo route) { 1151c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1152c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1153c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1154fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when the supplied media route becomes unselected as the active route. 1155c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1156fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1157c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has been unselected. 1158c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1159c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteUnselected(MediaRouter router, RouteInfo route) { 1160c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1161c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1162c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1163fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route has been added. 1164c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1165fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1166c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has become available for use. 1167c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1168c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteAdded(MediaRouter router, RouteInfo route) { 1169c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1170c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1171c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1172fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route has been removed. 1173c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1174fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1175c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has been removed from availability. 1176c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1177c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteRemoved(MediaRouter router, RouteInfo route) { 1178c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1179c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1180c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1181fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a property of the indicated media route has changed. 1182c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1183fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1184c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that was changed. 1185c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1186c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteChanged(MediaRouter router, RouteInfo route) { 1187c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1188c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1189c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1190fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route's volume changes. 1191c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1192fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1193c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route whose volume changed. 1194c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1195c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) { 1196c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1197c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1198c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1199fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route's presentation display changes. 1200c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1201c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This method is called whenever the route's presentation display becomes 1202fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * available, is removed or has changes to some of its properties (such as its size). 1203c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1204c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1205fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1206c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route whose presentation display changed. 1207c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1208c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#getPresentationDisplay() 1209c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1210c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo route) { 1211c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1212fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1213fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1214fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route provider has been added. 1215fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 1216fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1217fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param provider The provider that has become available for use. 1218fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1219fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void onProviderAdded(MediaRouter router, ProviderInfo provider) { 1220fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1221fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1222fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1223fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route provider has been removed. 1224fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 1225fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1226fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param provider The provider that has been removed from availability. 1227fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1228fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void onProviderRemoved(MediaRouter router, ProviderInfo provider) { 1229fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 123011417b1cfde8f1749905f2d735623af9214148afJeff Brown 123111417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 123211417b1cfde8f1749905f2d735623af9214148afJeff Brown * Called when a property of the indicated media route provider has changed. 123311417b1cfde8f1749905f2d735623af9214148afJeff Brown * 123411417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param router The media router reporting the event. 123511417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param provider The provider that was changed. 123611417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 123711417b1cfde8f1749905f2d735623af9214148afJeff Brown public void onProviderChanged(MediaRouter router, ProviderInfo provider) { 123811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1239c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1240c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1241c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1242c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Callback which is invoked with the result of a media control request. 1243c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1244c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#sendControlRequest 1245c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1246fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public static abstract class ControlRequestCallback { 1247c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 12483d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * Called when a media control request succeeds. 12493d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * 12503d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * @param data Result data, or null if none. 12513d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * Contents depend on the {@link MediaControlIntent media control action}. 1252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 12533d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown public void onResult(Bundle data) { 12543d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown } 1255c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1256c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 12573d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * Called when a media control request fails. 1258c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 12593d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * @param error A localized error message which may be shown to the user, or null 12603d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * if the cause of the error is unclear. 12613d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * @param data Error data, or null if none. 12623d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * Contents depend on the {@link MediaControlIntent media control action}. 1263c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 12643d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown public void onError(String error, Bundle data) { 1265c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1266c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1267c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 126811417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final class CallbackRecord { 12699fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown public final MediaRouter mRouter; 127011417b1cfde8f1749905f2d735623af9214148afJeff Brown public final Callback mCallback; 127111417b1cfde8f1749905f2d735623af9214148afJeff Brown public MediaRouteSelector mSelector; 127211417b1cfde8f1749905f2d735623af9214148afJeff Brown public int mFlags; 127311417b1cfde8f1749905f2d735623af9214148afJeff Brown 12749fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown public CallbackRecord(MediaRouter router, Callback callback) { 12759fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouter = router; 127611417b1cfde8f1749905f2d735623af9214148afJeff Brown mCallback = callback; 127711417b1cfde8f1749905f2d735623af9214148afJeff Brown mSelector = MediaRouteSelector.EMPTY; 127811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 127911417b1cfde8f1749905f2d735623af9214148afJeff Brown 128011417b1cfde8f1749905f2d735623af9214148afJeff Brown public boolean filterRouteEvent(RouteInfo route) { 128111417b1cfde8f1749905f2d735623af9214148afJeff Brown return (mFlags & CALLBACK_FLAG_UNFILTERED_EVENTS) != 0 128211417b1cfde8f1749905f2d735623af9214148afJeff Brown || route.matchesSelector(mSelector); 128311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 128411417b1cfde8f1749905f2d735623af9214148afJeff Brown } 128511417b1cfde8f1749905f2d735623af9214148afJeff Brown 1286c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1287c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Global state for the media router. 1288c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1289c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media routes and media route providers are global to the process; their 1290c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * state and the bulk of the media router implementation lives here. 1291c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1292c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1293c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private static final class GlobalMediaRouter implements SystemMediaRouteProvider.SyncCallback { 1294c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final Context mApplicationContext; 1295fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private final MediaRouter mApplicationRouter; 12969fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown private final ArrayList<WeakReference<MediaRouter>> mRouters = 12979fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown new ArrayList<WeakReference<MediaRouter>>(); 1298c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>(); 1299fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private final ArrayList<ProviderInfo> mProviders = 1300fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown new ArrayList<ProviderInfo>(); 1301c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final ProviderCallback mProviderCallback = new ProviderCallback(); 1302c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final CallbackHandler mCallbackHandler = new CallbackHandler(); 1303c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final DisplayManagerCompat mDisplayManager; 1304c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final SystemMediaRouteProvider mSystemProvider; 1305c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1306fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private RegisteredMediaRouteProviderWatcher mRegisteredProviderWatcher; 1307c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private RouteInfo mDefaultRoute; 1308c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private RouteInfo mSelectedRoute; 1309c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private MediaRouteProvider.RouteController mSelectedRouteController; 131011417b1cfde8f1749905f2d735623af9214148afJeff Brown private MediaRouteDiscoveryRequest mDiscoveryRequest; 1311c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1312c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown GlobalMediaRouter(Context applicationContext) { 1313c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mApplicationContext = applicationContext; 1314c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDisplayManager = DisplayManagerCompat.getInstance(applicationContext); 1315fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mApplicationRouter = getRouter(applicationContext); 1316fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1317fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // Add the system media route provider for interoperating with 1318fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // the framework media router. This one is special and receives 1319fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // synchronization messages from the media router. 1320c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSystemProvider = SystemMediaRouteProvider.obtain(applicationContext, this); 1321fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown addProvider(mSystemProvider); 1322fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1323fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1324fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void start() { 1325fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // Start watching for routes published by registered media route 1326fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // provider services. 1327fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mRegisteredProviderWatcher = new RegisteredMediaRouteProviderWatcher( 1328fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mApplicationContext, mApplicationRouter); 1329fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mRegisteredProviderWatcher.start(); 1330c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1331c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1332c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public MediaRouter getRouter(Context context) { 13339fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown MediaRouter router; 13349fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int i = mRouters.size(); --i >= 0; ) { 13359fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown router = mRouters.get(i).get(); 13369fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown if (router == null) { 13379fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouters.remove(i); 13389fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } else if (router.mContext == context) { 13399fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown return router; 13409fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } 1341c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 13429fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown router = new MediaRouter(context); 13439fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouters.add(new WeakReference<MediaRouter>(router)); 1344c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return router; 1345c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1346c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1347c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public ContentResolver getContentResolver() { 1348c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mApplicationContext.getContentResolver(); 1349c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1350c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1351fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public Context getProviderContext(String packageName) { 1352fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (packageName.equals(SystemMediaRouteProvider.PACKAGE_NAME)) { 1353fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mApplicationContext; 1354fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1355fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown try { 1356fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mApplicationContext.createPackageContext( 1357fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown packageName, Context.CONTEXT_RESTRICTED); 1358fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } catch (NameNotFoundException ex) { 1359fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return null; 1360fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1361fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1362fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1363c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public Display getDisplay(int displayId) { 1364c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mDisplayManager.getDisplay(displayId); 1365c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1366c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1367fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void sendControlRequest(RouteInfo route, 1368c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Intent intent, ControlRequestCallback callback) { 1369c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == mSelectedRoute && mSelectedRouteController != null) { 1370129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown if (mSelectedRouteController.onControlRequest(intent, callback)) { 1371fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return; 1372fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1373fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1374fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (callback != null) { 13753d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown callback.onError(null, null); 1376c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1377c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1378c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1379c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestSetVolume(RouteInfo route, int volume) { 1380c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == mSelectedRoute && mSelectedRouteController != null) { 1381129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown mSelectedRouteController.onSetVolume(volume); 1382c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1383c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1384c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1385c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestUpdateVolume(RouteInfo route, int delta) { 1386c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == mSelectedRoute && mSelectedRouteController != null) { 1387129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown mSelectedRouteController.onUpdateVolume(delta); 1388c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1389c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1390c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1391c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public List<RouteInfo> getRoutes() { 1392c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mRoutes; 1393c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1394c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1395fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public List<ProviderInfo> getProviders() { 1396fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mProviders; 1397fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1398fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1399c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getDefaultRoute() { 1400c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mDefaultRoute == null) { 1401c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This should never happen once the media router has been fully 1402c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // initialized but it is good to check for the error in case there 1403c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // is a bug in provider initialization. 1404c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalStateException("There is no default route. " 1405c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + "The media router has not yet been fully initialized."); 1406c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1407c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mDefaultRoute; 1408c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1409c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1410c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getSelectedRoute() { 1411c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute == null) { 1412c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This should never happen once the media router has been fully 1413c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // initialized but it is good to check for the error in case there 1414c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // is a bug in provider initialization. 1415c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalStateException("There is no currently selected route. " 1416c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + "The media router has not yet been fully initialized."); 1417c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1418c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mSelectedRoute; 1419c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1420c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1421c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void selectRoute(RouteInfo route) { 1422c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (!mRoutes.contains(route)) { 1423c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Log.w(TAG, "Ignoring attempt to select removed route: " + route); 1424c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return; 1425c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1426c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (!route.mEnabled) { 1427c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Log.w(TAG, "Ignoring attempt to select disabled route: " + route); 1428c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return; 1429c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1430c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1431c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown setSelectedRouteInternal(route); 1432c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1433c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 143411417b1cfde8f1749905f2d735623af9214148afJeff Brown public boolean isRouteAvailable(MediaRouteSelector selector, int flags) { 143511417b1cfde8f1749905f2d735623af9214148afJeff Brown // Check whether any existing routes match the selector. 143611417b1cfde8f1749905f2d735623af9214148afJeff Brown final int routeCount = mRoutes.size(); 143711417b1cfde8f1749905f2d735623af9214148afJeff Brown for (int i = 0; i < routeCount; i++) { 143811417b1cfde8f1749905f2d735623af9214148afJeff Brown RouteInfo route = mRoutes.get(i); 143911417b1cfde8f1749905f2d735623af9214148afJeff Brown if ((flags & AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE) != 0 144011417b1cfde8f1749905f2d735623af9214148afJeff Brown && route.isDefault()) { 144111417b1cfde8f1749905f2d735623af9214148afJeff Brown continue; 144211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 144311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (route.matchesSelector(selector)) { 144411417b1cfde8f1749905f2d735623af9214148afJeff Brown return true; 144511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 144611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 144711417b1cfde8f1749905f2d735623af9214148afJeff Brown 144811417b1cfde8f1749905f2d735623af9214148afJeff Brown // It doesn't look like we can find a matching route right now. 144911417b1cfde8f1749905f2d735623af9214148afJeff Brown return false; 145011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 145111417b1cfde8f1749905f2d735623af9214148afJeff Brown 145211417b1cfde8f1749905f2d735623af9214148afJeff Brown public void updateDiscoveryRequest() { 145311417b1cfde8f1749905f2d735623af9214148afJeff Brown // Combine all of the callback selectors and active scan flags. 1454f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown boolean discover = false; 145511417b1cfde8f1749905f2d735623af9214148afJeff Brown boolean activeScan = false; 145611417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder(); 14579fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int i = mRouters.size(); --i >= 0; ) { 14589fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown MediaRouter router = mRouters.get(i).get(); 14599fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown if (router == null) { 14609fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouters.remove(i); 14619fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } else { 14629fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown final int count = router.mCallbackRecords.size(); 14639fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int j = 0; j < count; j++) { 14649fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown CallbackRecord callback = router.mCallbackRecords.get(j); 14659fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown builder.addSelector(callback.mSelector); 1466f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown if ((callback.mFlags & CALLBACK_FLAG_PERFORM_ACTIVE_SCAN) != 0) { 14679fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown activeScan = true; 1468f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown discover = true; // perform active scan implies request discovery 1469f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown } 1470f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown if ((callback.mFlags & CALLBACK_FLAG_REQUEST_DISCOVERY) != 0) { 1471f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown discover = true; 14729fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } 147311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 147411417b1cfde8f1749905f2d735623af9214148afJeff Brown } 147511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1476f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown MediaRouteSelector selector = discover ? builder.build() : MediaRouteSelector.EMPTY; 147711417b1cfde8f1749905f2d735623af9214148afJeff Brown 147811417b1cfde8f1749905f2d735623af9214148afJeff Brown // Create a new discovery request. 147911417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mDiscoveryRequest != null 148011417b1cfde8f1749905f2d735623af9214148afJeff Brown && mDiscoveryRequest.getSelector().equals(selector) 148111417b1cfde8f1749905f2d735623af9214148afJeff Brown && mDiscoveryRequest.isActiveScan() == activeScan) { 148211417b1cfde8f1749905f2d735623af9214148afJeff Brown return; // no change 148311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 148411417b1cfde8f1749905f2d735623af9214148afJeff Brown if (selector.isEmpty() && !activeScan) { 148511417b1cfde8f1749905f2d735623af9214148afJeff Brown // Discovery is not needed. 148611417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mDiscoveryRequest == null) { 148711417b1cfde8f1749905f2d735623af9214148afJeff Brown return; // no change 148811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 148911417b1cfde8f1749905f2d735623af9214148afJeff Brown mDiscoveryRequest = null; 149011417b1cfde8f1749905f2d735623af9214148afJeff Brown } else { 149111417b1cfde8f1749905f2d735623af9214148afJeff Brown // Discovery is needed. 149211417b1cfde8f1749905f2d735623af9214148afJeff Brown mDiscoveryRequest = new MediaRouteDiscoveryRequest(selector, activeScan); 149311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 149411417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 149511417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Updated discovery request: " + mDiscoveryRequest); 149611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 149711417b1cfde8f1749905f2d735623af9214148afJeff Brown 149811417b1cfde8f1749905f2d735623af9214148afJeff Brown // Notify providers. 149911417b1cfde8f1749905f2d735623af9214148afJeff Brown final int providerCount = mProviders.size(); 150011417b1cfde8f1749905f2d735623af9214148afJeff Brown for (int i = 0; i < providerCount; i++) { 150111417b1cfde8f1749905f2d735623af9214148afJeff Brown mProviders.get(i).mProviderInstance.setDiscoveryRequest(mDiscoveryRequest); 150228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 150328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 150428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 1505fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void addProvider(MediaRouteProvider providerInstance) { 1506fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int index = findProviderInfo(providerInstance); 1507c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index < 0) { 1508c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 1. Add the provider to the list. 1509fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo provider = new ProviderInfo(providerInstance); 1510fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mProviders.add(provider); 151111417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 151211417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Provider added: " + provider); 151311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1514fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_ADDED, provider); 1515c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 2. Create the provider's contents. 1516fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown updateProviderContents(provider, providerInstance.getDescriptor()); 1517c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 3. Register the provider callback. 151811417b1cfde8f1749905f2d735623af9214148afJeff Brown providerInstance.setCallback(mProviderCallback); 151911417b1cfde8f1749905f2d735623af9214148afJeff Brown // 4. Set the discovery request. 152011417b1cfde8f1749905f2d735623af9214148afJeff Brown providerInstance.setDiscoveryRequest(mDiscoveryRequest); 1521c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1522c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1523c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1524fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void removeProvider(MediaRouteProvider providerInstance) { 1525fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int index = findProviderInfo(providerInstance); 1526c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index >= 0) { 152711417b1cfde8f1749905f2d735623af9214148afJeff Brown // 1. Unregister the provider callback. 152811417b1cfde8f1749905f2d735623af9214148afJeff Brown providerInstance.setCallback(null); 152911417b1cfde8f1749905f2d735623af9214148afJeff Brown // 2. Clear the discovery request. 153011417b1cfde8f1749905f2d735623af9214148afJeff Brown providerInstance.setDiscoveryRequest(null); 153128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown // 3. Delete the provider's contents. 1532fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo provider = mProviders.get(index); 1533fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown updateProviderContents(provider, null); 153428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown // 4. Remove the provider from the list. 153511417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 153611417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Provider removed: " + provider); 153711417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1538fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_REMOVED, provider); 1539fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mProviders.remove(index); 1540c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1541c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1542c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1543fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private void updateProviderDescriptor(MediaRouteProvider providerInstance, 154411417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteProviderDescriptor descriptor) { 1545fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int index = findProviderInfo(providerInstance); 1546c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index >= 0) { 1547fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // Update the provider's contents. 1548fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo provider = mProviders.get(index); 1549fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown updateProviderContents(provider, descriptor); 1550c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1551c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1552c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1553fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private int findProviderInfo(MediaRouteProvider providerInstance) { 1554fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown final int count = mProviders.size(); 1555c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown for (int i = 0; i < count; i++) { 1556fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mProviders.get(i).mProviderInstance == providerInstance) { 1557c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return i; 1558c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1559c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1560c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return -1; 1561c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1562c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1563fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private void updateProviderContents(ProviderInfo provider, 156411417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteProviderDescriptor providerDescriptor) { 1565fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (provider.updateDescriptor(providerDescriptor)) { 1566c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Update all existing routes and reorder them to match 1567c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // the order of their descriptors. 1568c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int targetIndex = 0; 1569c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (providerDescriptor != null) { 1570fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (providerDescriptor.isValid()) { 157111417b1cfde8f1749905f2d735623af9214148afJeff Brown final List<MediaRouteDescriptor> routeDescriptors = 157211417b1cfde8f1749905f2d735623af9214148afJeff Brown providerDescriptor.getRoutes(); 157311417b1cfde8f1749905f2d735623af9214148afJeff Brown final int routeCount = routeDescriptors.size(); 157411417b1cfde8f1749905f2d735623af9214148afJeff Brown for (int i = 0; i < routeCount; i++) { 157511417b1cfde8f1749905f2d735623af9214148afJeff Brown final MediaRouteDescriptor routeDescriptor = routeDescriptors.get(i); 1576c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final String id = routeDescriptor.getId(); 1577fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown final int sourceIndex = provider.findRouteByDescriptorId(id); 1578c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (sourceIndex < 0) { 1579c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 1. Add the route to the list. 1580cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown String uniqueId = assignRouteUniqueId(provider, id); 1581cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown RouteInfo route = new RouteInfo(provider, id, uniqueId); 1582fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown provider.mRoutes.add(targetIndex++, route); 1583c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mRoutes.add(route); 1584c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 2. Create the route's contents. 1585c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown route.updateDescriptor(routeDescriptor); 1586fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // 3. Notify clients about addition. 158711417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 158811417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Route added: " + route); 158911417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1590c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route); 1591fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } else if (sourceIndex < targetIndex) { 1592fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Log.w(TAG, "Ignoring route descriptor with duplicate id: " 1593fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown + routeDescriptor); 1594c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } else { 1595c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 1. Reorder the route within the list. 1596fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown RouteInfo route = provider.mRoutes.get(sourceIndex); 1597fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Collections.swap(provider.mRoutes, 1598c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sourceIndex, targetIndex++); 1599c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 2. Update the route's contents. 1600c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int changes = route.updateDescriptor(routeDescriptor); 1601fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // 3. Unselect route if needed before notifying about changes. 1602fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown unselectRouteIfNeeded(route); 1603fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // 4. Notify clients about changes. 1604c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if ((changes & RouteInfo.CHANGE_GENERAL) != 0) { 160511417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 160611417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Route changed: " + route); 160711417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1608c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallbackHandler.post( 1609c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown CallbackHandler.MSG_ROUTE_CHANGED, route); 1610c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1611c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if ((changes & RouteInfo.CHANGE_VOLUME) != 0) { 161211417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 161311417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Route volume changed: " + route); 161411417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1615c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallbackHandler.post( 1616c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown CallbackHandler.MSG_ROUTE_VOLUME_CHANGED, route); 1617c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1618c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if ((changes & RouteInfo.CHANGE_PRESENTATION_DISPLAY) != 0) { 161911417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 162011417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Route presentation display changed: " 162111417b1cfde8f1749905f2d735623af9214148afJeff Brown + route); 162211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1623fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mCallbackHandler.post(CallbackHandler. 1624fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED, route); 1625c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1626c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1627c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1628fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } else { 1629fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Log.w(TAG, "Ignoring invalid provider descriptor: " + providerDescriptor); 1630c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1631c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1632c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1633c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Dispose all remaining routes that do not have matching descriptors. 1634fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) { 1635fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // 1. Delete the route's contents. 1636fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown RouteInfo route = provider.mRoutes.get(i); 1637c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown route.updateDescriptor(null); 1638fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // 2. Remove the route from the list. 163911417b1cfde8f1749905f2d735623af9214148afJeff Brown mRoutes.remove(route); 1640fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown provider.mRoutes.remove(i); 1641fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // 3. Unselect route if needed before notifying about removal. 1642fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown unselectRouteIfNeeded(route); 1643fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // 4. Notify clients about removal. 164411417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 164511417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Route removed: " + route); 164611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1647fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_ROUTE_REMOVED, route); 1648c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1649fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 165011417b1cfde8f1749905f2d735623af9214148afJeff Brown // Notify provider changed. 165111417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 165211417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Provider changed: " + provider); 165311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 165411417b1cfde8f1749905f2d735623af9214148afJeff Brown mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_CHANGED, provider); 165511417b1cfde8f1749905f2d735623af9214148afJeff Brown 1656fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // Choose a new selected route if needed. 1657fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown selectRouteIfNeeded(); 1658c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1659c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1660c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1661cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown private String assignRouteUniqueId(ProviderInfo provider, String routeDescriptorId) { 1662cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown // Although route descriptor ids are unique within a provider, it's 1663cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown // possible for there to be two providers with the same package name. 1664cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown // Therefore we must dedupe the composite id. 1665cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown String uniqueId = provider.getPackageName() + ":" + routeDescriptorId; 1666cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown if (findRouteByUniqueId(uniqueId) < 0) { 1667cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return uniqueId; 1668cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 1669cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown for (int i = 2; ; i++) { 1670cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown String newUniqueId = String.format(Locale.US, "%s_%d", uniqueId, i); 1671cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown if (findRouteByUniqueId(newUniqueId) < 0) { 1672cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return newUniqueId; 1673cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 1674cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 1675cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 1676cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown 1677cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown private int findRouteByUniqueId(String uniqueId) { 1678cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown final int count = mRoutes.size(); 1679cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown for (int i = 0; i < count; i++) { 1680cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown if (mRoutes.get(i).mUniqueId.equals(uniqueId)) { 1681cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return i; 1682cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 1683cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 1684cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return -1; 1685cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 1686cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown 1687fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private void unselectRouteIfNeeded(RouteInfo route) { 1688fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mDefaultRoute == route && !isRouteSelectable(route)) { 1689c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Log.i(TAG, "Choosing a new default route because the current one " 1690fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown + "is no longer selectable: " + route); 1691c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDefaultRoute = null; 1692c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1693fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mSelectedRoute == route && !isRouteSelectable(route)) { 1694fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Log.i(TAG, "Choosing a new selected route because the current one " 1695fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown + "is no longer selectable: " + route); 1696fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown setSelectedRouteInternal(null); 1697fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1698fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1699fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1700fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private void selectRouteIfNeeded() { 1701c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mDefaultRoute == null && !mRoutes.isEmpty()) { 1702c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown for (RouteInfo route : mRoutes) { 1703c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (isSystemDefaultRoute(route) && isRouteSelectable(route)) { 1704c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDefaultRoute = route; 1705c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 1706c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1707c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1708c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1709c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute == null) { 1710c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown setSelectedRouteInternal(mDefaultRoute); 1711c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1712c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1713c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1714c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private boolean isRouteSelectable(RouteInfo route) { 1715c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This tests whether the route is still valid and enabled. 1716c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // The route descriptor field is set to null when the route is removed. 1717c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return route.mDescriptor != null && route.mEnabled; 1718c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1719c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1720c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private boolean isSystemDefaultRoute(RouteInfo route) { 1721fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return route.getProviderInstance() == mSystemProvider 1722c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown && route.mDescriptorId.equals( 1723c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown SystemMediaRouteProvider.DEFAULT_ROUTE_ID); 1724c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1725c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1726c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private void setSelectedRouteInternal(RouteInfo route) { 1727c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute != route) { 1728c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute != null) { 172911417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 173011417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Route unselected: " + mSelectedRoute); 173111417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1732c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_ROUTE_UNSELECTED, mSelectedRoute); 1733c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRouteController != null) { 1734129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown mSelectedRouteController.onUnselect(); 1735129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown mSelectedRouteController.onRelease(); 1736c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSelectedRouteController = null; 1737c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1738c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1739c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1740c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSelectedRoute = route; 1741c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1742c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute != null) { 1743fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSelectedRouteController = route.getProviderInstance().onCreateRouteController( 1744c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown route.mDescriptorId); 1745c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRouteController != null) { 1746129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown mSelectedRouteController.onSelect(); 1747c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 174811417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 174911417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Route selected: " + mSelectedRoute); 175011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1751c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_ROUTE_SELECTED, mSelectedRoute); 1752c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1753c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1754c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1755c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1756c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 1757c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getSystemRouteByDescriptorId(String id) { 1758fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int providerIndex = findProviderInfo(mSystemProvider); 1759c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (providerIndex >= 0) { 1760fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo provider = mProviders.get(providerIndex); 1761fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int routeIndex = provider.findRouteByDescriptorId(id); 1762c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (routeIndex >= 0) { 1763fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return provider.mRoutes.get(routeIndex); 1764c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1765c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1766c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return null; 1767c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1768c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1769c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final class ProviderCallback extends MediaRouteProvider.Callback { 1770c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 1771c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onDescriptorChanged(MediaRouteProvider provider, 177211417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteProviderDescriptor descriptor) { 1773c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown updateProviderDescriptor(provider, descriptor); 1774c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1775c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1776c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1777c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final class CallbackHandler extends Handler { 17789fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown private final ArrayList<CallbackRecord> mTempCallbackRecords = 17799fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown new ArrayList<CallbackRecord>(); 1780c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 178111417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final int MSG_TYPE_MASK = 0xff00; 178211417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final int MSG_TYPE_ROUTE = 0x0100; 178311417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final int MSG_TYPE_PROVIDER = 0x0200; 178411417b1cfde8f1749905f2d735623af9214148afJeff Brown 178511417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_ADDED = MSG_TYPE_ROUTE | 1; 178611417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_REMOVED = MSG_TYPE_ROUTE | 2; 178711417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_CHANGED = MSG_TYPE_ROUTE | 3; 178811417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_VOLUME_CHANGED = MSG_TYPE_ROUTE | 4; 178911417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED = MSG_TYPE_ROUTE | 5; 179011417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_SELECTED = MSG_TYPE_ROUTE | 6; 179111417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_UNSELECTED = MSG_TYPE_ROUTE | 7; 179211417b1cfde8f1749905f2d735623af9214148afJeff Brown 179311417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_PROVIDER_ADDED = MSG_TYPE_PROVIDER | 1; 179411417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_PROVIDER_REMOVED = MSG_TYPE_PROVIDER | 2; 179511417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_PROVIDER_CHANGED = MSG_TYPE_PROVIDER | 3; 1796c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1797fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void post(int msg, Object obj) { 1798fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown obtainMessage(msg, obj).sendToTarget(); 1799c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1800c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1801c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 1802c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void handleMessage(Message msg) { 1803c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final int what = msg.what; 1804fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown final Object obj = msg.obj; 1805c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1806c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Synchronize state with the system media router. 1807fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown syncWithSystemProvider(what, obj); 1808c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1809c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Invoke all registered callbacks. 18109fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown // Build a list of callbacks before invoking them in case callbacks 18119fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown // are added or removed during dispatch. 1812c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown try { 18139fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int i = mRouters.size(); --i >= 0; ) { 18149fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown MediaRouter router = mRouters.get(i).get(); 18159fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown if (router == null) { 18169fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouters.remove(i); 18179fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } else { 18189fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mTempCallbackRecords.addAll(router.mCallbackRecords); 1819c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1820c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 18219fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown 18229fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown final int callbackCount = mTempCallbackRecords.size(); 18239fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int i = 0; i < callbackCount; i++) { 18249fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown invokeCallback(mTempCallbackRecords.get(i), what, obj); 18259fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } 1826c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } finally { 18279fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mTempCallbackRecords.clear(); 1828c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1829c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1830c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1831fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private void syncWithSystemProvider(int what, Object obj) { 1832c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown switch (what) { 1833c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_ADDED: 1834fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSystemProvider.onSyncRouteAdded((RouteInfo)obj); 1835c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 1836c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_REMOVED: 1837fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSystemProvider.onSyncRouteRemoved((RouteInfo)obj); 1838c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 1839c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_CHANGED: 1840fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSystemProvider.onSyncRouteChanged((RouteInfo)obj); 1841c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 1842c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_SELECTED: 1843fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSystemProvider.onSyncRouteSelected((RouteInfo)obj); 1844c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 1845c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1846c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1847c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 18489fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown private void invokeCallback(CallbackRecord record, int what, Object obj) { 18499fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown final MediaRouter router = record.mRouter; 185011417b1cfde8f1749905f2d735623af9214148afJeff Brown final MediaRouter.Callback callback = record.mCallback; 185111417b1cfde8f1749905f2d735623af9214148afJeff Brown switch (what & MSG_TYPE_MASK) { 185211417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_TYPE_ROUTE: { 185311417b1cfde8f1749905f2d735623af9214148afJeff Brown final RouteInfo route = (RouteInfo)obj; 185411417b1cfde8f1749905f2d735623af9214148afJeff Brown if (!record.filterRouteEvent(route)) { 185511417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 185611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 185711417b1cfde8f1749905f2d735623af9214148afJeff Brown switch (what) { 185811417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_ADDED: 185911417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteAdded(router, route); 186011417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 186111417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_REMOVED: 186211417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteRemoved(router, route); 186311417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 186411417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_CHANGED: 186511417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteChanged(router, route); 186611417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 186711417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_VOLUME_CHANGED: 186811417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteVolumeChanged(router, route); 186911417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 187011417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED: 187111417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRoutePresentationDisplayChanged(router, route); 187211417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 187311417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_SELECTED: 187411417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteSelected(router, route); 187511417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 187611417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_UNSELECTED: 187711417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteUnselected(router, route); 187811417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 187911417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1880c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 188111417b1cfde8f1749905f2d735623af9214148afJeff Brown } 188211417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_TYPE_PROVIDER: { 188311417b1cfde8f1749905f2d735623af9214148afJeff Brown final ProviderInfo provider = (ProviderInfo)obj; 188411417b1cfde8f1749905f2d735623af9214148afJeff Brown switch (what) { 188511417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_PROVIDER_ADDED: 188611417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onProviderAdded(router, provider); 188711417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 188811417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_PROVIDER_REMOVED: 188911417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onProviderRemoved(router, provider); 189011417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 189111417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_PROVIDER_CHANGED: 189211417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onProviderChanged(router, provider); 189311417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 189411417b1cfde8f1749905f2d735623af9214148afJeff Brown } 189511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1896c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1897c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1898c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1899c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1900c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown} 1901