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 19fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brownimport android.app.ActivityManager; 2094be6100218126ce6a08bf1f56209578500b361fRoboErikimport android.app.PendingIntent; 21adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brownimport android.content.ComponentName; 22c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.ContentResolver; 23c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.Context; 24c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.Intent; 25c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.IntentFilter; 2694be6100218126ce6a08bf1f56209578500b361fRoboErikimport android.content.IntentSender; 27fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.pm.PackageManager.NameNotFoundException; 28fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.res.Resources; 29c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Bundle; 30c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Handler; 31c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Looper; 32c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Message; 33429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport android.support.annotation.IntDef; 34429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport android.support.annotation.NonNull; 35429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport android.support.annotation.Nullable; 36fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brownimport android.support.v4.app.ActivityManagerCompat; 37c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.support.v4.hardware.display.DisplayManagerCompat; 38bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErikimport android.support.v4.media.VolumeProviderCompat; 39bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErikimport android.support.v4.media.session.MediaSessionCompat; 40b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brownimport android.support.v7.media.MediaRouteProvider.ProviderMetadata; 41c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.util.Log; 42c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.view.Display; 43c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 44429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport java.lang.annotation.Retention; 45429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport java.lang.annotation.RetentionPolicy; 469fcedc160282e6620f409ea46bf6728b35d011ddJeff Brownimport java.lang.ref.WeakReference; 47c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.ArrayList; 48c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.Collections; 49c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.List; 50cb63b6ecac9786891514f241dec71695f09d3efbJeff Brownimport java.util.Locale; 51c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 52c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown/** 53c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter allows applications to control the routing of media channels 54c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * and streams from the current device to external speakers and destination devices. 55c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 56c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * A MediaRouter instance is retrieved through {@link #getInstance}. Applications 57c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * can query the media router about the currently selected route and its capabilities 58c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to determine how to send content to the route's destination. Applications can 59c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * also {@link RouteInfo#sendControlRequest send control requests} to the route 60c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to ask the route's destination to perform certain remote control functions 61fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * such as playing media. 62c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 63c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * See also {@link MediaRouteProvider} for information on how an application 64c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * can publish new media routes to the media router. 65c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 66c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The media router API is not thread-safe; all interactions with it must be 67c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * done from the main thread of the process. 68c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 69c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 70c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownpublic final class MediaRouter { 71c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private static final String TAG = "MediaRouter"; 72f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 73c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 7494be6100218126ce6a08bf1f56209578500b361fRoboErik /** 75b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)} 76b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * when the reason the route was unselected is unknown. 7794be6100218126ce6a08bf1f56209578500b361fRoboErik */ 7894be6100218126ce6a08bf1f56209578500b361fRoboErik public static final int UNSELECT_REASON_UNKNOWN = 0; 7994be6100218126ce6a08bf1f56209578500b361fRoboErik /** 80b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)} 81b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * when the user pressed the disconnect button to disconnect and keep playing. 8294be6100218126ce6a08bf1f56209578500b361fRoboErik * <p> 8394be6100218126ce6a08bf1f56209578500b361fRoboErik * 8494be6100218126ce6a08bf1f56209578500b361fRoboErik * @see {@link MediaRouteDescriptor#canDisconnectAndKeepPlaying()}. 8594be6100218126ce6a08bf1f56209578500b361fRoboErik */ 8694be6100218126ce6a08bf1f56209578500b361fRoboErik public static final int UNSELECT_REASON_DISCONNECTED = 1; 8794be6100218126ce6a08bf1f56209578500b361fRoboErik /** 88b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)} 89b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * when the user pressed the stop casting button. 9094be6100218126ce6a08bf1f56209578500b361fRoboErik */ 9194be6100218126ce6a08bf1f56209578500b361fRoboErik public static final int UNSELECT_REASON_STOPPED = 2; 9294be6100218126ce6a08bf1f56209578500b361fRoboErik /** 93b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)} 94b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * when the user selected a different route. 9594be6100218126ce6a08bf1f56209578500b361fRoboErik */ 9694be6100218126ce6a08bf1f56209578500b361fRoboErik public static final int UNSELECT_REASON_ROUTE_CHANGED = 3; 9794be6100218126ce6a08bf1f56209578500b361fRoboErik 98c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Maintains global media router state for the process. 99c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This field is initialized in MediaRouter.getInstance() before any 100c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // MediaRouter objects are instantiated so it is guaranteed to be 101c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // valid whenever any instance method is invoked. 102c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static GlobalMediaRouter sGlobal; 103c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 104c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Context-bound state of the media router. 105c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final Context mContext; 1069fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown final ArrayList<CallbackRecord> mCallbackRecords = new ArrayList<CallbackRecord>(); 10711417b1cfde8f1749905f2d735623af9214148afJeff Brown 108429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye /** @hide */ 109429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @IntDef(flag = true, 110429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye value = { 111429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye CALLBACK_FLAG_PERFORM_ACTIVE_SCAN, 112429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye CALLBACK_FLAG_REQUEST_DISCOVERY, 113429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye CALLBACK_FLAG_UNFILTERED_EVENTS 114429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye } 115429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye ) 116429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Retention(RetentionPolicy.SOURCE) 117429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye private @interface CallbackFlags {} 118429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye 11911417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 12011417b1cfde8f1749905f2d735623af9214148afJeff Brown * Flag for {@link #addCallback}: Actively scan for routes while this callback 12111417b1cfde8f1749905f2d735623af9214148afJeff Brown * is registered. 12211417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 12311417b1cfde8f1749905f2d735623af9214148afJeff Brown * When this flag is specified, the media router will actively scan for new 12411417b1cfde8f1749905f2d735623af9214148afJeff Brown * routes. Certain routes, such as wifi display routes, may not be discoverable 12511417b1cfde8f1749905f2d735623af9214148afJeff Brown * except when actively scanning. This flag is typically used when the route picker 12611417b1cfde8f1749905f2d735623af9214148afJeff Brown * dialog has been opened by the user to ensure that the route information is 12711417b1cfde8f1749905f2d735623af9214148afJeff Brown * up to date. 12811417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p><p> 12911417b1cfde8f1749905f2d735623af9214148afJeff Brown * Active scanning may consume a significant amount of power and may have intrusive 13011417b1cfde8f1749905f2d735623af9214148afJeff Brown * effects on wireless connectivity. Therefore it is important that active scanning 13111417b1cfde8f1749905f2d735623af9214148afJeff Brown * only be requested when it is actually needed to satisfy a user request to 13211417b1cfde8f1749905f2d735623af9214148afJeff Brown * discover and select a new route. 133f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * </p><p> 134f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * This flag implies {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} but performing 135f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * active scans is much more expensive than a normal discovery request. 13611417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 137f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * 138f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * @see #CALLBACK_FLAG_REQUEST_DISCOVERY 13911417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 140f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1 << 0; 14111417b1cfde8f1749905f2d735623af9214148afJeff Brown 14211417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 14311417b1cfde8f1749905f2d735623af9214148afJeff Brown * Flag for {@link #addCallback}: Do not filter route events. 14411417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 14511417b1cfde8f1749905f2d735623af9214148afJeff Brown * When this flag is specified, the callback will be invoked for events that affect any 1463efa63d3b896244713e84acbb5945562dce41d77Jeff Brown * route even if they do not match the callback's filter. 14711417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 14811417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 14911417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 1 << 1; 15011417b1cfde8f1749905f2d735623af9214148afJeff Brown 15111417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 152fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * Flag for {@link #addCallback}: Request passive route discovery while this 153fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * callback is registered, except on {@link ActivityManager#isLowRamDevice low-RAM devices}. 154f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * <p> 155f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * When this flag is specified, the media router will try to discover routes. 156f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * Although route discovery is intended to be efficient, checking for new routes may 157f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * result in some network activity and could slowly drain the battery. Therefore 158f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * applications should only specify {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} when 159f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * they are running in the foreground and would like to provide the user with the 160f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * option of connecting to new routes. 161f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * </p><p> 162f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * Applications should typically add a callback using this flag in the 163f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * {@link android.app.Activity activity's} {@link android.app.Activity#onStart onStart} 164f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * method and remove it in the {@link android.app.Activity#onStop onStop} method. 165f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * The {@link android.support.v7.app.MediaRouteDiscoveryFragment} fragment may 166f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * also be used for this purpose. 167fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p class="note"> 168fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * On {@link ActivityManager#isLowRamDevice low-RAM devices} this flag 169fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * will be ignored. Refer to 170fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * {@link #addCallback(MediaRouteSelector, Callback, int) addCallback} for details. 171f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * </p> 172f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * 173f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * @see android.support.v7.app.MediaRouteDiscoveryFragment 174f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown */ 175f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 1 << 2; 176f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown 177f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown /** 178fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * Flag for {@link #addCallback}: Request passive route discovery while this 179fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * callback is registered, even on {@link ActivityManager#isLowRamDevice low-RAM devices}. 180fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * <p class="note"> 181fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * This flag has a significant performance impact on low-RAM devices 182fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * since it may cause many media route providers to be started simultaneously. 183fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * It is much better to use {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} instead to avoid 184fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * performing passive discovery on these devices altogether. Refer to 185fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * {@link #addCallback(MediaRouteSelector, Callback, int) addCallback} for details. 186fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p> 187fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * 188fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * @see android.support.v7.app.MediaRouteDiscoveryFragment 189fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown */ 190fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown public static final int CALLBACK_FLAG_FORCE_DISCOVERY = 1 << 3; 191fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown 192fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown /** 19311417b1cfde8f1749905f2d735623af9214148afJeff Brown * Flag for {@link #isRouteAvailable}: Ignore the default route. 19411417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 19511417b1cfde8f1749905f2d735623af9214148afJeff Brown * This flag is used to determine whether a matching non-default route is available. 19611417b1cfde8f1749905f2d735623af9214148afJeff Brown * This constraint may be used to decide whether to offer the route chooser dialog 19711417b1cfde8f1749905f2d735623af9214148afJeff Brown * to the user. There is no point offering the chooser if there are no 19811417b1cfde8f1749905f2d735623af9214148afJeff Brown * non-default choices. 19911417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 20011417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 20111417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1 << 0; 20211417b1cfde8f1749905f2d735623af9214148afJeff Brown 203fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown /** 204fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * Flag for {@link #isRouteAvailable}: Require an actual route to be matched. 205fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * <p> 206fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * If this flag is not set, then {@link #isRouteAvailable} will return true 207fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * if it is possible to discover a matching route even if discovery is not in 208fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * progress or if no matching route has yet been found. This feature is used to 209fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * save resources by removing the need to perform passive route discovery on 210fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * {@link ActivityManager#isLowRamDevice low-RAM devices}. 211fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p> 212fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * If this flag is set, then {@link #isRouteAvailable} will only return true if 213fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * a matching route has actually been discovered. 214fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p> 215fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown */ 216fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown public static final int AVAILABILITY_FLAG_REQUIRE_MATCH = 1 << 1; 217fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown 218c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouter(Context context) { 219c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mContext = context; 220c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 221c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 222c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 2239fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * Gets an instance of the media router service associated with the context. 2249fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * <p> 2259fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * The application is responsible for holding a strong reference to the returned 2269fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * {@link MediaRouter} instance, such as by storing the instance in a field of 2279fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * the {@link android.app.Activity}, to ensure that the media router remains alive 2289fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * as long as the application is using its features. 2299fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * </p><p> 2309fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * In other words, the support library only holds a {@link WeakReference weak reference} 2319fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * to each media router instance. When there are no remaining strong references to the 2329fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * media router instance, all of its callbacks will be removed and route discovery 2339fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * will no longer be performed on its behalf. 2349fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * </p> 2359fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * 2369fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * @return The media router instance for the context. The application must hold 2379fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * a strong reference to this object as long as it is in use. 238c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 239429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public static MediaRouter getInstance(@NonNull Context context) { 240c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (context == null) { 241c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("context must not be null"); 242c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 243c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 244c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 245c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (sGlobal == null) { 246c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal = new GlobalMediaRouter(context.getApplicationContext()); 247fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown sGlobal.start(); 248c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 249c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getRouter(context); 250c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 251c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 253fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets information about the {@link MediaRouter.RouteInfo routes} currently known to 254fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * this media router. 255c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 256c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public List<RouteInfo> getRoutes() { 257c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 258c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getRoutes(); 259c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 260c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 261c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 262fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets information about the {@link MediaRouter.ProviderInfo route providers} 263fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * currently known to this media router. 264fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 265fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public List<ProviderInfo> getProviders() { 266fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 267fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return sGlobal.getProviders(); 268fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 269fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 270fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 271c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the default route for playing media content on the system. 272c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 273c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The system always provides a default route. 274c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 275c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 276c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The default route, which is guaranteed to never be null. 277c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 278429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @NonNull 279c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getDefaultRoute() { 280c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 281c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getDefaultRoute(); 282c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 283c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 284c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 285c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the currently selected route. 286c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 287c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The application should examine the route's 288fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * {@link RouteInfo#getControlFilters media control intent filters} to assess the 289c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * capabilities of the route before attempting to use it. 290c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 291c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 292c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <h3>Example</h3> 293c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <pre> 294c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * public boolean playMovie() { 295c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter mediaRouter = MediaRouter.getInstance(context); 296c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute(); 297c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 298c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // First try using the remote playback interface, if supported. 299c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) { 300c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // The route supports remote playback. 301c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // Try to send it the Uri of the movie to play. 302c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Intent intent = new Intent(MediaControlIntent.ACTION_PLAY); 303c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); 304c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * intent.setDataAndType("http://example.com/videos/movie.mp4", "video/mp4"); 305fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * if (route.supportsControlRequest(intent)) { 306fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * route.sendControlRequest(intent, null); 307fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * return true; // sent the request to play the movie 308c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 309c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 310c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 311c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // If remote playback was not possible, then play locally. 312c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * if (route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)) { 313c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // The route supports live video streaming. 314c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // Prepare to play content locally in a window or in a presentation. 315c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * return playMovieInWindow(); 316c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 317c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 318c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // Neither interface is supported, so we can't play the movie to this route. 319c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * return false; 320c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 321c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </pre> 322c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 323c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The selected route, which is guaranteed to never be null. 324c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 325fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see RouteInfo#getControlFilters 326c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#supportsControlCategory 327c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#supportsControlRequest 328c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 329429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @NonNull 330c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getSelectedRoute() { 331c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 332c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getSelectedRoute(); 333c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 334c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 335c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 33628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * Returns the selected route if it matches the specified selector, otherwise 33728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * selects the default route and returns it. 33828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 33928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @param selector The selector to match. 34028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @return The previously selected route if it matched the selector, otherwise the 34128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * newly selected default route which is guaranteed to never be null. 34228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 34311417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see MediaRouteSelector 34428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @see RouteInfo#matchesSelector 34528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @see RouteInfo#isDefault 34628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown */ 347429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @NonNull 348429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public RouteInfo updateSelectedRoute(@NonNull MediaRouteSelector selector) { 34928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown if (selector == null) { 35028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown throw new IllegalArgumentException("selector must not be null"); 35128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 35228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown checkCallingThread(); 35328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 35411417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 35511417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "updateSelectedRoute: " + selector); 35611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 35728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown RouteInfo route = sGlobal.getSelectedRoute(); 35811417b1cfde8f1749905f2d735623af9214148afJeff Brown if (!route.isDefault() && !route.matchesSelector(selector)) { 35928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown route = sGlobal.getDefaultRoute(); 36028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown sGlobal.selectRoute(route); 36128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 36228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown return route; 36328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 36428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 36528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 366c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Selects the specified route. 367c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 368c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route to select. 369c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 370429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void selectRoute(@NonNull RouteInfo route) { 371c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == null) { 372c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("route must not be null"); 373c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 374c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 375c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 37611417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 37711417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "selectRoute: " + route); 37811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 379c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal.selectRoute(route); 380c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 381c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 382c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 38394be6100218126ce6a08bf1f56209578500b361fRoboErik * Unselects the current round and selects the default route instead. 38494be6100218126ce6a08bf1f56209578500b361fRoboErik * <p> 38594be6100218126ce6a08bf1f56209578500b361fRoboErik * The reason given must be one of: 38694be6100218126ce6a08bf1f56209578500b361fRoboErik * <ul> 38794be6100218126ce6a08bf1f56209578500b361fRoboErik * <li>{@link MediaRouter#UNSELECT_REASON_UNKNOWN}</li> 38894be6100218126ce6a08bf1f56209578500b361fRoboErik * <li>{@link MediaRouter#UNSELECT_REASON_DISCONNECTED}</li> 38994be6100218126ce6a08bf1f56209578500b361fRoboErik * <li>{@link MediaRouter#UNSELECT_REASON_STOPPED}</li> 39094be6100218126ce6a08bf1f56209578500b361fRoboErik * <li>{@link MediaRouter#UNSELECT_REASON_ROUTE_CHANGED}</li> 39194be6100218126ce6a08bf1f56209578500b361fRoboErik * </ul> 39294be6100218126ce6a08bf1f56209578500b361fRoboErik * 39394be6100218126ce6a08bf1f56209578500b361fRoboErik * @param reason The reason for disconnecting the current route. 39494be6100218126ce6a08bf1f56209578500b361fRoboErik */ 39594be6100218126ce6a08bf1f56209578500b361fRoboErik public void unselect(int reason) { 39694be6100218126ce6a08bf1f56209578500b361fRoboErik if (reason < MediaRouter.UNSELECT_REASON_UNKNOWN || 39794be6100218126ce6a08bf1f56209578500b361fRoboErik reason > MediaRouter.UNSELECT_REASON_ROUTE_CHANGED) { 39894be6100218126ce6a08bf1f56209578500b361fRoboErik throw new IllegalArgumentException("Unsupported reason to unselect route"); 39994be6100218126ce6a08bf1f56209578500b361fRoboErik } 40094be6100218126ce6a08bf1f56209578500b361fRoboErik checkCallingThread(); 40194be6100218126ce6a08bf1f56209578500b361fRoboErik 40294be6100218126ce6a08bf1f56209578500b361fRoboErik sGlobal.selectRoute(getDefaultRoute(), reason); 40394be6100218126ce6a08bf1f56209578500b361fRoboErik } 40494be6100218126ce6a08bf1f56209578500b361fRoboErik 40594be6100218126ce6a08bf1f56209578500b361fRoboErik /** 406d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown * Returns true if there is a route that matches the specified selector. 40711417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 40894be6100218126ce6a08bf1f56209578500b361fRoboErik * This method returns true if there are any available routes that match the 40994be6100218126ce6a08bf1f56209578500b361fRoboErik * selector regardless of whether they are enabled or disabled. If the 410d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} flag is specified, then 411d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown * the method will only consider non-default routes. 41294be6100218126ce6a08bf1f56209578500b361fRoboErik * </p> 41394be6100218126ce6a08bf1f56209578500b361fRoboErik * <p class="note"> 414fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * On {@link ActivityManager#isLowRamDevice low-RAM devices} this method 415fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * will return true if it is possible to discover a matching route even if 416fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * discovery is not in progress or if no matching route has yet been found. 417fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * Use {@link #AVAILABILITY_FLAG_REQUIRE_MATCH} to require an actual match. 41811417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 419c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 42011417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param selector The selector to match. 42194be6100218126ce6a08bf1f56209578500b361fRoboErik * @param flags Flags to control the determination of whether a route may be 42294be6100218126ce6a08bf1f56209578500b361fRoboErik * available. May be zero or some combination of 42394be6100218126ce6a08bf1f56209578500b361fRoboErik * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} and 42494be6100218126ce6a08bf1f56209578500b361fRoboErik * {@link #AVAILABILITY_FLAG_REQUIRE_MATCH}. 42511417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if a matching route may be available. 426c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 427429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public boolean isRouteAvailable(@NonNull MediaRouteSelector selector, int flags) { 42811417b1cfde8f1749905f2d735623af9214148afJeff Brown if (selector == null) { 42911417b1cfde8f1749905f2d735623af9214148afJeff Brown throw new IllegalArgumentException("selector must not be null"); 430c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 431c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 432c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 43311417b1cfde8f1749905f2d735623af9214148afJeff Brown return sGlobal.isRouteAvailable(selector, flags); 434c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 435c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 436c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 43711417b1cfde8f1749905f2d735623af9214148afJeff Brown * Registers a callback to discover routes that match the selector and to receive 43811417b1cfde8f1749905f2d735623af9214148afJeff Brown * events when they change. 43911417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 44011417b1cfde8f1749905f2d735623af9214148afJeff Brown * This is a convenience method that has the same effect as calling 44111417b1cfde8f1749905f2d735623af9214148afJeff Brown * {@link #addCallback(MediaRouteSelector, Callback, int)} without flags. 44211417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 443c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 44411417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param selector A route selector that indicates the kinds of routes that the 44511417b1cfde8f1749905f2d735623af9214148afJeff Brown * callback would like to discover. 44611417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param callback The callback to add. 44711417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see #removeCallback 448c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 44911417b1cfde8f1749905f2d735623af9214148afJeff Brown public void addCallback(MediaRouteSelector selector, Callback callback) { 45011417b1cfde8f1749905f2d735623af9214148afJeff Brown addCallback(selector, callback, 0); 451c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 452c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 453c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 45411417b1cfde8f1749905f2d735623af9214148afJeff Brown * Registers a callback to discover routes that match the selector and to receive 45511417b1cfde8f1749905f2d735623af9214148afJeff Brown * events when they change. 45628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * <p> 45711417b1cfde8f1749905f2d735623af9214148afJeff Brown * The selector describes the kinds of routes that the application wants to 45811417b1cfde8f1749905f2d735623af9214148afJeff Brown * discover. For example, if the application wants to use 45911417b1cfde8f1749905f2d735623af9214148afJeff Brown * live audio routes then it should include the 46011417b1cfde8f1749905f2d735623af9214148afJeff Brown * {@link MediaControlIntent#CATEGORY_LIVE_AUDIO live audio media control intent category} 46111417b1cfde8f1749905f2d735623af9214148afJeff Brown * in its selector when it adds a callback to the media router. 46211417b1cfde8f1749905f2d735623af9214148afJeff Brown * The selector may include any number of categories. 46328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * </p><p> 46411417b1cfde8f1749905f2d735623af9214148afJeff Brown * If the callback has already been registered, then the selector is added to 46511417b1cfde8f1749905f2d735623af9214148afJeff Brown * the set of selectors being monitored by the callback. 46611417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p><p> 46711417b1cfde8f1749905f2d735623af9214148afJeff Brown * By default, the callback will only be invoked for events that affect routes 46811417b1cfde8f1749905f2d735623af9214148afJeff Brown * that match the specified selector. Event filtering may be disabled by specifying 46911417b1cfde8f1749905f2d735623af9214148afJeff Brown * the {@link #CALLBACK_FLAG_UNFILTERED_EVENTS} flag when the callback is registered. 470fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p> 471fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * Applications should use the {@link #isRouteAvailable} method to determine 472fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * whether is it possible to discover a route with the desired capabilities 473fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * and therefore whether the media route button should be shown to the user. 474fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p> 475fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * The {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} flag should be used while the application 476fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * is in the foreground to request that passive discovery be performed if there are 477fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * sufficient resources to allow continuous passive discovery. 478fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * On {@link ActivityManager#isLowRamDevice low-RAM devices} this flag will be 479fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * ignored to conserve resources. 480fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p> 481fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * The {@link #CALLBACK_FLAG_FORCE_DISCOVERY} flag should be used when 482fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * passive discovery absolutely must be performed, even on low-RAM devices. 483fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * This flag has a significant performance impact on low-RAM devices 484fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * since it may cause many media route providers to be started simultaneously. 485fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * It is much better to use {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} instead to avoid 486fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * performing passive discovery on these devices altogether. 487fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p> 488fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * The {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} flag should be used when the 489fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * media route chooser dialog is showing to confirm the presence of available 490fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * routes that the user may connect to. This flag may use substantially more 491fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * power. 49228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * </p> 49328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 49428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * <h3>Example</h3> 49528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * <pre> 49611417b1cfde8f1749905f2d735623af9214148afJeff Brown * public class MyActivity extends Activity { 49711417b1cfde8f1749905f2d735623af9214148afJeff Brown * private MediaRouter mRouter; 49811417b1cfde8f1749905f2d735623af9214148afJeff Brown * private MediaRouter.Callback mCallback; 49911417b1cfde8f1749905f2d735623af9214148afJeff Brown * private MediaRouteSelector mSelector; 50028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 50111417b1cfde8f1749905f2d735623af9214148afJeff Brown * protected void onCreate(Bundle savedInstanceState) { 50211417b1cfde8f1749905f2d735623af9214148afJeff Brown * super.onCreate(savedInstanceState); 50311417b1cfde8f1749905f2d735623af9214148afJeff Brown * 50411417b1cfde8f1749905f2d735623af9214148afJeff Brown * mRouter = Mediarouter.getInstance(this); 50511417b1cfde8f1749905f2d735623af9214148afJeff Brown * mCallback = new MyCallback(); 50611417b1cfde8f1749905f2d735623af9214148afJeff Brown * mSelector = new MediaRouteSelector.Builder() 50711417b1cfde8f1749905f2d735623af9214148afJeff Brown * .addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO) 50811417b1cfde8f1749905f2d735623af9214148afJeff Brown * .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK) 50911417b1cfde8f1749905f2d735623af9214148afJeff Brown * .build(); 51028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * } 51128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 512f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * // Add the callback on start to tell the media router what kinds of routes 51311417b1cfde8f1749905f2d735623af9214148afJeff Brown * // the application is interested in so that it can try to discover suitable ones. 514f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * public void onStart() { 515f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * super.onStart(); 51611417b1cfde8f1749905f2d735623af9214148afJeff Brown * 517f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * mediaRouter.addCallback(mSelector, mCallback, 518f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY); 51911417b1cfde8f1749905f2d735623af9214148afJeff Brown * 52011417b1cfde8f1749905f2d735623af9214148afJeff Brown * MediaRouter.RouteInfo route = mediaRouter.updateSelectedRoute(mSelector); 52111417b1cfde8f1749905f2d735623af9214148afJeff Brown * // do something with the route... 52211417b1cfde8f1749905f2d735623af9214148afJeff Brown * } 52311417b1cfde8f1749905f2d735623af9214148afJeff Brown * 524f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * // Remove the selector on stop to tell the media router that it no longer 52511417b1cfde8f1749905f2d735623af9214148afJeff Brown * // needs to invest effort trying to discover routes of these kinds for now. 526f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * public void onStop() { 527f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * super.onStop(); 52811417b1cfde8f1749905f2d735623af9214148afJeff Brown * 52911417b1cfde8f1749905f2d735623af9214148afJeff Brown * mediaRouter.removeCallback(mCallback); 53011417b1cfde8f1749905f2d735623af9214148afJeff Brown * } 53111417b1cfde8f1749905f2d735623af9214148afJeff Brown * 53211417b1cfde8f1749905f2d735623af9214148afJeff Brown * private final class MyCallback extends MediaRouter.Callback { 53311417b1cfde8f1749905f2d735623af9214148afJeff Brown * // Implement callback methods as needed. 53428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * } 53528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * } 53628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * </pre> 53728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 53811417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param selector A route selector that indicates the kinds of routes that the 53911417b1cfde8f1749905f2d735623af9214148afJeff Brown * callback would like to discover. 54011417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param callback The callback to add. 54111417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param flags Flags to control the behavior of the callback. 542f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * May be zero or a combination of {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and 54311417b1cfde8f1749905f2d735623af9214148afJeff Brown * {@link #CALLBACK_FLAG_UNFILTERED_EVENTS}. 54411417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see #removeCallback 54528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown */ 546429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void addCallback(@NonNull MediaRouteSelector selector, @NonNull Callback callback, 547429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @CallbackFlags int flags) { 54828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown if (selector == null) { 54928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown throw new IllegalArgumentException("selector must not be null"); 55028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 55111417b1cfde8f1749905f2d735623af9214148afJeff Brown if (callback == null) { 55211417b1cfde8f1749905f2d735623af9214148afJeff Brown throw new IllegalArgumentException("callback must not be null"); 55311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 55428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown checkCallingThread(); 55528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 55611417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 55711417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "addCallback: selector=" + selector 55811417b1cfde8f1749905f2d735623af9214148afJeff Brown + ", callback=" + callback + ", flags=" + Integer.toHexString(flags)); 55911417b1cfde8f1749905f2d735623af9214148afJeff Brown } 56011417b1cfde8f1749905f2d735623af9214148afJeff Brown 56111417b1cfde8f1749905f2d735623af9214148afJeff Brown CallbackRecord record; 56211417b1cfde8f1749905f2d735623af9214148afJeff Brown int index = findCallbackRecord(callback); 56311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (index < 0) { 5649fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown record = new CallbackRecord(this, callback); 56511417b1cfde8f1749905f2d735623af9214148afJeff Brown mCallbackRecords.add(record); 56611417b1cfde8f1749905f2d735623af9214148afJeff Brown } else { 56711417b1cfde8f1749905f2d735623af9214148afJeff Brown record = mCallbackRecords.get(index); 56811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 56911417b1cfde8f1749905f2d735623af9214148afJeff Brown boolean updateNeeded = false; 57011417b1cfde8f1749905f2d735623af9214148afJeff Brown if ((flags & ~record.mFlags) != 0) { 57111417b1cfde8f1749905f2d735623af9214148afJeff Brown record.mFlags |= flags; 57211417b1cfde8f1749905f2d735623af9214148afJeff Brown updateNeeded = true; 57311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 57411417b1cfde8f1749905f2d735623af9214148afJeff Brown if (!record.mSelector.contains(selector)) { 57511417b1cfde8f1749905f2d735623af9214148afJeff Brown record.mSelector = new MediaRouteSelector.Builder(record.mSelector) 57611417b1cfde8f1749905f2d735623af9214148afJeff Brown .addSelector(selector) 57711417b1cfde8f1749905f2d735623af9214148afJeff Brown .build(); 57811417b1cfde8f1749905f2d735623af9214148afJeff Brown updateNeeded = true; 57911417b1cfde8f1749905f2d735623af9214148afJeff Brown } 58011417b1cfde8f1749905f2d735623af9214148afJeff Brown if (updateNeeded) { 58111417b1cfde8f1749905f2d735623af9214148afJeff Brown sGlobal.updateDiscoveryRequest(); 58228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 58328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 58428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 58528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 58611417b1cfde8f1749905f2d735623af9214148afJeff Brown * Removes the specified callback. It will no longer receive events about 58711417b1cfde8f1749905f2d735623af9214148afJeff Brown * changes to media routes. 58828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 58911417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param callback The callback to remove. 59011417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see #addCallback 59128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown */ 592429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void removeCallback(@NonNull Callback callback) { 59311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (callback == null) { 59411417b1cfde8f1749905f2d735623af9214148afJeff Brown throw new IllegalArgumentException("callback must not be null"); 59528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 59628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown checkCallingThread(); 59728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 59811417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 59911417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "removeCallback: callback=" + callback); 60011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 60111417b1cfde8f1749905f2d735623af9214148afJeff Brown 60211417b1cfde8f1749905f2d735623af9214148afJeff Brown int index = findCallbackRecord(callback); 60311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (index >= 0) { 60411417b1cfde8f1749905f2d735623af9214148afJeff Brown mCallbackRecords.remove(index); 60511417b1cfde8f1749905f2d735623af9214148afJeff Brown sGlobal.updateDiscoveryRequest(); 60628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 60728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 60828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 60911417b1cfde8f1749905f2d735623af9214148afJeff Brown private int findCallbackRecord(Callback callback) { 61011417b1cfde8f1749905f2d735623af9214148afJeff Brown final int count = mCallbackRecords.size(); 61111417b1cfde8f1749905f2d735623af9214148afJeff Brown for (int i = 0; i < count; i++) { 61211417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mCallbackRecords.get(i).mCallback == callback) { 61311417b1cfde8f1749905f2d735623af9214148afJeff Brown return i; 61411417b1cfde8f1749905f2d735623af9214148afJeff Brown } 61511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 61611417b1cfde8f1749905f2d735623af9214148afJeff Brown return -1; 61711417b1cfde8f1749905f2d735623af9214148afJeff Brown } 61811417b1cfde8f1749905f2d735623af9214148afJeff Brown 61928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 6209942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * Registers a media route provider within this application process. 6219942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * <p> 6229942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * The provider will be added to the list of providers that all {@link MediaRouter} 6239942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * instances within this process can use to discover routes. 6249942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * </p> 625c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 626fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param providerInstance The media route provider instance to add. 627c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 628c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaRouteProvider 62928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @see #removeCallback 630c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 631429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void addProvider(@NonNull MediaRouteProvider providerInstance) { 632fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (providerInstance == null) { 633fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown throw new IllegalArgumentException("providerInstance must not be null"); 634c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 635c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 636c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 63711417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 63811417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "addProvider: " + providerInstance); 63911417b1cfde8f1749905f2d735623af9214148afJeff Brown } 640fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown sGlobal.addProvider(providerInstance); 641c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 642c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 643c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 6449942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * Unregisters a media route provider within this application process. 6459942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * <p> 6469942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * The provider will be removed from the list of providers that all {@link MediaRouter} 6479942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * instances within this process can use to discover routes. 6489942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * </p> 649c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 650fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param providerInstance The media route provider instance to remove. 651c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 652c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaRouteProvider 65328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @see #addCallback 654c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 655429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void removeProvider(@NonNull MediaRouteProvider providerInstance) { 656fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (providerInstance == null) { 657fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown throw new IllegalArgumentException("providerInstance must not be null"); 658c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 659c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 660c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 66111417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 66211417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "removeProvider: " + providerInstance); 66328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 66411417b1cfde8f1749905f2d735623af9214148afJeff Brown sGlobal.removeProvider(providerInstance); 66528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 66628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 66728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 668567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * Adds a remote control client to enable remote control of the volume 669567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * of the selected route. 670567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * <p> 671567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * The remote control client must have previously been registered with 672567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * the audio manager using the {@link android.media.AudioManager#registerRemoteControlClient 673567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * AudioManager.registerRemoteControlClient} method. 674567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * </p> 675567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * 676567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * @param remoteControlClient The {@link android.media.RemoteControlClient} to register. 677567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown */ 678429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void addRemoteControlClient(@NonNull Object remoteControlClient) { 679567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (remoteControlClient == null) { 680567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown throw new IllegalArgumentException("remoteControlClient must not be null"); 681567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 682567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown checkCallingThread(); 683567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 684567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (DEBUG) { 685567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown Log.d(TAG, "addRemoteControlClient: " + remoteControlClient); 686567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 687567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown sGlobal.addRemoteControlClient(remoteControlClient); 688567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 689567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 690567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown /** 691567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * Removes a remote control client. 692567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * 693bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * @param remoteControlClient The {@link android.media.RemoteControlClient} 694bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * to unregister. 695567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown */ 696429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void removeRemoteControlClient(@NonNull Object remoteControlClient) { 697567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (remoteControlClient == null) { 698567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown throw new IllegalArgumentException("remoteControlClient must not be null"); 699567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 700567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 701567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (DEBUG) { 702567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown Log.d(TAG, "removeRemoteControlClient: " + remoteControlClient); 703567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 704567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown sGlobal.removeRemoteControlClient(remoteControlClient); 705567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 706567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 707567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown /** 708bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * Sets the media session to enable remote control of the volume of the 709bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * selected route. This should be used instead of 710bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * {@link #addRemoteControlClient} when using media sessions. Set the 711bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * session to null to clear it. 712bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * 713bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * @param mediaSession The {@link android.media.session.MediaSession} to 714bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * use. 715bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik */ 716bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik public void setMediaSession(Object mediaSession) { 717bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik if (DEBUG) { 718bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik Log.d(TAG, "addMediaSession: " + mediaSession); 719bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 720bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik sGlobal.setMediaSession(mediaSession); 721bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 722bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 723bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik /** 72494be6100218126ce6a08bf1f56209578500b361fRoboErik * Sets a compat media session to enable remote control of the volume of the 72594be6100218126ce6a08bf1f56209578500b361fRoboErik * selected route. This should be used instead of 72694be6100218126ce6a08bf1f56209578500b361fRoboErik * {@link #addRemoteControlClient} when using {@link MediaSessionCompat}. 72794be6100218126ce6a08bf1f56209578500b361fRoboErik * Set the session to null to clear it. 72894be6100218126ce6a08bf1f56209578500b361fRoboErik * 72994be6100218126ce6a08bf1f56209578500b361fRoboErik * @param mediaSession 73094be6100218126ce6a08bf1f56209578500b361fRoboErik */ 73194be6100218126ce6a08bf1f56209578500b361fRoboErik public void setMediaSessionCompat(MediaSessionCompat mediaSession) { 73294be6100218126ce6a08bf1f56209578500b361fRoboErik if (DEBUG) { 73394be6100218126ce6a08bf1f56209578500b361fRoboErik Log.d(TAG, "addMediaSessionCompat: " + mediaSession); 73494be6100218126ce6a08bf1f56209578500b361fRoboErik } 7355c9469e010106467791b47b0fa83efda84491a21RoboErik sGlobal.setMediaSessionCompat(mediaSession); 73694be6100218126ce6a08bf1f56209578500b361fRoboErik } 73794be6100218126ce6a08bf1f56209578500b361fRoboErik 73894be6100218126ce6a08bf1f56209578500b361fRoboErik public MediaSessionCompat.Token getMediaSessionToken() { 73994be6100218126ce6a08bf1f56209578500b361fRoboErik return sGlobal.getMediaSessionToken(); 74094be6100218126ce6a08bf1f56209578500b361fRoboErik } 74194be6100218126ce6a08bf1f56209578500b361fRoboErik 74294be6100218126ce6a08bf1f56209578500b361fRoboErik /** 743c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Ensures that calls into the media router are on the correct thread. 744c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * It pays to be a little paranoid when global state invariants are at risk. 745c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 746c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static void checkCallingThread() { 747c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (Looper.myLooper() != Looper.getMainLooper()) { 748c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalStateException("The media router service must only be " 749c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + "accessed on the application's main thread."); 750c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 751c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 752c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 753c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static <T> boolean equal(T a, T b) { 754c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return a == b || (a != null && b != null && a.equals(b)); 755c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 756c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 757c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 758c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Provides information about a media route. 759c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 760fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Each media route has a list of {@link MediaControlIntent media control} 761fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * {@link #getControlFilters intent filters} that describe the capabilities of the 762c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * route and the manner in which it is used and controlled. 763c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 764c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 765c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final class RouteInfo { 766fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private final ProviderInfo mProvider; 767c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final String mDescriptorId; 768cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown private final String mUniqueId; 769c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private String mName; 770d63957d28aaabcec588b8cde12eac16414783aebJeff Brown private String mDescription; 771c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private boolean mEnabled; 77211417b1cfde8f1749905f2d735623af9214148afJeff Brown private boolean mConnecting; 77394be6100218126ce6a08bf1f56209578500b361fRoboErik private boolean mCanDisconnect; 774fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private final ArrayList<IntentFilter> mControlFilters = new ArrayList<IntentFilter>(); 775c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mPlaybackType; 776c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mPlaybackStream; 777c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mVolumeHandling; 778c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mVolume; 779c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mVolumeMax; 780c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private Display mPresentationDisplay; 781c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mPresentationDisplayId = -1; 782c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private Bundle mExtras; 78394be6100218126ce6a08bf1f56209578500b361fRoboErik private IntentSender mSettingsIntent; 78411417b1cfde8f1749905f2d735623af9214148afJeff Brown private MediaRouteDescriptor mDescriptor; 785c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 786429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye /** @hide */ 787429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @IntDef({PLAYBACK_TYPE_LOCAL,PLAYBACK_TYPE_REMOTE}) 788429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Retention(RetentionPolicy.SOURCE) 789429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye private @interface PlaybackType {} 790429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye 791c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 792c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The default playback type, "local", indicating the presentation of the media 793c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * is happening on the same device (e.g. a phone, a tablet) as where it is 794c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * controlled from. 795c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 796c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getPlaybackType 797c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 798c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_TYPE_LOCAL = 0; 799c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 800c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 801c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * A playback type indicating the presentation of the media is happening on 802c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * a different device (i.e. the remote device) than where it is controlled from. 803c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 804c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getPlaybackType 805c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 806c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_TYPE_REMOTE = 1; 807c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 808429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye /** @hide */ 809429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @IntDef({PLAYBACK_VOLUME_FIXED,PLAYBACK_VOLUME_VARIABLE}) 810429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Retention(RetentionPolicy.SOURCE) 811429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye private @interface PlaybackVolume {} 812429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye 813c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 814c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Playback information indicating the playback volume is fixed, i.e. it cannot be 815c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * controlled from this object. An example of fixed playback volume is a remote player, 816c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather 817c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * than attenuate at the source. 818c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 819c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getVolumeHandling 820c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 821c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_VOLUME_FIXED = 0; 822c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 823c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 824c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Playback information indicating the playback volume is variable and can be controlled 825c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * from this object. 826c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 827c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getVolumeHandling 828c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 829c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_VOLUME_VARIABLE = 1; 830c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 831c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static final int CHANGE_GENERAL = 1 << 0; 832c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static final int CHANGE_VOLUME = 1 << 1; 833c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static final int CHANGE_PRESENTATION_DISPLAY = 1 << 2; 834c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 835cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown RouteInfo(ProviderInfo provider, String descriptorId, String uniqueId) { 836fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mProvider = provider; 837c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDescriptorId = descriptorId; 838cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown mUniqueId = uniqueId; 839c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 840c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 841fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 842fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets information about the provider of this media route. 843fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 844fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public ProviderInfo getProvider() { 845fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mProvider; 846c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 847c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 848c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 849cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * Gets the unique id of the route. 850cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * <p> 851cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * The route unique id functions as a stable identifier by which the route is known. 852cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * For example, an application can use this id as a token to remember the 853cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * selected route across restarts or to communicate its identity to a service. 854cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * </p> 855cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * 856cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * @return The unique id of the route, never null. 857cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown */ 858429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @NonNull 859cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown public String getId() { 860cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return mUniqueId; 861cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 862cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown 863cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown /** 864d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * Gets the user-visible name of the route. 865d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * <p> 866d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * The route name identifies the destination represented by the route. 867d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * It may be a user-supplied name, an alias, or device serial number. 868d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * </p> 869c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 870d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * @return The user-visible name of a media route. This is the string presented 871c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to users who may select this as the active route. 872c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 873c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public String getName() { 874c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mName; 875c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 876c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 877c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 878d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * Gets the user-visible description of the route. 879d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * <p> 880d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * The route description describes the kind of destination represented by the route. 881d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * It may be a user-supplied string, a model number or brand of device. 882d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * </p> 883c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 884d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * @return The description of the route, or null if none. 885c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 886429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Nullable 887d63957d28aaabcec588b8cde12eac16414783aebJeff Brown public String getDescription() { 888d63957d28aaabcec588b8cde12eac16414783aebJeff Brown return mDescription; 889c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 890c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 891c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 892c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Returns true if this route is enabled and may be selected. 893c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 89411417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if this route is enabled. 895c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 896c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public boolean isEnabled() { 897c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mEnabled; 898c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 899c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 900c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 90111417b1cfde8f1749905f2d735623af9214148afJeff Brown * Returns true if the route is in the process of connecting and is not 90211417b1cfde8f1749905f2d735623af9214148afJeff Brown * yet ready for use. 90311417b1cfde8f1749905f2d735623af9214148afJeff Brown * 90411417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if this route is in the process of connecting. 90511417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 90611417b1cfde8f1749905f2d735623af9214148afJeff Brown public boolean isConnecting() { 90711417b1cfde8f1749905f2d735623af9214148afJeff Brown return mConnecting; 90811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 90911417b1cfde8f1749905f2d735623af9214148afJeff Brown 91011417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 911fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Returns true if this route is currently selected. 912c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 91311417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if this route is currently selected. 914fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 915fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see MediaRouter#getSelectedRoute 916fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 917fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public boolean isSelected() { 918fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 919fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return sGlobal.getSelectedRoute() == this; 920fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 921fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 922fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 923fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Returns true if this route is the default route. 924fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 92511417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if this route is the default route. 926fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 927fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see MediaRouter#getDefaultRoute 928fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 929fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public boolean isDefault() { 930fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 931fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return sGlobal.getDefaultRoute() == this; 932fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 933fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 934fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 935fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets a list of {@link MediaControlIntent media control intent} filters that 936fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * describe the capabilities of this route and the media control actions that 937fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * it supports. 938fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 939fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @return A list of intent filters that specifies the media control intents that 940fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * this route supports. 941c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 942c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 943c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #supportsControlCategory 944c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #supportsControlRequest 945c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 946fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public List<IntentFilter> getControlFilters() { 947fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mControlFilters; 948c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 949c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 950c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 95128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * Returns true if the route supports at least one of the capabilities 95228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * described by a media route selector. 95328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 95428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @param selector The selector that specifies the capabilities to check. 95528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @return True if the route supports at least one of the capabilities 95628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * described in the media route selector. 95728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown */ 958429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public boolean matchesSelector(@NonNull MediaRouteSelector selector) { 95928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown if (selector == null) { 96028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown throw new IllegalArgumentException("selector must not be null"); 96128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 96228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown checkCallingThread(); 96311417b1cfde8f1749905f2d735623af9214148afJeff Brown return selector.matchesControlFilters(mControlFilters); 96428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 96528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 96628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 967c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Returns true if the route supports the specified 968c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent media control} category. 969c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 970c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media control categories describe the capabilities of this route 971c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * such as whether it supports live audio streaming or remote playback. 972c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 973c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 974c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param category A {@link MediaControlIntent media control} category 975c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO}, 976c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO}, 977fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined 978c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * media control category. 97928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @return True if the route supports the specified intent category. 980c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 981c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 982fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see #getControlFilters 983c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 984429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public boolean supportsControlCategory(@NonNull String category) { 985c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (category == null) { 986c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("category must not be null"); 987c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 988fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 989c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 990fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int count = mControlFilters.size(); 991fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown for (int i = 0; i < count; i++) { 992fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mControlFilters.get(i).hasCategory(category)) { 993fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return true; 994fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 995fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 996fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return false; 997c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 998c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 999c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1000c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Returns true if the route supports the specified 1001a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * {@link MediaControlIntent media control} category and action. 1002a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * <p> 1003a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * Media control actions describe specific requests that an application 1004a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * can ask a route to perform. 1005a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * </p> 1006a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * 1007a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * @param category A {@link MediaControlIntent media control} category 1008a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO}, 1009a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO}, 1010a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined 1011a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * media control category. 1012a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * @param action A {@link MediaControlIntent media control} action 1013a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * such as {@link MediaControlIntent#ACTION_PLAY}. 1014a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * @return True if the route supports the specified intent action. 1015a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * 1016a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * @see MediaControlIntent 1017a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * @see #getControlFilters 1018a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown */ 1019429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public boolean supportsControlAction(@NonNull String category, @NonNull String action) { 1020a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown if (category == null) { 1021a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown throw new IllegalArgumentException("category must not be null"); 1022a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown } 1023a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown if (action == null) { 1024a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown throw new IllegalArgumentException("action must not be null"); 1025a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown } 1026a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown checkCallingThread(); 1027a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown 1028a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown int count = mControlFilters.size(); 1029a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown for (int i = 0; i < count; i++) { 1030a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown IntentFilter filter = mControlFilters.get(i); 1031a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown if (filter.hasCategory(category) && filter.hasAction(action)) { 1032a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown return true; 1033a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown } 1034a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown } 1035a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown return false; 1036a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown } 1037a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown 1038a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown /** 1039a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * Returns true if the route supports the specified 1040c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent media control} request. 1041c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1042c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media control requests are used to request the route to perform 104343f79f79a5117550a7dfedf9c65124afd163ea43Jeff Brown * actions such as starting remote playback of a media item. 1044c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1045c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1046c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param intent A {@link MediaControlIntent media control intent}. 1047c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return True if the route can handle the specified intent. 1048c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1049c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 1050fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see #getControlFilters 1051c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1052429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public boolean supportsControlRequest(@NonNull Intent intent) { 1053c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (intent == null) { 1054c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("intent must not be null"); 1055c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1056c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 1057c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1058c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown ContentResolver contentResolver = sGlobal.getContentResolver(); 1059fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int count = mControlFilters.size(); 1060fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown for (int i = 0; i < count; i++) { 1061fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mControlFilters.get(i).match(contentResolver, intent, true, TAG) >= 0) { 1062fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return true; 1063fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1064fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1065fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return false; 1066c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1067c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1068c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1069c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Sends a {@link MediaControlIntent media control} request to be performed 1070c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * asynchronously by the route's destination. 1071c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1072c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media control requests are used to request the route to perform 107343f79f79a5117550a7dfedf9c65124afd163ea43Jeff Brown * actions such as starting remote playback of a media item. 1074c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 1075fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * This function may only be called on a selected route. Control requests 1076fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * sent to unselected routes will fail. 1077c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1078c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1079c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param intent A {@link MediaControlIntent media control intent}. 1080c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param callback A {@link ControlRequestCallback} to invoke with the result 1081c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * of the request, or null if no result is required. 1082c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1083c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 1084c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1085429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void sendControlRequest(@NonNull Intent intent, 1086429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Nullable ControlRequestCallback callback) { 1087c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (intent == null) { 1088c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("intent must not be null"); 1089c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1090c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 1091c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1092fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown sGlobal.sendControlRequest(this, intent, callback); 1093c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1094c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1095c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1096c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the type of playback associated with this route. 1097c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1098c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The type of playback associated with this route: {@link #PLAYBACK_TYPE_LOCAL} 1099c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * or {@link #PLAYBACK_TYPE_REMOTE}. 1100c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1101429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @PlaybackType 1102c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getPlaybackType() { 1103c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mPlaybackType; 1104c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1105c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1106c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1107350ba6e4a1b5ec28721a098e50eaf6a508eb28f0Jeff Brown * Gets the audio stream over which the playback associated with this route is performed. 1108c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1109c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The stream over which the playback associated with this route is performed. 1110c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1111c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getPlaybackStream() { 1112c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mPlaybackStream; 1113c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1114c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1115c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1116c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets information about how volume is handled on the route. 1117c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1118c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return How volume is handled on the route: {@link #PLAYBACK_VOLUME_FIXED} 1119c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * or {@link #PLAYBACK_VOLUME_VARIABLE}. 1120c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1121429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @PlaybackVolume 1122c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getVolumeHandling() { 1123c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mVolumeHandling; 1124c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1125c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1126c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1127c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the current volume for this route. Depending on the route, this may only 1128c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * be valid if the route is currently selected. 1129c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1130c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The volume at which the playback associated with this route is performed. 1131c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1132c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getVolume() { 1133c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mVolume; 1134c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1135c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1136c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1137c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the maximum volume at which the playback associated with this route is performed. 1138c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1139c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The maximum volume at which the playback associated with 1140c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * this route is performed. 1141c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1142c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getVolumeMax() { 1143c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mVolumeMax; 1144c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1145c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1146c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 114794be6100218126ce6a08bf1f56209578500b361fRoboErik * Gets whether this route supports disconnecting without interrupting 114894be6100218126ce6a08bf1f56209578500b361fRoboErik * playback. 114994be6100218126ce6a08bf1f56209578500b361fRoboErik * 115094be6100218126ce6a08bf1f56209578500b361fRoboErik * @return True if this route can disconnect without stopping playback, 115194be6100218126ce6a08bf1f56209578500b361fRoboErik * false otherwise. 115294be6100218126ce6a08bf1f56209578500b361fRoboErik */ 115394be6100218126ce6a08bf1f56209578500b361fRoboErik public boolean canDisconnect() { 115494be6100218126ce6a08bf1f56209578500b361fRoboErik return mCanDisconnect; 115594be6100218126ce6a08bf1f56209578500b361fRoboErik } 115694be6100218126ce6a08bf1f56209578500b361fRoboErik 115794be6100218126ce6a08bf1f56209578500b361fRoboErik /** 1158c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Requests a volume change for this route asynchronously. 1159c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1160c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This function may only be called on a selected route. It will have 1161c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * no effect if the route is currently unselected. 1162c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1163c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1164c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param volume The new volume value between 0 and {@link #getVolumeMax}. 1165c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1166c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestSetVolume(int volume) { 1167c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 1168c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal.requestSetVolume(this, Math.min(mVolumeMax, Math.max(0, volume))); 1169c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1170c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1171c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1172c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Requests an incremental volume update for this route asynchronously. 1173c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1174c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This function may only be called on a selected route. It will have 1175c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * no effect if the route is currently unselected. 1176c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1177c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1178c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param delta The delta to add to the current volume. 1179c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1180c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestUpdateVolume(int delta) { 1181c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 1182c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (delta != 0) { 1183c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal.requestUpdateVolume(this, delta); 1184c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1185c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1186c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1187c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1188c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the {@link Display} that should be used by the application to show 1189c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * a {@link android.app.Presentation} on an external display when this route is selected. 1190c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Depending on the route, this may only be valid if the route is currently 1191c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * selected. 1192c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1193c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The preferred presentation display may change independently of the route 1194c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * being selected or unselected. For example, the presentation display 1195c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * of the default system route may change when an external HDMI display is connected 1196c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * or disconnected even though the route itself has not changed. 1197c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 1198c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This method may return null if there is no external display associated with 1199c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * the route or if the display is not ready to show UI yet. 1200c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 1201c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The application should listen for changes to the presentation display 1202c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * using the {@link Callback#onRoutePresentationDisplayChanged} callback and 1203c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * show or dismiss its {@link android.app.Presentation} accordingly when the display 1204c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * becomes available or is removed. 1205c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 1206c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This method only makes sense for 1207c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO live video} routes. 1208c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1209c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1210c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The preferred presentation display to use when this route is 1211c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * selected or null if none. 1212c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1213c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent#CATEGORY_LIVE_VIDEO 1214c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see android.app.Presentation 1215c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1216429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Nullable 1217c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public Display getPresentationDisplay() { 1218fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1219c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mPresentationDisplayId >= 0 && mPresentationDisplay == null) { 1220c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mPresentationDisplay = sGlobal.getDisplay(mPresentationDisplayId); 1221c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1222c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mPresentationDisplay; 1223c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1224c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1225c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1226c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets a collection of extra properties about this route that were supplied 1227c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * by its media route provider, or null if none. 1228c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1229429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Nullable 1230c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public Bundle getExtras() { 1231c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mExtras; 1232c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1233c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1234fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 123594be6100218126ce6a08bf1f56209578500b361fRoboErik * Gets an intent sender for launching a settings activity for this 123694be6100218126ce6a08bf1f56209578500b361fRoboErik * route. 123794be6100218126ce6a08bf1f56209578500b361fRoboErik */ 123894be6100218126ce6a08bf1f56209578500b361fRoboErik @Nullable 123994be6100218126ce6a08bf1f56209578500b361fRoboErik public IntentSender getSettingsIntent() { 124094be6100218126ce6a08bf1f56209578500b361fRoboErik return mSettingsIntent; 124194be6100218126ce6a08bf1f56209578500b361fRoboErik } 124294be6100218126ce6a08bf1f56209578500b361fRoboErik 124394be6100218126ce6a08bf1f56209578500b361fRoboErik /** 1244fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Selects this media route. 1245fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1246fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void select() { 1247fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1248fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown sGlobal.selectRoute(this); 1249fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1250fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1251c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 1252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public String toString() { 1253cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return "MediaRouter.RouteInfo{ uniqueId=" + mUniqueId 1254cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown + ", name=" + mName 1255d63957d28aaabcec588b8cde12eac16414783aebJeff Brown + ", description=" + mDescription 1256c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", enabled=" + mEnabled 125711417b1cfde8f1749905f2d735623af9214148afJeff Brown + ", connecting=" + mConnecting 125894be6100218126ce6a08bf1f56209578500b361fRoboErik + ", canDisconnect=" + mCanDisconnect 1259c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", playbackType=" + mPlaybackType 1260c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", playbackStream=" + mPlaybackStream 1261c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", volumeHandling=" + mVolumeHandling 1262c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", volume=" + mVolume 1263c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", volumeMax=" + mVolumeMax 1264c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", presentationDisplayId=" + mPresentationDisplayId 1265c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", extras=" + mExtras 126694be6100218126ce6a08bf1f56209578500b361fRoboErik + ", settingsIntent=" + mSettingsIntent 1267fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown + ", providerPackageName=" + mProvider.getPackageName() 1268fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown + " }"; 1269fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1270fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 127111417b1cfde8f1749905f2d735623af9214148afJeff Brown int updateDescriptor(MediaRouteDescriptor descriptor) { 1272fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int changes = 0; 1273fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mDescriptor != descriptor) { 1274fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mDescriptor = descriptor; 1275fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (descriptor != null) { 1276fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (!equal(mName, descriptor.getName())) { 1277fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mName = descriptor.getName(); 1278fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown changes |= CHANGE_GENERAL; 1279fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1280d63957d28aaabcec588b8cde12eac16414783aebJeff Brown if (!equal(mDescription, descriptor.getDescription())) { 1281d63957d28aaabcec588b8cde12eac16414783aebJeff Brown mDescription = descriptor.getDescription(); 1282fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown changes |= CHANGE_GENERAL; 1283fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1284fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mEnabled != descriptor.isEnabled()) { 1285fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mEnabled = descriptor.isEnabled(); 1286fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown changes |= CHANGE_GENERAL; 1287fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 128811417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mConnecting != descriptor.isConnecting()) { 128911417b1cfde8f1749905f2d735623af9214148afJeff Brown mConnecting = descriptor.isConnecting(); 129011417b1cfde8f1749905f2d735623af9214148afJeff Brown changes |= CHANGE_GENERAL; 129111417b1cfde8f1749905f2d735623af9214148afJeff Brown } 129211417b1cfde8f1749905f2d735623af9214148afJeff Brown if (!mControlFilters.equals(descriptor.getControlFilters())) { 1293fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mControlFilters.clear(); 129411417b1cfde8f1749905f2d735623af9214148afJeff Brown mControlFilters.addAll(descriptor.getControlFilters()); 1295fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown changes |= CHANGE_GENERAL; 1296fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1297fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mPlaybackType != descriptor.getPlaybackType()) { 1298fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mPlaybackType = descriptor.getPlaybackType(); 1299fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown changes |= CHANGE_GENERAL; 1300fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1301fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mPlaybackStream != descriptor.getPlaybackStream()) { 1302fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mPlaybackStream = descriptor.getPlaybackStream(); 1303fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown changes |= CHANGE_GENERAL; 1304fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1305fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mVolumeHandling != descriptor.getVolumeHandling()) { 1306fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mVolumeHandling = descriptor.getVolumeHandling(); 1307fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown changes |= CHANGE_GENERAL | CHANGE_VOLUME; 1308fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1309fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mVolume != descriptor.getVolume()) { 1310fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mVolume = descriptor.getVolume(); 1311fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown changes |= CHANGE_GENERAL | CHANGE_VOLUME; 1312fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1313fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mVolumeMax != descriptor.getVolumeMax()) { 1314fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mVolumeMax = descriptor.getVolumeMax(); 1315fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown changes |= CHANGE_GENERAL | CHANGE_VOLUME; 1316fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1317fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mPresentationDisplayId != descriptor.getPresentationDisplayId()) { 1318fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mPresentationDisplayId = descriptor.getPresentationDisplayId(); 1319fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mPresentationDisplay = null; 1320fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY; 1321fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1322fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (!equal(mExtras, descriptor.getExtras())) { 1323fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mExtras = descriptor.getExtras(); 1324fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown changes |= CHANGE_GENERAL; 1325fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 132694be6100218126ce6a08bf1f56209578500b361fRoboErik if (!equal(mSettingsIntent, descriptor.getSettingsActivity())) { 132794be6100218126ce6a08bf1f56209578500b361fRoboErik mSettingsIntent = descriptor.getSettingsActivity(); 132894be6100218126ce6a08bf1f56209578500b361fRoboErik changes |= CHANGE_GENERAL; 132994be6100218126ce6a08bf1f56209578500b361fRoboErik } 133094be6100218126ce6a08bf1f56209578500b361fRoboErik if (mCanDisconnect != descriptor.canDisconnectAndKeepPlaying()) { 133194be6100218126ce6a08bf1f56209578500b361fRoboErik mCanDisconnect = descriptor.canDisconnectAndKeepPlaying(); 133294be6100218126ce6a08bf1f56209578500b361fRoboErik changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY; 133394be6100218126ce6a08bf1f56209578500b361fRoboErik } 1334fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1335fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1336fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return changes; 1337fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1338fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1339fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown String getDescriptorId() { 1340fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mDescriptorId; 1341fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1342fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1343fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown MediaRouteProvider getProviderInstance() { 1344fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mProvider.getProviderInstance(); 1345fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1346fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1347fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1348fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1349fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Provides information about a media route provider. 1350fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * <p> 1351fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * This object may be used to determine which media route provider has 1352fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * published a particular route. 1353fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * </p> 1354fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1355fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public static final class ProviderInfo { 1356fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private final MediaRouteProvider mProviderInstance; 1357fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>(); 1358fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1359fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private final ProviderMetadata mMetadata; 136011417b1cfde8f1749905f2d735623af9214148afJeff Brown private MediaRouteProviderDescriptor mDescriptor; 1361fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private Resources mResources; 1362fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private boolean mResourcesNotAvailable; 1363fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1364fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo(MediaRouteProvider provider) { 1365fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mProviderInstance = provider; 1366fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mMetadata = provider.getMetadata(); 1367fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1368fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1369fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1370fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets the provider's underlying {@link MediaRouteProvider} instance. 1371fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1372fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public MediaRouteProvider getProviderInstance() { 1373fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1374fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mProviderInstance; 1375fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1376fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1377fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1378adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown * Gets the package name of the media route provider. 1379fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1380fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public String getPackageName() { 1381fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mMetadata.getPackageName(); 1382fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1383fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1384fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1385adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown * Gets the component name of the media route provider. 1386adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown */ 1387adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown public ComponentName getComponentName() { 1388adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown return mMetadata.getComponentName(); 1389adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown } 1390adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown 1391adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown /** 1392fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets the {@link MediaRouter.RouteInfo routes} published by this route provider. 1393fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1394fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public List<RouteInfo> getRoutes() { 1395fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1396fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mRoutes; 1397fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1398fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1399fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Resources getResources() { 1400fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mResources == null && !mResourcesNotAvailable) { 1401fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown String packageName = getPackageName(); 1402fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Context context = sGlobal.getProviderContext(packageName); 1403fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (context != null) { 1404fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mResources = context.getResources(); 1405fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } else { 1406fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Log.w(TAG, "Unable to obtain resources for route provider package: " 1407fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown + packageName); 1408fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mResourcesNotAvailable = true; 1409fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1410fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1411fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mResources; 1412fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1413fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 141411417b1cfde8f1749905f2d735623af9214148afJeff Brown boolean updateDescriptor(MediaRouteProviderDescriptor descriptor) { 1415fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mDescriptor != descriptor) { 1416fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mDescriptor = descriptor; 1417fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return true; 1418fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1419fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return false; 1420fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1421fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1422fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int findRouteByDescriptorId(String id) { 1423fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown final int count = mRoutes.size(); 1424fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown for (int i = 0; i < count; i++) { 1425fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mRoutes.get(i).mDescriptorId.equals(id)) { 1426fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return i; 1427fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1428fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1429fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return -1; 1430fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1431fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1432fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown @Override 1433fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public String toString() { 1434fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return "MediaRouter.RouteProviderInfo{ packageName=" + getPackageName() 1435c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + " }"; 1436c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1437c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1438c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1439c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1440c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Interface for receiving events about media routing changes. 1441c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * All methods of this interface will be called from the application's main thread. 1442c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1443c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * A Callback will only receive events relevant to routes that the callback 144411417b1cfde8f1749905f2d735623af9214148afJeff Brown * was registered for unless the {@link MediaRouter#CALLBACK_FLAG_UNFILTERED_EVENTS} 144511417b1cfde8f1749905f2d735623af9214148afJeff Brown * flag was specified in {@link MediaRouter#addCallback(MediaRouteSelector, Callback, int)}. 1446c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1447c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 144811417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see MediaRouter#addCallback(MediaRouteSelector, Callback, int) 1449c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaRouter#removeCallback(Callback) 1450c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1451c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static abstract class Callback { 1452c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1453fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when the supplied media route becomes selected as the active route. 1454c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1455fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1456c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has been selected. 1457c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1458c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteSelected(MediaRouter router, RouteInfo route) { 1459c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1460c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1461c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1462fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when the supplied media route becomes unselected as the active route. 1463c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1464fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1465c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has been unselected. 1466c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1467c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteUnselected(MediaRouter router, RouteInfo route) { 1468c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1469c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1470c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1471fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route has been added. 1472c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1473fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1474c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has become available for use. 1475c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1476c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteAdded(MediaRouter router, RouteInfo route) { 1477c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1478c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1479c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1480fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route has been removed. 1481c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1482fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1483c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has been removed from availability. 1484c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1485c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteRemoved(MediaRouter router, RouteInfo route) { 1486c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1487c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1488c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1489fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a property of the indicated media route has changed. 1490c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1491fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1492c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that was changed. 1493c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1494c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteChanged(MediaRouter router, RouteInfo route) { 1495c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1496c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1497c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1498fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route's volume changes. 1499c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1500fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1501c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route whose volume changed. 1502c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1503c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) { 1504c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1505c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1506c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1507fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route's presentation display changes. 1508c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1509c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This method is called whenever the route's presentation display becomes 1510fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * available, is removed or has changes to some of its properties (such as its size). 1511c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1512c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1513fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1514c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route whose presentation display changed. 1515c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1516c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#getPresentationDisplay() 1517c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1518c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo route) { 1519c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1520fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1521fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1522fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route provider has been added. 1523fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 1524fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1525fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param provider The provider that has become available for use. 1526fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1527fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void onProviderAdded(MediaRouter router, ProviderInfo provider) { 1528fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1529fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1530fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1531fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route provider has been removed. 1532fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 1533fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1534fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param provider The provider that has been removed from availability. 1535fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1536fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void onProviderRemoved(MediaRouter router, ProviderInfo provider) { 1537fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 153811417b1cfde8f1749905f2d735623af9214148afJeff Brown 153911417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 154011417b1cfde8f1749905f2d735623af9214148afJeff Brown * Called when a property of the indicated media route provider has changed. 154111417b1cfde8f1749905f2d735623af9214148afJeff Brown * 154211417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param router The media router reporting the event. 154311417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param provider The provider that was changed. 154411417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 154511417b1cfde8f1749905f2d735623af9214148afJeff Brown public void onProviderChanged(MediaRouter router, ProviderInfo provider) { 154611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1547c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1548c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1549c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1550c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Callback which is invoked with the result of a media control request. 1551c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1552c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#sendControlRequest 1553c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1554fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public static abstract class ControlRequestCallback { 1555c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 15563d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * Called when a media control request succeeds. 15573d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * 15583d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * @param data Result data, or null if none. 15593d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * Contents depend on the {@link MediaControlIntent media control action}. 1560c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 15613d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown public void onResult(Bundle data) { 15623d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown } 1563c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1564c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 15653d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * Called when a media control request fails. 1566c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 15673d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * @param error A localized error message which may be shown to the user, or null 15683d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * if the cause of the error is unclear. 15693d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * @param data Error data, or null if none. 15703d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * Contents depend on the {@link MediaControlIntent media control action}. 1571c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 15723d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown public void onError(String error, Bundle data) { 1573c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1574c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1575c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 157611417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final class CallbackRecord { 15779fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown public final MediaRouter mRouter; 157811417b1cfde8f1749905f2d735623af9214148afJeff Brown public final Callback mCallback; 157911417b1cfde8f1749905f2d735623af9214148afJeff Brown public MediaRouteSelector mSelector; 158011417b1cfde8f1749905f2d735623af9214148afJeff Brown public int mFlags; 158111417b1cfde8f1749905f2d735623af9214148afJeff Brown 15829fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown public CallbackRecord(MediaRouter router, Callback callback) { 15839fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouter = router; 158411417b1cfde8f1749905f2d735623af9214148afJeff Brown mCallback = callback; 158511417b1cfde8f1749905f2d735623af9214148afJeff Brown mSelector = MediaRouteSelector.EMPTY; 158611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 158711417b1cfde8f1749905f2d735623af9214148afJeff Brown 158811417b1cfde8f1749905f2d735623af9214148afJeff Brown public boolean filterRouteEvent(RouteInfo route) { 158911417b1cfde8f1749905f2d735623af9214148afJeff Brown return (mFlags & CALLBACK_FLAG_UNFILTERED_EVENTS) != 0 159011417b1cfde8f1749905f2d735623af9214148afJeff Brown || route.matchesSelector(mSelector); 159111417b1cfde8f1749905f2d735623af9214148afJeff Brown } 159211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 159311417b1cfde8f1749905f2d735623af9214148afJeff Brown 1594c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1595c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Global state for the media router. 1596c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1597c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media routes and media route providers are global to the process; their 1598c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * state and the bulk of the media router implementation lives here. 1599c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1600c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 16013efa63d3b896244713e84acbb5945562dce41d77Jeff Brown private static final class GlobalMediaRouter 16023efa63d3b896244713e84acbb5945562dce41d77Jeff Brown implements SystemMediaRouteProvider.SyncCallback, 16033efa63d3b896244713e84acbb5945562dce41d77Jeff Brown RegisteredMediaRouteProviderWatcher.Callback { 1604c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final Context mApplicationContext; 16059fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown private final ArrayList<WeakReference<MediaRouter>> mRouters = 16069fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown new ArrayList<WeakReference<MediaRouter>>(); 1607c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>(); 1608fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private final ArrayList<ProviderInfo> mProviders = 1609fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown new ArrayList<ProviderInfo>(); 1610567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private final ArrayList<RemoteControlClientRecord> mRemoteControlClients = 1611567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown new ArrayList<RemoteControlClientRecord>(); 1612567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private final RemoteControlClientCompat.PlaybackInfo mPlaybackInfo = 1613567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown new RemoteControlClientCompat.PlaybackInfo(); 1614c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final ProviderCallback mProviderCallback = new ProviderCallback(); 1615c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final CallbackHandler mCallbackHandler = new CallbackHandler(); 1616c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final DisplayManagerCompat mDisplayManager; 1617c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final SystemMediaRouteProvider mSystemProvider; 1618fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown private final boolean mLowRam; 1619c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1620fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private RegisteredMediaRouteProviderWatcher mRegisteredProviderWatcher; 1621c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private RouteInfo mDefaultRoute; 1622c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private RouteInfo mSelectedRoute; 1623c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private MediaRouteProvider.RouteController mSelectedRouteController; 162411417b1cfde8f1749905f2d735623af9214148afJeff Brown private MediaRouteDiscoveryRequest mDiscoveryRequest; 1625bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik private MediaSessionRecord mMediaSession; 16265c9469e010106467791b47b0fa83efda84491a21RoboErik private MediaSessionCompat mRccMediaSession; 1627e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik private MediaSessionCompat mCompatSession; 16285c9469e010106467791b47b0fa83efda84491a21RoboErik private MediaSessionCompat.OnActiveChangeListener mSessionActiveListener = 16295c9469e010106467791b47b0fa83efda84491a21RoboErik new MediaSessionCompat.OnActiveChangeListener() { 16305c9469e010106467791b47b0fa83efda84491a21RoboErik @Override 16315c9469e010106467791b47b0fa83efda84491a21RoboErik public void onActiveChanged() { 16325c9469e010106467791b47b0fa83efda84491a21RoboErik if(mRccMediaSession != null) { 16335c9469e010106467791b47b0fa83efda84491a21RoboErik if (mRccMediaSession.isActive()) { 16345c9469e010106467791b47b0fa83efda84491a21RoboErik addRemoteControlClient(mRccMediaSession.getRemoteControlClient()); 16355c9469e010106467791b47b0fa83efda84491a21RoboErik } else { 16365c9469e010106467791b47b0fa83efda84491a21RoboErik removeRemoteControlClient(mRccMediaSession.getRemoteControlClient()); 16375c9469e010106467791b47b0fa83efda84491a21RoboErik } 16385c9469e010106467791b47b0fa83efda84491a21RoboErik } 16395c9469e010106467791b47b0fa83efda84491a21RoboErik } 16405c9469e010106467791b47b0fa83efda84491a21RoboErik }; 1641c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1642c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown GlobalMediaRouter(Context applicationContext) { 1643c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mApplicationContext = applicationContext; 1644c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDisplayManager = DisplayManagerCompat.getInstance(applicationContext); 1645fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown mLowRam = ActivityManagerCompat.isLowRamDevice( 1646fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown (ActivityManager)applicationContext.getSystemService( 1647fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown Context.ACTIVITY_SERVICE)); 1648fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1649fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // Add the system media route provider for interoperating with 1650fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // the framework media router. This one is special and receives 1651fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // synchronization messages from the media router. 1652c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSystemProvider = SystemMediaRouteProvider.obtain(applicationContext, this); 1653fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown addProvider(mSystemProvider); 1654fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1655fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1656fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void start() { 1657fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // Start watching for routes published by registered media route 1658fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // provider services. 1659fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mRegisteredProviderWatcher = new RegisteredMediaRouteProviderWatcher( 16603efa63d3b896244713e84acbb5945562dce41d77Jeff Brown mApplicationContext, this); 1661fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mRegisteredProviderWatcher.start(); 1662c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1663c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1664c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public MediaRouter getRouter(Context context) { 16659fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown MediaRouter router; 16669fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int i = mRouters.size(); --i >= 0; ) { 16679fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown router = mRouters.get(i).get(); 16689fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown if (router == null) { 16699fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouters.remove(i); 16709fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } else if (router.mContext == context) { 16719fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown return router; 16729fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } 1673c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 16749fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown router = new MediaRouter(context); 16759fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouters.add(new WeakReference<MediaRouter>(router)); 1676c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return router; 1677c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1678c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1679c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public ContentResolver getContentResolver() { 1680c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mApplicationContext.getContentResolver(); 1681c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1682c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1683fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public Context getProviderContext(String packageName) { 1684fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (packageName.equals(SystemMediaRouteProvider.PACKAGE_NAME)) { 1685fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mApplicationContext; 1686fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1687fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown try { 1688fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mApplicationContext.createPackageContext( 1689fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown packageName, Context.CONTEXT_RESTRICTED); 1690fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } catch (NameNotFoundException ex) { 1691fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return null; 1692fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1693fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1694fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1695c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public Display getDisplay(int displayId) { 1696c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mDisplayManager.getDisplay(displayId); 1697c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1698c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1699fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void sendControlRequest(RouteInfo route, 1700c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Intent intent, ControlRequestCallback callback) { 1701c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == mSelectedRoute && mSelectedRouteController != null) { 1702129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown if (mSelectedRouteController.onControlRequest(intent, callback)) { 1703fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return; 1704fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1705fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1706fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (callback != null) { 17073d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown callback.onError(null, null); 1708c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1709c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1710c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1711c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestSetVolume(RouteInfo route, int volume) { 1712c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == mSelectedRoute && mSelectedRouteController != null) { 1713129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown mSelectedRouteController.onSetVolume(volume); 1714c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1715c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1716c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1717c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestUpdateVolume(RouteInfo route, int delta) { 1718c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == mSelectedRoute && mSelectedRouteController != null) { 1719129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown mSelectedRouteController.onUpdateVolume(delta); 1720c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1721c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1722c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1723c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public List<RouteInfo> getRoutes() { 1724c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mRoutes; 1725c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1726c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1727fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public List<ProviderInfo> getProviders() { 1728fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mProviders; 1729fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1730fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1731c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getDefaultRoute() { 1732c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mDefaultRoute == null) { 1733c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This should never happen once the media router has been fully 1734c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // initialized but it is good to check for the error in case there 1735c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // is a bug in provider initialization. 1736c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalStateException("There is no default route. " 1737c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + "The media router has not yet been fully initialized."); 1738c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1739c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mDefaultRoute; 1740c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1741c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1742c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getSelectedRoute() { 1743c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute == null) { 1744c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This should never happen once the media router has been fully 1745c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // initialized but it is good to check for the error in case there 1746c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // is a bug in provider initialization. 1747c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalStateException("There is no currently selected route. " 1748c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + "The media router has not yet been fully initialized."); 1749c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1750c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mSelectedRoute; 1751c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1752c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1753c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void selectRoute(RouteInfo route) { 175494be6100218126ce6a08bf1f56209578500b361fRoboErik selectRoute(route, MediaRouter.UNSELECT_REASON_ROUTE_CHANGED); 175594be6100218126ce6a08bf1f56209578500b361fRoboErik } 175694be6100218126ce6a08bf1f56209578500b361fRoboErik 175794be6100218126ce6a08bf1f56209578500b361fRoboErik public void selectRoute(RouteInfo route, int unselectReason) { 1758c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (!mRoutes.contains(route)) { 1759c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Log.w(TAG, "Ignoring attempt to select removed route: " + route); 1760c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return; 1761c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1762c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (!route.mEnabled) { 1763c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Log.w(TAG, "Ignoring attempt to select disabled route: " + route); 1764c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return; 1765c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1766c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 176794be6100218126ce6a08bf1f56209578500b361fRoboErik setSelectedRouteInternal(route, unselectReason); 1768c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1769c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 177011417b1cfde8f1749905f2d735623af9214148afJeff Brown public boolean isRouteAvailable(MediaRouteSelector selector, int flags) { 1771fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown if (selector.isEmpty()) { 1772fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown return false; 1773fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown } 1774fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown 1775fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown // On low-RAM devices, do not rely on actual discovery results unless asked to. 1776fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown if ((flags & AVAILABILITY_FLAG_REQUIRE_MATCH) == 0 && mLowRam) { 1777fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown return true; 1778fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown } 1779fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown 178011417b1cfde8f1749905f2d735623af9214148afJeff Brown // Check whether any existing routes match the selector. 178111417b1cfde8f1749905f2d735623af9214148afJeff Brown final int routeCount = mRoutes.size(); 178211417b1cfde8f1749905f2d735623af9214148afJeff Brown for (int i = 0; i < routeCount; i++) { 178311417b1cfde8f1749905f2d735623af9214148afJeff Brown RouteInfo route = mRoutes.get(i); 178411417b1cfde8f1749905f2d735623af9214148afJeff Brown if ((flags & AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE) != 0 178511417b1cfde8f1749905f2d735623af9214148afJeff Brown && route.isDefault()) { 178611417b1cfde8f1749905f2d735623af9214148afJeff Brown continue; 178711417b1cfde8f1749905f2d735623af9214148afJeff Brown } 178811417b1cfde8f1749905f2d735623af9214148afJeff Brown if (route.matchesSelector(selector)) { 178911417b1cfde8f1749905f2d735623af9214148afJeff Brown return true; 179011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 179111417b1cfde8f1749905f2d735623af9214148afJeff Brown } 179211417b1cfde8f1749905f2d735623af9214148afJeff Brown 179311417b1cfde8f1749905f2d735623af9214148afJeff Brown // It doesn't look like we can find a matching route right now. 179411417b1cfde8f1749905f2d735623af9214148afJeff Brown return false; 179511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 179611417b1cfde8f1749905f2d735623af9214148afJeff Brown 179711417b1cfde8f1749905f2d735623af9214148afJeff Brown public void updateDiscoveryRequest() { 179811417b1cfde8f1749905f2d735623af9214148afJeff Brown // Combine all of the callback selectors and active scan flags. 1799f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown boolean discover = false; 180011417b1cfde8f1749905f2d735623af9214148afJeff Brown boolean activeScan = false; 180111417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder(); 18029fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int i = mRouters.size(); --i >= 0; ) { 18039fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown MediaRouter router = mRouters.get(i).get(); 18049fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown if (router == null) { 18059fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouters.remove(i); 18069fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } else { 18079fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown final int count = router.mCallbackRecords.size(); 18089fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int j = 0; j < count; j++) { 18099fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown CallbackRecord callback = router.mCallbackRecords.get(j); 18109fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown builder.addSelector(callback.mSelector); 1811f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown if ((callback.mFlags & CALLBACK_FLAG_PERFORM_ACTIVE_SCAN) != 0) { 18129fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown activeScan = true; 1813f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown discover = true; // perform active scan implies request discovery 1814f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown } 1815f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown if ((callback.mFlags & CALLBACK_FLAG_REQUEST_DISCOVERY) != 0) { 1816fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown if (!mLowRam) { 1817fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown discover = true; 1818fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown } 1819fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown } 1820fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown if ((callback.mFlags & CALLBACK_FLAG_FORCE_DISCOVERY) != 0) { 1821f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown discover = true; 18229fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } 182311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 182411417b1cfde8f1749905f2d735623af9214148afJeff Brown } 182511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1826f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown MediaRouteSelector selector = discover ? builder.build() : MediaRouteSelector.EMPTY; 182711417b1cfde8f1749905f2d735623af9214148afJeff Brown 182811417b1cfde8f1749905f2d735623af9214148afJeff Brown // Create a new discovery request. 182911417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mDiscoveryRequest != null 183011417b1cfde8f1749905f2d735623af9214148afJeff Brown && mDiscoveryRequest.getSelector().equals(selector) 183111417b1cfde8f1749905f2d735623af9214148afJeff Brown && mDiscoveryRequest.isActiveScan() == activeScan) { 183211417b1cfde8f1749905f2d735623af9214148afJeff Brown return; // no change 183311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 183411417b1cfde8f1749905f2d735623af9214148afJeff Brown if (selector.isEmpty() && !activeScan) { 183511417b1cfde8f1749905f2d735623af9214148afJeff Brown // Discovery is not needed. 183611417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mDiscoveryRequest == null) { 183711417b1cfde8f1749905f2d735623af9214148afJeff Brown return; // no change 183811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 183911417b1cfde8f1749905f2d735623af9214148afJeff Brown mDiscoveryRequest = null; 184011417b1cfde8f1749905f2d735623af9214148afJeff Brown } else { 184111417b1cfde8f1749905f2d735623af9214148afJeff Brown // Discovery is needed. 184211417b1cfde8f1749905f2d735623af9214148afJeff Brown mDiscoveryRequest = new MediaRouteDiscoveryRequest(selector, activeScan); 184311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 184411417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 184511417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Updated discovery request: " + mDiscoveryRequest); 184611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1847fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown if (discover && !activeScan && mLowRam) { 1848fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown Log.i(TAG, "Forcing passive route discovery on a low-RAM device, " 1849fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown + "system performance may be affected. Please consider using " 1850fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown + "CALLBACK_FLAG_REQUEST_DISCOVERY instead of " 1851fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown + "CALLBACK_FLAG_FORCE_DISCOVERY."); 1852fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown } 185311417b1cfde8f1749905f2d735623af9214148afJeff Brown 185411417b1cfde8f1749905f2d735623af9214148afJeff Brown // Notify providers. 185511417b1cfde8f1749905f2d735623af9214148afJeff Brown final int providerCount = mProviders.size(); 185611417b1cfde8f1749905f2d735623af9214148afJeff Brown for (int i = 0; i < providerCount; i++) { 185711417b1cfde8f1749905f2d735623af9214148afJeff Brown mProviders.get(i).mProviderInstance.setDiscoveryRequest(mDiscoveryRequest); 185828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 185928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 186028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 18613efa63d3b896244713e84acbb5945562dce41d77Jeff Brown @Override 1862fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void addProvider(MediaRouteProvider providerInstance) { 1863fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int index = findProviderInfo(providerInstance); 1864c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index < 0) { 1865c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 1. Add the provider to the list. 1866fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo provider = new ProviderInfo(providerInstance); 1867fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mProviders.add(provider); 186811417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 186911417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Provider added: " + provider); 187011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1871fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_ADDED, provider); 1872c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 2. Create the provider's contents. 1873fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown updateProviderContents(provider, providerInstance.getDescriptor()); 1874c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 3. Register the provider callback. 187511417b1cfde8f1749905f2d735623af9214148afJeff Brown providerInstance.setCallback(mProviderCallback); 187611417b1cfde8f1749905f2d735623af9214148afJeff Brown // 4. Set the discovery request. 187711417b1cfde8f1749905f2d735623af9214148afJeff Brown providerInstance.setDiscoveryRequest(mDiscoveryRequest); 1878c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1879c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1880c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 18813efa63d3b896244713e84acbb5945562dce41d77Jeff Brown @Override 1882fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void removeProvider(MediaRouteProvider providerInstance) { 1883fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int index = findProviderInfo(providerInstance); 1884c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index >= 0) { 188511417b1cfde8f1749905f2d735623af9214148afJeff Brown // 1. Unregister the provider callback. 188611417b1cfde8f1749905f2d735623af9214148afJeff Brown providerInstance.setCallback(null); 188711417b1cfde8f1749905f2d735623af9214148afJeff Brown // 2. Clear the discovery request. 188811417b1cfde8f1749905f2d735623af9214148afJeff Brown providerInstance.setDiscoveryRequest(null); 188928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown // 3. Delete the provider's contents. 1890fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo provider = mProviders.get(index); 1891fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown updateProviderContents(provider, null); 189228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown // 4. Remove the provider from the list. 189311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 189411417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Provider removed: " + provider); 189511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1896fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_REMOVED, provider); 1897fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mProviders.remove(index); 1898c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1899c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1900c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1901fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private void updateProviderDescriptor(MediaRouteProvider providerInstance, 190211417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteProviderDescriptor descriptor) { 1903fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int index = findProviderInfo(providerInstance); 1904c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index >= 0) { 1905fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // Update the provider's contents. 1906fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo provider = mProviders.get(index); 1907fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown updateProviderContents(provider, descriptor); 1908c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1909c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1910c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1911fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private int findProviderInfo(MediaRouteProvider providerInstance) { 1912fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown final int count = mProviders.size(); 1913c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown for (int i = 0; i < count; i++) { 1914fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mProviders.get(i).mProviderInstance == providerInstance) { 1915c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return i; 1916c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1917c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1918c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return -1; 1919c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1920c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1921fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private void updateProviderContents(ProviderInfo provider, 192211417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteProviderDescriptor providerDescriptor) { 1923fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (provider.updateDescriptor(providerDescriptor)) { 1924c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Update all existing routes and reorder them to match 1925c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // the order of their descriptors. 1926c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int targetIndex = 0; 1927567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown boolean selectedRouteDescriptorChanged = false; 1928c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (providerDescriptor != null) { 1929fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (providerDescriptor.isValid()) { 193011417b1cfde8f1749905f2d735623af9214148afJeff Brown final List<MediaRouteDescriptor> routeDescriptors = 193111417b1cfde8f1749905f2d735623af9214148afJeff Brown providerDescriptor.getRoutes(); 193211417b1cfde8f1749905f2d735623af9214148afJeff Brown final int routeCount = routeDescriptors.size(); 193311417b1cfde8f1749905f2d735623af9214148afJeff Brown for (int i = 0; i < routeCount; i++) { 193411417b1cfde8f1749905f2d735623af9214148afJeff Brown final MediaRouteDescriptor routeDescriptor = routeDescriptors.get(i); 1935c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final String id = routeDescriptor.getId(); 1936fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown final int sourceIndex = provider.findRouteByDescriptorId(id); 1937c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (sourceIndex < 0) { 1938c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 1. Add the route to the list. 1939cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown String uniqueId = assignRouteUniqueId(provider, id); 1940cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown RouteInfo route = new RouteInfo(provider, id, uniqueId); 1941fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown provider.mRoutes.add(targetIndex++, route); 1942c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mRoutes.add(route); 1943c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 2. Create the route's contents. 1944c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown route.updateDescriptor(routeDescriptor); 1945fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // 3. Notify clients about addition. 194611417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 194711417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Route added: " + route); 194811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1949c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route); 1950fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } else if (sourceIndex < targetIndex) { 1951fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Log.w(TAG, "Ignoring route descriptor with duplicate id: " 1952fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown + routeDescriptor); 1953c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } else { 1954c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 1. Reorder the route within the list. 1955fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown RouteInfo route = provider.mRoutes.get(sourceIndex); 1956fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Collections.swap(provider.mRoutes, 1957c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sourceIndex, targetIndex++); 1958c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 2. Update the route's contents. 1959c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int changes = route.updateDescriptor(routeDescriptor); 1960567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // 3. Notify clients about changes. 1961567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (changes != 0) { 1962567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if ((changes & RouteInfo.CHANGE_GENERAL) != 0) { 1963567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (DEBUG) { 1964567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown Log.d(TAG, "Route changed: " + route); 1965567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 1966567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mCallbackHandler.post( 1967567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown CallbackHandler.MSG_ROUTE_CHANGED, route); 196811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1969567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if ((changes & RouteInfo.CHANGE_VOLUME) != 0) { 1970567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (DEBUG) { 1971567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown Log.d(TAG, "Route volume changed: " + route); 1972567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 1973567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mCallbackHandler.post( 1974567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown CallbackHandler.MSG_ROUTE_VOLUME_CHANGED, route); 197511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1976567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if ((changes & RouteInfo.CHANGE_PRESENTATION_DISPLAY) != 0) { 1977567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (DEBUG) { 1978567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown Log.d(TAG, "Route presentation display changed: " 1979567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown + route); 1980567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 1981567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mCallbackHandler.post(CallbackHandler. 1982567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED, route); 1983567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 1984567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (route == mSelectedRoute) { 1985567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown selectedRouteDescriptorChanged = true; 198611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1987c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1988c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1989c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1990fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } else { 1991fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Log.w(TAG, "Ignoring invalid provider descriptor: " + providerDescriptor); 1992c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1993c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1994c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1995c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Dispose all remaining routes that do not have matching descriptors. 1996fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) { 1997fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // 1. Delete the route's contents. 1998fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown RouteInfo route = provider.mRoutes.get(i); 1999c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown route.updateDescriptor(null); 2000fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // 2. Remove the route from the list. 200111417b1cfde8f1749905f2d735623af9214148afJeff Brown mRoutes.remove(route); 200235af7c925d660508fe0892f38eec99a3845630e4Chong Zhang } 200335af7c925d660508fe0892f38eec99a3845630e4Chong Zhang 2004567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // Update the selected route if needed. 2005567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown updateSelectedRouteIfNeeded(selectedRouteDescriptorChanged); 200635af7c925d660508fe0892f38eec99a3845630e4Chong Zhang 200735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // Now notify clients about routes that were removed. 200835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // We do this after updating the selected route to ensure 200935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // that the framework media router observes the new route 201035af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // selection before the removal since removing the currently 201135af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // selected route may have side-effects. 201235af7c925d660508fe0892f38eec99a3845630e4Chong Zhang for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) { 201335af7c925d660508fe0892f38eec99a3845630e4Chong Zhang RouteInfo route = provider.mRoutes.remove(i); 201411417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 201511417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Route removed: " + route); 201611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2017fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_ROUTE_REMOVED, route); 2018c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2019fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 202011417b1cfde8f1749905f2d735623af9214148afJeff Brown // Notify provider changed. 202111417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 202211417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Provider changed: " + provider); 202311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 202411417b1cfde8f1749905f2d735623af9214148afJeff Brown mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_CHANGED, provider); 2025c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2026c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2027c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2028cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown private String assignRouteUniqueId(ProviderInfo provider, String routeDescriptorId) { 2029cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown // Although route descriptor ids are unique within a provider, it's 2030cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown // possible for there to be two providers with the same package name. 2031cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown // Therefore we must dedupe the composite id. 2032adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown String uniqueId = provider.getComponentName().flattenToShortString() 2033adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown + ":" + routeDescriptorId; 2034cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown if (findRouteByUniqueId(uniqueId) < 0) { 2035cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return uniqueId; 2036cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2037cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown for (int i = 2; ; i++) { 2038cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown String newUniqueId = String.format(Locale.US, "%s_%d", uniqueId, i); 2039cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown if (findRouteByUniqueId(newUniqueId) < 0) { 2040cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return newUniqueId; 2041cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2042cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2043cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2044cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown 2045cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown private int findRouteByUniqueId(String uniqueId) { 2046cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown final int count = mRoutes.size(); 2047cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown for (int i = 0; i < count; i++) { 2048cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown if (mRoutes.get(i).mUniqueId.equals(uniqueId)) { 2049cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return i; 2050cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2051cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2052cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return -1; 2053cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2054cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown 2055567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private void updateSelectedRouteIfNeeded(boolean selectedRouteDescriptorChanged) { 2056567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // Update default route. 2057567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (mDefaultRoute != null && !isRouteSelectable(mDefaultRoute)) { 205835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang Log.i(TAG, "Clearing the default route because it " 2059567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown + "is no longer selectable: " + mDefaultRoute); 2060c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDefaultRoute = null; 2061c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2062c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mDefaultRoute == null && !mRoutes.isEmpty()) { 2063c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown for (RouteInfo route : mRoutes) { 2064c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (isSystemDefaultRoute(route) && isRouteSelectable(route)) { 2065c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDefaultRoute = route; 2066567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown Log.i(TAG, "Found default route: " + mDefaultRoute); 2067c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 2068c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2069c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2070c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2071567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2072567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // Update selected route. 2073567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (mSelectedRoute != null && !isRouteSelectable(mSelectedRoute)) { 2074567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown Log.i(TAG, "Unselecting the current route because it " 2075567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown + "is no longer selectable: " + mSelectedRoute); 207694be6100218126ce6a08bf1f56209578500b361fRoboErik setSelectedRouteInternal(null, 207794be6100218126ce6a08bf1f56209578500b361fRoboErik MediaRouter.UNSELECT_REASON_UNKNOWN); 2078567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2079c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute == null) { 2080567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // Choose a new route. 2081567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // This will have the side-effect of updating the playback info when 2082567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // the new route is selected. 208394be6100218126ce6a08bf1f56209578500b361fRoboErik setSelectedRouteInternal(chooseFallbackRoute(), 208494be6100218126ce6a08bf1f56209578500b361fRoboErik MediaRouter.UNSELECT_REASON_UNKNOWN); 2085567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } else if (selectedRouteDescriptorChanged) { 2086567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // Update the playback info because the properties of the route have changed. 2087567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown updatePlaybackInfoFromSelectedRoute(); 2088c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2089c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2090c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 209135af7c925d660508fe0892f38eec99a3845630e4Chong Zhang private RouteInfo chooseFallbackRoute() { 209235af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // When the current route is removed or no longer selectable, 209335af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // we want to revert to a live audio route if there is 209435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // one (usually Bluetooth A2DP). Failing that, use 209535af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // the default route. 209635af7c925d660508fe0892f38eec99a3845630e4Chong Zhang for (RouteInfo route : mRoutes) { 209735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang if (route != mDefaultRoute 209835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang && isSystemLiveAudioOnlyRoute(route) 209935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang && isRouteSelectable(route)) { 210035af7c925d660508fe0892f38eec99a3845630e4Chong Zhang return route; 210135af7c925d660508fe0892f38eec99a3845630e4Chong Zhang } 210235af7c925d660508fe0892f38eec99a3845630e4Chong Zhang } 210335af7c925d660508fe0892f38eec99a3845630e4Chong Zhang return mDefaultRoute; 210435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang } 210535af7c925d660508fe0892f38eec99a3845630e4Chong Zhang 210635af7c925d660508fe0892f38eec99a3845630e4Chong Zhang private boolean isSystemLiveAudioOnlyRoute(RouteInfo route) { 210735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang return route.getProviderInstance() == mSystemProvider 210835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang && route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO) 210935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang && !route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO); 211035af7c925d660508fe0892f38eec99a3845630e4Chong Zhang } 211135af7c925d660508fe0892f38eec99a3845630e4Chong Zhang 2112c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private boolean isRouteSelectable(RouteInfo route) { 2113c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This tests whether the route is still valid and enabled. 2114c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // The route descriptor field is set to null when the route is removed. 2115c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return route.mDescriptor != null && route.mEnabled; 2116c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2117c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2118c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private boolean isSystemDefaultRoute(RouteInfo route) { 2119fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return route.getProviderInstance() == mSystemProvider 2120c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown && route.mDescriptorId.equals( 2121c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown SystemMediaRouteProvider.DEFAULT_ROUTE_ID); 2122c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2123c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 212494be6100218126ce6a08bf1f56209578500b361fRoboErik private void setSelectedRouteInternal(RouteInfo route, int unselectReason) { 2125c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute != route) { 2126c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute != null) { 212711417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 212894be6100218126ce6a08bf1f56209578500b361fRoboErik Log.d(TAG, "Route unselected: " + mSelectedRoute + " reason: " 212994be6100218126ce6a08bf1f56209578500b361fRoboErik + unselectReason); 213011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2131c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_ROUTE_UNSELECTED, mSelectedRoute); 2132c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRouteController != null) { 213394be6100218126ce6a08bf1f56209578500b361fRoboErik mSelectedRouteController.onUnselect(unselectReason); 2134129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown mSelectedRouteController.onRelease(); 2135c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSelectedRouteController = null; 2136c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2137c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2138c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2139c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSelectedRoute = route; 2140c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2141c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute != null) { 2142fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSelectedRouteController = route.getProviderInstance().onCreateRouteController( 2143c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown route.mDescriptorId); 2144c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRouteController != null) { 2145129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown mSelectedRouteController.onSelect(); 2146c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 214711417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 214811417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Route selected: " + mSelectedRoute); 214911417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2150c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_ROUTE_SELECTED, mSelectedRoute); 2151c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2152567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2153567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown updatePlaybackInfoFromSelectedRoute(); 2154c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2155c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2156c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2157c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 2158c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getSystemRouteByDescriptorId(String id) { 2159fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int providerIndex = findProviderInfo(mSystemProvider); 2160c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (providerIndex >= 0) { 2161fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo provider = mProviders.get(providerIndex); 2162fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int routeIndex = provider.findRouteByDescriptorId(id); 2163c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (routeIndex >= 0) { 2164fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return provider.mRoutes.get(routeIndex); 2165c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2166c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2167c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return null; 2168c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2169c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2170567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void addRemoteControlClient(Object rcc) { 2171567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown int index = findRemoteControlClientRecord(rcc); 2172567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (index < 0) { 2173567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown RemoteControlClientRecord record = new RemoteControlClientRecord(rcc); 2174567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRemoteControlClients.add(record); 2175567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2176567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2177567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2178567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void removeRemoteControlClient(Object rcc) { 2179567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown int index = findRemoteControlClientRecord(rcc); 2180567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (index >= 0) { 2181567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown RemoteControlClientRecord record = mRemoteControlClients.remove(index); 2182567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown record.disconnect(); 2183567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2184567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2185567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2186bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik public void setMediaSession(Object session) { 2187bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik if (mMediaSession != null) { 2188bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mMediaSession.clearVolumeHandling(); 2189bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2190bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik if (session == null) { 2191bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mMediaSession = null; 2192bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } else { 2193bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mMediaSession = new MediaSessionRecord(session); 2194bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik updatePlaybackInfoFromSelectedRoute(); 2195bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2196bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2197bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 21985c9469e010106467791b47b0fa83efda84491a21RoboErik public void setMediaSessionCompat(final MediaSessionCompat session) { 2199e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik mCompatSession = session; 22005c9469e010106467791b47b0fa83efda84491a21RoboErik if (android.os.Build.VERSION.SDK_INT >= 21) { 2201d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake setMediaSession(session != null ? session.getMediaSession() : null); 22025c9469e010106467791b47b0fa83efda84491a21RoboErik } else if (android.os.Build.VERSION.SDK_INT >= 14) { 22035c9469e010106467791b47b0fa83efda84491a21RoboErik if (mRccMediaSession != null) { 22045c9469e010106467791b47b0fa83efda84491a21RoboErik removeRemoteControlClient(mRccMediaSession.getRemoteControlClient()); 22055c9469e010106467791b47b0fa83efda84491a21RoboErik mRccMediaSession.removeOnActiveChangeListener(mSessionActiveListener); 22065c9469e010106467791b47b0fa83efda84491a21RoboErik } 22075c9469e010106467791b47b0fa83efda84491a21RoboErik mRccMediaSession = session; 2208d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake if (session != null) { 2209d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake session.addOnActiveChangeListener(mSessionActiveListener); 2210d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake if (session.isActive()) { 2211d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake addRemoteControlClient(session.getRemoteControlClient()); 2212d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake } 22135c9469e010106467791b47b0fa83efda84491a21RoboErik } 22145c9469e010106467791b47b0fa83efda84491a21RoboErik } 22155c9469e010106467791b47b0fa83efda84491a21RoboErik } 22165c9469e010106467791b47b0fa83efda84491a21RoboErik 221794be6100218126ce6a08bf1f56209578500b361fRoboErik public MediaSessionCompat.Token getMediaSessionToken() { 221894be6100218126ce6a08bf1f56209578500b361fRoboErik if (mMediaSession != null) { 221994be6100218126ce6a08bf1f56209578500b361fRoboErik return mMediaSession.getToken(); 2220e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik } else if (mCompatSession != null) { 2221e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik return mCompatSession.getSessionToken(); 222294be6100218126ce6a08bf1f56209578500b361fRoboErik } 222394be6100218126ce6a08bf1f56209578500b361fRoboErik return null; 222494be6100218126ce6a08bf1f56209578500b361fRoboErik } 222594be6100218126ce6a08bf1f56209578500b361fRoboErik 2226567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private int findRemoteControlClientRecord(Object rcc) { 2227567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown final int count = mRemoteControlClients.size(); 2228567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown for (int i = 0; i < count; i++) { 2229567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown RemoteControlClientRecord record = mRemoteControlClients.get(i); 2230567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (record.getRemoteControlClient() == rcc) { 2231567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown return i; 2232567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2233567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2234567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown return -1; 2235567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2236567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2237567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private void updatePlaybackInfoFromSelectedRoute() { 2238567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (mSelectedRoute != null) { 2239567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mPlaybackInfo.volume = mSelectedRoute.getVolume(); 2240567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mPlaybackInfo.volumeMax = mSelectedRoute.getVolumeMax(); 2241567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mPlaybackInfo.volumeHandling = mSelectedRoute.getVolumeHandling(); 2242567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mPlaybackInfo.playbackStream = mSelectedRoute.getPlaybackStream(); 2243567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mPlaybackInfo.playbackType = mSelectedRoute.getPlaybackType(); 2244567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2245567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown final int count = mRemoteControlClients.size(); 2246567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown for (int i = 0; i < count; i++) { 2247567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown RemoteControlClientRecord record = mRemoteControlClients.get(i); 2248567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown record.updatePlaybackInfo(); 2249567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2250bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik if (mMediaSession != null) { 2251f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik if (mSelectedRoute == getDefaultRoute()) { 2252f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik // Local route 2253f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik mMediaSession.clearVolumeHandling(); 2254f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik } else { 2255f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake @VolumeProviderCompat.ControlType int controlType = 2256f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake VolumeProviderCompat.VOLUME_CONTROL_FIXED; 2257f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik if (mPlaybackInfo.volumeHandling 2258f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE) { 2259f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik controlType = VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE; 2260f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik } 2261f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik mMediaSession.configureVolume(controlType, mPlaybackInfo.volumeMax, 2262f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik mPlaybackInfo.volume); 2263bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2264f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik } 2265f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik } else { 2266f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik if (mMediaSession != null) { 2267f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik mMediaSession.clearVolumeHandling(); 2268bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2269567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2270567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2271567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2272c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final class ProviderCallback extends MediaRouteProvider.Callback { 2273c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 2274c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onDescriptorChanged(MediaRouteProvider provider, 227511417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteProviderDescriptor descriptor) { 2276c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown updateProviderDescriptor(provider, descriptor); 2277c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2278c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2279c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2280bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik private final class MediaSessionRecord { 2281bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik private final MediaSessionCompat mMsCompat; 2282bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 2283f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake private @VolumeProviderCompat.ControlType int mControlType; 2284bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik private int mMaxVolume; 2285bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik private VolumeProviderCompat mVpCompat; 2286bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 2287bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik public MediaSessionRecord(Object mediaSession) { 22885c9469e010106467791b47b0fa83efda84491a21RoboErik mMsCompat = MediaSessionCompat.obtain(mApplicationContext, mediaSession); 2289bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2290bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 2291f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake public void configureVolume(@VolumeProviderCompat.ControlType int controlType, 2292f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake int max, int current) { 2293bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik if (mVpCompat != null && controlType == mControlType && max == mMaxVolume) { 2294bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik // If we haven't changed control type or max just set the 2295bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik // new current volume 2296bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mVpCompat.setCurrentVolume(current); 2297bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } else { 2298bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik // Otherwise create a new provider and update 2299bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mVpCompat = new VolumeProviderCompat(controlType, max, current) { 2300bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik @Override 2301a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik public void onSetVolumeTo(final int volume) { 2302a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik mCallbackHandler.post(new Runnable() { 2303a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik @Override 2304a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik public void run() { 2305a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik if (mSelectedRoute != null) { 2306a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik mSelectedRoute.requestSetVolume(volume); 2307a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik } 2308a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik } 2309a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik }); 2310bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2311bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 2312bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik @Override 2313a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik public void onAdjustVolume(final int direction) { 2314a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik mCallbackHandler.post(new Runnable() { 2315a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik @Override 2316a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik public void run() { 2317a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik if (mSelectedRoute != null) { 2318a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik mSelectedRoute.requestUpdateVolume(direction); 2319a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik } 2320a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik } 2321a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik }); 2322bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2323bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik }; 2324bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mMsCompat.setPlaybackToRemote(mVpCompat); 2325bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2326bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2327bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 2328bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik public void clearVolumeHandling() { 2329bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mMsCompat.setPlaybackToLocal(mPlaybackInfo.playbackStream); 2330bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mVpCompat = null; 2331bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2332bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 233394be6100218126ce6a08bf1f56209578500b361fRoboErik public MediaSessionCompat.Token getToken() { 233494be6100218126ce6a08bf1f56209578500b361fRoboErik return mMsCompat.getSessionToken(); 233594be6100218126ce6a08bf1f56209578500b361fRoboErik } 233694be6100218126ce6a08bf1f56209578500b361fRoboErik 2337bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2338bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 2339567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private final class RemoteControlClientRecord 2340567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown implements RemoteControlClientCompat.VolumeCallback { 2341567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private final RemoteControlClientCompat mRccCompat; 2342567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private boolean mDisconnected; 2343567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2344567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public RemoteControlClientRecord(Object rcc) { 2345567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRccCompat = RemoteControlClientCompat.obtain(mApplicationContext, rcc); 2346567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRccCompat.setVolumeCallback(this); 2347567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown updatePlaybackInfo(); 2348567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2349567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2350567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public Object getRemoteControlClient() { 2351567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown return mRccCompat.getRemoteControlClient(); 2352567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2353567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2354567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void disconnect() { 2355567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mDisconnected = true; 2356567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRccCompat.setVolumeCallback(null); 2357567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2358567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2359567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void updatePlaybackInfo() { 2360567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRccCompat.setPlaybackInfo(mPlaybackInfo); 2361567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2362567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2363567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown @Override 2364567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void onVolumeSetRequest(int volume) { 2365567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (!mDisconnected && mSelectedRoute != null) { 2366567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mSelectedRoute.requestSetVolume(volume); 2367567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2368567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2369567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2370567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown @Override 2371567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void onVolumeUpdateRequest(int direction) { 2372567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (!mDisconnected && mSelectedRoute != null) { 2373567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mSelectedRoute.requestUpdateVolume(direction); 2374567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2375567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2376567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2377567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2378c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final class CallbackHandler extends Handler { 23799fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown private final ArrayList<CallbackRecord> mTempCallbackRecords = 23809fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown new ArrayList<CallbackRecord>(); 2381c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 238211417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final int MSG_TYPE_MASK = 0xff00; 238311417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final int MSG_TYPE_ROUTE = 0x0100; 238411417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final int MSG_TYPE_PROVIDER = 0x0200; 238511417b1cfde8f1749905f2d735623af9214148afJeff Brown 238611417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_ADDED = MSG_TYPE_ROUTE | 1; 238711417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_REMOVED = MSG_TYPE_ROUTE | 2; 238811417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_CHANGED = MSG_TYPE_ROUTE | 3; 238911417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_VOLUME_CHANGED = MSG_TYPE_ROUTE | 4; 239011417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED = MSG_TYPE_ROUTE | 5; 239111417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_SELECTED = MSG_TYPE_ROUTE | 6; 239211417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_UNSELECTED = MSG_TYPE_ROUTE | 7; 239311417b1cfde8f1749905f2d735623af9214148afJeff Brown 239411417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_PROVIDER_ADDED = MSG_TYPE_PROVIDER | 1; 239511417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_PROVIDER_REMOVED = MSG_TYPE_PROVIDER | 2; 239611417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_PROVIDER_CHANGED = MSG_TYPE_PROVIDER | 3; 2397c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2398fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void post(int msg, Object obj) { 2399fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown obtainMessage(msg, obj).sendToTarget(); 2400c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2401c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2402c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 2403c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void handleMessage(Message msg) { 2404c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final int what = msg.what; 2405fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown final Object obj = msg.obj; 2406c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2407c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Synchronize state with the system media router. 2408fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown syncWithSystemProvider(what, obj); 2409c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2410c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Invoke all registered callbacks. 24119fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown // Build a list of callbacks before invoking them in case callbacks 24129fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown // are added or removed during dispatch. 2413c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown try { 24149fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int i = mRouters.size(); --i >= 0; ) { 24159fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown MediaRouter router = mRouters.get(i).get(); 24169fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown if (router == null) { 24179fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouters.remove(i); 24189fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } else { 24199fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mTempCallbackRecords.addAll(router.mCallbackRecords); 2420c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2421c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 24229fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown 24239fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown final int callbackCount = mTempCallbackRecords.size(); 24249fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int i = 0; i < callbackCount; i++) { 24259fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown invokeCallback(mTempCallbackRecords.get(i), what, obj); 24269fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } 2427c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } finally { 24289fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mTempCallbackRecords.clear(); 2429c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2430c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2431c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2432fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private void syncWithSystemProvider(int what, Object obj) { 2433c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown switch (what) { 2434c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_ADDED: 2435fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSystemProvider.onSyncRouteAdded((RouteInfo)obj); 2436c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 2437c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_REMOVED: 2438fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSystemProvider.onSyncRouteRemoved((RouteInfo)obj); 2439c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 2440c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_CHANGED: 2441fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSystemProvider.onSyncRouteChanged((RouteInfo)obj); 2442c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 2443c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_SELECTED: 2444fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSystemProvider.onSyncRouteSelected((RouteInfo)obj); 2445c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 2446c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2447c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2448c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 24499fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown private void invokeCallback(CallbackRecord record, int what, Object obj) { 24509fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown final MediaRouter router = record.mRouter; 245111417b1cfde8f1749905f2d735623af9214148afJeff Brown final MediaRouter.Callback callback = record.mCallback; 245211417b1cfde8f1749905f2d735623af9214148afJeff Brown switch (what & MSG_TYPE_MASK) { 245311417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_TYPE_ROUTE: { 245411417b1cfde8f1749905f2d735623af9214148afJeff Brown final RouteInfo route = (RouteInfo)obj; 245511417b1cfde8f1749905f2d735623af9214148afJeff Brown if (!record.filterRouteEvent(route)) { 245611417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 245711417b1cfde8f1749905f2d735623af9214148afJeff Brown } 245811417b1cfde8f1749905f2d735623af9214148afJeff Brown switch (what) { 245911417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_ADDED: 246011417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteAdded(router, route); 246111417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 246211417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_REMOVED: 246311417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteRemoved(router, route); 246411417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 246511417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_CHANGED: 246611417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteChanged(router, route); 246711417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 246811417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_VOLUME_CHANGED: 246911417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteVolumeChanged(router, route); 247011417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 247111417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED: 247211417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRoutePresentationDisplayChanged(router, route); 247311417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 247411417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_SELECTED: 247511417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteSelected(router, route); 247611417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 247711417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_UNSELECTED: 247811417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteUnselected(router, route); 247911417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 248011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2481c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 248211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 248311417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_TYPE_PROVIDER: { 248411417b1cfde8f1749905f2d735623af9214148afJeff Brown final ProviderInfo provider = (ProviderInfo)obj; 248511417b1cfde8f1749905f2d735623af9214148afJeff Brown switch (what) { 248611417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_PROVIDER_ADDED: 248711417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onProviderAdded(router, provider); 248811417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 248911417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_PROVIDER_REMOVED: 249011417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onProviderRemoved(router, provider); 249111417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 249211417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_PROVIDER_CHANGED: 249311417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onProviderChanged(router, provider); 249411417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 249511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 249611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2497c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2498c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2499c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2500c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2501c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown} 2502