MediaRouter.java revision 14a7007aaefb96b3b6fb06171aa10e1200116d6a
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; 20adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brownimport android.content.ComponentName; 21c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.ContentResolver; 22c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.Context; 23c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.Intent; 24c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.IntentFilter; 2594be6100218126ce6a08bf1f56209578500b361fRoboErikimport android.content.IntentSender; 26fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.pm.PackageManager.NameNotFoundException; 27fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.res.Resources; 28119de6bb3353341cfd465cea9e545abec3762d20Jae Seoimport android.net.Uri; 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; 40e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seoimport android.support.v4.util.Pair; 41b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brownimport android.support.v7.media.MediaRouteProvider.ProviderMetadata; 42bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kangimport android.support.v7.media.MediaRouteProvider.RouteController; 43c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.util.Log; 44c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.view.Display; 45c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 46429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport java.lang.annotation.Retention; 47429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport java.lang.annotation.RetentionPolicy; 489fcedc160282e6620f409ea46bf6728b35d011ddJeff Brownimport java.lang.ref.WeakReference; 49c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.ArrayList; 50c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.Collections; 5138e4efe885198f63fb26b73d79d4636263ba6a5cJae Seoimport java.util.HashMap; 52c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.List; 53cb63b6ecac9786891514f241dec71695f09d3efbJeff Brownimport java.util.Locale; 5438e4efe885198f63fb26b73d79d4636263ba6a5cJae Seoimport java.util.Map; 55c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 56c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown/** 57c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter allows applications to control the routing of media channels 58c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * and streams from the current device to external speakers and destination devices. 59c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 60c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * A MediaRouter instance is retrieved through {@link #getInstance}. Applications 61c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * can query the media router about the currently selected route and its capabilities 62c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to determine how to send content to the route's destination. Applications can 63c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * also {@link RouteInfo#sendControlRequest send control requests} to the route 64c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to ask the route's destination to perform certain remote control functions 65fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * such as playing media. 66c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 67c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * See also {@link MediaRouteProvider} for information on how an application 68c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * can publish new media routes to the media router. 69c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 70c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The media router API is not thread-safe; all interactions with it must be 71c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * done from the main thread of the process. 72c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 73c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 74c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownpublic final class MediaRouter { 75c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private static final String TAG = "MediaRouter"; 76f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 77c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 7894be6100218126ce6a08bf1f56209578500b361fRoboErik /** 79b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)} 80b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * when the reason the route was unselected is unknown. 8194be6100218126ce6a08bf1f56209578500b361fRoboErik */ 8294be6100218126ce6a08bf1f56209578500b361fRoboErik public static final int UNSELECT_REASON_UNKNOWN = 0; 8394be6100218126ce6a08bf1f56209578500b361fRoboErik /** 84b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)} 85b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * when the user pressed the disconnect button to disconnect and keep playing. 8694be6100218126ce6a08bf1f56209578500b361fRoboErik * <p> 8794be6100218126ce6a08bf1f56209578500b361fRoboErik * 8894be6100218126ce6a08bf1f56209578500b361fRoboErik * @see {@link MediaRouteDescriptor#canDisconnectAndKeepPlaying()}. 8994be6100218126ce6a08bf1f56209578500b361fRoboErik */ 9094be6100218126ce6a08bf1f56209578500b361fRoboErik public static final int UNSELECT_REASON_DISCONNECTED = 1; 9194be6100218126ce6a08bf1f56209578500b361fRoboErik /** 92b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)} 93b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * when the user pressed the stop casting button. 9494be6100218126ce6a08bf1f56209578500b361fRoboErik */ 9594be6100218126ce6a08bf1f56209578500b361fRoboErik public static final int UNSELECT_REASON_STOPPED = 2; 9694be6100218126ce6a08bf1f56209578500b361fRoboErik /** 97b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)} 98b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * when the user selected a different route. 9994be6100218126ce6a08bf1f56209578500b361fRoboErik */ 10094be6100218126ce6a08bf1f56209578500b361fRoboErik public static final int UNSELECT_REASON_ROUTE_CHANGED = 3; 10194be6100218126ce6a08bf1f56209578500b361fRoboErik 102c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Maintains global media router state for the process. 103c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This field is initialized in MediaRouter.getInstance() before any 104c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // MediaRouter objects are instantiated so it is guaranteed to be 105c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // valid whenever any instance method is invoked. 106c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static GlobalMediaRouter sGlobal; 107c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 108c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Context-bound state of the media router. 109c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final Context mContext; 1109fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown final ArrayList<CallbackRecord> mCallbackRecords = new ArrayList<CallbackRecord>(); 11111417b1cfde8f1749905f2d735623af9214148afJeff Brown 112429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye /** @hide */ 113429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @IntDef(flag = true, 114429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye value = { 115429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye CALLBACK_FLAG_PERFORM_ACTIVE_SCAN, 116429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye CALLBACK_FLAG_REQUEST_DISCOVERY, 117429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye CALLBACK_FLAG_UNFILTERED_EVENTS 118429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye } 119429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye ) 120429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Retention(RetentionPolicy.SOURCE) 121429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye private @interface CallbackFlags {} 122429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye 12311417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 12411417b1cfde8f1749905f2d735623af9214148afJeff Brown * Flag for {@link #addCallback}: Actively scan for routes while this callback 12511417b1cfde8f1749905f2d735623af9214148afJeff Brown * is registered. 12611417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 12711417b1cfde8f1749905f2d735623af9214148afJeff Brown * When this flag is specified, the media router will actively scan for new 12811417b1cfde8f1749905f2d735623af9214148afJeff Brown * routes. Certain routes, such as wifi display routes, may not be discoverable 12911417b1cfde8f1749905f2d735623af9214148afJeff Brown * except when actively scanning. This flag is typically used when the route picker 13011417b1cfde8f1749905f2d735623af9214148afJeff Brown * dialog has been opened by the user to ensure that the route information is 13111417b1cfde8f1749905f2d735623af9214148afJeff Brown * up to date. 13211417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p><p> 13311417b1cfde8f1749905f2d735623af9214148afJeff Brown * Active scanning may consume a significant amount of power and may have intrusive 13411417b1cfde8f1749905f2d735623af9214148afJeff Brown * effects on wireless connectivity. Therefore it is important that active scanning 13511417b1cfde8f1749905f2d735623af9214148afJeff Brown * only be requested when it is actually needed to satisfy a user request to 13611417b1cfde8f1749905f2d735623af9214148afJeff Brown * discover and select a new route. 137f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * </p><p> 138f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * This flag implies {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} but performing 139f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * active scans is much more expensive than a normal discovery request. 14011417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 141f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * 142f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * @see #CALLBACK_FLAG_REQUEST_DISCOVERY 14311417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 144f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1 << 0; 14511417b1cfde8f1749905f2d735623af9214148afJeff Brown 14611417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 14711417b1cfde8f1749905f2d735623af9214148afJeff Brown * Flag for {@link #addCallback}: Do not filter route events. 14811417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 14911417b1cfde8f1749905f2d735623af9214148afJeff Brown * When this flag is specified, the callback will be invoked for events that affect any 1503efa63d3b896244713e84acbb5945562dce41d77Jeff Brown * route even if they do not match the callback's filter. 15111417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 15211417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 15311417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 1 << 1; 15411417b1cfde8f1749905f2d735623af9214148afJeff Brown 15511417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 156fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * Flag for {@link #addCallback}: Request passive route discovery while this 157fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * callback is registered, except on {@link ActivityManager#isLowRamDevice low-RAM devices}. 158f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * <p> 159f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * When this flag is specified, the media router will try to discover routes. 160f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * Although route discovery is intended to be efficient, checking for new routes may 161f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * result in some network activity and could slowly drain the battery. Therefore 162f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * applications should only specify {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} when 163f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * they are running in the foreground and would like to provide the user with the 164f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * option of connecting to new routes. 165f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * </p><p> 166f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * Applications should typically add a callback using this flag in the 167f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * {@link android.app.Activity activity's} {@link android.app.Activity#onStart onStart} 168f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * method and remove it in the {@link android.app.Activity#onStop onStop} method. 169f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * The {@link android.support.v7.app.MediaRouteDiscoveryFragment} fragment may 170f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * also be used for this purpose. 171fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p class="note"> 172fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * On {@link ActivityManager#isLowRamDevice low-RAM devices} this flag 173fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * will be ignored. Refer to 174fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * {@link #addCallback(MediaRouteSelector, Callback, int) addCallback} for details. 175f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * </p> 176f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * 177f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * @see android.support.v7.app.MediaRouteDiscoveryFragment 178f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown */ 179f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 1 << 2; 180f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown 181f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown /** 182fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * Flag for {@link #addCallback}: Request passive route discovery while this 183fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * callback is registered, even on {@link ActivityManager#isLowRamDevice low-RAM devices}. 184fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * <p class="note"> 185fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * This flag has a significant performance impact on low-RAM devices 186fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * since it may cause many media route providers to be started simultaneously. 187fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * It is much better to use {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} instead to avoid 188fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * performing passive discovery on these devices altogether. Refer to 189fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * {@link #addCallback(MediaRouteSelector, Callback, int) addCallback} for details. 190fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p> 191fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * 192fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * @see android.support.v7.app.MediaRouteDiscoveryFragment 193fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown */ 194fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown public static final int CALLBACK_FLAG_FORCE_DISCOVERY = 1 << 3; 195fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown 196fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown /** 19711417b1cfde8f1749905f2d735623af9214148afJeff Brown * Flag for {@link #isRouteAvailable}: Ignore the default route. 19811417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 19911417b1cfde8f1749905f2d735623af9214148afJeff Brown * This flag is used to determine whether a matching non-default route is available. 20011417b1cfde8f1749905f2d735623af9214148afJeff Brown * This constraint may be used to decide whether to offer the route chooser dialog 20111417b1cfde8f1749905f2d735623af9214148afJeff Brown * to the user. There is no point offering the chooser if there are no 20211417b1cfde8f1749905f2d735623af9214148afJeff Brown * non-default choices. 20311417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 20411417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 20511417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1 << 0; 20611417b1cfde8f1749905f2d735623af9214148afJeff Brown 207fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown /** 208fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * Flag for {@link #isRouteAvailable}: Require an actual route to be matched. 209fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * <p> 210fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * If this flag is not set, then {@link #isRouteAvailable} will return true 211fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * if it is possible to discover a matching route even if discovery is not in 212fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * progress or if no matching route has yet been found. This feature is used to 213fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * save resources by removing the need to perform passive route discovery on 214fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * {@link ActivityManager#isLowRamDevice low-RAM devices}. 215fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p> 216fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * If this flag is set, then {@link #isRouteAvailable} will only return true if 217fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * a matching route has actually been discovered. 218fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p> 219fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown */ 220fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown public static final int AVAILABILITY_FLAG_REQUIRE_MATCH = 1 << 1; 221fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown 222c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouter(Context context) { 223c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mContext = context; 224c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 225c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 226c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 2279fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * Gets an instance of the media router service associated with the context. 2289fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * <p> 2299fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * The application is responsible for holding a strong reference to the returned 2309fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * {@link MediaRouter} instance, such as by storing the instance in a field of 2319fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * the {@link android.app.Activity}, to ensure that the media router remains alive 2329fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * as long as the application is using its features. 2339fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * </p><p> 2349fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * In other words, the support library only holds a {@link WeakReference weak reference} 2359fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * to each media router instance. When there are no remaining strong references to the 2369fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * media router instance, all of its callbacks will be removed and route discovery 2379fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * will no longer be performed on its behalf. 2389fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * </p> 2399fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * 2409fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * @return The media router instance for the context. The application must hold 2419fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * a strong reference to this object as long as it is in use. 242c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 243429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public static MediaRouter getInstance(@NonNull Context context) { 244c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (context == null) { 245c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("context must not be null"); 246c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 247c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 248c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 249c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (sGlobal == null) { 250c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal = new GlobalMediaRouter(context.getApplicationContext()); 251fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown sGlobal.start(); 252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 253c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getRouter(context); 254c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 255c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 256c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 257fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets information about the {@link MediaRouter.RouteInfo routes} currently known to 258fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * this media router. 259c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 260c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public List<RouteInfo> getRoutes() { 261c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 262c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getRoutes(); 263c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 264c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 265c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 266fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets information about the {@link MediaRouter.ProviderInfo route providers} 267fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * currently known to this media router. 268fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 269fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public List<ProviderInfo> getProviders() { 270fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 271fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return sGlobal.getProviders(); 272fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 273fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 274fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 275c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the default route for playing media content on the system. 276c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 277c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The system always provides a default route. 278c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 279c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 280c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The default route, which is guaranteed to never be null. 281c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 282429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @NonNull 283c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getDefaultRoute() { 284c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 285c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getDefaultRoute(); 286c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 287c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 288c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 289c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the currently selected route. 290c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 291c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The application should examine the route's 292fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * {@link RouteInfo#getControlFilters media control intent filters} to assess the 293c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * capabilities of the route before attempting to use it. 294c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 295c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 296c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <h3>Example</h3> 297c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <pre> 298c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * public boolean playMovie() { 299c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter mediaRouter = MediaRouter.getInstance(context); 300c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute(); 301c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 302c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // First try using the remote playback interface, if supported. 303c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) { 304c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // The route supports remote playback. 305c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // Try to send it the Uri of the movie to play. 306c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Intent intent = new Intent(MediaControlIntent.ACTION_PLAY); 307c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); 308c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * intent.setDataAndType("http://example.com/videos/movie.mp4", "video/mp4"); 309fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * if (route.supportsControlRequest(intent)) { 310fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * route.sendControlRequest(intent, null); 311fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * return true; // sent the request to play the movie 312c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 313c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 314c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 315c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // If remote playback was not possible, then play locally. 316c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * if (route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)) { 317c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // The route supports live video streaming. 318c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // Prepare to play content locally in a window or in a presentation. 319c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * return playMovieInWindow(); 320c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 321c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 322c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // Neither interface is supported, so we can't play the movie to this route. 323c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * return false; 324c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 325c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </pre> 326c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 327c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The selected route, which is guaranteed to never be null. 328c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 329fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see RouteInfo#getControlFilters 330c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#supportsControlCategory 331c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#supportsControlRequest 332c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 333429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @NonNull 334c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getSelectedRoute() { 335c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 336c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getSelectedRoute(); 337c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 338c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 339c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 34028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * Returns the selected route if it matches the specified selector, otherwise 34128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * selects the default route and returns it. 34228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 34328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @param selector The selector to match. 34428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @return The previously selected route if it matched the selector, otherwise the 34528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * newly selected default route which is guaranteed to never be null. 34628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 34711417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see MediaRouteSelector 34828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @see RouteInfo#matchesSelector 34928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @see RouteInfo#isDefault 35028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown */ 351429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @NonNull 352429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public RouteInfo updateSelectedRoute(@NonNull MediaRouteSelector selector) { 35328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown if (selector == null) { 35428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown throw new IllegalArgumentException("selector must not be null"); 35528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 35628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown checkCallingThread(); 35728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 35811417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 35911417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "updateSelectedRoute: " + selector); 36011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 36128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown RouteInfo route = sGlobal.getSelectedRoute(); 36211417b1cfde8f1749905f2d735623af9214148afJeff Brown if (!route.isDefault() && !route.matchesSelector(selector)) { 36328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown route = sGlobal.getDefaultRoute(); 36428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown sGlobal.selectRoute(route); 36528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 36628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown return route; 36728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 36828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 36928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 370c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Selects the specified route. 371c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 372c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route to select. 373c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 374429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void selectRoute(@NonNull RouteInfo route) { 375c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == null) { 376c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("route must not be null"); 377c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 378c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 379c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 38011417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 38111417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "selectRoute: " + route); 38211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 383c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal.selectRoute(route); 384c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 385c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 386c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 38794be6100218126ce6a08bf1f56209578500b361fRoboErik * Unselects the current round and selects the default route instead. 38894be6100218126ce6a08bf1f56209578500b361fRoboErik * <p> 38994be6100218126ce6a08bf1f56209578500b361fRoboErik * The reason given must be one of: 39094be6100218126ce6a08bf1f56209578500b361fRoboErik * <ul> 39194be6100218126ce6a08bf1f56209578500b361fRoboErik * <li>{@link MediaRouter#UNSELECT_REASON_UNKNOWN}</li> 39294be6100218126ce6a08bf1f56209578500b361fRoboErik * <li>{@link MediaRouter#UNSELECT_REASON_DISCONNECTED}</li> 39394be6100218126ce6a08bf1f56209578500b361fRoboErik * <li>{@link MediaRouter#UNSELECT_REASON_STOPPED}</li> 39494be6100218126ce6a08bf1f56209578500b361fRoboErik * <li>{@link MediaRouter#UNSELECT_REASON_ROUTE_CHANGED}</li> 39594be6100218126ce6a08bf1f56209578500b361fRoboErik * </ul> 39694be6100218126ce6a08bf1f56209578500b361fRoboErik * 39794be6100218126ce6a08bf1f56209578500b361fRoboErik * @param reason The reason for disconnecting the current route. 39894be6100218126ce6a08bf1f56209578500b361fRoboErik */ 39994be6100218126ce6a08bf1f56209578500b361fRoboErik public void unselect(int reason) { 40094be6100218126ce6a08bf1f56209578500b361fRoboErik if (reason < MediaRouter.UNSELECT_REASON_UNKNOWN || 40194be6100218126ce6a08bf1f56209578500b361fRoboErik reason > MediaRouter.UNSELECT_REASON_ROUTE_CHANGED) { 40294be6100218126ce6a08bf1f56209578500b361fRoboErik throw new IllegalArgumentException("Unsupported reason to unselect route"); 40394be6100218126ce6a08bf1f56209578500b361fRoboErik } 40494be6100218126ce6a08bf1f56209578500b361fRoboErik checkCallingThread(); 40594be6100218126ce6a08bf1f56209578500b361fRoboErik 40694be6100218126ce6a08bf1f56209578500b361fRoboErik sGlobal.selectRoute(getDefaultRoute(), reason); 40794be6100218126ce6a08bf1f56209578500b361fRoboErik } 40894be6100218126ce6a08bf1f56209578500b361fRoboErik 40994be6100218126ce6a08bf1f56209578500b361fRoboErik /** 410d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown * Returns true if there is a route that matches the specified selector. 41111417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 41294be6100218126ce6a08bf1f56209578500b361fRoboErik * This method returns true if there are any available routes that match the 41394be6100218126ce6a08bf1f56209578500b361fRoboErik * selector regardless of whether they are enabled or disabled. If the 414d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} flag is specified, then 415d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown * the method will only consider non-default routes. 41694be6100218126ce6a08bf1f56209578500b361fRoboErik * </p> 41794be6100218126ce6a08bf1f56209578500b361fRoboErik * <p class="note"> 418fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * On {@link ActivityManager#isLowRamDevice low-RAM devices} this method 419fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * will return true if it is possible to discover a matching route even if 420fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * discovery is not in progress or if no matching route has yet been found. 421fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * Use {@link #AVAILABILITY_FLAG_REQUIRE_MATCH} to require an actual match. 42211417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 423c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 42411417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param selector The selector to match. 42594be6100218126ce6a08bf1f56209578500b361fRoboErik * @param flags Flags to control the determination of whether a route may be 42694be6100218126ce6a08bf1f56209578500b361fRoboErik * available. May be zero or some combination of 42794be6100218126ce6a08bf1f56209578500b361fRoboErik * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} and 42894be6100218126ce6a08bf1f56209578500b361fRoboErik * {@link #AVAILABILITY_FLAG_REQUIRE_MATCH}. 42911417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if a matching route may be available. 430c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 431429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public boolean isRouteAvailable(@NonNull MediaRouteSelector selector, int flags) { 43211417b1cfde8f1749905f2d735623af9214148afJeff Brown if (selector == null) { 43311417b1cfde8f1749905f2d735623af9214148afJeff Brown throw new IllegalArgumentException("selector must not be null"); 434c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 435c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 436c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 43711417b1cfde8f1749905f2d735623af9214148afJeff Brown return sGlobal.isRouteAvailable(selector, flags); 438c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 439c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 440c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 44111417b1cfde8f1749905f2d735623af9214148afJeff Brown * Registers a callback to discover routes that match the selector and to receive 44211417b1cfde8f1749905f2d735623af9214148afJeff Brown * events when they change. 44311417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 44411417b1cfde8f1749905f2d735623af9214148afJeff Brown * This is a convenience method that has the same effect as calling 44511417b1cfde8f1749905f2d735623af9214148afJeff Brown * {@link #addCallback(MediaRouteSelector, Callback, int)} without flags. 44611417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 447c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 44811417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param selector A route selector that indicates the kinds of routes that the 44911417b1cfde8f1749905f2d735623af9214148afJeff Brown * callback would like to discover. 45011417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param callback The callback to add. 45111417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see #removeCallback 452c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 45311417b1cfde8f1749905f2d735623af9214148afJeff Brown public void addCallback(MediaRouteSelector selector, Callback callback) { 45411417b1cfde8f1749905f2d735623af9214148afJeff Brown addCallback(selector, callback, 0); 455c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 456c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 457c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 45811417b1cfde8f1749905f2d735623af9214148afJeff Brown * Registers a callback to discover routes that match the selector and to receive 45911417b1cfde8f1749905f2d735623af9214148afJeff Brown * events when they change. 46028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * <p> 46111417b1cfde8f1749905f2d735623af9214148afJeff Brown * The selector describes the kinds of routes that the application wants to 46211417b1cfde8f1749905f2d735623af9214148afJeff Brown * discover. For example, if the application wants to use 46311417b1cfde8f1749905f2d735623af9214148afJeff Brown * live audio routes then it should include the 46411417b1cfde8f1749905f2d735623af9214148afJeff Brown * {@link MediaControlIntent#CATEGORY_LIVE_AUDIO live audio media control intent category} 46511417b1cfde8f1749905f2d735623af9214148afJeff Brown * in its selector when it adds a callback to the media router. 46611417b1cfde8f1749905f2d735623af9214148afJeff Brown * The selector may include any number of categories. 46728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * </p><p> 46811417b1cfde8f1749905f2d735623af9214148afJeff Brown * If the callback has already been registered, then the selector is added to 46911417b1cfde8f1749905f2d735623af9214148afJeff Brown * the set of selectors being monitored by the callback. 47011417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p><p> 47111417b1cfde8f1749905f2d735623af9214148afJeff Brown * By default, the callback will only be invoked for events that affect routes 47211417b1cfde8f1749905f2d735623af9214148afJeff Brown * that match the specified selector. Event filtering may be disabled by specifying 47311417b1cfde8f1749905f2d735623af9214148afJeff Brown * the {@link #CALLBACK_FLAG_UNFILTERED_EVENTS} flag when the callback is registered. 474fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p> 475fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * Applications should use the {@link #isRouteAvailable} method to determine 476fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * whether is it possible to discover a route with the desired capabilities 477fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * and therefore whether the media route button should be shown to the user. 478fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p> 479fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * The {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} flag should be used while the application 480fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * is in the foreground to request that passive discovery be performed if there are 481fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * sufficient resources to allow continuous passive discovery. 482fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * On {@link ActivityManager#isLowRamDevice low-RAM devices} this flag will be 483fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * ignored to conserve resources. 484fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p> 485fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * The {@link #CALLBACK_FLAG_FORCE_DISCOVERY} flag should be used when 486fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * passive discovery absolutely must be performed, even on low-RAM devices. 487fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * This flag has a significant performance impact on low-RAM devices 488fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * since it may cause many media route providers to be started simultaneously. 489fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * It is much better to use {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} instead to avoid 490fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * performing passive discovery on these devices altogether. 491fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p> 492fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * The {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} flag should be used when the 493fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * media route chooser dialog is showing to confirm the presence of available 494fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * routes that the user may connect to. This flag may use substantially more 495fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * power. 49628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * </p> 49728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 49828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * <h3>Example</h3> 49928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * <pre> 50011417b1cfde8f1749905f2d735623af9214148afJeff Brown * public class MyActivity extends Activity { 50111417b1cfde8f1749905f2d735623af9214148afJeff Brown * private MediaRouter mRouter; 50211417b1cfde8f1749905f2d735623af9214148afJeff Brown * private MediaRouter.Callback mCallback; 50311417b1cfde8f1749905f2d735623af9214148afJeff Brown * private MediaRouteSelector mSelector; 50428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 50511417b1cfde8f1749905f2d735623af9214148afJeff Brown * protected void onCreate(Bundle savedInstanceState) { 50611417b1cfde8f1749905f2d735623af9214148afJeff Brown * super.onCreate(savedInstanceState); 50711417b1cfde8f1749905f2d735623af9214148afJeff Brown * 50811417b1cfde8f1749905f2d735623af9214148afJeff Brown * mRouter = Mediarouter.getInstance(this); 50911417b1cfde8f1749905f2d735623af9214148afJeff Brown * mCallback = new MyCallback(); 51011417b1cfde8f1749905f2d735623af9214148afJeff Brown * mSelector = new MediaRouteSelector.Builder() 51111417b1cfde8f1749905f2d735623af9214148afJeff Brown * .addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO) 51211417b1cfde8f1749905f2d735623af9214148afJeff Brown * .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK) 51311417b1cfde8f1749905f2d735623af9214148afJeff Brown * .build(); 51428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * } 51528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 516f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * // Add the callback on start to tell the media router what kinds of routes 51711417b1cfde8f1749905f2d735623af9214148afJeff Brown * // the application is interested in so that it can try to discover suitable ones. 518f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * public void onStart() { 519f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * super.onStart(); 52011417b1cfde8f1749905f2d735623af9214148afJeff Brown * 521f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * mediaRouter.addCallback(mSelector, mCallback, 522f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY); 52311417b1cfde8f1749905f2d735623af9214148afJeff Brown * 52411417b1cfde8f1749905f2d735623af9214148afJeff Brown * MediaRouter.RouteInfo route = mediaRouter.updateSelectedRoute(mSelector); 52511417b1cfde8f1749905f2d735623af9214148afJeff Brown * // do something with the route... 52611417b1cfde8f1749905f2d735623af9214148afJeff Brown * } 52711417b1cfde8f1749905f2d735623af9214148afJeff Brown * 528f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * // Remove the selector on stop to tell the media router that it no longer 52911417b1cfde8f1749905f2d735623af9214148afJeff Brown * // needs to invest effort trying to discover routes of these kinds for now. 530f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * public void onStop() { 531f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * super.onStop(); 53211417b1cfde8f1749905f2d735623af9214148afJeff Brown * 53311417b1cfde8f1749905f2d735623af9214148afJeff Brown * mediaRouter.removeCallback(mCallback); 53411417b1cfde8f1749905f2d735623af9214148afJeff Brown * } 53511417b1cfde8f1749905f2d735623af9214148afJeff Brown * 53611417b1cfde8f1749905f2d735623af9214148afJeff Brown * private final class MyCallback extends MediaRouter.Callback { 53711417b1cfde8f1749905f2d735623af9214148afJeff Brown * // Implement callback methods as needed. 53828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * } 53928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * } 54028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * </pre> 54128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 54211417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param selector A route selector that indicates the kinds of routes that the 54311417b1cfde8f1749905f2d735623af9214148afJeff Brown * callback would like to discover. 54411417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param callback The callback to add. 54511417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param flags Flags to control the behavior of the callback. 546f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * May be zero or a combination of {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and 54711417b1cfde8f1749905f2d735623af9214148afJeff Brown * {@link #CALLBACK_FLAG_UNFILTERED_EVENTS}. 54811417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see #removeCallback 54928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown */ 550429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void addCallback(@NonNull MediaRouteSelector selector, @NonNull Callback callback, 551429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @CallbackFlags int flags) { 55228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown if (selector == null) { 55328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown throw new IllegalArgumentException("selector must not be null"); 55428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 55511417b1cfde8f1749905f2d735623af9214148afJeff Brown if (callback == null) { 55611417b1cfde8f1749905f2d735623af9214148afJeff Brown throw new IllegalArgumentException("callback must not be null"); 55711417b1cfde8f1749905f2d735623af9214148afJeff Brown } 55828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown checkCallingThread(); 55928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 56011417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 56111417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "addCallback: selector=" + selector 56211417b1cfde8f1749905f2d735623af9214148afJeff Brown + ", callback=" + callback + ", flags=" + Integer.toHexString(flags)); 56311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 56411417b1cfde8f1749905f2d735623af9214148afJeff Brown 56511417b1cfde8f1749905f2d735623af9214148afJeff Brown CallbackRecord record; 56611417b1cfde8f1749905f2d735623af9214148afJeff Brown int index = findCallbackRecord(callback); 56711417b1cfde8f1749905f2d735623af9214148afJeff Brown if (index < 0) { 5689fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown record = new CallbackRecord(this, callback); 56911417b1cfde8f1749905f2d735623af9214148afJeff Brown mCallbackRecords.add(record); 57011417b1cfde8f1749905f2d735623af9214148afJeff Brown } else { 57111417b1cfde8f1749905f2d735623af9214148afJeff Brown record = mCallbackRecords.get(index); 57211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 57311417b1cfde8f1749905f2d735623af9214148afJeff Brown boolean updateNeeded = false; 57411417b1cfde8f1749905f2d735623af9214148afJeff Brown if ((flags & ~record.mFlags) != 0) { 57511417b1cfde8f1749905f2d735623af9214148afJeff Brown record.mFlags |= flags; 57611417b1cfde8f1749905f2d735623af9214148afJeff Brown updateNeeded = true; 57711417b1cfde8f1749905f2d735623af9214148afJeff Brown } 57811417b1cfde8f1749905f2d735623af9214148afJeff Brown if (!record.mSelector.contains(selector)) { 57911417b1cfde8f1749905f2d735623af9214148afJeff Brown record.mSelector = new MediaRouteSelector.Builder(record.mSelector) 58011417b1cfde8f1749905f2d735623af9214148afJeff Brown .addSelector(selector) 58111417b1cfde8f1749905f2d735623af9214148afJeff Brown .build(); 58211417b1cfde8f1749905f2d735623af9214148afJeff Brown updateNeeded = true; 58311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 58411417b1cfde8f1749905f2d735623af9214148afJeff Brown if (updateNeeded) { 58511417b1cfde8f1749905f2d735623af9214148afJeff Brown sGlobal.updateDiscoveryRequest(); 58628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 58728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 58828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 58928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 59011417b1cfde8f1749905f2d735623af9214148afJeff Brown * Removes the specified callback. It will no longer receive events about 59111417b1cfde8f1749905f2d735623af9214148afJeff Brown * changes to media routes. 59228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 59311417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param callback The callback to remove. 59411417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see #addCallback 59528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown */ 596429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void removeCallback(@NonNull Callback callback) { 59711417b1cfde8f1749905f2d735623af9214148afJeff Brown if (callback == null) { 59811417b1cfde8f1749905f2d735623af9214148afJeff Brown throw new IllegalArgumentException("callback must not be null"); 59928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 60028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown checkCallingThread(); 60128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 60211417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 60311417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "removeCallback: callback=" + callback); 60411417b1cfde8f1749905f2d735623af9214148afJeff Brown } 60511417b1cfde8f1749905f2d735623af9214148afJeff Brown 60611417b1cfde8f1749905f2d735623af9214148afJeff Brown int index = findCallbackRecord(callback); 60711417b1cfde8f1749905f2d735623af9214148afJeff Brown if (index >= 0) { 60811417b1cfde8f1749905f2d735623af9214148afJeff Brown mCallbackRecords.remove(index); 60911417b1cfde8f1749905f2d735623af9214148afJeff Brown sGlobal.updateDiscoveryRequest(); 61028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 61128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 61228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 61311417b1cfde8f1749905f2d735623af9214148afJeff Brown private int findCallbackRecord(Callback callback) { 61411417b1cfde8f1749905f2d735623af9214148afJeff Brown final int count = mCallbackRecords.size(); 61511417b1cfde8f1749905f2d735623af9214148afJeff Brown for (int i = 0; i < count; i++) { 61611417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mCallbackRecords.get(i).mCallback == callback) { 61711417b1cfde8f1749905f2d735623af9214148afJeff Brown return i; 61811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 61911417b1cfde8f1749905f2d735623af9214148afJeff Brown } 62011417b1cfde8f1749905f2d735623af9214148afJeff Brown return -1; 62111417b1cfde8f1749905f2d735623af9214148afJeff Brown } 62211417b1cfde8f1749905f2d735623af9214148afJeff Brown 62328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 6249942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * Registers a media route provider within this application process. 6259942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * <p> 6269942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * The provider will be added to the list of providers that all {@link MediaRouter} 6279942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * instances within this process can use to discover routes. 6289942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * </p> 629c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 630fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param providerInstance The media route provider instance to add. 631c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 632c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaRouteProvider 63328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @see #removeCallback 634c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 635429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void addProvider(@NonNull MediaRouteProvider providerInstance) { 636fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (providerInstance == null) { 637fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown throw new IllegalArgumentException("providerInstance must not be null"); 638c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 639c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 640c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 64111417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 64211417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "addProvider: " + providerInstance); 64311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 644fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown sGlobal.addProvider(providerInstance); 645c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 646c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 647c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 6489942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * Unregisters a media route provider within this application process. 6499942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * <p> 6509942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * The provider will be removed from the list of providers that all {@link MediaRouter} 6519942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * instances within this process can use to discover routes. 6529942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * </p> 653c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 654fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param providerInstance The media route provider instance to remove. 655c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 656c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaRouteProvider 65728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @see #addCallback 658c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 659429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void removeProvider(@NonNull MediaRouteProvider providerInstance) { 660fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (providerInstance == null) { 661fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown throw new IllegalArgumentException("providerInstance must not be null"); 662c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 663c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 664c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 66511417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 66611417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "removeProvider: " + providerInstance); 66728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 66811417b1cfde8f1749905f2d735623af9214148afJeff Brown sGlobal.removeProvider(providerInstance); 66928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 67028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 67128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 672567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * Adds a remote control client to enable remote control of the volume 673567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * of the selected route. 674567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * <p> 675567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * The remote control client must have previously been registered with 676567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * the audio manager using the {@link android.media.AudioManager#registerRemoteControlClient 677567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * AudioManager.registerRemoteControlClient} method. 678567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * </p> 679567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * 680567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * @param remoteControlClient The {@link android.media.RemoteControlClient} to register. 681567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown */ 682429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void addRemoteControlClient(@NonNull Object remoteControlClient) { 683567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (remoteControlClient == null) { 684567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown throw new IllegalArgumentException("remoteControlClient must not be null"); 685567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 686567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown checkCallingThread(); 687567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 688567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (DEBUG) { 689567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown Log.d(TAG, "addRemoteControlClient: " + remoteControlClient); 690567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 691567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown sGlobal.addRemoteControlClient(remoteControlClient); 692567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 693567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 694567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown /** 695567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * Removes a remote control client. 696567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * 697bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * @param remoteControlClient The {@link android.media.RemoteControlClient} 698bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * to unregister. 699567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown */ 700429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void removeRemoteControlClient(@NonNull Object remoteControlClient) { 701567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (remoteControlClient == null) { 702567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown throw new IllegalArgumentException("remoteControlClient must not be null"); 703567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 704567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 705567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (DEBUG) { 706567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown Log.d(TAG, "removeRemoteControlClient: " + remoteControlClient); 707567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 708567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown sGlobal.removeRemoteControlClient(remoteControlClient); 709567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 710567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 711567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown /** 712bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * Sets the media session to enable remote control of the volume of the 713bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * selected route. This should be used instead of 714bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * {@link #addRemoteControlClient} when using media sessions. Set the 715bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * session to null to clear it. 716bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * 717bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * @param mediaSession The {@link android.media.session.MediaSession} to 718bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * use. 719bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik */ 720bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik public void setMediaSession(Object mediaSession) { 721bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik if (DEBUG) { 722bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik Log.d(TAG, "addMediaSession: " + mediaSession); 723bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 724bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik sGlobal.setMediaSession(mediaSession); 725bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 726bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 727bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik /** 72894be6100218126ce6a08bf1f56209578500b361fRoboErik * Sets a compat media session to enable remote control of the volume of the 72994be6100218126ce6a08bf1f56209578500b361fRoboErik * selected route. This should be used instead of 73094be6100218126ce6a08bf1f56209578500b361fRoboErik * {@link #addRemoteControlClient} when using {@link MediaSessionCompat}. 73194be6100218126ce6a08bf1f56209578500b361fRoboErik * Set the session to null to clear it. 73294be6100218126ce6a08bf1f56209578500b361fRoboErik * 73394be6100218126ce6a08bf1f56209578500b361fRoboErik * @param mediaSession 73494be6100218126ce6a08bf1f56209578500b361fRoboErik */ 73594be6100218126ce6a08bf1f56209578500b361fRoboErik public void setMediaSessionCompat(MediaSessionCompat mediaSession) { 73694be6100218126ce6a08bf1f56209578500b361fRoboErik if (DEBUG) { 73794be6100218126ce6a08bf1f56209578500b361fRoboErik Log.d(TAG, "addMediaSessionCompat: " + mediaSession); 73894be6100218126ce6a08bf1f56209578500b361fRoboErik } 7395c9469e010106467791b47b0fa83efda84491a21RoboErik sGlobal.setMediaSessionCompat(mediaSession); 74094be6100218126ce6a08bf1f56209578500b361fRoboErik } 74194be6100218126ce6a08bf1f56209578500b361fRoboErik 74294be6100218126ce6a08bf1f56209578500b361fRoboErik public MediaSessionCompat.Token getMediaSessionToken() { 74394be6100218126ce6a08bf1f56209578500b361fRoboErik return sGlobal.getMediaSessionToken(); 74494be6100218126ce6a08bf1f56209578500b361fRoboErik } 74594be6100218126ce6a08bf1f56209578500b361fRoboErik 74694be6100218126ce6a08bf1f56209578500b361fRoboErik /** 747c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Ensures that calls into the media router are on the correct thread. 748c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * It pays to be a little paranoid when global state invariants are at risk. 749c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 750c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static void checkCallingThread() { 751c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (Looper.myLooper() != Looper.getMainLooper()) { 752c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalStateException("The media router service must only be " 753c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + "accessed on the application's main thread."); 754c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 755c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 756c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 757c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static <T> boolean equal(T a, T b) { 758c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return a == b || (a != null && b != null && a.equals(b)); 759c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 760c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 761c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 762c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Provides information about a media route. 763c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 764fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Each media route has a list of {@link MediaControlIntent media control} 765fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * {@link #getControlFilters intent filters} that describe the capabilities of the 766c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * route and the manner in which it is used and controlled. 767c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 768c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 769e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo public static class RouteInfo { 770fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private final ProviderInfo mProvider; 771c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final String mDescriptorId; 772cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown private final String mUniqueId; 773c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private String mName; 774d63957d28aaabcec588b8cde12eac16414783aebJeff Brown private String mDescription; 775119de6bb3353341cfd465cea9e545abec3762d20Jae Seo private Uri mIconUri; 776c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private boolean mEnabled; 77711417b1cfde8f1749905f2d735623af9214148afJeff Brown private boolean mConnecting; 7783766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo private int mConnectionState; 77994be6100218126ce6a08bf1f56209578500b361fRoboErik private boolean mCanDisconnect; 780e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo private final ArrayList<IntentFilter> mControlFilters = new ArrayList<>(); 781c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mPlaybackType; 782c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mPlaybackStream; 783893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo private int mDeviceType; 784c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mVolumeHandling; 785c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mVolume; 786c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mVolumeMax; 787c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private Display mPresentationDisplay; 78877367b4a1871417198d0399d9ad074314c758567Jae Seo private int mPresentationDisplayId = PRESENTATION_DISPLAY_ID_NONE; 789c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private Bundle mExtras; 79094be6100218126ce6a08bf1f56209578500b361fRoboErik private IntentSender mSettingsIntent; 791e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo MediaRouteDescriptor mDescriptor; 792c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 793429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye /** @hide */ 7943766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo @IntDef({CONNECTION_STATE_DISCONNECTED, CONNECTION_STATE_CONNECTING, 7953766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo CONNECTION_STATE_CONNECTED}) 7963766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo @Retention(RetentionPolicy.SOURCE) 7973766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo private @interface ConnectionState {} 7983766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo 7993766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo /** 8003766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * The default connection state indicating the route is disconnected. 8013766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * 8023766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * @see #getConnectionState 8033766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo */ 8043766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo public static final int CONNECTION_STATE_DISCONNECTED = 0; 8053766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo 8063766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo /** 8073766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * A connection state indicating the route is in the process of connecting and is not yet 8083766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * ready for use. 8093766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * 8103766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * @see #getConnectionState 8113766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo */ 8123766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo public static final int CONNECTION_STATE_CONNECTING = 1; 8133766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo 8143766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo /** 8153766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * A connection state indicating the route is connected. 8163766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * 8173766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * @see #getConnectionState 8183766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo */ 8193766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo public static final int CONNECTION_STATE_CONNECTED = 2; 8203766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo 8213766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo /** @hide */ 822429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @IntDef({PLAYBACK_TYPE_LOCAL,PLAYBACK_TYPE_REMOTE}) 823429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Retention(RetentionPolicy.SOURCE) 824429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye private @interface PlaybackType {} 825429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye 826c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 827c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The default playback type, "local", indicating the presentation of the media 828c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * is happening on the same device (e.g. a phone, a tablet) as where it is 829c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * controlled from. 830c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 831c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getPlaybackType 832c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 833c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_TYPE_LOCAL = 0; 834c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 835c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 836c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * A playback type indicating the presentation of the media is happening on 837c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * a different device (i.e. the remote device) than where it is controlled from. 838c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 839c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getPlaybackType 840c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 841c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_TYPE_REMOTE = 1; 842c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 843429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye /** @hide */ 844893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo @IntDef({DEVICE_TYPE_UNKNOWN, DEVICE_TYPE_TV, DEVICE_TYPE_SPEAKER, DEVICE_TYPE_BLUETOOTH}) 845893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo @Retention(RetentionPolicy.SOURCE) 846893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo private @interface DeviceType {} 847893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo 848893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo /** 849893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * The default receiver device type of the route indicating the type is unknown. 850893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * 851893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * @see #getDeviceType 852893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * @hide 853893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo */ 854893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo public static final int DEVICE_TYPE_UNKNOWN = 0; 855893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo 856893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo 857893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo /** 858893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * A receiver device type of the route indicating the presentation of the media is happening 859893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * on a bluetooth device such as a bluetooth speaker. 860893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * 861893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * @see #getDeviceType 862893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * @hide 863893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo */ 864893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo public static final int DEVICE_TYPE_BLUETOOTH = -1; 865893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo 866893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo /** 867893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * A receiver device type of the route indicating the presentation of the media is happening 868893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * on a TV. 869893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * 870893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * @see #getDeviceType 871893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo */ 872893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo public static final int DEVICE_TYPE_TV = 1; 873893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo 874893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo /** 875893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * A receiver device type of the route indicating the presentation of the media is happening 876893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * on a speaker. 877893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * 878893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * @see #getDeviceType 879893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo */ 880893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo public static final int DEVICE_TYPE_SPEAKER = 2; 881893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo 882893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo /** @hide */ 883429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @IntDef({PLAYBACK_VOLUME_FIXED,PLAYBACK_VOLUME_VARIABLE}) 884429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Retention(RetentionPolicy.SOURCE) 885429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye private @interface PlaybackVolume {} 886429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye 887c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 888c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Playback information indicating the playback volume is fixed, i.e. it cannot be 889c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * controlled from this object. An example of fixed playback volume is a remote player, 890c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather 891c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * than attenuate at the source. 892c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 893c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getVolumeHandling 894c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 895c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_VOLUME_FIXED = 0; 896c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 897c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 898c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Playback information indicating the playback volume is variable and can be controlled 899c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * from this object. 900c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 901c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getVolumeHandling 902c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 903c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_VOLUME_VARIABLE = 1; 904c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 90577367b4a1871417198d0399d9ad074314c758567Jae Seo /** 90677367b4a1871417198d0399d9ad074314c758567Jae Seo * The default presentation display id indicating no presentation display is associated 90777367b4a1871417198d0399d9ad074314c758567Jae Seo * with the route. 90877367b4a1871417198d0399d9ad074314c758567Jae Seo * @hide 90977367b4a1871417198d0399d9ad074314c758567Jae Seo */ 91077367b4a1871417198d0399d9ad074314c758567Jae Seo public static final int PRESENTATION_DISPLAY_ID_NONE = -1; 91177367b4a1871417198d0399d9ad074314c758567Jae Seo 912c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static final int CHANGE_GENERAL = 1 << 0; 913c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static final int CHANGE_VOLUME = 1 << 1; 914c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static final int CHANGE_PRESENTATION_DISPLAY = 1 << 2; 915c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 916cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown RouteInfo(ProviderInfo provider, String descriptorId, String uniqueId) { 917fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mProvider = provider; 918c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDescriptorId = descriptorId; 919cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown mUniqueId = uniqueId; 920c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 921c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 922fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 923fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets information about the provider of this media route. 924fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 925fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public ProviderInfo getProvider() { 926fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mProvider; 927c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 928c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 929c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 930cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * Gets the unique id of the route. 931cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * <p> 932cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * The route unique id functions as a stable identifier by which the route is known. 933cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * For example, an application can use this id as a token to remember the 934cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * selected route across restarts or to communicate its identity to a service. 935cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * </p> 936cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * 937cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * @return The unique id of the route, never null. 938cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown */ 939429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @NonNull 940cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown public String getId() { 941cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return mUniqueId; 942cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 943cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown 944cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown /** 945d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * Gets the user-visible name of the route. 946d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * <p> 947d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * The route name identifies the destination represented by the route. 948d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * It may be a user-supplied name, an alias, or device serial number. 949d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * </p> 950c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 951d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * @return The user-visible name of a media route. This is the string presented 952c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to users who may select this as the active route. 953c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 954c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public String getName() { 955c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mName; 956c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 957c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 958c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 959d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * Gets the user-visible description of the route. 960d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * <p> 961d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * The route description describes the kind of destination represented by the route. 962d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * It may be a user-supplied string, a model number or brand of device. 963d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * </p> 964c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 965d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * @return The description of the route, or null if none. 966c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 967429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Nullable 968d63957d28aaabcec588b8cde12eac16414783aebJeff Brown public String getDescription() { 969d63957d28aaabcec588b8cde12eac16414783aebJeff Brown return mDescription; 970c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 971c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 972c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 973119de6bb3353341cfd465cea9e545abec3762d20Jae Seo * Gets the URI of the icon representing this route. 974119de6bb3353341cfd465cea9e545abec3762d20Jae Seo * <p> 975119de6bb3353341cfd465cea9e545abec3762d20Jae Seo * This icon will be used in picker UIs if available. 976119de6bb3353341cfd465cea9e545abec3762d20Jae Seo * </p> 977119de6bb3353341cfd465cea9e545abec3762d20Jae Seo * 978119de6bb3353341cfd465cea9e545abec3762d20Jae Seo * @return The URI of the icon representing this route, or null if none. 979119de6bb3353341cfd465cea9e545abec3762d20Jae Seo */ 980119de6bb3353341cfd465cea9e545abec3762d20Jae Seo public Uri getIconUri() { 981119de6bb3353341cfd465cea9e545abec3762d20Jae Seo return mIconUri; 982119de6bb3353341cfd465cea9e545abec3762d20Jae Seo } 983119de6bb3353341cfd465cea9e545abec3762d20Jae Seo 984119de6bb3353341cfd465cea9e545abec3762d20Jae Seo /** 985c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Returns true if this route is enabled and may be selected. 986c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 98711417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if this route is enabled. 988c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 989c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public boolean isEnabled() { 990c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mEnabled; 991c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 992c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 993c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 99411417b1cfde8f1749905f2d735623af9214148afJeff Brown * Returns true if the route is in the process of connecting and is not 99511417b1cfde8f1749905f2d735623af9214148afJeff Brown * yet ready for use. 99611417b1cfde8f1749905f2d735623af9214148afJeff Brown * 99711417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if this route is in the process of connecting. 99811417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 99911417b1cfde8f1749905f2d735623af9214148afJeff Brown public boolean isConnecting() { 100011417b1cfde8f1749905f2d735623af9214148afJeff Brown return mConnecting; 100111417b1cfde8f1749905f2d735623af9214148afJeff Brown } 100211417b1cfde8f1749905f2d735623af9214148afJeff Brown 100311417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 10043766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * Gets the connection state of the route. 10053766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * 10063766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * @return The connection state of this route: {@link #CONNECTION_STATE_DISCONNECTED}, 10073766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * {@link #CONNECTION_STATE_CONNECTING}, or {@link #CONNECTION_STATE_CONNECTED}. 10083766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo */ 10093766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo @ConnectionState 10103766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo public int getConnectionState() { 10113766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo return mConnectionState; 10123766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo } 10133766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo 10143766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo /** 1015fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Returns true if this route is currently selected. 1016c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 101711417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if this route is currently selected. 1018fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 1019fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see MediaRouter#getSelectedRoute 1020fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1021fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public boolean isSelected() { 1022fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1023fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return sGlobal.getSelectedRoute() == this; 1024fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1025fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1026fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1027fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Returns true if this route is the default route. 1028fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 102911417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if this route is the default route. 1030fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 1031fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see MediaRouter#getDefaultRoute 1032fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1033fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public boolean isDefault() { 1034fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1035fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return sGlobal.getDefaultRoute() == this; 1036fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1037fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1038fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1039fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets a list of {@link MediaControlIntent media control intent} filters that 1040fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * describe the capabilities of this route and the media control actions that 1041fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * it supports. 1042fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 1043fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @return A list of intent filters that specifies the media control intents that 1044fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * this route supports. 1045c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1046c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 1047c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #supportsControlCategory 1048c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #supportsControlRequest 1049c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1050fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public List<IntentFilter> getControlFilters() { 1051fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mControlFilters; 1052c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1053c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1054c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 105528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * Returns true if the route supports at least one of the capabilities 105628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * described by a media route selector. 105728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 105828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @param selector The selector that specifies the capabilities to check. 105928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @return True if the route supports at least one of the capabilities 106028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * described in the media route selector. 106128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown */ 1062429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public boolean matchesSelector(@NonNull MediaRouteSelector selector) { 106328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown if (selector == null) { 106428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown throw new IllegalArgumentException("selector must not be null"); 106528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 106628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown checkCallingThread(); 106711417b1cfde8f1749905f2d735623af9214148afJeff Brown return selector.matchesControlFilters(mControlFilters); 106828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 106928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 107028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 1071c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Returns true if the route supports the specified 1072c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent media control} category. 1073c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1074c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media control categories describe the capabilities of this route 1075c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * such as whether it supports live audio streaming or remote playback. 1076c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1077c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1078c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param category A {@link MediaControlIntent media control} category 1079c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO}, 1080c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO}, 1081fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined 1082c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * media control category. 108328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @return True if the route supports the specified intent category. 1084c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1085c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 1086fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see #getControlFilters 1087c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1088429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public boolean supportsControlCategory(@NonNull String category) { 1089c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (category == null) { 1090c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("category must not be null"); 1091c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1092fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1093c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1094fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int count = mControlFilters.size(); 1095fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown for (int i = 0; i < count; i++) { 1096fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mControlFilters.get(i).hasCategory(category)) { 1097fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return true; 1098fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1099fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1100fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return false; 1101c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1102c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1103c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1104c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Returns true if the route supports the specified 1105a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * {@link MediaControlIntent media control} category and action. 1106a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * <p> 1107a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * Media control actions describe specific requests that an application 1108a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * can ask a route to perform. 1109a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * </p> 1110a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * 1111a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * @param category A {@link MediaControlIntent media control} category 1112a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO}, 1113a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO}, 1114a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined 1115a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * media control category. 1116a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * @param action A {@link MediaControlIntent media control} action 1117a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * such as {@link MediaControlIntent#ACTION_PLAY}. 1118a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * @return True if the route supports the specified intent action. 1119a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * 1120a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * @see MediaControlIntent 1121a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * @see #getControlFilters 1122a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown */ 1123429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public boolean supportsControlAction(@NonNull String category, @NonNull String action) { 1124a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown if (category == null) { 1125a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown throw new IllegalArgumentException("category must not be null"); 1126a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown } 1127a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown if (action == null) { 1128a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown throw new IllegalArgumentException("action must not be null"); 1129a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown } 1130a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown checkCallingThread(); 1131a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown 1132a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown int count = mControlFilters.size(); 1133a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown for (int i = 0; i < count; i++) { 1134a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown IntentFilter filter = mControlFilters.get(i); 1135a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown if (filter.hasCategory(category) && filter.hasAction(action)) { 1136a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown return true; 1137a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown } 1138a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown } 1139a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown return false; 1140a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown } 1141a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown 1142a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown /** 1143a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * Returns true if the route supports the specified 1144c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent media control} request. 1145c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1146c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media control requests are used to request the route to perform 114743f79f79a5117550a7dfedf9c65124afd163ea43Jeff Brown * actions such as starting remote playback of a media item. 1148c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1149c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1150c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param intent A {@link MediaControlIntent media control intent}. 1151c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return True if the route can handle the specified intent. 1152c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1153c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 1154fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see #getControlFilters 1155c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1156429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public boolean supportsControlRequest(@NonNull Intent intent) { 1157c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (intent == null) { 1158c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("intent must not be null"); 1159c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1160c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 1161c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1162c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown ContentResolver contentResolver = sGlobal.getContentResolver(); 1163fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int count = mControlFilters.size(); 1164fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown for (int i = 0; i < count; i++) { 1165fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mControlFilters.get(i).match(contentResolver, intent, true, TAG) >= 0) { 1166fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return true; 1167fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1168fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1169fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return false; 1170c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1171c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1172c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1173c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Sends a {@link MediaControlIntent media control} request to be performed 1174c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * asynchronously by the route's destination. 1175c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1176c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media control requests are used to request the route to perform 117743f79f79a5117550a7dfedf9c65124afd163ea43Jeff Brown * actions such as starting remote playback of a media item. 1178c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 1179fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * This function may only be called on a selected route. Control requests 1180fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * sent to unselected routes will fail. 1181c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1182c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1183c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param intent A {@link MediaControlIntent media control intent}. 1184c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param callback A {@link ControlRequestCallback} to invoke with the result 1185c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * of the request, or null if no result is required. 1186c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1187c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 1188c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1189429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void sendControlRequest(@NonNull Intent intent, 1190429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Nullable ControlRequestCallback callback) { 1191c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (intent == null) { 1192c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("intent must not be null"); 1193c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1194c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 1195c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1196fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown sGlobal.sendControlRequest(this, intent, callback); 1197c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1198c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1199c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1200c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the type of playback associated with this route. 1201c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1202c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The type of playback associated with this route: {@link #PLAYBACK_TYPE_LOCAL} 1203c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * or {@link #PLAYBACK_TYPE_REMOTE}. 1204c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1205429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @PlaybackType 1206c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getPlaybackType() { 1207c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mPlaybackType; 1208c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1209c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1210c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1211350ba6e4a1b5ec28721a098e50eaf6a508eb28f0Jeff Brown * Gets the audio stream over which the playback associated with this route is performed. 1212c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1213c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The stream over which the playback associated with this route is performed. 1214c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1215c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getPlaybackStream() { 1216c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mPlaybackStream; 1217c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1218c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1219c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1220893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * Gets the type of the receiver device associated with this route. 1221893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * 1222893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * @return The type of the receiver device associated with this route: 1223050b7ec33bd9a2fe493f0b0b85d49ed213046219Jae Seo * {@link #DEVICE_TYPE_TV} or {@link #DEVICE_TYPE_SPEAKER}. 1224893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo */ 1225893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo public int getDeviceType() { 1226893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo return mDeviceType; 1227893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo } 1228893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo 1229893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo /** 1230c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets information about how volume is handled on the route. 1231c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1232c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return How volume is handled on the route: {@link #PLAYBACK_VOLUME_FIXED} 1233c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * or {@link #PLAYBACK_VOLUME_VARIABLE}. 1234c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1235429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @PlaybackVolume 1236c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getVolumeHandling() { 1237c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mVolumeHandling; 1238c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1239c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1240c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1241c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the current volume for this route. Depending on the route, this may only 1242c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * be valid if the route is currently selected. 1243c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1244c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The volume at which the playback associated with this route is performed. 1245c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1246c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getVolume() { 1247c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mVolume; 1248c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1249c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1250c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1251c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the maximum volume at which the playback associated with this route is performed. 1252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1253c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The maximum volume at which the playback associated with 1254c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * this route is performed. 1255c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1256c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getVolumeMax() { 1257c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mVolumeMax; 1258c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1259c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1260c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 126194be6100218126ce6a08bf1f56209578500b361fRoboErik * Gets whether this route supports disconnecting without interrupting 126294be6100218126ce6a08bf1f56209578500b361fRoboErik * playback. 126394be6100218126ce6a08bf1f56209578500b361fRoboErik * 126494be6100218126ce6a08bf1f56209578500b361fRoboErik * @return True if this route can disconnect without stopping playback, 126594be6100218126ce6a08bf1f56209578500b361fRoboErik * false otherwise. 126694be6100218126ce6a08bf1f56209578500b361fRoboErik */ 126794be6100218126ce6a08bf1f56209578500b361fRoboErik public boolean canDisconnect() { 126894be6100218126ce6a08bf1f56209578500b361fRoboErik return mCanDisconnect; 126994be6100218126ce6a08bf1f56209578500b361fRoboErik } 127094be6100218126ce6a08bf1f56209578500b361fRoboErik 127194be6100218126ce6a08bf1f56209578500b361fRoboErik /** 1272c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Requests a volume change for this route asynchronously. 1273c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1274c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This function may only be called on a selected route. It will have 1275c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * no effect if the route is currently unselected. 1276c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1277c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1278c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param volume The new volume value between 0 and {@link #getVolumeMax}. 1279c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1280c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestSetVolume(int volume) { 1281c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 1282c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal.requestSetVolume(this, Math.min(mVolumeMax, Math.max(0, volume))); 1283c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1284c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1285c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1286c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Requests an incremental volume update for this route asynchronously. 1287c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1288c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This function may only be called on a selected route. It will have 1289c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * no effect if the route is currently unselected. 1290c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1291c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1292c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param delta The delta to add to the current volume. 1293c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1294c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestUpdateVolume(int delta) { 1295c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 1296c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (delta != 0) { 1297c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal.requestUpdateVolume(this, delta); 1298c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1299c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1300c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1301c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1302c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the {@link Display} that should be used by the application to show 1303c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * a {@link android.app.Presentation} on an external display when this route is selected. 1304c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Depending on the route, this may only be valid if the route is currently 1305c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * selected. 1306c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1307c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The preferred presentation display may change independently of the route 1308c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * being selected or unselected. For example, the presentation display 1309c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * of the default system route may change when an external HDMI display is connected 1310c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * or disconnected even though the route itself has not changed. 1311c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 1312c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This method may return null if there is no external display associated with 1313c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * the route or if the display is not ready to show UI yet. 1314c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 1315c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The application should listen for changes to the presentation display 1316c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * using the {@link Callback#onRoutePresentationDisplayChanged} callback and 1317c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * show or dismiss its {@link android.app.Presentation} accordingly when the display 1318c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * becomes available or is removed. 1319c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 1320c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This method only makes sense for 1321c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO live video} routes. 1322c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1323c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1324c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The preferred presentation display to use when this route is 1325c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * selected or null if none. 1326c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1327c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent#CATEGORY_LIVE_VIDEO 1328c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see android.app.Presentation 1329c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1330429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Nullable 1331c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public Display getPresentationDisplay() { 1332fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1333c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mPresentationDisplayId >= 0 && mPresentationDisplay == null) { 1334c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mPresentationDisplay = sGlobal.getDisplay(mPresentationDisplayId); 1335c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1336c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mPresentationDisplay; 1337c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1338c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1339c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 134077367b4a1871417198d0399d9ad074314c758567Jae Seo * Gets the route's presentation display id, or -1 if none. 134177367b4a1871417198d0399d9ad074314c758567Jae Seo * @hide 134277367b4a1871417198d0399d9ad074314c758567Jae Seo */ 134377367b4a1871417198d0399d9ad074314c758567Jae Seo public int getPresentationDisplayId() { 134477367b4a1871417198d0399d9ad074314c758567Jae Seo return mPresentationDisplayId; 134577367b4a1871417198d0399d9ad074314c758567Jae Seo } 134677367b4a1871417198d0399d9ad074314c758567Jae Seo 134777367b4a1871417198d0399d9ad074314c758567Jae Seo /** 1348c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets a collection of extra properties about this route that were supplied 1349c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * by its media route provider, or null if none. 1350c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1351429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Nullable 1352c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public Bundle getExtras() { 1353c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mExtras; 1354c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1355c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1356fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 135794be6100218126ce6a08bf1f56209578500b361fRoboErik * Gets an intent sender for launching a settings activity for this 135894be6100218126ce6a08bf1f56209578500b361fRoboErik * route. 135994be6100218126ce6a08bf1f56209578500b361fRoboErik */ 136094be6100218126ce6a08bf1f56209578500b361fRoboErik @Nullable 136194be6100218126ce6a08bf1f56209578500b361fRoboErik public IntentSender getSettingsIntent() { 136294be6100218126ce6a08bf1f56209578500b361fRoboErik return mSettingsIntent; 136394be6100218126ce6a08bf1f56209578500b361fRoboErik } 136494be6100218126ce6a08bf1f56209578500b361fRoboErik 136594be6100218126ce6a08bf1f56209578500b361fRoboErik /** 1366fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Selects this media route. 1367fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1368fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void select() { 1369fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1370fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown sGlobal.selectRoute(this); 1371fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1372fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1373c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 1374c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public String toString() { 1375cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return "MediaRouter.RouteInfo{ uniqueId=" + mUniqueId 1376cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown + ", name=" + mName 1377d63957d28aaabcec588b8cde12eac16414783aebJeff Brown + ", description=" + mDescription 1378119de6bb3353341cfd465cea9e545abec3762d20Jae Seo + ", iconUri=" + mIconUri 1379c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", enabled=" + mEnabled 138011417b1cfde8f1749905f2d735623af9214148afJeff Brown + ", connecting=" + mConnecting 13813766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo + ", connectionState=" + mConnectionState 138294be6100218126ce6a08bf1f56209578500b361fRoboErik + ", canDisconnect=" + mCanDisconnect 1383c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", playbackType=" + mPlaybackType 1384c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", playbackStream=" + mPlaybackStream 1385893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo + ", deviceType=" + mDeviceType 1386c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", volumeHandling=" + mVolumeHandling 1387c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", volume=" + mVolume 1388c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", volumeMax=" + mVolumeMax 1389c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", presentationDisplayId=" + mPresentationDisplayId 1390c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", extras=" + mExtras 139194be6100218126ce6a08bf1f56209578500b361fRoboErik + ", settingsIntent=" + mSettingsIntent 1392fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown + ", providerPackageName=" + mProvider.getPackageName() 1393fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown + " }"; 1394fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1395fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1396e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo int maybeUpdateDescriptor(MediaRouteDescriptor descriptor) { 1397fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int changes = 0; 1398fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mDescriptor != descriptor) { 1399e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes = updateDescriptor(descriptor); 1400e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1401e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return changes; 1402e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1403e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 1404e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo int updateDescriptor(MediaRouteDescriptor descriptor) { 1405e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo int changes = 0; 1406e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mDescriptor = descriptor; 1407e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (descriptor != null) { 1408e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (!equal(mName, descriptor.getName())) { 1409e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mName = descriptor.getName(); 1410e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1411e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1412e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (!equal(mDescription, descriptor.getDescription())) { 1413e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mDescription = descriptor.getDescription(); 1414e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1415e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1416119de6bb3353341cfd465cea9e545abec3762d20Jae Seo if (!equal(mIconUri, descriptor.getIconUri())) { 1417119de6bb3353341cfd465cea9e545abec3762d20Jae Seo mIconUri = descriptor.getIconUri(); 1418119de6bb3353341cfd465cea9e545abec3762d20Jae Seo changes |= CHANGE_GENERAL; 1419119de6bb3353341cfd465cea9e545abec3762d20Jae Seo } 1420e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mEnabled != descriptor.isEnabled()) { 1421e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mEnabled = descriptor.isEnabled(); 1422e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1423e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1424e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mConnecting != descriptor.isConnecting()) { 1425e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mConnecting = descriptor.isConnecting(); 1426e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1427e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 14283766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo if (mConnectionState != descriptor.getConnectionState()) { 14293766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo mConnectionState = descriptor.getConnectionState(); 14303766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo changes |= CHANGE_GENERAL; 14313766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo } 1432e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (!mControlFilters.equals(descriptor.getControlFilters())) { 1433e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mControlFilters.clear(); 1434e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mControlFilters.addAll(descriptor.getControlFilters()); 1435e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1436e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1437e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mPlaybackType != descriptor.getPlaybackType()) { 1438e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mPlaybackType = descriptor.getPlaybackType(); 1439e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1440e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1441e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mPlaybackStream != descriptor.getPlaybackStream()) { 1442e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mPlaybackStream = descriptor.getPlaybackStream(); 1443e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1444e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1445893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo if (mDeviceType != descriptor.getDeviceType()) { 1446893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo mDeviceType = descriptor.getDeviceType(); 1447893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo changes |= CHANGE_GENERAL; 1448893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo } 1449e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mVolumeHandling != descriptor.getVolumeHandling()) { 1450e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mVolumeHandling = descriptor.getVolumeHandling(); 1451e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL | CHANGE_VOLUME; 1452e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1453e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mVolume != descriptor.getVolume()) { 1454e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mVolume = descriptor.getVolume(); 1455e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL | CHANGE_VOLUME; 1456e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1457e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mVolumeMax != descriptor.getVolumeMax()) { 1458e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mVolumeMax = descriptor.getVolumeMax(); 1459e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL | CHANGE_VOLUME; 1460e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1461e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mPresentationDisplayId != descriptor.getPresentationDisplayId()) { 1462e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mPresentationDisplayId = descriptor.getPresentationDisplayId(); 1463e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mPresentationDisplay = null; 1464e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY; 1465e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1466e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (!equal(mExtras, descriptor.getExtras())) { 1467e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mExtras = descriptor.getExtras(); 1468e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1469e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1470e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (!equal(mSettingsIntent, descriptor.getSettingsActivity())) { 1471e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mSettingsIntent = descriptor.getSettingsActivity(); 1472e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1473e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1474e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mCanDisconnect != descriptor.canDisconnectAndKeepPlaying()) { 1475e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mCanDisconnect = descriptor.canDisconnectAndKeepPlaying(); 1476e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY; 1477fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1478fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1479fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return changes; 1480fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1481fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1482fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown String getDescriptorId() { 1483fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mDescriptorId; 1484fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1485fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 148614b631fc79b2fc69f392ab8d2f096b0e86b9bea2Jae Seo /** @hide */ 148714b631fc79b2fc69f392ab8d2f096b0e86b9bea2Jae Seo public MediaRouteProvider getProviderInstance() { 1488fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mProvider.getProviderInstance(); 1489fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1490fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1491fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1492fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1493e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo * Information about a route that consists of multiple other routes in a group. 149414a7007aaefb96b3b6fb06171aa10e1200116d6aJae Seo * @hide 1495e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo */ 1496e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo public static class RouteGroup extends RouteInfo { 1497e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo private List<RouteInfo> mRoutes = new ArrayList<>(); 1498e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 1499e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo RouteGroup(ProviderInfo provider, String descriptorId, String uniqueId) { 1500e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo super(provider, descriptorId, uniqueId); 1501e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1502e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 1503e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo /** 1504e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo * @return The number of routes in this group 1505e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo */ 1506e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo public int getRouteCount() { 1507e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return mRoutes.size(); 1508e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1509e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 1510e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo /** 1511e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo * Returns the route in this group at the specified index 1512e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo * 1513e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo * @param index Index to fetch 1514e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo * @return The route at index 1515e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo */ 1516e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo public RouteInfo getRouteAt(int index) { 1517e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return mRoutes.get(index); 1518e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1519e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 15206051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo /** 15216051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo * Returns the routes in this group 15226051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo * 15236051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo * @return The list of the routes in this group 15246051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo */ 15256051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo public List<RouteInfo> getRoutes() { 15266051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo return mRoutes; 15276051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo } 15286051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo 1529e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo @Override 1530e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo public String toString() { 1531e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo StringBuilder sb = new StringBuilder(super.toString()); 1532e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo sb.append('['); 1533e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo final int count = mRoutes.size(); 1534e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo for (int i = 0; i < count; i++) { 1535e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (i > 0) sb.append(", "); 1536e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo sb.append(mRoutes.get(i)); 1537e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1538e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo sb.append(']'); 1539e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return sb.toString(); 1540e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1541e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 1542e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo @Override 1543e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo int maybeUpdateDescriptor(MediaRouteDescriptor descriptor) { 1544e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo boolean changed = false; 1545e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mDescriptor != descriptor) { 1546e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mDescriptor = descriptor; 1547e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (descriptor != null) { 15481bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo List<String> groupMemberIds = descriptor.getGroupMemberIds(); 1549e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo List<RouteInfo> routes = new ArrayList<>(); 15501bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo changed = groupMemberIds.size() != mRoutes.size(); 15511bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo for (String groupMemberId : groupMemberIds) { 15521bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo String uniqueId = sGlobal.getUniqueId(getProvider(), groupMemberId); 15531bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo RouteInfo groupMember = sGlobal.getRoute(uniqueId); 15541bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo if (groupMember != null) { 15551bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo routes.add(groupMember); 15561bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo if (!changed && !mRoutes.contains(groupMember)) { 1557e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changed = true; 1558e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1559e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1560e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1561e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (changed) { 1562e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mRoutes = routes; 1563e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1564e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1565e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1566e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return (changed ? CHANGE_GENERAL : 0) | super.updateDescriptor(descriptor); 1567e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1568e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1569e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 1570e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo /** 1571fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Provides information about a media route provider. 1572fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * <p> 1573fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * This object may be used to determine which media route provider has 1574fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * published a particular route. 1575fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * </p> 1576fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1577fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public static final class ProviderInfo { 1578fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private final MediaRouteProvider mProviderInstance; 1579e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo private final List<RouteInfo> mRoutes = new ArrayList<>(); 1580fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1581fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private final ProviderMetadata mMetadata; 158211417b1cfde8f1749905f2d735623af9214148afJeff Brown private MediaRouteProviderDescriptor mDescriptor; 1583fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private Resources mResources; 1584fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private boolean mResourcesNotAvailable; 1585fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1586fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo(MediaRouteProvider provider) { 1587fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mProviderInstance = provider; 1588fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mMetadata = provider.getMetadata(); 1589fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1590fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1591fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1592fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets the provider's underlying {@link MediaRouteProvider} instance. 1593fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1594fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public MediaRouteProvider getProviderInstance() { 1595fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1596fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mProviderInstance; 1597fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1598fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1599fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1600adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown * Gets the package name of the media route provider. 1601fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1602fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public String getPackageName() { 1603fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mMetadata.getPackageName(); 1604fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1605fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1606fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1607adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown * Gets the component name of the media route provider. 1608adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown */ 1609adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown public ComponentName getComponentName() { 1610adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown return mMetadata.getComponentName(); 1611adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown } 1612adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown 1613adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown /** 1614fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets the {@link MediaRouter.RouteInfo routes} published by this route provider. 1615fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1616fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public List<RouteInfo> getRoutes() { 1617fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1618fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mRoutes; 1619fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1620fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1621fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Resources getResources() { 1622fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mResources == null && !mResourcesNotAvailable) { 1623fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown String packageName = getPackageName(); 1624fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Context context = sGlobal.getProviderContext(packageName); 1625fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (context != null) { 1626fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mResources = context.getResources(); 1627fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } else { 1628fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Log.w(TAG, "Unable to obtain resources for route provider package: " 1629fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown + packageName); 1630fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mResourcesNotAvailable = true; 1631fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1632fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1633fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mResources; 1634fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1635fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 163611417b1cfde8f1749905f2d735623af9214148afJeff Brown boolean updateDescriptor(MediaRouteProviderDescriptor descriptor) { 1637fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mDescriptor != descriptor) { 1638fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mDescriptor = descriptor; 1639fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return true; 1640fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1641fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return false; 1642fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1643fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1644fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int findRouteByDescriptorId(String id) { 1645fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown final int count = mRoutes.size(); 1646fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown for (int i = 0; i < count; i++) { 1647fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mRoutes.get(i).mDescriptorId.equals(id)) { 1648fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return i; 1649fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1650fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1651fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return -1; 1652fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1653fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1654fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown @Override 1655fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public String toString() { 1656fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return "MediaRouter.RouteProviderInfo{ packageName=" + getPackageName() 1657c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + " }"; 1658c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1659c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1660c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1661c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1662c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Interface for receiving events about media routing changes. 1663c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * All methods of this interface will be called from the application's main thread. 1664c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1665c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * A Callback will only receive events relevant to routes that the callback 166611417b1cfde8f1749905f2d735623af9214148afJeff Brown * was registered for unless the {@link MediaRouter#CALLBACK_FLAG_UNFILTERED_EVENTS} 166711417b1cfde8f1749905f2d735623af9214148afJeff Brown * flag was specified in {@link MediaRouter#addCallback(MediaRouteSelector, Callback, int)}. 1668c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1669c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 167011417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see MediaRouter#addCallback(MediaRouteSelector, Callback, int) 1671c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaRouter#removeCallback(Callback) 1672c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1673c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static abstract class Callback { 1674c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1675fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when the supplied media route becomes selected as the active route. 1676c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1677fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1678c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has been selected. 1679c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1680c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteSelected(MediaRouter router, RouteInfo route) { 1681c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1682c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1683c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1684fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when the supplied media route becomes unselected as the active route. 1685c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1686fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1687c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has been unselected. 1688c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1689c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteUnselected(MediaRouter router, RouteInfo route) { 1690c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1691c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1692c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1693fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route has been added. 1694c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1695fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1696c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has become available for use. 1697c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1698c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteAdded(MediaRouter router, RouteInfo route) { 1699c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1700c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1701c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1702fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route has been removed. 1703c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1704fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1705c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has been removed from availability. 1706c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1707c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteRemoved(MediaRouter router, RouteInfo route) { 1708c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1709c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1710c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1711fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a property of the indicated media route has changed. 1712c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1713fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1714c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that was changed. 1715c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1716c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteChanged(MediaRouter router, RouteInfo route) { 1717c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1718c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1719c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1720fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route's volume changes. 1721c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1722fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1723c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route whose volume changed. 1724c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1725c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) { 1726c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1727c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1728c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1729fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route's presentation display changes. 1730c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1731c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This method is called whenever the route's presentation display becomes 1732fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * available, is removed or has changes to some of its properties (such as its size). 1733c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1734c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1735fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1736c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route whose presentation display changed. 1737c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1738c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#getPresentationDisplay() 1739c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1740c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo route) { 1741c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1742fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1743fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1744fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route provider has been added. 1745fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 1746fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1747fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param provider The provider that has become available for use. 1748fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1749fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void onProviderAdded(MediaRouter router, ProviderInfo provider) { 1750fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1751fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1752fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1753fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route provider has been removed. 1754fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 1755fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1756fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param provider The provider that has been removed from availability. 1757fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1758fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void onProviderRemoved(MediaRouter router, ProviderInfo provider) { 1759fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 176011417b1cfde8f1749905f2d735623af9214148afJeff Brown 176111417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 176211417b1cfde8f1749905f2d735623af9214148afJeff Brown * Called when a property of the indicated media route provider has changed. 176311417b1cfde8f1749905f2d735623af9214148afJeff Brown * 176411417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param router The media router reporting the event. 176511417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param provider The provider that was changed. 176611417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 176711417b1cfde8f1749905f2d735623af9214148afJeff Brown public void onProviderChanged(MediaRouter router, ProviderInfo provider) { 176811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1769c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1770c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1771c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1772c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Callback which is invoked with the result of a media control request. 1773c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1774c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#sendControlRequest 1775c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1776fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public static abstract class ControlRequestCallback { 1777c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 17783d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * Called when a media control request succeeds. 17793d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * 17803d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * @param data Result data, or null if none. 17813d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * Contents depend on the {@link MediaControlIntent media control action}. 1782c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 17833d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown public void onResult(Bundle data) { 17843d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown } 1785c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1786c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 17873d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * Called when a media control request fails. 1788c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 17893d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * @param error A localized error message which may be shown to the user, or null 17903d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * if the cause of the error is unclear. 17913d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * @param data Error data, or null if none. 17923d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * Contents depend on the {@link MediaControlIntent media control action}. 1793c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 17943d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown public void onError(String error, Bundle data) { 1795c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1796c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1797c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 179811417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final class CallbackRecord { 17999fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown public final MediaRouter mRouter; 180011417b1cfde8f1749905f2d735623af9214148afJeff Brown public final Callback mCallback; 180111417b1cfde8f1749905f2d735623af9214148afJeff Brown public MediaRouteSelector mSelector; 180211417b1cfde8f1749905f2d735623af9214148afJeff Brown public int mFlags; 180311417b1cfde8f1749905f2d735623af9214148afJeff Brown 18049fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown public CallbackRecord(MediaRouter router, Callback callback) { 18059fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouter = router; 180611417b1cfde8f1749905f2d735623af9214148afJeff Brown mCallback = callback; 180711417b1cfde8f1749905f2d735623af9214148afJeff Brown mSelector = MediaRouteSelector.EMPTY; 180811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 180911417b1cfde8f1749905f2d735623af9214148afJeff Brown 181011417b1cfde8f1749905f2d735623af9214148afJeff Brown public boolean filterRouteEvent(RouteInfo route) { 181111417b1cfde8f1749905f2d735623af9214148afJeff Brown return (mFlags & CALLBACK_FLAG_UNFILTERED_EVENTS) != 0 181211417b1cfde8f1749905f2d735623af9214148afJeff Brown || route.matchesSelector(mSelector); 181311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 181411417b1cfde8f1749905f2d735623af9214148afJeff Brown } 181511417b1cfde8f1749905f2d735623af9214148afJeff Brown 1816c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1817c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Global state for the media router. 1818c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1819c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media routes and media route providers are global to the process; their 1820c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * state and the bulk of the media router implementation lives here. 1821c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1822c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 18233efa63d3b896244713e84acbb5945562dce41d77Jeff Brown private static final class GlobalMediaRouter 18243efa63d3b896244713e84acbb5945562dce41d77Jeff Brown implements SystemMediaRouteProvider.SyncCallback, 18253efa63d3b896244713e84acbb5945562dce41d77Jeff Brown RegisteredMediaRouteProviderWatcher.Callback { 1826c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final Context mApplicationContext; 182738e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo private final ArrayList<WeakReference<MediaRouter>> mRouters = new ArrayList<>(); 182838e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo private final ArrayList<RouteInfo> mRoutes = new ArrayList<>(); 182938e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo private final Map<Pair<String, String>, String> mUniqueIdMap = new HashMap<>(); 183038e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo private final ArrayList<ProviderInfo> mProviders = new ArrayList<>(); 1831567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private final ArrayList<RemoteControlClientRecord> mRemoteControlClients = 183238e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo new ArrayList<>(); 1833567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private final RemoteControlClientCompat.PlaybackInfo mPlaybackInfo = 1834567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown new RemoteControlClientCompat.PlaybackInfo(); 1835c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final ProviderCallback mProviderCallback = new ProviderCallback(); 1836c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final CallbackHandler mCallbackHandler = new CallbackHandler(); 1837c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final DisplayManagerCompat mDisplayManager; 1838c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final SystemMediaRouteProvider mSystemProvider; 1839fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown private final boolean mLowRam; 1840c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1841fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private RegisteredMediaRouteProviderWatcher mRegisteredProviderWatcher; 1842c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private RouteInfo mDefaultRoute; 1843c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private RouteInfo mSelectedRoute; 1844bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang private RouteController mSelectedRouteController; 18451bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo private Map<String, RouteController> mGroupMemberControllers; 184611417b1cfde8f1749905f2d735623af9214148afJeff Brown private MediaRouteDiscoveryRequest mDiscoveryRequest; 1847bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik private MediaSessionRecord mMediaSession; 18485c9469e010106467791b47b0fa83efda84491a21RoboErik private MediaSessionCompat mRccMediaSession; 1849e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik private MediaSessionCompat mCompatSession; 18505c9469e010106467791b47b0fa83efda84491a21RoboErik private MediaSessionCompat.OnActiveChangeListener mSessionActiveListener = 18515c9469e010106467791b47b0fa83efda84491a21RoboErik new MediaSessionCompat.OnActiveChangeListener() { 18525c9469e010106467791b47b0fa83efda84491a21RoboErik @Override 18535c9469e010106467791b47b0fa83efda84491a21RoboErik public void onActiveChanged() { 18545c9469e010106467791b47b0fa83efda84491a21RoboErik if(mRccMediaSession != null) { 18555c9469e010106467791b47b0fa83efda84491a21RoboErik if (mRccMediaSession.isActive()) { 18565c9469e010106467791b47b0fa83efda84491a21RoboErik addRemoteControlClient(mRccMediaSession.getRemoteControlClient()); 18575c9469e010106467791b47b0fa83efda84491a21RoboErik } else { 18585c9469e010106467791b47b0fa83efda84491a21RoboErik removeRemoteControlClient(mRccMediaSession.getRemoteControlClient()); 18595c9469e010106467791b47b0fa83efda84491a21RoboErik } 18605c9469e010106467791b47b0fa83efda84491a21RoboErik } 18615c9469e010106467791b47b0fa83efda84491a21RoboErik } 18625c9469e010106467791b47b0fa83efda84491a21RoboErik }; 1863c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1864c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown GlobalMediaRouter(Context applicationContext) { 1865c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mApplicationContext = applicationContext; 1866c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDisplayManager = DisplayManagerCompat.getInstance(applicationContext); 1867fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown mLowRam = ActivityManagerCompat.isLowRamDevice( 1868fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown (ActivityManager)applicationContext.getSystemService( 1869fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown Context.ACTIVITY_SERVICE)); 1870fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1871fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // Add the system media route provider for interoperating with 1872fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // the framework media router. This one is special and receives 1873fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // synchronization messages from the media router. 1874c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSystemProvider = SystemMediaRouteProvider.obtain(applicationContext, this); 1875fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown addProvider(mSystemProvider); 1876fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1877fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1878fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void start() { 1879fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // Start watching for routes published by registered media route 1880fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // provider services. 1881fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mRegisteredProviderWatcher = new RegisteredMediaRouteProviderWatcher( 18823efa63d3b896244713e84acbb5945562dce41d77Jeff Brown mApplicationContext, this); 1883fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mRegisteredProviderWatcher.start(); 1884c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1885c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1886c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public MediaRouter getRouter(Context context) { 18879fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown MediaRouter router; 18889fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int i = mRouters.size(); --i >= 0; ) { 18899fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown router = mRouters.get(i).get(); 18909fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown if (router == null) { 18919fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouters.remove(i); 18929fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } else if (router.mContext == context) { 18939fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown return router; 18949fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } 1895c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 18969fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown router = new MediaRouter(context); 18979fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouters.add(new WeakReference<MediaRouter>(router)); 1898c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return router; 1899c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1900c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1901c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public ContentResolver getContentResolver() { 1902c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mApplicationContext.getContentResolver(); 1903c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1904c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1905fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public Context getProviderContext(String packageName) { 1906fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (packageName.equals(SystemMediaRouteProvider.PACKAGE_NAME)) { 1907fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mApplicationContext; 1908fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1909fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown try { 1910fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mApplicationContext.createPackageContext( 1911fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown packageName, Context.CONTEXT_RESTRICTED); 1912fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } catch (NameNotFoundException ex) { 1913fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return null; 1914fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1915fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1916fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1917c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public Display getDisplay(int displayId) { 1918c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mDisplayManager.getDisplay(displayId); 1919c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1920c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1921fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void sendControlRequest(RouteInfo route, 1922c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Intent intent, ControlRequestCallback callback) { 1923c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == mSelectedRoute && mSelectedRouteController != null) { 1924129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown if (mSelectedRouteController.onControlRequest(intent, callback)) { 1925fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return; 1926fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1927fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1928fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (callback != null) { 19293d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown callback.onError(null, null); 1930c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1931c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1932c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1933c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestSetVolume(RouteInfo route, int volume) { 1934c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == mSelectedRoute && mSelectedRouteController != null) { 1935129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown mSelectedRouteController.onSetVolume(volume); 19361bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo } else if (mGroupMemberControllers != null) { 19371bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo RouteController controller = mGroupMemberControllers.get(route.mDescriptorId); 1938bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang if (controller != null) { 1939bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang controller.onSetVolume(volume); 1940bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang } 1941c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1942c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1943c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1944c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestUpdateVolume(RouteInfo route, int delta) { 1945c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == mSelectedRoute && mSelectedRouteController != null) { 1946129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown mSelectedRouteController.onUpdateVolume(delta); 1947c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1948c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1949c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1950e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo public RouteInfo getRoute(String uniqueId) { 1951e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo for (RouteInfo info : mRoutes) { 1952e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (info.mUniqueId.equals(uniqueId)) { 1953e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return info; 1954e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1955e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1956e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return null; 1957e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1958e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 1959c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public List<RouteInfo> getRoutes() { 1960c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mRoutes; 1961c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1962c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1963fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public List<ProviderInfo> getProviders() { 1964fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mProviders; 1965fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1966fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1967c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getDefaultRoute() { 1968c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mDefaultRoute == null) { 1969c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This should never happen once the media router has been fully 1970c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // initialized but it is good to check for the error in case there 1971c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // is a bug in provider initialization. 1972c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalStateException("There is no default route. " 1973c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + "The media router has not yet been fully initialized."); 1974c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1975c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mDefaultRoute; 1976c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1977c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1978c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getSelectedRoute() { 1979c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute == null) { 1980c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This should never happen once the media router has been fully 1981c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // initialized but it is good to check for the error in case there 1982c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // is a bug in provider initialization. 1983c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalStateException("There is no currently selected route. " 1984c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + "The media router has not yet been fully initialized."); 1985c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1986c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mSelectedRoute; 1987c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1988c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1989c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void selectRoute(RouteInfo route) { 199094be6100218126ce6a08bf1f56209578500b361fRoboErik selectRoute(route, MediaRouter.UNSELECT_REASON_ROUTE_CHANGED); 199194be6100218126ce6a08bf1f56209578500b361fRoboErik } 199294be6100218126ce6a08bf1f56209578500b361fRoboErik 199394be6100218126ce6a08bf1f56209578500b361fRoboErik public void selectRoute(RouteInfo route, int unselectReason) { 1994c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (!mRoutes.contains(route)) { 1995c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Log.w(TAG, "Ignoring attempt to select removed route: " + route); 1996c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return; 1997c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1998c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (!route.mEnabled) { 1999c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Log.w(TAG, "Ignoring attempt to select disabled route: " + route); 2000c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return; 2001c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2002c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 200394be6100218126ce6a08bf1f56209578500b361fRoboErik setSelectedRouteInternal(route, unselectReason); 2004c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2005c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 200611417b1cfde8f1749905f2d735623af9214148afJeff Brown public boolean isRouteAvailable(MediaRouteSelector selector, int flags) { 2007fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown if (selector.isEmpty()) { 2008fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown return false; 2009fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown } 2010fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown 2011fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown // On low-RAM devices, do not rely on actual discovery results unless asked to. 2012fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown if ((flags & AVAILABILITY_FLAG_REQUIRE_MATCH) == 0 && mLowRam) { 2013fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown return true; 2014fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown } 2015fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown 201611417b1cfde8f1749905f2d735623af9214148afJeff Brown // Check whether any existing routes match the selector. 201711417b1cfde8f1749905f2d735623af9214148afJeff Brown final int routeCount = mRoutes.size(); 201811417b1cfde8f1749905f2d735623af9214148afJeff Brown for (int i = 0; i < routeCount; i++) { 201911417b1cfde8f1749905f2d735623af9214148afJeff Brown RouteInfo route = mRoutes.get(i); 202011417b1cfde8f1749905f2d735623af9214148afJeff Brown if ((flags & AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE) != 0 202111417b1cfde8f1749905f2d735623af9214148afJeff Brown && route.isDefault()) { 202211417b1cfde8f1749905f2d735623af9214148afJeff Brown continue; 202311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 202411417b1cfde8f1749905f2d735623af9214148afJeff Brown if (route.matchesSelector(selector)) { 202511417b1cfde8f1749905f2d735623af9214148afJeff Brown return true; 202611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 202711417b1cfde8f1749905f2d735623af9214148afJeff Brown } 202811417b1cfde8f1749905f2d735623af9214148afJeff Brown 202911417b1cfde8f1749905f2d735623af9214148afJeff Brown // It doesn't look like we can find a matching route right now. 203011417b1cfde8f1749905f2d735623af9214148afJeff Brown return false; 203111417b1cfde8f1749905f2d735623af9214148afJeff Brown } 203211417b1cfde8f1749905f2d735623af9214148afJeff Brown 203311417b1cfde8f1749905f2d735623af9214148afJeff Brown public void updateDiscoveryRequest() { 203411417b1cfde8f1749905f2d735623af9214148afJeff Brown // Combine all of the callback selectors and active scan flags. 2035f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown boolean discover = false; 203611417b1cfde8f1749905f2d735623af9214148afJeff Brown boolean activeScan = false; 203711417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder(); 20389fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int i = mRouters.size(); --i >= 0; ) { 20399fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown MediaRouter router = mRouters.get(i).get(); 20409fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown if (router == null) { 20419fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouters.remove(i); 20429fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } else { 20439fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown final int count = router.mCallbackRecords.size(); 20449fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int j = 0; j < count; j++) { 20459fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown CallbackRecord callback = router.mCallbackRecords.get(j); 20469fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown builder.addSelector(callback.mSelector); 2047f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown if ((callback.mFlags & CALLBACK_FLAG_PERFORM_ACTIVE_SCAN) != 0) { 20489fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown activeScan = true; 2049f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown discover = true; // perform active scan implies request discovery 2050f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown } 2051f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown if ((callback.mFlags & CALLBACK_FLAG_REQUEST_DISCOVERY) != 0) { 2052fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown if (!mLowRam) { 2053fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown discover = true; 2054fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown } 2055fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown } 2056fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown if ((callback.mFlags & CALLBACK_FLAG_FORCE_DISCOVERY) != 0) { 2057f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown discover = true; 20589fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } 205911417b1cfde8f1749905f2d735623af9214148afJeff Brown } 206011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 206111417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2062f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown MediaRouteSelector selector = discover ? builder.build() : MediaRouteSelector.EMPTY; 206311417b1cfde8f1749905f2d735623af9214148afJeff Brown 206411417b1cfde8f1749905f2d735623af9214148afJeff Brown // Create a new discovery request. 206511417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mDiscoveryRequest != null 206611417b1cfde8f1749905f2d735623af9214148afJeff Brown && mDiscoveryRequest.getSelector().equals(selector) 206711417b1cfde8f1749905f2d735623af9214148afJeff Brown && mDiscoveryRequest.isActiveScan() == activeScan) { 206811417b1cfde8f1749905f2d735623af9214148afJeff Brown return; // no change 206911417b1cfde8f1749905f2d735623af9214148afJeff Brown } 207011417b1cfde8f1749905f2d735623af9214148afJeff Brown if (selector.isEmpty() && !activeScan) { 207111417b1cfde8f1749905f2d735623af9214148afJeff Brown // Discovery is not needed. 207211417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mDiscoveryRequest == null) { 207311417b1cfde8f1749905f2d735623af9214148afJeff Brown return; // no change 207411417b1cfde8f1749905f2d735623af9214148afJeff Brown } 207511417b1cfde8f1749905f2d735623af9214148afJeff Brown mDiscoveryRequest = null; 207611417b1cfde8f1749905f2d735623af9214148afJeff Brown } else { 207711417b1cfde8f1749905f2d735623af9214148afJeff Brown // Discovery is needed. 207811417b1cfde8f1749905f2d735623af9214148afJeff Brown mDiscoveryRequest = new MediaRouteDiscoveryRequest(selector, activeScan); 207911417b1cfde8f1749905f2d735623af9214148afJeff Brown } 208011417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 208111417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Updated discovery request: " + mDiscoveryRequest); 208211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2083fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown if (discover && !activeScan && mLowRam) { 2084fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown Log.i(TAG, "Forcing passive route discovery on a low-RAM device, " 2085fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown + "system performance may be affected. Please consider using " 2086fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown + "CALLBACK_FLAG_REQUEST_DISCOVERY instead of " 2087fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown + "CALLBACK_FLAG_FORCE_DISCOVERY."); 2088fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown } 208911417b1cfde8f1749905f2d735623af9214148afJeff Brown 209011417b1cfde8f1749905f2d735623af9214148afJeff Brown // Notify providers. 209111417b1cfde8f1749905f2d735623af9214148afJeff Brown final int providerCount = mProviders.size(); 209211417b1cfde8f1749905f2d735623af9214148afJeff Brown for (int i = 0; i < providerCount; i++) { 209311417b1cfde8f1749905f2d735623af9214148afJeff Brown mProviders.get(i).mProviderInstance.setDiscoveryRequest(mDiscoveryRequest); 209428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 209528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 209628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 20973efa63d3b896244713e84acbb5945562dce41d77Jeff Brown @Override 2098fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void addProvider(MediaRouteProvider providerInstance) { 2099fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int index = findProviderInfo(providerInstance); 2100c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index < 0) { 2101c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 1. Add the provider to the list. 2102fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo provider = new ProviderInfo(providerInstance); 2103fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mProviders.add(provider); 210411417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 210511417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Provider added: " + provider); 210611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2107fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_ADDED, provider); 2108c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 2. Create the provider's contents. 2109fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown updateProviderContents(provider, providerInstance.getDescriptor()); 2110c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 3. Register the provider callback. 211111417b1cfde8f1749905f2d735623af9214148afJeff Brown providerInstance.setCallback(mProviderCallback); 211211417b1cfde8f1749905f2d735623af9214148afJeff Brown // 4. Set the discovery request. 211311417b1cfde8f1749905f2d735623af9214148afJeff Brown providerInstance.setDiscoveryRequest(mDiscoveryRequest); 2114c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2115c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2116c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 21173efa63d3b896244713e84acbb5945562dce41d77Jeff Brown @Override 2118fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void removeProvider(MediaRouteProvider providerInstance) { 2119fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int index = findProviderInfo(providerInstance); 2120c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index >= 0) { 212111417b1cfde8f1749905f2d735623af9214148afJeff Brown // 1. Unregister the provider callback. 212211417b1cfde8f1749905f2d735623af9214148afJeff Brown providerInstance.setCallback(null); 212311417b1cfde8f1749905f2d735623af9214148afJeff Brown // 2. Clear the discovery request. 212411417b1cfde8f1749905f2d735623af9214148afJeff Brown providerInstance.setDiscoveryRequest(null); 212528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown // 3. Delete the provider's contents. 2126fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo provider = mProviders.get(index); 2127fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown updateProviderContents(provider, null); 212828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown // 4. Remove the provider from the list. 212911417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 213011417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Provider removed: " + provider); 213111417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2132fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_REMOVED, provider); 2133fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mProviders.remove(index); 2134c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2135c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2136c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2137fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private void updateProviderDescriptor(MediaRouteProvider providerInstance, 213811417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteProviderDescriptor descriptor) { 2139fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int index = findProviderInfo(providerInstance); 2140c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index >= 0) { 2141fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // Update the provider's contents. 2142fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo provider = mProviders.get(index); 2143fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown updateProviderContents(provider, descriptor); 2144c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2145c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2146c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2147fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private int findProviderInfo(MediaRouteProvider providerInstance) { 2148fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown final int count = mProviders.size(); 2149c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown for (int i = 0; i < count; i++) { 2150fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mProviders.get(i).mProviderInstance == providerInstance) { 2151c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return i; 2152c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2153c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2154c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return -1; 2155c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2156c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2157fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private void updateProviderContents(ProviderInfo provider, 215811417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteProviderDescriptor providerDescriptor) { 2159fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (provider.updateDescriptor(providerDescriptor)) { 2160c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Update all existing routes and reorder them to match 2161c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // the order of their descriptors. 2162c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int targetIndex = 0; 2163567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown boolean selectedRouteDescriptorChanged = false; 2164c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (providerDescriptor != null) { 2165fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (providerDescriptor.isValid()) { 216611417b1cfde8f1749905f2d735623af9214148afJeff Brown final List<MediaRouteDescriptor> routeDescriptors = 216711417b1cfde8f1749905f2d735623af9214148afJeff Brown providerDescriptor.getRoutes(); 216811417b1cfde8f1749905f2d735623af9214148afJeff Brown final int routeCount = routeDescriptors.size(); 2169e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo // Updating route group's contents requires all member routes' information. 2170e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo // Add the groups to the lists and update them later. 2171e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo List<Pair<RouteInfo, MediaRouteDescriptor>> addedGroups = new ArrayList<>(); 2172e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo List<Pair<RouteInfo, MediaRouteDescriptor>> updatedGroups = 2173e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo new ArrayList<>(); 217411417b1cfde8f1749905f2d735623af9214148afJeff Brown for (int i = 0; i < routeCount; i++) { 217511417b1cfde8f1749905f2d735623af9214148afJeff Brown final MediaRouteDescriptor routeDescriptor = routeDescriptors.get(i); 2176c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final String id = routeDescriptor.getId(); 2177fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown final int sourceIndex = provider.findRouteByDescriptorId(id); 2178c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (sourceIndex < 0) { 2179c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 1. Add the route to the list. 2180cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown String uniqueId = assignRouteUniqueId(provider, id); 21811bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo boolean isGroup = routeDescriptor.getGroupMemberIds() != null; 2182e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo RouteInfo route = isGroup ? new RouteGroup(provider, id, uniqueId) : 2183e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo new RouteInfo(provider, id, uniqueId); 2184fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown provider.mRoutes.add(targetIndex++, route); 2185c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mRoutes.add(route); 2186c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 2. Create the route's contents. 2187e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (isGroup) { 2188e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo addedGroups.add(new Pair(route, routeDescriptor)); 2189e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } else { 2190e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo route.maybeUpdateDescriptor(routeDescriptor); 2191e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo // 3. Notify clients about addition. 2192e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (DEBUG) { 2193e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo Log.d(TAG, "Route added: " + route); 2194e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2195e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route); 219611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2197e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 2198fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } else if (sourceIndex < targetIndex) { 2199fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Log.w(TAG, "Ignoring route descriptor with duplicate id: " 2200fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown + routeDescriptor); 2201c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } else { 2202c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 1. Reorder the route within the list. 2203fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown RouteInfo route = provider.mRoutes.get(sourceIndex); 2204fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Collections.swap(provider.mRoutes, 2205c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sourceIndex, targetIndex++); 2206c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 2. Update the route's contents. 2207e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (route instanceof RouteGroup) { 2208e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo updatedGroups.add(new Pair(route, routeDescriptor)); 2209e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } else { 2210e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo // 3. Notify clients about changes. 2211e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (updateRouteDescriptorAndNotify(route, routeDescriptor) 2212e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo != 0) { 2213e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (route == mSelectedRoute) { 2214e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo selectedRouteDescriptorChanged = true; 2215567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 221611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2217c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2218c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2219c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2220e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo // Update the new and/or existing groups. 2221e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo for (Pair<RouteInfo, MediaRouteDescriptor> pair : addedGroups) { 2222e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo RouteInfo route = pair.first; 2223e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo route.maybeUpdateDescriptor(pair.second); 2224e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (DEBUG) { 2225e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo Log.d(TAG, "Route added: " + route); 2226e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2227e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route); 2228e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2229e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo for (Pair<RouteInfo, MediaRouteDescriptor> pair : updatedGroups) { 2230e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo RouteInfo route = pair.first; 2231e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (updateRouteDescriptorAndNotify(route, pair.second) != 0) { 2232e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (route == mSelectedRoute) { 2233e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo selectedRouteDescriptorChanged = true; 2234e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2235e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2236e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2237fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } else { 2238fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Log.w(TAG, "Ignoring invalid provider descriptor: " + providerDescriptor); 2239c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2240c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2241c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2242c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Dispose all remaining routes that do not have matching descriptors. 2243fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) { 2244fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // 1. Delete the route's contents. 2245fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown RouteInfo route = provider.mRoutes.get(i); 2246e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo route.maybeUpdateDescriptor(null); 2247fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // 2. Remove the route from the list. 224811417b1cfde8f1749905f2d735623af9214148afJeff Brown mRoutes.remove(route); 224935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang } 225035af7c925d660508fe0892f38eec99a3845630e4Chong Zhang 2251567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // Update the selected route if needed. 2252567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown updateSelectedRouteIfNeeded(selectedRouteDescriptorChanged); 225335af7c925d660508fe0892f38eec99a3845630e4Chong Zhang 225435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // Now notify clients about routes that were removed. 225535af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // We do this after updating the selected route to ensure 225635af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // that the framework media router observes the new route 225735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // selection before the removal since removing the currently 225835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // selected route may have side-effects. 225935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) { 226035af7c925d660508fe0892f38eec99a3845630e4Chong Zhang RouteInfo route = provider.mRoutes.remove(i); 226111417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 226211417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Route removed: " + route); 226311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2264fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_ROUTE_REMOVED, route); 2265c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2266fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 226711417b1cfde8f1749905f2d735623af9214148afJeff Brown // Notify provider changed. 226811417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 226911417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Provider changed: " + provider); 227011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 227111417b1cfde8f1749905f2d735623af9214148afJeff Brown mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_CHANGED, provider); 2272c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2273c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2274c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2275e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo private int updateRouteDescriptorAndNotify(RouteInfo route, 2276e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo MediaRouteDescriptor routeDescriptor) { 2277e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo int changes = route.maybeUpdateDescriptor(routeDescriptor); 2278e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (changes != 0) { 2279e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if ((changes & RouteInfo.CHANGE_GENERAL) != 0) { 2280e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (DEBUG) { 2281e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo Log.d(TAG, "Route changed: " + route); 2282e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2283e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mCallbackHandler.post( 2284e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo CallbackHandler.MSG_ROUTE_CHANGED, route); 2285e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2286e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if ((changes & RouteInfo.CHANGE_VOLUME) != 0) { 2287e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (DEBUG) { 2288e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo Log.d(TAG, "Route volume changed: " + route); 2289e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2290e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mCallbackHandler.post( 2291e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo CallbackHandler.MSG_ROUTE_VOLUME_CHANGED, route); 2292e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2293e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if ((changes & RouteInfo.CHANGE_PRESENTATION_DISPLAY) != 0) { 2294e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (DEBUG) { 2295e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo Log.d(TAG, "Route presentation display changed: " 2296e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo + route); 2297e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2298e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mCallbackHandler.post(CallbackHandler. 2299e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED, route); 2300e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2301e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2302e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return changes; 2303e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2304e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 2305cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown private String assignRouteUniqueId(ProviderInfo provider, String routeDescriptorId) { 2306cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown // Although route descriptor ids are unique within a provider, it's 2307cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown // possible for there to be two providers with the same package name. 2308cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown // Therefore we must dedupe the composite id. 230938e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo String componentName = provider.getComponentName().flattenToShortString(); 231038e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo String uniqueId = componentName + ":" + routeDescriptorId; 2311cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown if (findRouteByUniqueId(uniqueId) < 0) { 231238e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo mUniqueIdMap.put(new Pair(componentName, routeDescriptorId), uniqueId); 2313cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return uniqueId; 2314cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 231538e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo Log.w(TAG, "Either " + routeDescriptorId + " isn't unique in " + componentName 2316480052774a9fb375d68b7a0373a5bd8d733f8dc0Jae Seo + " or we're trying to assign a unique ID for an already added route"); 2317cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown for (int i = 2; ; i++) { 2318cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown String newUniqueId = String.format(Locale.US, "%s_%d", uniqueId, i); 2319cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown if (findRouteByUniqueId(newUniqueId) < 0) { 232038e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo mUniqueIdMap.put(new Pair(componentName, routeDescriptorId), newUniqueId); 2321cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return newUniqueId; 2322cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2323cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2324cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2325cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown 2326cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown private int findRouteByUniqueId(String uniqueId) { 2327cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown final int count = mRoutes.size(); 2328cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown for (int i = 0; i < count; i++) { 2329cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown if (mRoutes.get(i).mUniqueId.equals(uniqueId)) { 2330cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return i; 2331cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2332cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2333cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return -1; 2334cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2335cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown 233638e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo private String getUniqueId(ProviderInfo provider, String routeDescriptorId) { 233738e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo String componentName = provider.getComponentName().flattenToShortString(); 233838e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo return mUniqueIdMap.get(new Pair(componentName, routeDescriptorId)); 233938e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo } 234038e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo 2341567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private void updateSelectedRouteIfNeeded(boolean selectedRouteDescriptorChanged) { 2342567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // Update default route. 2343567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (mDefaultRoute != null && !isRouteSelectable(mDefaultRoute)) { 234435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang Log.i(TAG, "Clearing the default route because it " 2345567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown + "is no longer selectable: " + mDefaultRoute); 2346c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDefaultRoute = null; 2347c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2348c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mDefaultRoute == null && !mRoutes.isEmpty()) { 2349c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown for (RouteInfo route : mRoutes) { 2350c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (isSystemDefaultRoute(route) && isRouteSelectable(route)) { 2351c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDefaultRoute = route; 2352567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown Log.i(TAG, "Found default route: " + mDefaultRoute); 2353c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 2354c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2355c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2356c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2357567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2358567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // Update selected route. 2359567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (mSelectedRoute != null && !isRouteSelectable(mSelectedRoute)) { 2360567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown Log.i(TAG, "Unselecting the current route because it " 2361567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown + "is no longer selectable: " + mSelectedRoute); 236294be6100218126ce6a08bf1f56209578500b361fRoboErik setSelectedRouteInternal(null, 236394be6100218126ce6a08bf1f56209578500b361fRoboErik MediaRouter.UNSELECT_REASON_UNKNOWN); 2364567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2365c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute == null) { 2366567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // Choose a new route. 2367567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // This will have the side-effect of updating the playback info when 2368567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // the new route is selected. 236994be6100218126ce6a08bf1f56209578500b361fRoboErik setSelectedRouteInternal(chooseFallbackRoute(), 237094be6100218126ce6a08bf1f56209578500b361fRoboErik MediaRouter.UNSELECT_REASON_UNKNOWN); 2371567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } else if (selectedRouteDescriptorChanged) { 2372567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // Update the playback info because the properties of the route have changed. 2373567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown updatePlaybackInfoFromSelectedRoute(); 2374c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2375c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2376c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 237735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang private RouteInfo chooseFallbackRoute() { 237835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // When the current route is removed or no longer selectable, 237935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // we want to revert to a live audio route if there is 238035af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // one (usually Bluetooth A2DP). Failing that, use 238135af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // the default route. 238235af7c925d660508fe0892f38eec99a3845630e4Chong Zhang for (RouteInfo route : mRoutes) { 238335af7c925d660508fe0892f38eec99a3845630e4Chong Zhang if (route != mDefaultRoute 238435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang && isSystemLiveAudioOnlyRoute(route) 238535af7c925d660508fe0892f38eec99a3845630e4Chong Zhang && isRouteSelectable(route)) { 238635af7c925d660508fe0892f38eec99a3845630e4Chong Zhang return route; 238735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang } 238835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang } 238935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang return mDefaultRoute; 239035af7c925d660508fe0892f38eec99a3845630e4Chong Zhang } 239135af7c925d660508fe0892f38eec99a3845630e4Chong Zhang 239235af7c925d660508fe0892f38eec99a3845630e4Chong Zhang private boolean isSystemLiveAudioOnlyRoute(RouteInfo route) { 239335af7c925d660508fe0892f38eec99a3845630e4Chong Zhang return route.getProviderInstance() == mSystemProvider 239435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang && route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO) 239535af7c925d660508fe0892f38eec99a3845630e4Chong Zhang && !route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO); 239635af7c925d660508fe0892f38eec99a3845630e4Chong Zhang } 239735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang 2398c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private boolean isRouteSelectable(RouteInfo route) { 2399c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This tests whether the route is still valid and enabled. 2400c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // The route descriptor field is set to null when the route is removed. 2401c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return route.mDescriptor != null && route.mEnabled; 2402c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2403c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2404c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private boolean isSystemDefaultRoute(RouteInfo route) { 2405fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return route.getProviderInstance() == mSystemProvider 2406c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown && route.mDescriptorId.equals( 2407c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown SystemMediaRouteProvider.DEFAULT_ROUTE_ID); 2408c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2409c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 241094be6100218126ce6a08bf1f56209578500b361fRoboErik private void setSelectedRouteInternal(RouteInfo route, int unselectReason) { 2411c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute != route) { 2412c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute != null) { 241311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 241494be6100218126ce6a08bf1f56209578500b361fRoboErik Log.d(TAG, "Route unselected: " + mSelectedRoute + " reason: " 241594be6100218126ce6a08bf1f56209578500b361fRoboErik + unselectReason); 241611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2417c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_ROUTE_UNSELECTED, mSelectedRoute); 2418c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRouteController != null) { 241994be6100218126ce6a08bf1f56209578500b361fRoboErik mSelectedRouteController.onUnselect(unselectReason); 2420129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown mSelectedRouteController.onRelease(); 2421c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSelectedRouteController = null; 2422c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 24231bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo if (mGroupMemberControllers != null) { 24241bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo for (RouteController controller : mGroupMemberControllers.values()) { 24251bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo controller.onUnselect(); 24261bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo controller.onRelease(); 2427bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang } 24281bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo mGroupMemberControllers = null; 2429bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang } 2430c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2431c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2432c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSelectedRoute = route; 2433c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2434c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute != null) { 2435fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSelectedRouteController = route.getProviderInstance().onCreateRouteController( 2436c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown route.mDescriptorId); 2437c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRouteController != null) { 2438129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown mSelectedRouteController.onSelect(); 2439c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 244011417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 244111417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Route selected: " + mSelectedRoute); 244211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2443c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_ROUTE_SELECTED, mSelectedRoute); 2444bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang 2445bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang if (mSelectedRoute instanceof RouteGroup) { 24461bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo mGroupMemberControllers = new HashMap<>(); 2447bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang RouteGroup group = (RouteGroup) mSelectedRoute; 24481bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo for (RouteInfo groupMember : group.getRoutes()) { 24491bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo RouteController controller = groupMember.getProviderInstance() 24501bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo .onCreateRouteController(groupMember.mDescriptorId); 2451bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang controller.onSelect(); 24521bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo mGroupMemberControllers.put(groupMember.mDescriptorId, controller); 2453bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang } 2454bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang } 2455c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2456567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2457567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown updatePlaybackInfoFromSelectedRoute(); 2458c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2459c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2460c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2461c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 2462c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getSystemRouteByDescriptorId(String id) { 2463fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int providerIndex = findProviderInfo(mSystemProvider); 2464c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (providerIndex >= 0) { 2465fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo provider = mProviders.get(providerIndex); 2466fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int routeIndex = provider.findRouteByDescriptorId(id); 2467c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (routeIndex >= 0) { 2468fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return provider.mRoutes.get(routeIndex); 2469c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2470c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2471c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return null; 2472c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2473c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2474567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void addRemoteControlClient(Object rcc) { 2475567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown int index = findRemoteControlClientRecord(rcc); 2476567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (index < 0) { 2477567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown RemoteControlClientRecord record = new RemoteControlClientRecord(rcc); 2478567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRemoteControlClients.add(record); 2479567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2480567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2481567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2482567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void removeRemoteControlClient(Object rcc) { 2483567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown int index = findRemoteControlClientRecord(rcc); 2484567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (index >= 0) { 2485567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown RemoteControlClientRecord record = mRemoteControlClients.remove(index); 2486567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown record.disconnect(); 2487567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2488567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2489567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2490bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik public void setMediaSession(Object session) { 2491bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik if (mMediaSession != null) { 2492bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mMediaSession.clearVolumeHandling(); 2493bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2494bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik if (session == null) { 2495bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mMediaSession = null; 2496bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } else { 2497bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mMediaSession = new MediaSessionRecord(session); 2498bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik updatePlaybackInfoFromSelectedRoute(); 2499bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2500bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2501bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 25025c9469e010106467791b47b0fa83efda84491a21RoboErik public void setMediaSessionCompat(final MediaSessionCompat session) { 2503e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik mCompatSession = session; 25045c9469e010106467791b47b0fa83efda84491a21RoboErik if (android.os.Build.VERSION.SDK_INT >= 21) { 2505d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake setMediaSession(session != null ? session.getMediaSession() : null); 25065c9469e010106467791b47b0fa83efda84491a21RoboErik } else if (android.os.Build.VERSION.SDK_INT >= 14) { 25075c9469e010106467791b47b0fa83efda84491a21RoboErik if (mRccMediaSession != null) { 25085c9469e010106467791b47b0fa83efda84491a21RoboErik removeRemoteControlClient(mRccMediaSession.getRemoteControlClient()); 25095c9469e010106467791b47b0fa83efda84491a21RoboErik mRccMediaSession.removeOnActiveChangeListener(mSessionActiveListener); 25105c9469e010106467791b47b0fa83efda84491a21RoboErik } 25115c9469e010106467791b47b0fa83efda84491a21RoboErik mRccMediaSession = session; 2512d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake if (session != null) { 2513d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake session.addOnActiveChangeListener(mSessionActiveListener); 2514d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake if (session.isActive()) { 2515d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake addRemoteControlClient(session.getRemoteControlClient()); 2516d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake } 25175c9469e010106467791b47b0fa83efda84491a21RoboErik } 25185c9469e010106467791b47b0fa83efda84491a21RoboErik } 25195c9469e010106467791b47b0fa83efda84491a21RoboErik } 25205c9469e010106467791b47b0fa83efda84491a21RoboErik 252194be6100218126ce6a08bf1f56209578500b361fRoboErik public MediaSessionCompat.Token getMediaSessionToken() { 252294be6100218126ce6a08bf1f56209578500b361fRoboErik if (mMediaSession != null) { 252394be6100218126ce6a08bf1f56209578500b361fRoboErik return mMediaSession.getToken(); 2524e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik } else if (mCompatSession != null) { 2525e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik return mCompatSession.getSessionToken(); 252694be6100218126ce6a08bf1f56209578500b361fRoboErik } 252794be6100218126ce6a08bf1f56209578500b361fRoboErik return null; 252894be6100218126ce6a08bf1f56209578500b361fRoboErik } 252994be6100218126ce6a08bf1f56209578500b361fRoboErik 2530567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private int findRemoteControlClientRecord(Object rcc) { 2531567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown final int count = mRemoteControlClients.size(); 2532567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown for (int i = 0; i < count; i++) { 2533567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown RemoteControlClientRecord record = mRemoteControlClients.get(i); 2534567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (record.getRemoteControlClient() == rcc) { 2535567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown return i; 2536567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2537567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2538567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown return -1; 2539567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2540567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2541567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private void updatePlaybackInfoFromSelectedRoute() { 2542567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (mSelectedRoute != null) { 2543567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mPlaybackInfo.volume = mSelectedRoute.getVolume(); 2544567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mPlaybackInfo.volumeMax = mSelectedRoute.getVolumeMax(); 2545567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mPlaybackInfo.volumeHandling = mSelectedRoute.getVolumeHandling(); 2546567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mPlaybackInfo.playbackStream = mSelectedRoute.getPlaybackStream(); 2547567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mPlaybackInfo.playbackType = mSelectedRoute.getPlaybackType(); 2548567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2549567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown final int count = mRemoteControlClients.size(); 2550567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown for (int i = 0; i < count; i++) { 2551567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown RemoteControlClientRecord record = mRemoteControlClients.get(i); 2552567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown record.updatePlaybackInfo(); 2553567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2554bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik if (mMediaSession != null) { 2555f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik if (mSelectedRoute == getDefaultRoute()) { 2556f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik // Local route 2557f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik mMediaSession.clearVolumeHandling(); 2558f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik } else { 2559f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake @VolumeProviderCompat.ControlType int controlType = 2560f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake VolumeProviderCompat.VOLUME_CONTROL_FIXED; 2561f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik if (mPlaybackInfo.volumeHandling 2562f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE) { 2563f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik controlType = VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE; 2564f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik } 2565f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik mMediaSession.configureVolume(controlType, mPlaybackInfo.volumeMax, 2566f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik mPlaybackInfo.volume); 2567bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2568f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik } 2569f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik } else { 2570f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik if (mMediaSession != null) { 2571f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik mMediaSession.clearVolumeHandling(); 2572bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2573567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2574567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2575567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2576c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final class ProviderCallback extends MediaRouteProvider.Callback { 2577c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 2578c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onDescriptorChanged(MediaRouteProvider provider, 257911417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteProviderDescriptor descriptor) { 2580c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown updateProviderDescriptor(provider, descriptor); 2581c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2582c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2583c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2584bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik private final class MediaSessionRecord { 2585bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik private final MediaSessionCompat mMsCompat; 2586bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 2587f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake private @VolumeProviderCompat.ControlType int mControlType; 2588bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik private int mMaxVolume; 2589bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik private VolumeProviderCompat mVpCompat; 2590bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 2591bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik public MediaSessionRecord(Object mediaSession) { 25925c9469e010106467791b47b0fa83efda84491a21RoboErik mMsCompat = MediaSessionCompat.obtain(mApplicationContext, mediaSession); 2593bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2594bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 2595f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake public void configureVolume(@VolumeProviderCompat.ControlType int controlType, 2596f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake int max, int current) { 2597bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik if (mVpCompat != null && controlType == mControlType && max == mMaxVolume) { 2598bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik // If we haven't changed control type or max just set the 2599bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik // new current volume 2600bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mVpCompat.setCurrentVolume(current); 2601bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } else { 2602bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik // Otherwise create a new provider and update 2603bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mVpCompat = new VolumeProviderCompat(controlType, max, current) { 2604bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik @Override 2605a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik public void onSetVolumeTo(final int volume) { 2606a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik mCallbackHandler.post(new Runnable() { 2607a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik @Override 2608a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik public void run() { 2609a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik if (mSelectedRoute != null) { 2610a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik mSelectedRoute.requestSetVolume(volume); 2611a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik } 2612a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik } 2613a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik }); 2614bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2615bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 2616bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik @Override 2617a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik public void onAdjustVolume(final int direction) { 2618a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik mCallbackHandler.post(new Runnable() { 2619a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik @Override 2620a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik public void run() { 2621a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik if (mSelectedRoute != null) { 2622a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik mSelectedRoute.requestUpdateVolume(direction); 2623a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik } 2624a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik } 2625a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik }); 2626bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2627bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik }; 2628bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mMsCompat.setPlaybackToRemote(mVpCompat); 2629bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2630bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2631bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 2632bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik public void clearVolumeHandling() { 2633bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mMsCompat.setPlaybackToLocal(mPlaybackInfo.playbackStream); 2634bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mVpCompat = null; 2635bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2636bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 263794be6100218126ce6a08bf1f56209578500b361fRoboErik public MediaSessionCompat.Token getToken() { 263894be6100218126ce6a08bf1f56209578500b361fRoboErik return mMsCompat.getSessionToken(); 263994be6100218126ce6a08bf1f56209578500b361fRoboErik } 264094be6100218126ce6a08bf1f56209578500b361fRoboErik 2641bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2642bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 2643567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private final class RemoteControlClientRecord 2644567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown implements RemoteControlClientCompat.VolumeCallback { 2645567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private final RemoteControlClientCompat mRccCompat; 2646567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private boolean mDisconnected; 2647567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2648567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public RemoteControlClientRecord(Object rcc) { 2649567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRccCompat = RemoteControlClientCompat.obtain(mApplicationContext, rcc); 2650567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRccCompat.setVolumeCallback(this); 2651567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown updatePlaybackInfo(); 2652567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2653567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2654567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public Object getRemoteControlClient() { 2655567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown return mRccCompat.getRemoteControlClient(); 2656567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2657567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2658567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void disconnect() { 2659567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mDisconnected = true; 2660567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRccCompat.setVolumeCallback(null); 2661567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2662567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2663567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void updatePlaybackInfo() { 2664567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRccCompat.setPlaybackInfo(mPlaybackInfo); 2665567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2666567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2667567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown @Override 2668567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void onVolumeSetRequest(int volume) { 2669567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (!mDisconnected && mSelectedRoute != null) { 2670567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mSelectedRoute.requestSetVolume(volume); 2671567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2672567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2673567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2674567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown @Override 2675567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void onVolumeUpdateRequest(int direction) { 2676567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (!mDisconnected && mSelectedRoute != null) { 2677567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mSelectedRoute.requestUpdateVolume(direction); 2678567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2679567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2680567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2681567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2682c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final class CallbackHandler extends Handler { 26839fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown private final ArrayList<CallbackRecord> mTempCallbackRecords = 26849fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown new ArrayList<CallbackRecord>(); 2685c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 268611417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final int MSG_TYPE_MASK = 0xff00; 268711417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final int MSG_TYPE_ROUTE = 0x0100; 268811417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final int MSG_TYPE_PROVIDER = 0x0200; 268911417b1cfde8f1749905f2d735623af9214148afJeff Brown 269011417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_ADDED = MSG_TYPE_ROUTE | 1; 269111417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_REMOVED = MSG_TYPE_ROUTE | 2; 269211417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_CHANGED = MSG_TYPE_ROUTE | 3; 269311417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_VOLUME_CHANGED = MSG_TYPE_ROUTE | 4; 269411417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED = MSG_TYPE_ROUTE | 5; 269511417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_SELECTED = MSG_TYPE_ROUTE | 6; 269611417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_UNSELECTED = MSG_TYPE_ROUTE | 7; 269711417b1cfde8f1749905f2d735623af9214148afJeff Brown 269811417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_PROVIDER_ADDED = MSG_TYPE_PROVIDER | 1; 269911417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_PROVIDER_REMOVED = MSG_TYPE_PROVIDER | 2; 270011417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_PROVIDER_CHANGED = MSG_TYPE_PROVIDER | 3; 2701c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2702fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void post(int msg, Object obj) { 2703fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown obtainMessage(msg, obj).sendToTarget(); 2704c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2705c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2706c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 2707c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void handleMessage(Message msg) { 2708c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final int what = msg.what; 2709fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown final Object obj = msg.obj; 2710c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2711c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Synchronize state with the system media router. 2712fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown syncWithSystemProvider(what, obj); 2713c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2714c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Invoke all registered callbacks. 27159fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown // Build a list of callbacks before invoking them in case callbacks 27169fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown // are added or removed during dispatch. 2717c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown try { 27189fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int i = mRouters.size(); --i >= 0; ) { 27199fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown MediaRouter router = mRouters.get(i).get(); 27209fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown if (router == null) { 27219fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouters.remove(i); 27229fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } else { 27239fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mTempCallbackRecords.addAll(router.mCallbackRecords); 2724c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2725c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 27269fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown 27279fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown final int callbackCount = mTempCallbackRecords.size(); 27289fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int i = 0; i < callbackCount; i++) { 27299fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown invokeCallback(mTempCallbackRecords.get(i), what, obj); 27309fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } 2731c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } finally { 27329fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mTempCallbackRecords.clear(); 2733c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2734c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2735c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2736fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private void syncWithSystemProvider(int what, Object obj) { 2737c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown switch (what) { 2738c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_ADDED: 2739fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSystemProvider.onSyncRouteAdded((RouteInfo)obj); 2740c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 2741c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_REMOVED: 2742fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSystemProvider.onSyncRouteRemoved((RouteInfo)obj); 2743c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 2744c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_CHANGED: 2745fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSystemProvider.onSyncRouteChanged((RouteInfo)obj); 2746c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 2747c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_SELECTED: 2748fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSystemProvider.onSyncRouteSelected((RouteInfo)obj); 2749c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 2750c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2751c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2752c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 27539fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown private void invokeCallback(CallbackRecord record, int what, Object obj) { 27549fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown final MediaRouter router = record.mRouter; 275511417b1cfde8f1749905f2d735623af9214148afJeff Brown final MediaRouter.Callback callback = record.mCallback; 275611417b1cfde8f1749905f2d735623af9214148afJeff Brown switch (what & MSG_TYPE_MASK) { 275711417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_TYPE_ROUTE: { 275811417b1cfde8f1749905f2d735623af9214148afJeff Brown final RouteInfo route = (RouteInfo)obj; 275911417b1cfde8f1749905f2d735623af9214148afJeff Brown if (!record.filterRouteEvent(route)) { 276011417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 276111417b1cfde8f1749905f2d735623af9214148afJeff Brown } 276211417b1cfde8f1749905f2d735623af9214148afJeff Brown switch (what) { 276311417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_ADDED: 276411417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteAdded(router, route); 276511417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 276611417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_REMOVED: 276711417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteRemoved(router, route); 276811417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 276911417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_CHANGED: 277011417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteChanged(router, route); 277111417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 277211417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_VOLUME_CHANGED: 277311417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteVolumeChanged(router, route); 277411417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 277511417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED: 277611417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRoutePresentationDisplayChanged(router, route); 277711417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 277811417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_SELECTED: 277911417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteSelected(router, route); 278011417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 278111417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_UNSELECTED: 278211417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteUnselected(router, route); 278311417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 278411417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2785c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 278611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 278711417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_TYPE_PROVIDER: { 278811417b1cfde8f1749905f2d735623af9214148afJeff Brown final ProviderInfo provider = (ProviderInfo)obj; 278911417b1cfde8f1749905f2d735623af9214148afJeff Brown switch (what) { 279011417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_PROVIDER_ADDED: 279111417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onProviderAdded(router, provider); 279211417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 279311417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_PROVIDER_REMOVED: 279411417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onProviderRemoved(router, provider); 279511417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 279611417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_PROVIDER_CHANGED: 279711417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onProviderChanged(router, provider); 279811417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 279911417b1cfde8f1749905f2d735623af9214148afJeff Brown } 280011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2801c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2802c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2803c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2804c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2805c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown} 2806