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