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 198e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikasimport static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP; 202573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim 21fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brownimport android.app.ActivityManager; 22adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brownimport android.content.ComponentName; 23c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.ContentResolver; 24c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.Context; 25c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.Intent; 26c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.IntentFilter; 2794be6100218126ce6a08bf1f56209578500b361fRoboErikimport android.content.IntentSender; 28fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.pm.PackageManager.NameNotFoundException; 29fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.res.Resources; 30119de6bb3353341cfd465cea9e545abec3762d20Jae Seoimport android.net.Uri; 31c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Bundle; 32c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Handler; 33c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Looper; 34c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Message; 35429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport android.support.annotation.IntDef; 36429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport android.support.annotation.NonNull; 37429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport android.support.annotation.Nullable; 38c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport android.support.annotation.RestrictTo; 39fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brownimport android.support.v4.app.ActivityManagerCompat; 40c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.support.v4.hardware.display.DisplayManagerCompat; 41bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErikimport android.support.v4.media.VolumeProviderCompat; 42bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErikimport android.support.v4.media.session.MediaSessionCompat; 43e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seoimport android.support.v4.util.Pair; 44b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brownimport android.support.v7.media.MediaRouteProvider.ProviderMetadata; 45bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kangimport android.support.v7.media.MediaRouteProvider.RouteController; 46eb51f617d95c9a88a6ba528b1ad756f7bb7e7640Jae Seoimport android.text.TextUtils; 47c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.util.Log; 48c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.view.Display; 49c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 50429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport java.lang.annotation.Retention; 51429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport java.lang.annotation.RetentionPolicy; 529fcedc160282e6620f409ea46bf6728b35d011ddJeff Brownimport java.lang.ref.WeakReference; 53c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.ArrayList; 54c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.Collections; 5538e4efe885198f63fb26b73d79d4636263ba6a5cJae Seoimport java.util.HashMap; 56b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seoimport java.util.HashSet; 57b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seoimport java.util.Iterator; 58c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.List; 59cb63b6ecac9786891514f241dec71695f09d3efbJeff Brownimport java.util.Locale; 6038e4efe885198f63fb26b73d79d4636263ba6a5cJae Seoimport java.util.Map; 61b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seoimport java.util.Set; 62c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 63c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown/** 64c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter allows applications to control the routing of media channels 65c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * and streams from the current device to external speakers and destination devices. 66c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 67c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * A MediaRouter instance is retrieved through {@link #getInstance}. Applications 68c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * can query the media router about the currently selected route and its capabilities 69c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to determine how to send content to the route's destination. Applications can 70c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * also {@link RouteInfo#sendControlRequest send control requests} to the route 71c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to ask the route's destination to perform certain remote control functions 72fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * such as playing media. 73c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 74c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * See also {@link MediaRouteProvider} for information on how an application 75c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * can publish new media routes to the media router. 76c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 77c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The media router API is not thread-safe; all interactions with it must be 78c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * done from the main thread of the process. 79c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 80c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 81c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownpublic final class MediaRouter { 82e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas static final String TAG = "MediaRouter"; 83e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 84c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 8594be6100218126ce6a08bf1f56209578500b361fRoboErik /** 86b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)} 8734f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the reason the route 8834f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * was unselected is unknown. 8994be6100218126ce6a08bf1f56209578500b361fRoboErik */ 9094be6100218126ce6a08bf1f56209578500b361fRoboErik public static final int UNSELECT_REASON_UNKNOWN = 0; 9194be6100218126ce6a08bf1f56209578500b361fRoboErik /** 92b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)} 9334f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the user pressed 9434f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * the disconnect button to disconnect and keep playing. 9594be6100218126ce6a08bf1f56209578500b361fRoboErik * <p> 9694be6100218126ce6a08bf1f56209578500b361fRoboErik * 97377357a8c26c8c54ba8cb876ae775265635a8448Elliot Waite * @see MediaRouteDescriptor#canDisconnectAndKeepPlaying() 9894be6100218126ce6a08bf1f56209578500b361fRoboErik */ 9994be6100218126ce6a08bf1f56209578500b361fRoboErik public static final int UNSELECT_REASON_DISCONNECTED = 1; 10094be6100218126ce6a08bf1f56209578500b361fRoboErik /** 101b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)} 10234f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the user pressed 10334f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * the stop casting button. 10494be6100218126ce6a08bf1f56209578500b361fRoboErik */ 10594be6100218126ce6a08bf1f56209578500b361fRoboErik public static final int UNSELECT_REASON_STOPPED = 2; 10694be6100218126ce6a08bf1f56209578500b361fRoboErik /** 107b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)} 10834f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the user selected 10934f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * a different route. 11094be6100218126ce6a08bf1f56209578500b361fRoboErik */ 11194be6100218126ce6a08bf1f56209578500b361fRoboErik public static final int UNSELECT_REASON_ROUTE_CHANGED = 3; 11294be6100218126ce6a08bf1f56209578500b361fRoboErik 113c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Maintains global media router state for the process. 114c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This field is initialized in MediaRouter.getInstance() before any 115c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // MediaRouter objects are instantiated so it is guaranteed to be 116c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // valid whenever any instance method is invoked. 117c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static GlobalMediaRouter sGlobal; 118c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 119c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Context-bound state of the media router. 120c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final Context mContext; 1219fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown final ArrayList<CallbackRecord> mCallbackRecords = new ArrayList<CallbackRecord>(); 12211417b1cfde8f1749905f2d735623af9214148afJeff Brown 123429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @IntDef(flag = true, 124429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye value = { 125429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye CALLBACK_FLAG_PERFORM_ACTIVE_SCAN, 126429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye CALLBACK_FLAG_REQUEST_DISCOVERY, 127429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye CALLBACK_FLAG_UNFILTERED_EVENTS 128429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye } 129429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye ) 130429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Retention(RetentionPolicy.SOURCE) 131429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye private @interface CallbackFlags {} 132429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye 13311417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 13411417b1cfde8f1749905f2d735623af9214148afJeff Brown * Flag for {@link #addCallback}: Actively scan for routes while this callback 13511417b1cfde8f1749905f2d735623af9214148afJeff Brown * is registered. 13611417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 13711417b1cfde8f1749905f2d735623af9214148afJeff Brown * When this flag is specified, the media router will actively scan for new 13811417b1cfde8f1749905f2d735623af9214148afJeff Brown * routes. Certain routes, such as wifi display routes, may not be discoverable 13911417b1cfde8f1749905f2d735623af9214148afJeff Brown * except when actively scanning. This flag is typically used when the route picker 14011417b1cfde8f1749905f2d735623af9214148afJeff Brown * dialog has been opened by the user to ensure that the route information is 14111417b1cfde8f1749905f2d735623af9214148afJeff Brown * up to date. 14211417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p><p> 14311417b1cfde8f1749905f2d735623af9214148afJeff Brown * Active scanning may consume a significant amount of power and may have intrusive 14411417b1cfde8f1749905f2d735623af9214148afJeff Brown * effects on wireless connectivity. Therefore it is important that active scanning 14511417b1cfde8f1749905f2d735623af9214148afJeff Brown * only be requested when it is actually needed to satisfy a user request to 14611417b1cfde8f1749905f2d735623af9214148afJeff Brown * discover and select a new route. 147f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * </p><p> 148f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * This flag implies {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} but performing 149f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * active scans is much more expensive than a normal discovery request. 15011417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 151f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * 152f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * @see #CALLBACK_FLAG_REQUEST_DISCOVERY 15311417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 154f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1 << 0; 15511417b1cfde8f1749905f2d735623af9214148afJeff Brown 15611417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 15711417b1cfde8f1749905f2d735623af9214148afJeff Brown * Flag for {@link #addCallback}: Do not filter route events. 15811417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 15911417b1cfde8f1749905f2d735623af9214148afJeff Brown * When this flag is specified, the callback will be invoked for events that affect any 1603efa63d3b896244713e84acbb5945562dce41d77Jeff Brown * route even if they do not match the callback's filter. 16111417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 16211417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 16311417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 1 << 1; 16411417b1cfde8f1749905f2d735623af9214148afJeff Brown 16511417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 166fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * Flag for {@link #addCallback}: Request passive route discovery while this 167fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * callback is registered, except on {@link ActivityManager#isLowRamDevice low-RAM devices}. 168f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * <p> 169f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * When this flag is specified, the media router will try to discover routes. 170f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * Although route discovery is intended to be efficient, checking for new routes may 171f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * result in some network activity and could slowly drain the battery. Therefore 172f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * applications should only specify {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} when 173f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * they are running in the foreground and would like to provide the user with the 174f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * option of connecting to new routes. 175f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * </p><p> 176f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * Applications should typically add a callback using this flag in the 177f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * {@link android.app.Activity activity's} {@link android.app.Activity#onStart onStart} 178f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * method and remove it in the {@link android.app.Activity#onStop onStop} method. 179f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * The {@link android.support.v7.app.MediaRouteDiscoveryFragment} fragment may 180f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * also be used for this purpose. 181fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p class="note"> 182fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * On {@link ActivityManager#isLowRamDevice low-RAM devices} this flag 183fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * will be ignored. Refer to 184fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * {@link #addCallback(MediaRouteSelector, Callback, int) addCallback} for details. 185f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * </p> 186f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * 187f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * @see android.support.v7.app.MediaRouteDiscoveryFragment 188f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown */ 189f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 1 << 2; 190f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown 191f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown /** 192fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * Flag for {@link #addCallback}: Request passive route discovery while this 193fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * callback is registered, even on {@link ActivityManager#isLowRamDevice low-RAM devices}. 194fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * <p class="note"> 195fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * This flag has a significant performance impact on low-RAM devices 196fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * since it may cause many media route providers to be started simultaneously. 197fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * It is much better to use {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} instead to avoid 198fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * performing passive discovery on these devices altogether. Refer to 199fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * {@link #addCallback(MediaRouteSelector, Callback, int) addCallback} for details. 200fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p> 201fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * 202fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * @see android.support.v7.app.MediaRouteDiscoveryFragment 203fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown */ 204fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown public static final int CALLBACK_FLAG_FORCE_DISCOVERY = 1 << 3; 205fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown 206fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown /** 20711417b1cfde8f1749905f2d735623af9214148afJeff Brown * Flag for {@link #isRouteAvailable}: Ignore the default route. 20811417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 20911417b1cfde8f1749905f2d735623af9214148afJeff Brown * This flag is used to determine whether a matching non-default route is available. 21011417b1cfde8f1749905f2d735623af9214148afJeff Brown * This constraint may be used to decide whether to offer the route chooser dialog 21111417b1cfde8f1749905f2d735623af9214148afJeff Brown * to the user. There is no point offering the chooser if there are no 21211417b1cfde8f1749905f2d735623af9214148afJeff Brown * non-default choices. 21311417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 21411417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 21511417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1 << 0; 21611417b1cfde8f1749905f2d735623af9214148afJeff Brown 217fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown /** 218fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * Flag for {@link #isRouteAvailable}: Require an actual route to be matched. 219fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * <p> 220fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * If this flag is not set, then {@link #isRouteAvailable} will return true 221fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * if it is possible to discover a matching route even if discovery is not in 222fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * progress or if no matching route has yet been found. This feature is used to 223fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * save resources by removing the need to perform passive route discovery on 224fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * {@link ActivityManager#isLowRamDevice low-RAM devices}. 225fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p> 226fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * If this flag is set, then {@link #isRouteAvailable} will only return true if 227fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * a matching route has actually been discovered. 228fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p> 229fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown */ 230fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown public static final int AVAILABILITY_FLAG_REQUIRE_MATCH = 1 << 1; 231fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown 232c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouter(Context context) { 233c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mContext = context; 234c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 235c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 236c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 2379fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * Gets an instance of the media router service associated with the context. 2389fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * <p> 2399fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * The application is responsible for holding a strong reference to the returned 2409fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * {@link MediaRouter} instance, such as by storing the instance in a field of 2419fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * the {@link android.app.Activity}, to ensure that the media router remains alive 2429fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * as long as the application is using its features. 2439fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * </p><p> 2449fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * In other words, the support library only holds a {@link WeakReference weak reference} 2459fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * to each media router instance. When there are no remaining strong references to the 2469fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * media router instance, all of its callbacks will be removed and route discovery 2479fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * will no longer be performed on its behalf. 2489fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * </p> 2499fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * 2509fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * @return The media router instance for the context. The application must hold 2519fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * a strong reference to this object as long as it is in use. 252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 253429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public static MediaRouter getInstance(@NonNull Context context) { 254c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (context == null) { 255c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("context must not be null"); 256c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 257c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 258c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 259c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (sGlobal == null) { 260c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal = new GlobalMediaRouter(context.getApplicationContext()); 261fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown sGlobal.start(); 262c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 263c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getRouter(context); 264c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 265c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 266c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 267fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets information about the {@link MediaRouter.RouteInfo routes} currently known to 268fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * this media router. 269c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 270c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public List<RouteInfo> getRoutes() { 271c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 272c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getRoutes(); 273c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 274c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 275c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 276fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets information about the {@link MediaRouter.ProviderInfo route providers} 277fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * currently known to this media router. 278fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 279fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public List<ProviderInfo> getProviders() { 280fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 281fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return sGlobal.getProviders(); 282fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 283fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 284fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 285c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the default route for playing media content on the system. 286c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 287c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The system always provides a default route. 288c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 289c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 290c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The default route, which is guaranteed to never be null. 291c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 292429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @NonNull 293c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getDefaultRoute() { 294c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 295c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getDefaultRoute(); 296c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 297c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 298c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 2992573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim * Gets a bluetooth route for playing media content on the system. 3002573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim * 3012573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim * @return A bluetooth route, if exist, otherwise null. 3022573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim */ 3032573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim public RouteInfo getBluetoothRoute() { 3042573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim checkCallingThread(); 3052573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim return sGlobal.getBluetoothRoute(); 3062573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim } 3072573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim 3082573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim /** 309c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the currently selected route. 310c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 311c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The application should examine the route's 312fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * {@link RouteInfo#getControlFilters media control intent filters} to assess the 313c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * capabilities of the route before attempting to use it. 314c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 315c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 316c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <h3>Example</h3> 317c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <pre> 318c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * public boolean playMovie() { 319c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter mediaRouter = MediaRouter.getInstance(context); 320c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute(); 321c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 322c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // First try using the remote playback interface, if supported. 323c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) { 324c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // The route supports remote playback. 325c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // Try to send it the Uri of the movie to play. 326c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Intent intent = new Intent(MediaControlIntent.ACTION_PLAY); 327c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); 328c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * intent.setDataAndType("http://example.com/videos/movie.mp4", "video/mp4"); 329fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * if (route.supportsControlRequest(intent)) { 330fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * route.sendControlRequest(intent, null); 331fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * return true; // sent the request to play the movie 332c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 333c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 334c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 335c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // If remote playback was not possible, then play locally. 336c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * if (route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)) { 337c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // The route supports live video streaming. 338c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // Prepare to play content locally in a window or in a presentation. 339c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * return playMovieInWindow(); 340c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 341c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 342c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // Neither interface is supported, so we can't play the movie to this route. 343c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * return false; 344c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 345c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </pre> 346c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 347c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The selected route, which is guaranteed to never be null. 348c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 349fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see RouteInfo#getControlFilters 350c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#supportsControlCategory 351c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#supportsControlRequest 352c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 353429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @NonNull 354c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getSelectedRoute() { 355c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 356c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getSelectedRoute(); 357c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 358c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 359c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 36028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * Returns the selected route if it matches the specified selector, otherwise 361fa19abbe11335366feb6c885f8d75504ab8fe4f9Insun Kang * selects the default route and returns it. If there is one live audio route 362fa19abbe11335366feb6c885f8d75504ab8fe4f9Insun Kang * (usually Bluetooth A2DP), it will be selected instead of default route. 36328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 36428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @param selector The selector to match. 36528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @return The previously selected route if it matched the selector, otherwise the 36628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * newly selected default route which is guaranteed to never be null. 36728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 36811417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see MediaRouteSelector 36928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @see RouteInfo#matchesSelector 37028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown */ 371429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @NonNull 372429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public RouteInfo updateSelectedRoute(@NonNull MediaRouteSelector selector) { 37328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown if (selector == null) { 37428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown throw new IllegalArgumentException("selector must not be null"); 37528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 37628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown checkCallingThread(); 37728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 37811417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 37911417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "updateSelectedRoute: " + selector); 38011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 38128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown RouteInfo route = sGlobal.getSelectedRoute(); 382fa19abbe11335366feb6c885f8d75504ab8fe4f9Insun Kang if (!route.isDefaultOrBluetooth() && !route.matchesSelector(selector)) { 383fa19abbe11335366feb6c885f8d75504ab8fe4f9Insun Kang route = sGlobal.chooseFallbackRoute(); 38428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown sGlobal.selectRoute(route); 38528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 38628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown return route; 38728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 38828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 38928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 390c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Selects the specified route. 391c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 392c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route to select. 393c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 394429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void selectRoute(@NonNull RouteInfo route) { 395c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == null) { 396c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("route must not be null"); 397c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 398c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 399c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 40011417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 40111417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "selectRoute: " + route); 40211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 403c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal.selectRoute(route); 404c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 405c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 406c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 40794be6100218126ce6a08bf1f56209578500b361fRoboErik * Unselects the current round and selects the default route instead. 40894be6100218126ce6a08bf1f56209578500b361fRoboErik * <p> 40994be6100218126ce6a08bf1f56209578500b361fRoboErik * The reason given must be one of: 41094be6100218126ce6a08bf1f56209578500b361fRoboErik * <ul> 41194be6100218126ce6a08bf1f56209578500b361fRoboErik * <li>{@link MediaRouter#UNSELECT_REASON_UNKNOWN}</li> 41294be6100218126ce6a08bf1f56209578500b361fRoboErik * <li>{@link MediaRouter#UNSELECT_REASON_DISCONNECTED}</li> 41394be6100218126ce6a08bf1f56209578500b361fRoboErik * <li>{@link MediaRouter#UNSELECT_REASON_STOPPED}</li> 41494be6100218126ce6a08bf1f56209578500b361fRoboErik * <li>{@link MediaRouter#UNSELECT_REASON_ROUTE_CHANGED}</li> 41594be6100218126ce6a08bf1f56209578500b361fRoboErik * </ul> 41694be6100218126ce6a08bf1f56209578500b361fRoboErik * 41794be6100218126ce6a08bf1f56209578500b361fRoboErik * @param reason The reason for disconnecting the current route. 41894be6100218126ce6a08bf1f56209578500b361fRoboErik */ 41994be6100218126ce6a08bf1f56209578500b361fRoboErik public void unselect(int reason) { 42094be6100218126ce6a08bf1f56209578500b361fRoboErik if (reason < MediaRouter.UNSELECT_REASON_UNKNOWN || 42194be6100218126ce6a08bf1f56209578500b361fRoboErik reason > MediaRouter.UNSELECT_REASON_ROUTE_CHANGED) { 42294be6100218126ce6a08bf1f56209578500b361fRoboErik throw new IllegalArgumentException("Unsupported reason to unselect route"); 42394be6100218126ce6a08bf1f56209578500b361fRoboErik } 42494be6100218126ce6a08bf1f56209578500b361fRoboErik checkCallingThread(); 42594be6100218126ce6a08bf1f56209578500b361fRoboErik 426beaaa68430d0ad0fcf686e5d4694536f61ee79c1Jae Seo // Choose the fallback route if it's not already selected. 427beaaa68430d0ad0fcf686e5d4694536f61ee79c1Jae Seo // Otherwise, select the default route. 428beaaa68430d0ad0fcf686e5d4694536f61ee79c1Jae Seo RouteInfo fallbackRoute = sGlobal.chooseFallbackRoute(); 429beaaa68430d0ad0fcf686e5d4694536f61ee79c1Jae Seo if (sGlobal.getSelectedRoute() != fallbackRoute) { 430beaaa68430d0ad0fcf686e5d4694536f61ee79c1Jae Seo sGlobal.selectRoute(fallbackRoute, reason); 431beaaa68430d0ad0fcf686e5d4694536f61ee79c1Jae Seo } else { 432beaaa68430d0ad0fcf686e5d4694536f61ee79c1Jae Seo sGlobal.selectRoute(sGlobal.getDefaultRoute(), reason); 433beaaa68430d0ad0fcf686e5d4694536f61ee79c1Jae Seo } 43494be6100218126ce6a08bf1f56209578500b361fRoboErik } 43594be6100218126ce6a08bf1f56209578500b361fRoboErik 43694be6100218126ce6a08bf1f56209578500b361fRoboErik /** 437d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown * Returns true if there is a route that matches the specified selector. 43811417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 43994be6100218126ce6a08bf1f56209578500b361fRoboErik * This method returns true if there are any available routes that match the 44094be6100218126ce6a08bf1f56209578500b361fRoboErik * selector regardless of whether they are enabled or disabled. If the 441d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} flag is specified, then 442d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown * the method will only consider non-default routes. 44394be6100218126ce6a08bf1f56209578500b361fRoboErik * </p> 44494be6100218126ce6a08bf1f56209578500b361fRoboErik * <p class="note"> 445fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * On {@link ActivityManager#isLowRamDevice low-RAM devices} this method 446fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * will return true if it is possible to discover a matching route even if 447fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * discovery is not in progress or if no matching route has yet been found. 448fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * Use {@link #AVAILABILITY_FLAG_REQUIRE_MATCH} to require an actual match. 44911417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 450c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 45111417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param selector The selector to match. 45294be6100218126ce6a08bf1f56209578500b361fRoboErik * @param flags Flags to control the determination of whether a route may be 45394be6100218126ce6a08bf1f56209578500b361fRoboErik * available. May be zero or some combination of 45494be6100218126ce6a08bf1f56209578500b361fRoboErik * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} and 45594be6100218126ce6a08bf1f56209578500b361fRoboErik * {@link #AVAILABILITY_FLAG_REQUIRE_MATCH}. 45611417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if a matching route may be available. 457c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 458429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public boolean isRouteAvailable(@NonNull MediaRouteSelector selector, int flags) { 45911417b1cfde8f1749905f2d735623af9214148afJeff Brown if (selector == null) { 46011417b1cfde8f1749905f2d735623af9214148afJeff Brown throw new IllegalArgumentException("selector must not be null"); 461c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 462c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 463c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 46411417b1cfde8f1749905f2d735623af9214148afJeff Brown return sGlobal.isRouteAvailable(selector, flags); 465c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 466c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 467c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 46811417b1cfde8f1749905f2d735623af9214148afJeff Brown * Registers a callback to discover routes that match the selector and to receive 46911417b1cfde8f1749905f2d735623af9214148afJeff Brown * events when they change. 47011417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 47111417b1cfde8f1749905f2d735623af9214148afJeff Brown * This is a convenience method that has the same effect as calling 47211417b1cfde8f1749905f2d735623af9214148afJeff Brown * {@link #addCallback(MediaRouteSelector, Callback, int)} without flags. 47311417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 474c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 47511417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param selector A route selector that indicates the kinds of routes that the 47611417b1cfde8f1749905f2d735623af9214148afJeff Brown * callback would like to discover. 47711417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param callback The callback to add. 47811417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see #removeCallback 479c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 48011417b1cfde8f1749905f2d735623af9214148afJeff Brown public void addCallback(MediaRouteSelector selector, Callback callback) { 48111417b1cfde8f1749905f2d735623af9214148afJeff Brown addCallback(selector, callback, 0); 482c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 483c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 484c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 48511417b1cfde8f1749905f2d735623af9214148afJeff Brown * Registers a callback to discover routes that match the selector and to receive 48611417b1cfde8f1749905f2d735623af9214148afJeff Brown * events when they change. 48728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * <p> 48811417b1cfde8f1749905f2d735623af9214148afJeff Brown * The selector describes the kinds of routes that the application wants to 48911417b1cfde8f1749905f2d735623af9214148afJeff Brown * discover. For example, if the application wants to use 49011417b1cfde8f1749905f2d735623af9214148afJeff Brown * live audio routes then it should include the 49111417b1cfde8f1749905f2d735623af9214148afJeff Brown * {@link MediaControlIntent#CATEGORY_LIVE_AUDIO live audio media control intent category} 49211417b1cfde8f1749905f2d735623af9214148afJeff Brown * in its selector when it adds a callback to the media router. 49311417b1cfde8f1749905f2d735623af9214148afJeff Brown * The selector may include any number of categories. 49428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * </p><p> 49511417b1cfde8f1749905f2d735623af9214148afJeff Brown * If the callback has already been registered, then the selector is added to 49611417b1cfde8f1749905f2d735623af9214148afJeff Brown * the set of selectors being monitored by the callback. 49711417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p><p> 49811417b1cfde8f1749905f2d735623af9214148afJeff Brown * By default, the callback will only be invoked for events that affect routes 49911417b1cfde8f1749905f2d735623af9214148afJeff Brown * that match the specified selector. Event filtering may be disabled by specifying 50011417b1cfde8f1749905f2d735623af9214148afJeff Brown * the {@link #CALLBACK_FLAG_UNFILTERED_EVENTS} flag when the callback is registered. 501fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p> 502fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * Applications should use the {@link #isRouteAvailable} method to determine 503fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * whether is it possible to discover a route with the desired capabilities 504fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * and therefore whether the media route button should be shown to the user. 505fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p> 506fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * The {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} flag should be used while the application 507fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * is in the foreground to request that passive discovery be performed if there are 508fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * sufficient resources to allow continuous passive discovery. 509fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * On {@link ActivityManager#isLowRamDevice low-RAM devices} this flag will be 510fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * ignored to conserve resources. 511fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p> 512fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * The {@link #CALLBACK_FLAG_FORCE_DISCOVERY} flag should be used when 513fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * passive discovery absolutely must be performed, even on low-RAM devices. 514fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * This flag has a significant performance impact on low-RAM devices 515fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * since it may cause many media route providers to be started simultaneously. 516fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * It is much better to use {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} instead to avoid 517fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * performing passive discovery on these devices altogether. 518fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p> 519fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * The {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} flag should be used when the 520fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * media route chooser dialog is showing to confirm the presence of available 521fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * routes that the user may connect to. This flag may use substantially more 522fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * power. 52328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * </p> 52428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 52528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * <h3>Example</h3> 52628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * <pre> 52711417b1cfde8f1749905f2d735623af9214148afJeff Brown * public class MyActivity extends Activity { 52811417b1cfde8f1749905f2d735623af9214148afJeff Brown * private MediaRouter mRouter; 52911417b1cfde8f1749905f2d735623af9214148afJeff Brown * private MediaRouter.Callback mCallback; 53011417b1cfde8f1749905f2d735623af9214148afJeff Brown * private MediaRouteSelector mSelector; 53128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 53211417b1cfde8f1749905f2d735623af9214148afJeff Brown * protected void onCreate(Bundle savedInstanceState) { 53311417b1cfde8f1749905f2d735623af9214148afJeff Brown * super.onCreate(savedInstanceState); 53411417b1cfde8f1749905f2d735623af9214148afJeff Brown * 53511417b1cfde8f1749905f2d735623af9214148afJeff Brown * mRouter = Mediarouter.getInstance(this); 53611417b1cfde8f1749905f2d735623af9214148afJeff Brown * mCallback = new MyCallback(); 53711417b1cfde8f1749905f2d735623af9214148afJeff Brown * mSelector = new MediaRouteSelector.Builder() 53811417b1cfde8f1749905f2d735623af9214148afJeff Brown * .addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO) 53911417b1cfde8f1749905f2d735623af9214148afJeff Brown * .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK) 54011417b1cfde8f1749905f2d735623af9214148afJeff Brown * .build(); 54128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * } 54228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 543f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * // Add the callback on start to tell the media router what kinds of routes 54411417b1cfde8f1749905f2d735623af9214148afJeff Brown * // the application is interested in so that it can try to discover suitable ones. 545f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * public void onStart() { 546f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * super.onStart(); 54711417b1cfde8f1749905f2d735623af9214148afJeff Brown * 548f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * mediaRouter.addCallback(mSelector, mCallback, 549f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY); 55011417b1cfde8f1749905f2d735623af9214148afJeff Brown * 55111417b1cfde8f1749905f2d735623af9214148afJeff Brown * MediaRouter.RouteInfo route = mediaRouter.updateSelectedRoute(mSelector); 55211417b1cfde8f1749905f2d735623af9214148afJeff Brown * // do something with the route... 55311417b1cfde8f1749905f2d735623af9214148afJeff Brown * } 55411417b1cfde8f1749905f2d735623af9214148afJeff Brown * 555f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * // Remove the selector on stop to tell the media router that it no longer 55611417b1cfde8f1749905f2d735623af9214148afJeff Brown * // needs to invest effort trying to discover routes of these kinds for now. 557f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * public void onStop() { 558f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * super.onStop(); 55911417b1cfde8f1749905f2d735623af9214148afJeff Brown * 56011417b1cfde8f1749905f2d735623af9214148afJeff Brown * mediaRouter.removeCallback(mCallback); 56111417b1cfde8f1749905f2d735623af9214148afJeff Brown * } 56211417b1cfde8f1749905f2d735623af9214148afJeff Brown * 56311417b1cfde8f1749905f2d735623af9214148afJeff Brown * private final class MyCallback extends MediaRouter.Callback { 56411417b1cfde8f1749905f2d735623af9214148afJeff Brown * // Implement callback methods as needed. 56528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * } 56628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * } 56728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * </pre> 56828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 56911417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param selector A route selector that indicates the kinds of routes that the 57011417b1cfde8f1749905f2d735623af9214148afJeff Brown * callback would like to discover. 57111417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param callback The callback to add. 57211417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param flags Flags to control the behavior of the callback. 573f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * May be zero or a combination of {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and 57411417b1cfde8f1749905f2d735623af9214148afJeff Brown * {@link #CALLBACK_FLAG_UNFILTERED_EVENTS}. 57511417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see #removeCallback 57628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown */ 577429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void addCallback(@NonNull MediaRouteSelector selector, @NonNull Callback callback, 578429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @CallbackFlags int flags) { 57928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown if (selector == null) { 58028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown throw new IllegalArgumentException("selector must not be null"); 58128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 58211417b1cfde8f1749905f2d735623af9214148afJeff Brown if (callback == null) { 58311417b1cfde8f1749905f2d735623af9214148afJeff Brown throw new IllegalArgumentException("callback must not be null"); 58411417b1cfde8f1749905f2d735623af9214148afJeff Brown } 58528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown checkCallingThread(); 58628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 58711417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 58811417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "addCallback: selector=" + selector 58911417b1cfde8f1749905f2d735623af9214148afJeff Brown + ", callback=" + callback + ", flags=" + Integer.toHexString(flags)); 59011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 59111417b1cfde8f1749905f2d735623af9214148afJeff Brown 59211417b1cfde8f1749905f2d735623af9214148afJeff Brown CallbackRecord record; 59311417b1cfde8f1749905f2d735623af9214148afJeff Brown int index = findCallbackRecord(callback); 59411417b1cfde8f1749905f2d735623af9214148afJeff Brown if (index < 0) { 5959fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown record = new CallbackRecord(this, callback); 59611417b1cfde8f1749905f2d735623af9214148afJeff Brown mCallbackRecords.add(record); 59711417b1cfde8f1749905f2d735623af9214148afJeff Brown } else { 59811417b1cfde8f1749905f2d735623af9214148afJeff Brown record = mCallbackRecords.get(index); 59911417b1cfde8f1749905f2d735623af9214148afJeff Brown } 60011417b1cfde8f1749905f2d735623af9214148afJeff Brown boolean updateNeeded = false; 60111417b1cfde8f1749905f2d735623af9214148afJeff Brown if ((flags & ~record.mFlags) != 0) { 60211417b1cfde8f1749905f2d735623af9214148afJeff Brown record.mFlags |= flags; 60311417b1cfde8f1749905f2d735623af9214148afJeff Brown updateNeeded = true; 60411417b1cfde8f1749905f2d735623af9214148afJeff Brown } 60511417b1cfde8f1749905f2d735623af9214148afJeff Brown if (!record.mSelector.contains(selector)) { 60611417b1cfde8f1749905f2d735623af9214148afJeff Brown record.mSelector = new MediaRouteSelector.Builder(record.mSelector) 60711417b1cfde8f1749905f2d735623af9214148afJeff Brown .addSelector(selector) 60811417b1cfde8f1749905f2d735623af9214148afJeff Brown .build(); 60911417b1cfde8f1749905f2d735623af9214148afJeff Brown updateNeeded = true; 61011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 61111417b1cfde8f1749905f2d735623af9214148afJeff Brown if (updateNeeded) { 61211417b1cfde8f1749905f2d735623af9214148afJeff Brown sGlobal.updateDiscoveryRequest(); 61328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 61428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 61528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 61628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 61711417b1cfde8f1749905f2d735623af9214148afJeff Brown * Removes the specified callback. It will no longer receive events about 61811417b1cfde8f1749905f2d735623af9214148afJeff Brown * changes to media routes. 61928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 62011417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param callback The callback to remove. 62111417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see #addCallback 62228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown */ 623429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void removeCallback(@NonNull Callback callback) { 62411417b1cfde8f1749905f2d735623af9214148afJeff Brown if (callback == null) { 62511417b1cfde8f1749905f2d735623af9214148afJeff Brown throw new IllegalArgumentException("callback must not be null"); 62628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 62728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown checkCallingThread(); 62828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 62911417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 63011417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "removeCallback: callback=" + callback); 63111417b1cfde8f1749905f2d735623af9214148afJeff Brown } 63211417b1cfde8f1749905f2d735623af9214148afJeff Brown 63311417b1cfde8f1749905f2d735623af9214148afJeff Brown int index = findCallbackRecord(callback); 63411417b1cfde8f1749905f2d735623af9214148afJeff Brown if (index >= 0) { 63511417b1cfde8f1749905f2d735623af9214148afJeff Brown mCallbackRecords.remove(index); 63611417b1cfde8f1749905f2d735623af9214148afJeff Brown sGlobal.updateDiscoveryRequest(); 63728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 63828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 63928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 64011417b1cfde8f1749905f2d735623af9214148afJeff Brown private int findCallbackRecord(Callback callback) { 64111417b1cfde8f1749905f2d735623af9214148afJeff Brown final int count = mCallbackRecords.size(); 64211417b1cfde8f1749905f2d735623af9214148afJeff Brown for (int i = 0; i < count; i++) { 64311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mCallbackRecords.get(i).mCallback == callback) { 64411417b1cfde8f1749905f2d735623af9214148afJeff Brown return i; 64511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 64611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 64711417b1cfde8f1749905f2d735623af9214148afJeff Brown return -1; 64811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 64911417b1cfde8f1749905f2d735623af9214148afJeff Brown 65028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 6519942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * Registers a media route provider within this application process. 6529942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * <p> 6539942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * The provider will be added to the list of providers that all {@link MediaRouter} 6549942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * instances within this process can use to discover routes. 6559942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * </p> 656c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 657fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param providerInstance The media route provider instance to add. 658c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 659c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaRouteProvider 66028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @see #removeCallback 661c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 662429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void addProvider(@NonNull MediaRouteProvider providerInstance) { 663fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (providerInstance == null) { 664fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown throw new IllegalArgumentException("providerInstance must not be null"); 665c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 666c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 667c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 66811417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 66911417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "addProvider: " + providerInstance); 67011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 671fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown sGlobal.addProvider(providerInstance); 672c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 673c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 674c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 6759942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * Unregisters a media route provider within this application process. 6769942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * <p> 6779942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * The provider will be removed from the list of providers that all {@link MediaRouter} 6789942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * instances within this process can use to discover routes. 6799942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * </p> 680c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 681fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param providerInstance The media route provider instance to remove. 682c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 683c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaRouteProvider 68428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @see #addCallback 685c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 686429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void removeProvider(@NonNull MediaRouteProvider providerInstance) { 687fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (providerInstance == null) { 688fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown throw new IllegalArgumentException("providerInstance must not be null"); 689c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 690c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 691c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 69211417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 69311417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "removeProvider: " + providerInstance); 69428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 69511417b1cfde8f1749905f2d735623af9214148afJeff Brown sGlobal.removeProvider(providerInstance); 69628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 69728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 69828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 699567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * Adds a remote control client to enable remote control of the volume 700567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * of the selected route. 701567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * <p> 702567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * The remote control client must have previously been registered with 703567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * the audio manager using the {@link android.media.AudioManager#registerRemoteControlClient 704567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * AudioManager.registerRemoteControlClient} method. 705567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * </p> 706567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * 707567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * @param remoteControlClient The {@link android.media.RemoteControlClient} to register. 708567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown */ 709429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void addRemoteControlClient(@NonNull Object remoteControlClient) { 710567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (remoteControlClient == null) { 711567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown throw new IllegalArgumentException("remoteControlClient must not be null"); 712567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 713567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown checkCallingThread(); 714567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 715567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (DEBUG) { 716567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown Log.d(TAG, "addRemoteControlClient: " + remoteControlClient); 717567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 718567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown sGlobal.addRemoteControlClient(remoteControlClient); 719567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 720567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 721567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown /** 722567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * Removes a remote control client. 723567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * 724bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * @param remoteControlClient The {@link android.media.RemoteControlClient} 725bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * to unregister. 726567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown */ 727429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void removeRemoteControlClient(@NonNull Object remoteControlClient) { 728567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (remoteControlClient == null) { 729567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown throw new IllegalArgumentException("remoteControlClient must not be null"); 730567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 731567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 732567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (DEBUG) { 733567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown Log.d(TAG, "removeRemoteControlClient: " + remoteControlClient); 734567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 735567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown sGlobal.removeRemoteControlClient(remoteControlClient); 736567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 737567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 738567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown /** 739bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * Sets the media session to enable remote control of the volume of the 740bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * selected route. This should be used instead of 741bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * {@link #addRemoteControlClient} when using media sessions. Set the 742bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * session to null to clear it. 743bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * 744bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * @param mediaSession The {@link android.media.session.MediaSession} to 745bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * use. 746bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik */ 747bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik public void setMediaSession(Object mediaSession) { 748bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik if (DEBUG) { 749bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik Log.d(TAG, "addMediaSession: " + mediaSession); 750bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 751bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik sGlobal.setMediaSession(mediaSession); 752bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 753bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 754bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik /** 75594be6100218126ce6a08bf1f56209578500b361fRoboErik * Sets a compat media session to enable remote control of the volume of the 75694be6100218126ce6a08bf1f56209578500b361fRoboErik * selected route. This should be used instead of 75794be6100218126ce6a08bf1f56209578500b361fRoboErik * {@link #addRemoteControlClient} when using {@link MediaSessionCompat}. 75894be6100218126ce6a08bf1f56209578500b361fRoboErik * Set the session to null to clear it. 75994be6100218126ce6a08bf1f56209578500b361fRoboErik * 76094be6100218126ce6a08bf1f56209578500b361fRoboErik * @param mediaSession 76194be6100218126ce6a08bf1f56209578500b361fRoboErik */ 76294be6100218126ce6a08bf1f56209578500b361fRoboErik public void setMediaSessionCompat(MediaSessionCompat mediaSession) { 76394be6100218126ce6a08bf1f56209578500b361fRoboErik if (DEBUG) { 76494be6100218126ce6a08bf1f56209578500b361fRoboErik Log.d(TAG, "addMediaSessionCompat: " + mediaSession); 76594be6100218126ce6a08bf1f56209578500b361fRoboErik } 7665c9469e010106467791b47b0fa83efda84491a21RoboErik sGlobal.setMediaSessionCompat(mediaSession); 76794be6100218126ce6a08bf1f56209578500b361fRoboErik } 76894be6100218126ce6a08bf1f56209578500b361fRoboErik 76994be6100218126ce6a08bf1f56209578500b361fRoboErik public MediaSessionCompat.Token getMediaSessionToken() { 77094be6100218126ce6a08bf1f56209578500b361fRoboErik return sGlobal.getMediaSessionToken(); 77194be6100218126ce6a08bf1f56209578500b361fRoboErik } 77294be6100218126ce6a08bf1f56209578500b361fRoboErik 77394be6100218126ce6a08bf1f56209578500b361fRoboErik /** 774c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Ensures that calls into the media router are on the correct thread. 775c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * It pays to be a little paranoid when global state invariants are at risk. 776c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 777c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static void checkCallingThread() { 778c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (Looper.myLooper() != Looper.getMainLooper()) { 779c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalStateException("The media router service must only be " 780c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + "accessed on the application's main thread."); 781c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 782c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 783c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 784c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static <T> boolean equal(T a, T b) { 785c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return a == b || (a != null && b != null && a.equals(b)); 786c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 787c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 788c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 789c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Provides information about a media route. 790c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 791fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Each media route has a list of {@link MediaControlIntent media control} 792fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * {@link #getControlFilters intent filters} that describe the capabilities of the 793c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * route and the manner in which it is used and controlled. 794c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 795c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 796e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo public static class RouteInfo { 797fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private final ProviderInfo mProvider; 798c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final String mDescriptorId; 799cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown private final String mUniqueId; 800c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private String mName; 801d63957d28aaabcec588b8cde12eac16414783aebJeff Brown private String mDescription; 802119de6bb3353341cfd465cea9e545abec3762d20Jae Seo private Uri mIconUri; 803c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private boolean mEnabled; 80411417b1cfde8f1749905f2d735623af9214148afJeff Brown private boolean mConnecting; 8053766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo private int mConnectionState; 80694be6100218126ce6a08bf1f56209578500b361fRoboErik private boolean mCanDisconnect; 807e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo private final ArrayList<IntentFilter> mControlFilters = new ArrayList<>(); 808c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mPlaybackType; 809c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mPlaybackStream; 810893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo private int mDeviceType; 811c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mVolumeHandling; 812c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mVolume; 813c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mVolumeMax; 814c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private Display mPresentationDisplay; 81577367b4a1871417198d0399d9ad074314c758567Jae Seo private int mPresentationDisplayId = PRESENTATION_DISPLAY_ID_NONE; 816c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private Bundle mExtras; 81794be6100218126ce6a08bf1f56209578500b361fRoboErik private IntentSender mSettingsIntent; 818e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo MediaRouteDescriptor mDescriptor; 819c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 8203766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo @IntDef({CONNECTION_STATE_DISCONNECTED, CONNECTION_STATE_CONNECTING, 8213766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo CONNECTION_STATE_CONNECTED}) 8223766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo @Retention(RetentionPolicy.SOURCE) 8233766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo private @interface ConnectionState {} 8243766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo 8253766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo /** 8263766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * The default connection state indicating the route is disconnected. 8273766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * 8283766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * @see #getConnectionState 8293766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo */ 8303766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo public static final int CONNECTION_STATE_DISCONNECTED = 0; 8313766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo 8323766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo /** 8333766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * A connection state indicating the route is in the process of connecting and is not yet 8343766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * ready for use. 8353766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * 8363766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * @see #getConnectionState 8373766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo */ 8383766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo public static final int CONNECTION_STATE_CONNECTING = 1; 8393766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo 8403766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo /** 8413766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * A connection state indicating the route is connected. 8423766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * 8433766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * @see #getConnectionState 8443766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo */ 8453766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo public static final int CONNECTION_STATE_CONNECTED = 2; 8463766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo 847429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @IntDef({PLAYBACK_TYPE_LOCAL,PLAYBACK_TYPE_REMOTE}) 848429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Retention(RetentionPolicy.SOURCE) 849429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye private @interface PlaybackType {} 850429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye 851c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 852c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The default playback type, "local", indicating the presentation of the media 853c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * is happening on the same device (e.g. a phone, a tablet) as where it is 854c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * controlled from. 855c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 856c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getPlaybackType 857c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 858c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_TYPE_LOCAL = 0; 859c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 860c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 861c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * A playback type indicating the presentation of the media is happening on 862c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * a different device (i.e. the remote device) than where it is controlled from. 863c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 864c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getPlaybackType 865c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 866c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_TYPE_REMOTE = 1; 867c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 868893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo @IntDef({DEVICE_TYPE_UNKNOWN, DEVICE_TYPE_TV, DEVICE_TYPE_SPEAKER, DEVICE_TYPE_BLUETOOTH}) 869893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo @Retention(RetentionPolicy.SOURCE) 870893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo private @interface DeviceType {} 871893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo 872893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo /** 873893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * The default receiver device type of the route indicating the type is unknown. 874893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * 875893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * @see #getDeviceType 876893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * @hide 877893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo */ 8788e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas @RestrictTo(LIBRARY_GROUP) 879893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo public static final int DEVICE_TYPE_UNKNOWN = 0; 880893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo 881893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo /** 882893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * A receiver device type of the route indicating the presentation of the media is happening 883cc16fc04c849fc0068fb1b033917ebd4d6db31f2Insun Kang * on a TV. 884893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * 885893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * @see #getDeviceType 886893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo */ 887cc16fc04c849fc0068fb1b033917ebd4d6db31f2Insun Kang public static final int DEVICE_TYPE_TV = 1; 888893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo 889893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo /** 890893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * A receiver device type of the route indicating the presentation of the media is happening 891cc16fc04c849fc0068fb1b033917ebd4d6db31f2Insun Kang * on a speaker. 892893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * 893893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * @see #getDeviceType 894893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo */ 895cc16fc04c849fc0068fb1b033917ebd4d6db31f2Insun Kang public static final int DEVICE_TYPE_SPEAKER = 2; 896893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo 897893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo /** 898893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * A receiver device type of the route indicating the presentation of the media is happening 899cc16fc04c849fc0068fb1b033917ebd4d6db31f2Insun Kang * on a bluetooth device such as a bluetooth speaker. 900893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * 901893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * @see #getDeviceType 902cc16fc04c849fc0068fb1b033917ebd4d6db31f2Insun Kang * @hide 903893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo */ 9048e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas @RestrictTo(LIBRARY_GROUP) 905cc16fc04c849fc0068fb1b033917ebd4d6db31f2Insun Kang public static final int DEVICE_TYPE_BLUETOOTH = 3; 906893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo 907429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @IntDef({PLAYBACK_VOLUME_FIXED,PLAYBACK_VOLUME_VARIABLE}) 908429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Retention(RetentionPolicy.SOURCE) 909429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye private @interface PlaybackVolume {} 910429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye 911c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 912c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Playback information indicating the playback volume is fixed, i.e. it cannot be 913c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * controlled from this object. An example of fixed playback volume is a remote player, 914c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather 915c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * than attenuate at the source. 916c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 917c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getVolumeHandling 918c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 919c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_VOLUME_FIXED = 0; 920c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 921c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 922c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Playback information indicating the playback volume is variable and can be controlled 923c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * from this object. 924c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 925c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getVolumeHandling 926c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 927c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_VOLUME_VARIABLE = 1; 928c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 92977367b4a1871417198d0399d9ad074314c758567Jae Seo /** 93077367b4a1871417198d0399d9ad074314c758567Jae Seo * The default presentation display id indicating no presentation display is associated 93177367b4a1871417198d0399d9ad074314c758567Jae Seo * with the route. 93277367b4a1871417198d0399d9ad074314c758567Jae Seo * @hide 93377367b4a1871417198d0399d9ad074314c758567Jae Seo */ 9348e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas @RestrictTo(LIBRARY_GROUP) 93577367b4a1871417198d0399d9ad074314c758567Jae Seo public static final int PRESENTATION_DISPLAY_ID_NONE = -1; 93677367b4a1871417198d0399d9ad074314c758567Jae Seo 937c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static final int CHANGE_GENERAL = 1 << 0; 938c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static final int CHANGE_VOLUME = 1 << 1; 939c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static final int CHANGE_PRESENTATION_DISPLAY = 1 << 2; 940c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 941eb51f617d95c9a88a6ba528b1ad756f7bb7e7640Jae Seo // Should match to SystemMediaRouteProvider.PACKAGE_NAME. 942eb51f617d95c9a88a6ba528b1ad756f7bb7e7640Jae Seo static final String SYSTEM_MEDIA_ROUTE_PROVIDER_PACKAGE_NAME = "android"; 943eb51f617d95c9a88a6ba528b1ad756f7bb7e7640Jae Seo 944cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown RouteInfo(ProviderInfo provider, String descriptorId, String uniqueId) { 945fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mProvider = provider; 946c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDescriptorId = descriptorId; 947cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown mUniqueId = uniqueId; 948c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 949c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 950fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 951fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets information about the provider of this media route. 952fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 953fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public ProviderInfo getProvider() { 954fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mProvider; 955c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 956c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 957c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 958cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * Gets the unique id of the route. 959cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * <p> 960cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * The route unique id functions as a stable identifier by which the route is known. 961cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * For example, an application can use this id as a token to remember the 962cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * selected route across restarts or to communicate its identity to a service. 963cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * </p> 964cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * 965cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * @return The unique id of the route, never null. 966cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown */ 967429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @NonNull 968cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown public String getId() { 969cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return mUniqueId; 970cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 971cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown 972cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown /** 973d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * Gets the user-visible name of the route. 974d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * <p> 975d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * The route name identifies the destination represented by the route. 976d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * It may be a user-supplied name, an alias, or device serial number. 977d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * </p> 978c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 979d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * @return The user-visible name of a media route. This is the string presented 980c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to users who may select this as the active route. 981c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 982c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public String getName() { 983c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mName; 984c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 985c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 986c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 987d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * Gets the user-visible description of the route. 988d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * <p> 989d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * The route description describes the kind of destination represented by the route. 990d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * It may be a user-supplied string, a model number or brand of device. 991d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * </p> 992c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 993d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * @return The description of the route, or null if none. 994c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 995429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Nullable 996d63957d28aaabcec588b8cde12eac16414783aebJeff Brown public String getDescription() { 997d63957d28aaabcec588b8cde12eac16414783aebJeff Brown return mDescription; 998c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 999c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1000c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1001119de6bb3353341cfd465cea9e545abec3762d20Jae Seo * Gets the URI of the icon representing this route. 1002119de6bb3353341cfd465cea9e545abec3762d20Jae Seo * <p> 1003119de6bb3353341cfd465cea9e545abec3762d20Jae Seo * This icon will be used in picker UIs if available. 1004119de6bb3353341cfd465cea9e545abec3762d20Jae Seo * </p> 1005119de6bb3353341cfd465cea9e545abec3762d20Jae Seo * 1006119de6bb3353341cfd465cea9e545abec3762d20Jae Seo * @return The URI of the icon representing this route, or null if none. 1007119de6bb3353341cfd465cea9e545abec3762d20Jae Seo */ 1008119de6bb3353341cfd465cea9e545abec3762d20Jae Seo public Uri getIconUri() { 1009119de6bb3353341cfd465cea9e545abec3762d20Jae Seo return mIconUri; 1010119de6bb3353341cfd465cea9e545abec3762d20Jae Seo } 1011119de6bb3353341cfd465cea9e545abec3762d20Jae Seo 1012119de6bb3353341cfd465cea9e545abec3762d20Jae Seo /** 1013c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Returns true if this route is enabled and may be selected. 1014c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 101511417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if this route is enabled. 1016c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1017c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public boolean isEnabled() { 1018c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mEnabled; 1019c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1020c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1021c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 102211417b1cfde8f1749905f2d735623af9214148afJeff Brown * Returns true if the route is in the process of connecting and is not 102311417b1cfde8f1749905f2d735623af9214148afJeff Brown * yet ready for use. 102411417b1cfde8f1749905f2d735623af9214148afJeff Brown * 102511417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if this route is in the process of connecting. 102611417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 102711417b1cfde8f1749905f2d735623af9214148afJeff Brown public boolean isConnecting() { 102811417b1cfde8f1749905f2d735623af9214148afJeff Brown return mConnecting; 102911417b1cfde8f1749905f2d735623af9214148afJeff Brown } 103011417b1cfde8f1749905f2d735623af9214148afJeff Brown 103111417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 10323766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * Gets the connection state of the route. 10333766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * 10343766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * @return The connection state of this route: {@link #CONNECTION_STATE_DISCONNECTED}, 10353766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo * {@link #CONNECTION_STATE_CONNECTING}, or {@link #CONNECTION_STATE_CONNECTED}. 10363766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo */ 10373766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo @ConnectionState 10383766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo public int getConnectionState() { 10393766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo return mConnectionState; 10403766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo } 10413766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo 10423766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo /** 1043fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Returns true if this route is currently selected. 1044c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 104511417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if this route is currently selected. 1046fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 1047fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see MediaRouter#getSelectedRoute 1048fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1049fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public boolean isSelected() { 1050fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1051fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return sGlobal.getSelectedRoute() == this; 1052fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1053fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1054fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1055fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Returns true if this route is the default route. 1056fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 105711417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if this route is the default route. 1058fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 1059fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see MediaRouter#getDefaultRoute 1060fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1061fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public boolean isDefault() { 1062fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1063fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return sGlobal.getDefaultRoute() == this; 1064fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1065fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1066fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 10672573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim * Returns true if this route is a bluetooth route. 10682573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim * 10692573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim * @return True if this route is a bluetooth route. 10702573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim * 10712573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim * @see MediaRouter#getBluetoothRoute 10722573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim */ 10732573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim public boolean isBluetooth() { 10742573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim checkCallingThread(); 10752573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim return sGlobal.getBluetoothRoute() == this; 10762573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim } 10772573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim 10782573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim /** 10792573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim * Returns true if this route is the default route and the device speaker. 10802573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim * 10812573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim * @return True if this route is the default route and the device speaker. 10822573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim */ 10832573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim public boolean isDeviceSpeaker() { 10842573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim int defaultAudioRouteNameResourceId = Resources.getSystem().getIdentifier( 10852573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim "default_audio_route_name", "string", "android"); 10862573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim return isDefault() 10872573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim && Resources.getSystem().getText(defaultAudioRouteNameResourceId).equals(mName); 10882573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim } 10892573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim 10902573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim /** 1091fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets a list of {@link MediaControlIntent media control intent} filters that 1092fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * describe the capabilities of this route and the media control actions that 1093fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * it supports. 1094fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 1095fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @return A list of intent filters that specifies the media control intents that 1096fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * this route supports. 1097c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1098c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 1099c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #supportsControlCategory 1100c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #supportsControlRequest 1101c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1102fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public List<IntentFilter> getControlFilters() { 1103fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mControlFilters; 1104c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1105c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1106c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 110728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * Returns true if the route supports at least one of the capabilities 110828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * described by a media route selector. 110928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 111028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @param selector The selector that specifies the capabilities to check. 111128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @return True if the route supports at least one of the capabilities 111228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * described in the media route selector. 111328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown */ 1114429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public boolean matchesSelector(@NonNull MediaRouteSelector selector) { 111528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown if (selector == null) { 111628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown throw new IllegalArgumentException("selector must not be null"); 111728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 111828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown checkCallingThread(); 111911417b1cfde8f1749905f2d735623af9214148afJeff Brown return selector.matchesControlFilters(mControlFilters); 112028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 112128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 112228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 1123c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Returns true if the route supports the specified 1124c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent media control} category. 1125c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1126c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media control categories describe the capabilities of this route 1127c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * such as whether it supports live audio streaming or remote playback. 1128c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1129c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1130c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param category A {@link MediaControlIntent media control} category 1131c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO}, 1132c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO}, 1133fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined 1134c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * media control category. 113528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @return True if the route supports the specified intent category. 1136c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1137c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 1138fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see #getControlFilters 1139c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1140429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public boolean supportsControlCategory(@NonNull String category) { 1141c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (category == null) { 1142c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("category must not be null"); 1143c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1144fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1145c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1146fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int count = mControlFilters.size(); 1147fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown for (int i = 0; i < count; i++) { 1148fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mControlFilters.get(i).hasCategory(category)) { 1149fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return true; 1150fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1151fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1152fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return false; 1153c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1154c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1155c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1156c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Returns true if the route supports the specified 1157a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * {@link MediaControlIntent media control} category and action. 1158a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * <p> 1159a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * Media control actions describe specific requests that an application 1160a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * can ask a route to perform. 1161a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * </p> 1162a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * 1163a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * @param category A {@link MediaControlIntent media control} category 1164a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO}, 1165a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO}, 1166a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined 1167a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * media control category. 1168a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * @param action A {@link MediaControlIntent media control} action 1169a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * such as {@link MediaControlIntent#ACTION_PLAY}. 1170a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * @return True if the route supports the specified intent action. 1171a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * 1172a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * @see MediaControlIntent 1173a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * @see #getControlFilters 1174a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown */ 1175429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public boolean supportsControlAction(@NonNull String category, @NonNull String action) { 1176a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown if (category == null) { 1177a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown throw new IllegalArgumentException("category must not be null"); 1178a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown } 1179a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown if (action == null) { 1180a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown throw new IllegalArgumentException("action must not be null"); 1181a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown } 1182a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown checkCallingThread(); 1183a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown 1184a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown int count = mControlFilters.size(); 1185a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown for (int i = 0; i < count; i++) { 1186a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown IntentFilter filter = mControlFilters.get(i); 1187a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown if (filter.hasCategory(category) && filter.hasAction(action)) { 1188a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown return true; 1189a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown } 1190a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown } 1191a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown return false; 1192a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown } 1193a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown 1194a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown /** 1195a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * Returns true if the route supports the specified 1196c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent media control} request. 1197c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1198c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media control requests are used to request the route to perform 119943f79f79a5117550a7dfedf9c65124afd163ea43Jeff Brown * actions such as starting remote playback of a media item. 1200c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1201c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1202c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param intent A {@link MediaControlIntent media control intent}. 1203c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return True if the route can handle the specified intent. 1204c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1205c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 1206fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see #getControlFilters 1207c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1208429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public boolean supportsControlRequest(@NonNull Intent intent) { 1209c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (intent == null) { 1210c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("intent must not be null"); 1211c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1212c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 1213c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1214c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown ContentResolver contentResolver = sGlobal.getContentResolver(); 1215fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int count = mControlFilters.size(); 1216fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown for (int i = 0; i < count; i++) { 1217fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mControlFilters.get(i).match(contentResolver, intent, true, TAG) >= 0) { 1218fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return true; 1219fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1220fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1221fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return false; 1222c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1223c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1224c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1225c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Sends a {@link MediaControlIntent media control} request to be performed 1226c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * asynchronously by the route's destination. 1227c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1228c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media control requests are used to request the route to perform 122943f79f79a5117550a7dfedf9c65124afd163ea43Jeff Brown * actions such as starting remote playback of a media item. 1230c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 1231fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * This function may only be called on a selected route. Control requests 1232fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * sent to unselected routes will fail. 1233c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1234c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1235c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param intent A {@link MediaControlIntent media control intent}. 1236c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param callback A {@link ControlRequestCallback} to invoke with the result 1237c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * of the request, or null if no result is required. 1238c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1239c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 1240c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1241429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void sendControlRequest(@NonNull Intent intent, 1242429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Nullable ControlRequestCallback callback) { 1243c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (intent == null) { 1244c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("intent must not be null"); 1245c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1246c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 1247c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1248fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown sGlobal.sendControlRequest(this, intent, callback); 1249c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1250c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1251c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the type of playback associated with this route. 1253c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1254c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The type of playback associated with this route: {@link #PLAYBACK_TYPE_LOCAL} 1255c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * or {@link #PLAYBACK_TYPE_REMOTE}. 1256c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1257429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @PlaybackType 1258c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getPlaybackType() { 1259c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mPlaybackType; 1260c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1261c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1262c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1263350ba6e4a1b5ec28721a098e50eaf6a508eb28f0Jeff Brown * Gets the audio stream over which the playback associated with this route is performed. 1264c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1265c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The stream over which the playback associated with this route is performed. 1266c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1267c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getPlaybackStream() { 1268c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mPlaybackStream; 1269c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1270c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1271c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1272893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * Gets the type of the receiver device associated with this route. 1273893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * 1274893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo * @return The type of the receiver device associated with this route: 1275050b7ec33bd9a2fe493f0b0b85d49ed213046219Jae Seo * {@link #DEVICE_TYPE_TV} or {@link #DEVICE_TYPE_SPEAKER}. 1276893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo */ 1277893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo public int getDeviceType() { 1278893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo return mDeviceType; 1279893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo } 1280893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo 1281eb51f617d95c9a88a6ba528b1ad756f7bb7e7640Jae Seo 1282eb51f617d95c9a88a6ba528b1ad756f7bb7e7640Jae Seo /** 1283eb51f617d95c9a88a6ba528b1ad756f7bb7e7640Jae Seo * @hide 1284eb51f617d95c9a88a6ba528b1ad756f7bb7e7640Jae Seo */ 12858e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas @RestrictTo(LIBRARY_GROUP) 1286fa19abbe11335366feb6c885f8d75504ab8fe4f9Insun Kang public boolean isDefaultOrBluetooth() { 1287fa19abbe11335366feb6c885f8d75504ab8fe4f9Insun Kang if (isDefault() || mDeviceType == DEVICE_TYPE_BLUETOOTH) { 1288eb51f617d95c9a88a6ba528b1ad756f7bb7e7640Jae Seo return true; 1289eb51f617d95c9a88a6ba528b1ad756f7bb7e7640Jae Seo } 1290fa19abbe11335366feb6c885f8d75504ab8fe4f9Insun Kang // This is a workaround for platform version 23 or below where the system route 1291fa19abbe11335366feb6c885f8d75504ab8fe4f9Insun Kang // provider doesn't specify device type for bluetooth media routes. 1292eb51f617d95c9a88a6ba528b1ad756f7bb7e7640Jae Seo return isSystemMediaRouteProvider(this) 1293eb51f617d95c9a88a6ba528b1ad756f7bb7e7640Jae Seo && supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO) 1294eb51f617d95c9a88a6ba528b1ad756f7bb7e7640Jae Seo && !supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO); 1295eb51f617d95c9a88a6ba528b1ad756f7bb7e7640Jae Seo } 1296eb51f617d95c9a88a6ba528b1ad756f7bb7e7640Jae Seo 1297eb51f617d95c9a88a6ba528b1ad756f7bb7e7640Jae Seo private static boolean isSystemMediaRouteProvider(MediaRouter.RouteInfo route) { 1298eb51f617d95c9a88a6ba528b1ad756f7bb7e7640Jae Seo return TextUtils.equals(route.getProviderInstance().getMetadata().getPackageName(), 1299eb51f617d95c9a88a6ba528b1ad756f7bb7e7640Jae Seo SYSTEM_MEDIA_ROUTE_PROVIDER_PACKAGE_NAME); 1300eb51f617d95c9a88a6ba528b1ad756f7bb7e7640Jae Seo } 1301eb51f617d95c9a88a6ba528b1ad756f7bb7e7640Jae Seo 1302893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo /** 1303c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets information about how volume is handled on the route. 1304c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1305c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return How volume is handled on the route: {@link #PLAYBACK_VOLUME_FIXED} 1306c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * or {@link #PLAYBACK_VOLUME_VARIABLE}. 1307c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1308429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @PlaybackVolume 1309c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getVolumeHandling() { 1310c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mVolumeHandling; 1311c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1312c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1313c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1314c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the current volume for this route. Depending on the route, this may only 1315c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * be valid if the route is currently selected. 1316c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1317c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The volume at which the playback associated with this route is performed. 1318c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1319c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getVolume() { 1320c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mVolume; 1321c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1322c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1323c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1324c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the maximum volume at which the playback associated with this route is performed. 1325c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1326c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The maximum volume at which the playback associated with 1327c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * this route is performed. 1328c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1329c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getVolumeMax() { 1330c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mVolumeMax; 1331c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1332c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1333c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 133494be6100218126ce6a08bf1f56209578500b361fRoboErik * Gets whether this route supports disconnecting without interrupting 133594be6100218126ce6a08bf1f56209578500b361fRoboErik * playback. 133694be6100218126ce6a08bf1f56209578500b361fRoboErik * 133794be6100218126ce6a08bf1f56209578500b361fRoboErik * @return True if this route can disconnect without stopping playback, 133894be6100218126ce6a08bf1f56209578500b361fRoboErik * false otherwise. 133994be6100218126ce6a08bf1f56209578500b361fRoboErik */ 134094be6100218126ce6a08bf1f56209578500b361fRoboErik public boolean canDisconnect() { 134194be6100218126ce6a08bf1f56209578500b361fRoboErik return mCanDisconnect; 134294be6100218126ce6a08bf1f56209578500b361fRoboErik } 134394be6100218126ce6a08bf1f56209578500b361fRoboErik 134494be6100218126ce6a08bf1f56209578500b361fRoboErik /** 1345c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Requests a volume change for this route asynchronously. 1346c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1347c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This function may only be called on a selected route. It will have 1348c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * no effect if the route is currently unselected. 1349c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1350c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1351c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param volume The new volume value between 0 and {@link #getVolumeMax}. 1352c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1353c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestSetVolume(int volume) { 1354c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 1355c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal.requestSetVolume(this, Math.min(mVolumeMax, Math.max(0, volume))); 1356c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1357c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1358c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1359c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Requests an incremental volume update for this route asynchronously. 1360c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1361c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This function may only be called on a selected route. It will have 1362c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * no effect if the route is currently unselected. 1363c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1364c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1365c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param delta The delta to add to the current volume. 1366c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1367c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestUpdateVolume(int delta) { 1368c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 1369c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (delta != 0) { 1370c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal.requestUpdateVolume(this, delta); 1371c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1372c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1373c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1374c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1375c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the {@link Display} that should be used by the application to show 1376c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * a {@link android.app.Presentation} on an external display when this route is selected. 1377c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Depending on the route, this may only be valid if the route is currently 1378c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * selected. 1379c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1380c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The preferred presentation display may change independently of the route 1381c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * being selected or unselected. For example, the presentation display 1382c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * of the default system route may change when an external HDMI display is connected 1383c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * or disconnected even though the route itself has not changed. 1384c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 1385c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This method may return null if there is no external display associated with 1386c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * the route or if the display is not ready to show UI yet. 1387c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 1388c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The application should listen for changes to the presentation display 1389c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * using the {@link Callback#onRoutePresentationDisplayChanged} callback and 1390c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * show or dismiss its {@link android.app.Presentation} accordingly when the display 1391c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * becomes available or is removed. 1392c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 1393c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This method only makes sense for 1394c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO live video} routes. 1395c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1396c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1397c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The preferred presentation display to use when this route is 1398c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * selected or null if none. 1399c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1400c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent#CATEGORY_LIVE_VIDEO 1401c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see android.app.Presentation 1402c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1403429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Nullable 1404c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public Display getPresentationDisplay() { 1405fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1406c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mPresentationDisplayId >= 0 && mPresentationDisplay == null) { 1407c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mPresentationDisplay = sGlobal.getDisplay(mPresentationDisplayId); 1408c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1409c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mPresentationDisplay; 1410c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1411c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1412c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 141377367b4a1871417198d0399d9ad074314c758567Jae Seo * Gets the route's presentation display id, or -1 if none. 141477367b4a1871417198d0399d9ad074314c758567Jae Seo * @hide 141577367b4a1871417198d0399d9ad074314c758567Jae Seo */ 14168e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas @RestrictTo(LIBRARY_GROUP) 141777367b4a1871417198d0399d9ad074314c758567Jae Seo public int getPresentationDisplayId() { 141877367b4a1871417198d0399d9ad074314c758567Jae Seo return mPresentationDisplayId; 141977367b4a1871417198d0399d9ad074314c758567Jae Seo } 142077367b4a1871417198d0399d9ad074314c758567Jae Seo 142177367b4a1871417198d0399d9ad074314c758567Jae Seo /** 1422c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets a collection of extra properties about this route that were supplied 1423c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * by its media route provider, or null if none. 1424c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1425429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Nullable 1426c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public Bundle getExtras() { 1427c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mExtras; 1428c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1429c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1430fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 143194be6100218126ce6a08bf1f56209578500b361fRoboErik * Gets an intent sender for launching a settings activity for this 143294be6100218126ce6a08bf1f56209578500b361fRoboErik * route. 143394be6100218126ce6a08bf1f56209578500b361fRoboErik */ 143494be6100218126ce6a08bf1f56209578500b361fRoboErik @Nullable 143594be6100218126ce6a08bf1f56209578500b361fRoboErik public IntentSender getSettingsIntent() { 143694be6100218126ce6a08bf1f56209578500b361fRoboErik return mSettingsIntent; 143794be6100218126ce6a08bf1f56209578500b361fRoboErik } 143894be6100218126ce6a08bf1f56209578500b361fRoboErik 143994be6100218126ce6a08bf1f56209578500b361fRoboErik /** 1440fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Selects this media route. 1441fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1442fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void select() { 1443fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1444fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown sGlobal.selectRoute(this); 1445fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1446fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1447c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 1448c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public String toString() { 1449cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return "MediaRouter.RouteInfo{ uniqueId=" + mUniqueId 1450cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown + ", name=" + mName 1451d63957d28aaabcec588b8cde12eac16414783aebJeff Brown + ", description=" + mDescription 1452119de6bb3353341cfd465cea9e545abec3762d20Jae Seo + ", iconUri=" + mIconUri 1453c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", enabled=" + mEnabled 145411417b1cfde8f1749905f2d735623af9214148afJeff Brown + ", connecting=" + mConnecting 14553766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo + ", connectionState=" + mConnectionState 145694be6100218126ce6a08bf1f56209578500b361fRoboErik + ", canDisconnect=" + mCanDisconnect 1457c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", playbackType=" + mPlaybackType 1458c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", playbackStream=" + mPlaybackStream 1459893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo + ", deviceType=" + mDeviceType 1460c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", volumeHandling=" + mVolumeHandling 1461c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", volume=" + mVolume 1462c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", volumeMax=" + mVolumeMax 1463c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", presentationDisplayId=" + mPresentationDisplayId 1464c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", extras=" + mExtras 146594be6100218126ce6a08bf1f56209578500b361fRoboErik + ", settingsIntent=" + mSettingsIntent 1466fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown + ", providerPackageName=" + mProvider.getPackageName() 1467fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown + " }"; 1468fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1469fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1470e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo int maybeUpdateDescriptor(MediaRouteDescriptor descriptor) { 1471fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int changes = 0; 1472fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mDescriptor != descriptor) { 1473e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes = updateDescriptor(descriptor); 1474e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1475e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return changes; 1476e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1477e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 1478e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo int updateDescriptor(MediaRouteDescriptor descriptor) { 1479e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo int changes = 0; 1480e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mDescriptor = descriptor; 1481e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (descriptor != null) { 1482e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (!equal(mName, descriptor.getName())) { 1483e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mName = descriptor.getName(); 1484e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1485e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1486e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (!equal(mDescription, descriptor.getDescription())) { 1487e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mDescription = descriptor.getDescription(); 1488e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1489e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1490119de6bb3353341cfd465cea9e545abec3762d20Jae Seo if (!equal(mIconUri, descriptor.getIconUri())) { 1491119de6bb3353341cfd465cea9e545abec3762d20Jae Seo mIconUri = descriptor.getIconUri(); 1492119de6bb3353341cfd465cea9e545abec3762d20Jae Seo changes |= CHANGE_GENERAL; 1493119de6bb3353341cfd465cea9e545abec3762d20Jae Seo } 1494e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mEnabled != descriptor.isEnabled()) { 1495e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mEnabled = descriptor.isEnabled(); 1496e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1497e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1498e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mConnecting != descriptor.isConnecting()) { 1499e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mConnecting = descriptor.isConnecting(); 1500e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1501e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 15023766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo if (mConnectionState != descriptor.getConnectionState()) { 15033766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo mConnectionState = descriptor.getConnectionState(); 15043766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo changes |= CHANGE_GENERAL; 15053766e1d92193a98f65a12b18a43bbb3f6508a25fJae Seo } 1506e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (!mControlFilters.equals(descriptor.getControlFilters())) { 1507e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mControlFilters.clear(); 1508e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mControlFilters.addAll(descriptor.getControlFilters()); 1509e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1510e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1511e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mPlaybackType != descriptor.getPlaybackType()) { 1512e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mPlaybackType = descriptor.getPlaybackType(); 1513e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1514e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1515e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mPlaybackStream != descriptor.getPlaybackStream()) { 1516e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mPlaybackStream = descriptor.getPlaybackStream(); 1517e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1518e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1519893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo if (mDeviceType != descriptor.getDeviceType()) { 1520893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo mDeviceType = descriptor.getDeviceType(); 1521893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo changes |= CHANGE_GENERAL; 1522893feff5ea67f24af45af6dc9d6d09640ea66b86Jae Seo } 1523e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mVolumeHandling != descriptor.getVolumeHandling()) { 1524e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mVolumeHandling = descriptor.getVolumeHandling(); 1525e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL | CHANGE_VOLUME; 1526e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1527e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mVolume != descriptor.getVolume()) { 1528e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mVolume = descriptor.getVolume(); 1529e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL | CHANGE_VOLUME; 1530e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1531e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mVolumeMax != descriptor.getVolumeMax()) { 1532e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mVolumeMax = descriptor.getVolumeMax(); 1533e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL | CHANGE_VOLUME; 1534e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1535e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mPresentationDisplayId != descriptor.getPresentationDisplayId()) { 1536e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mPresentationDisplayId = descriptor.getPresentationDisplayId(); 1537e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mPresentationDisplay = null; 1538e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY; 1539e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1540e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (!equal(mExtras, descriptor.getExtras())) { 1541e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mExtras = descriptor.getExtras(); 1542e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1543e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1544e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (!equal(mSettingsIntent, descriptor.getSettingsActivity())) { 1545e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mSettingsIntent = descriptor.getSettingsActivity(); 1546e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1547e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1548e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mCanDisconnect != descriptor.canDisconnectAndKeepPlaying()) { 1549e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mCanDisconnect = descriptor.canDisconnectAndKeepPlaying(); 1550e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY; 1551fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1552fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1553fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return changes; 1554fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1555fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1556fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown String getDescriptorId() { 1557fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mDescriptorId; 1558fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1559fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 156014b631fc79b2fc69f392ab8d2f096b0e86b9bea2Jae Seo /** @hide */ 15618e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas @RestrictTo(LIBRARY_GROUP) 156214b631fc79b2fc69f392ab8d2f096b0e86b9bea2Jae Seo public MediaRouteProvider getProviderInstance() { 1563fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mProvider.getProviderInstance(); 1564fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1565fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1566fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1567fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1568e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo * Information about a route that consists of multiple other routes in a group. 156914a7007aaefb96b3b6fb06171aa10e1200116d6aJae Seo * @hide 1570e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo */ 15718e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas @RestrictTo(LIBRARY_GROUP) 1572e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo public static class RouteGroup extends RouteInfo { 1573e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo private List<RouteInfo> mRoutes = new ArrayList<>(); 1574e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 1575e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo RouteGroup(ProviderInfo provider, String descriptorId, String uniqueId) { 1576e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo super(provider, descriptorId, uniqueId); 1577e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1578e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 1579e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo /** 1580e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo * @return The number of routes in this group 1581e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo */ 1582e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo public int getRouteCount() { 1583e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return mRoutes.size(); 1584e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1585e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 1586e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo /** 1587e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo * Returns the route in this group at the specified index 1588e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo * 1589e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo * @param index Index to fetch 1590e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo * @return The route at index 1591e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo */ 1592e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo public RouteInfo getRouteAt(int index) { 1593e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return mRoutes.get(index); 1594e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1595e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 15966051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo /** 15976051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo * Returns the routes in this group 15986051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo * 15996051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo * @return The list of the routes in this group 16006051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo */ 16016051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo public List<RouteInfo> getRoutes() { 16026051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo return mRoutes; 16036051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo } 16046051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo 1605e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo @Override 1606e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo public String toString() { 1607e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo StringBuilder sb = new StringBuilder(super.toString()); 1608e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo sb.append('['); 1609e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo final int count = mRoutes.size(); 1610e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo for (int i = 0; i < count; i++) { 1611e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (i > 0) sb.append(", "); 1612e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo sb.append(mRoutes.get(i)); 1613e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1614e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo sb.append(']'); 1615e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return sb.toString(); 1616e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1617e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 1618e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo @Override 1619e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo int maybeUpdateDescriptor(MediaRouteDescriptor descriptor) { 1620e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo boolean changed = false; 1621e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mDescriptor != descriptor) { 1622e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mDescriptor = descriptor; 1623e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (descriptor != null) { 16241bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo List<String> groupMemberIds = descriptor.getGroupMemberIds(); 1625e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo List<RouteInfo> routes = new ArrayList<>(); 16261bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo changed = groupMemberIds.size() != mRoutes.size(); 16271bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo for (String groupMemberId : groupMemberIds) { 16281bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo String uniqueId = sGlobal.getUniqueId(getProvider(), groupMemberId); 16291bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo RouteInfo groupMember = sGlobal.getRoute(uniqueId); 16301bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo if (groupMember != null) { 16311bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo routes.add(groupMember); 16321bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo if (!changed && !mRoutes.contains(groupMember)) { 1633e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changed = true; 1634e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1635e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1636e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1637e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (changed) { 1638e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mRoutes = routes; 1639e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1640e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1641e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1642e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return (changed ? CHANGE_GENERAL : 0) | super.updateDescriptor(descriptor); 1643e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1644e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1645e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 1646e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo /** 1647fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Provides information about a media route provider. 1648fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * <p> 1649fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * This object may be used to determine which media route provider has 1650fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * published a particular route. 1651fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * </p> 1652fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1653fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public static final class ProviderInfo { 1654fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private final MediaRouteProvider mProviderInstance; 1655e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo private final List<RouteInfo> mRoutes = new ArrayList<>(); 1656fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1657fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private final ProviderMetadata mMetadata; 165811417b1cfde8f1749905f2d735623af9214148afJeff Brown private MediaRouteProviderDescriptor mDescriptor; 1659fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private Resources mResources; 1660fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private boolean mResourcesNotAvailable; 1661fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1662fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo(MediaRouteProvider provider) { 1663fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mProviderInstance = provider; 1664fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mMetadata = provider.getMetadata(); 1665fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1666fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1667fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1668fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets the provider's underlying {@link MediaRouteProvider} instance. 1669fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1670fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public MediaRouteProvider getProviderInstance() { 1671fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1672fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mProviderInstance; 1673fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1674fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1675fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1676adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown * Gets the package name of the media route provider. 1677fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1678fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public String getPackageName() { 1679fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mMetadata.getPackageName(); 1680fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1681fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1682fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1683adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown * Gets the component name of the media route provider. 1684adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown */ 1685adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown public ComponentName getComponentName() { 1686adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown return mMetadata.getComponentName(); 1687adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown } 1688adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown 1689adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown /** 1690fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets the {@link MediaRouter.RouteInfo routes} published by this route provider. 1691fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1692fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public List<RouteInfo> getRoutes() { 1693fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1694fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mRoutes; 1695fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1696fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1697fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Resources getResources() { 1698fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mResources == null && !mResourcesNotAvailable) { 1699fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown String packageName = getPackageName(); 1700fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Context context = sGlobal.getProviderContext(packageName); 1701fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (context != null) { 1702fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mResources = context.getResources(); 1703fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } else { 1704fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Log.w(TAG, "Unable to obtain resources for route provider package: " 1705fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown + packageName); 1706fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mResourcesNotAvailable = true; 1707fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1708fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1709fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mResources; 1710fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1711fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 171211417b1cfde8f1749905f2d735623af9214148afJeff Brown boolean updateDescriptor(MediaRouteProviderDescriptor descriptor) { 1713fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mDescriptor != descriptor) { 1714fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mDescriptor = descriptor; 1715fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return true; 1716fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1717fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return false; 1718fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1719fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1720fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int findRouteByDescriptorId(String id) { 1721fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown final int count = mRoutes.size(); 1722fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown for (int i = 0; i < count; i++) { 1723fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mRoutes.get(i).mDescriptorId.equals(id)) { 1724fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return i; 1725fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1726fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1727fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return -1; 1728fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1729fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1730fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown @Override 1731fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public String toString() { 1732fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return "MediaRouter.RouteProviderInfo{ packageName=" + getPackageName() 1733c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + " }"; 1734c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1735c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1736c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1737c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1738c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Interface for receiving events about media routing changes. 1739c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * All methods of this interface will be called from the application's main thread. 1740c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1741c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * A Callback will only receive events relevant to routes that the callback 174211417b1cfde8f1749905f2d735623af9214148afJeff Brown * was registered for unless the {@link MediaRouter#CALLBACK_FLAG_UNFILTERED_EVENTS} 174311417b1cfde8f1749905f2d735623af9214148afJeff Brown * flag was specified in {@link MediaRouter#addCallback(MediaRouteSelector, Callback, int)}. 1744c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1745c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 174611417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see MediaRouter#addCallback(MediaRouteSelector, Callback, int) 1747c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaRouter#removeCallback(Callback) 1748c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1749c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static abstract class Callback { 1750c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1751fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when the supplied media route becomes selected as the active route. 1752c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1753fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1754c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has been selected. 1755c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1756c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteSelected(MediaRouter router, RouteInfo route) { 1757c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1758c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1759c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1760fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when the supplied media route becomes unselected as the active route. 176134f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * For detailed reason, override {@link #onRouteUnselected(MediaRouter, RouteInfo, int)} 176234f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * instead. 1763c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1764fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1765c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has been unselected. 1766c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1767c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteUnselected(MediaRouter router, RouteInfo route) { 1768c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1769c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1770c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 177134f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * Called when the supplied media route becomes unselected as the active route. 177234f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * The default implementation calls {@link #onRouteUnselected}. 177334f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * <p> 177434f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * The reason provided will be one of the following: 177534f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * <ul> 177634f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * <li>{@link MediaRouter#UNSELECT_REASON_UNKNOWN}</li> 177734f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * <li>{@link MediaRouter#UNSELECT_REASON_DISCONNECTED}</li> 177834f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * <li>{@link MediaRouter#UNSELECT_REASON_STOPPED}</li> 177934f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * <li>{@link MediaRouter#UNSELECT_REASON_ROUTE_CHANGED}</li> 178034f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * </ul> 178134f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * 178234f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * @param router The media router reporting the event. 178334f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * @param route The route that has been unselected. 178434f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho * @param reason The reason for unselecting the route. 178534f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho */ 178634f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho public void onRouteUnselected(MediaRouter router, RouteInfo route, int reason) { 178734f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho onRouteUnselected(router, route); 178834f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho } 178934f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho 179034f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho /** 1791fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route has been added. 1792c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1793fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1794c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has become available for use. 1795c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1796c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteAdded(MediaRouter router, RouteInfo route) { 1797c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1798c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1799c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1800fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route has been removed. 1801c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1802fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1803c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has been removed from availability. 1804c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1805c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteRemoved(MediaRouter router, RouteInfo route) { 1806c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1807c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1808c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1809fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a property of the indicated media route has changed. 1810c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1811fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1812c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that was changed. 1813c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1814c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteChanged(MediaRouter router, RouteInfo route) { 1815c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1816c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1817c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1818fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route's volume changes. 1819c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1820fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1821c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route whose volume changed. 1822c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1823c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) { 1824c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1825c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1826c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1827fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route's presentation display changes. 1828c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1829c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This method is called whenever the route's presentation display becomes 1830fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * available, is removed or has changes to some of its properties (such as its size). 1831c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1832c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1833fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1834c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route whose presentation display changed. 1835c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1836c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#getPresentationDisplay() 1837c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1838c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo route) { 1839c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1840fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1841fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1842fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route provider has been added. 1843fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 1844fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1845fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param provider The provider that has become available for use. 1846fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1847fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void onProviderAdded(MediaRouter router, ProviderInfo provider) { 1848fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1849fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1850fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1851fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route provider has been removed. 1852fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 1853fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1854fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param provider The provider that has been removed from availability. 1855fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1856fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void onProviderRemoved(MediaRouter router, ProviderInfo provider) { 1857fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 185811417b1cfde8f1749905f2d735623af9214148afJeff Brown 185911417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 186011417b1cfde8f1749905f2d735623af9214148afJeff Brown * Called when a property of the indicated media route provider has changed. 186111417b1cfde8f1749905f2d735623af9214148afJeff Brown * 186211417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param router The media router reporting the event. 186311417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param provider The provider that was changed. 186411417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 186511417b1cfde8f1749905f2d735623af9214148afJeff Brown public void onProviderChanged(MediaRouter router, ProviderInfo provider) { 186611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1867c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1868c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1869c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1870c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Callback which is invoked with the result of a media control request. 1871c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1872c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#sendControlRequest 1873c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1874fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public static abstract class ControlRequestCallback { 1875c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 18763d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * Called when a media control request succeeds. 18773d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * 18783d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * @param data Result data, or null if none. 18793d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * Contents depend on the {@link MediaControlIntent media control action}. 1880c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 18813d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown public void onResult(Bundle data) { 18823d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown } 1883c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1884c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 18853d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * Called when a media control request fails. 1886c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 18873d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * @param error A localized error message which may be shown to the user, or null 18883d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * if the cause of the error is unclear. 18893d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * @param data Error data, or null if none. 18903d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * Contents depend on the {@link MediaControlIntent media control action}. 1891c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 18923d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown public void onError(String error, Bundle data) { 1893c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1894c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1895c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 189611417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final class CallbackRecord { 18979fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown public final MediaRouter mRouter; 189811417b1cfde8f1749905f2d735623af9214148afJeff Brown public final Callback mCallback; 189911417b1cfde8f1749905f2d735623af9214148afJeff Brown public MediaRouteSelector mSelector; 190011417b1cfde8f1749905f2d735623af9214148afJeff Brown public int mFlags; 190111417b1cfde8f1749905f2d735623af9214148afJeff Brown 19029fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown public CallbackRecord(MediaRouter router, Callback callback) { 19039fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouter = router; 190411417b1cfde8f1749905f2d735623af9214148afJeff Brown mCallback = callback; 190511417b1cfde8f1749905f2d735623af9214148afJeff Brown mSelector = MediaRouteSelector.EMPTY; 190611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 190711417b1cfde8f1749905f2d735623af9214148afJeff Brown 190811417b1cfde8f1749905f2d735623af9214148afJeff Brown public boolean filterRouteEvent(RouteInfo route) { 190911417b1cfde8f1749905f2d735623af9214148afJeff Brown return (mFlags & CALLBACK_FLAG_UNFILTERED_EVENTS) != 0 191011417b1cfde8f1749905f2d735623af9214148afJeff Brown || route.matchesSelector(mSelector); 191111417b1cfde8f1749905f2d735623af9214148afJeff Brown } 191211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 191311417b1cfde8f1749905f2d735623af9214148afJeff Brown 1914c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1915c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Global state for the media router. 1916c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1917c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media routes and media route providers are global to the process; their 1918c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * state and the bulk of the media router implementation lives here. 1919c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1920c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 19213efa63d3b896244713e84acbb5945562dce41d77Jeff Brown private static final class GlobalMediaRouter 19223efa63d3b896244713e84acbb5945562dce41d77Jeff Brown implements SystemMediaRouteProvider.SyncCallback, 19233efa63d3b896244713e84acbb5945562dce41d77Jeff Brown RegisteredMediaRouteProviderWatcher.Callback { 1924e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas final Context mApplicationContext; 1925e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas final ArrayList<WeakReference<MediaRouter>> mRouters = new ArrayList<>(); 192638e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo private final ArrayList<RouteInfo> mRoutes = new ArrayList<>(); 192738e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo private final Map<Pair<String, String>, String> mUniqueIdMap = new HashMap<>(); 192838e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo private final ArrayList<ProviderInfo> mProviders = new ArrayList<>(); 1929567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private final ArrayList<RemoteControlClientRecord> mRemoteControlClients = 193038e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo new ArrayList<>(); 1931e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas final RemoteControlClientCompat.PlaybackInfo mPlaybackInfo = 1932567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown new RemoteControlClientCompat.PlaybackInfo(); 1933c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final ProviderCallback mProviderCallback = new ProviderCallback(); 1934e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas final CallbackHandler mCallbackHandler = new CallbackHandler(); 1935c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final DisplayManagerCompat mDisplayManager; 1936e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas final SystemMediaRouteProvider mSystemProvider; 1937fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown private final boolean mLowRam; 1938c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1939fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private RegisteredMediaRouteProviderWatcher mRegisteredProviderWatcher; 1940c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private RouteInfo mDefaultRoute; 19412573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim private RouteInfo mBluetoothRoute; 1942e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas RouteInfo mSelectedRoute; 1943bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang private RouteController mSelectedRouteController; 1944b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo // A map from route descriptor ID to RouteController for the member routes in the currently 1945b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo // selected route group. 1946b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo private final Map<String, RouteController> mRouteControllerMap = new HashMap<>(); 194711417b1cfde8f1749905f2d735623af9214148afJeff Brown private MediaRouteDiscoveryRequest mDiscoveryRequest; 1948bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik private MediaSessionRecord mMediaSession; 1949e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas MediaSessionCompat mRccMediaSession; 1950e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik private MediaSessionCompat mCompatSession; 19515c9469e010106467791b47b0fa83efda84491a21RoboErik private MediaSessionCompat.OnActiveChangeListener mSessionActiveListener = 19525c9469e010106467791b47b0fa83efda84491a21RoboErik new MediaSessionCompat.OnActiveChangeListener() { 19535c9469e010106467791b47b0fa83efda84491a21RoboErik @Override 19545c9469e010106467791b47b0fa83efda84491a21RoboErik public void onActiveChanged() { 19555c9469e010106467791b47b0fa83efda84491a21RoboErik if(mRccMediaSession != null) { 19565c9469e010106467791b47b0fa83efda84491a21RoboErik if (mRccMediaSession.isActive()) { 19575c9469e010106467791b47b0fa83efda84491a21RoboErik addRemoteControlClient(mRccMediaSession.getRemoteControlClient()); 19585c9469e010106467791b47b0fa83efda84491a21RoboErik } else { 19595c9469e010106467791b47b0fa83efda84491a21RoboErik removeRemoteControlClient(mRccMediaSession.getRemoteControlClient()); 19605c9469e010106467791b47b0fa83efda84491a21RoboErik } 19615c9469e010106467791b47b0fa83efda84491a21RoboErik } 19625c9469e010106467791b47b0fa83efda84491a21RoboErik } 19635c9469e010106467791b47b0fa83efda84491a21RoboErik }; 1964c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1965c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown GlobalMediaRouter(Context applicationContext) { 1966c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mApplicationContext = applicationContext; 1967c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDisplayManager = DisplayManagerCompat.getInstance(applicationContext); 1968fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown mLowRam = ActivityManagerCompat.isLowRamDevice( 1969fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown (ActivityManager)applicationContext.getSystemService( 1970fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown Context.ACTIVITY_SERVICE)); 1971fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1972fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // Add the system media route provider for interoperating with 1973fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // the framework media router. This one is special and receives 1974fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // synchronization messages from the media router. 1975c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSystemProvider = SystemMediaRouteProvider.obtain(applicationContext, this); 1976fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown addProvider(mSystemProvider); 1977fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1978fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1979fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void start() { 1980fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // Start watching for routes published by registered media route 1981fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // provider services. 1982fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mRegisteredProviderWatcher = new RegisteredMediaRouteProviderWatcher( 19833efa63d3b896244713e84acbb5945562dce41d77Jeff Brown mApplicationContext, this); 1984fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mRegisteredProviderWatcher.start(); 1985c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1986c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1987c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public MediaRouter getRouter(Context context) { 19889fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown MediaRouter router; 19899fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int i = mRouters.size(); --i >= 0; ) { 19909fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown router = mRouters.get(i).get(); 19919fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown if (router == null) { 19929fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouters.remove(i); 19939fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } else if (router.mContext == context) { 19949fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown return router; 19959fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } 1996c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 19979fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown router = new MediaRouter(context); 19989fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouters.add(new WeakReference<MediaRouter>(router)); 1999c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return router; 2000c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2001c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2002c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public ContentResolver getContentResolver() { 2003c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mApplicationContext.getContentResolver(); 2004c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2005c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2006fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public Context getProviderContext(String packageName) { 2007fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (packageName.equals(SystemMediaRouteProvider.PACKAGE_NAME)) { 2008fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mApplicationContext; 2009fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 2010fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown try { 2011fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mApplicationContext.createPackageContext( 2012fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown packageName, Context.CONTEXT_RESTRICTED); 2013fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } catch (NameNotFoundException ex) { 2014fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return null; 2015fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 2016fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 2017fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 2018c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public Display getDisplay(int displayId) { 2019c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mDisplayManager.getDisplay(displayId); 2020c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2021c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2022fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void sendControlRequest(RouteInfo route, 2023c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Intent intent, ControlRequestCallback callback) { 2024c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == mSelectedRoute && mSelectedRouteController != null) { 2025129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown if (mSelectedRouteController.onControlRequest(intent, callback)) { 2026fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return; 2027fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 2028fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 2029fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (callback != null) { 20303d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown callback.onError(null, null); 2031c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2032c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2033c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2034c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestSetVolume(RouteInfo route, int volume) { 2035c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == mSelectedRoute && mSelectedRouteController != null) { 2036129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown mSelectedRouteController.onSetVolume(volume); 2037b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo } else if (!mRouteControllerMap.isEmpty()) { 2038b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo RouteController controller = mRouteControllerMap.get(route.mDescriptorId); 2039bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang if (controller != null) { 2040bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang controller.onSetVolume(volume); 2041bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang } 2042c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2043c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2044c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2045c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestUpdateVolume(RouteInfo route, int delta) { 2046c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == mSelectedRoute && mSelectedRouteController != null) { 2047129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown mSelectedRouteController.onUpdateVolume(delta); 2048c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2049c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2050c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2051e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo public RouteInfo getRoute(String uniqueId) { 2052e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo for (RouteInfo info : mRoutes) { 2053e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (info.mUniqueId.equals(uniqueId)) { 2054e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return info; 2055e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2056e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2057e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return null; 2058e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2059e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 2060c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public List<RouteInfo> getRoutes() { 2061c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mRoutes; 2062c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2063c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2064fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public List<ProviderInfo> getProviders() { 2065fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mProviders; 2066fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 2067fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 2068c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getDefaultRoute() { 2069c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mDefaultRoute == null) { 2070c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This should never happen once the media router has been fully 2071c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // initialized but it is good to check for the error in case there 2072c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // is a bug in provider initialization. 2073c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalStateException("There is no default route. " 2074c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + "The media router has not yet been fully initialized."); 2075c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2076c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mDefaultRoute; 2077c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2078c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 20792573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim public RouteInfo getBluetoothRoute() { 20802573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim return mBluetoothRoute; 20812573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim } 20822573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim 2083c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getSelectedRoute() { 2084c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute == null) { 2085c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This should never happen once the media router has been fully 2086c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // initialized but it is good to check for the error in case there 2087c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // is a bug in provider initialization. 2088c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalStateException("There is no currently selected route. " 2089c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + "The media router has not yet been fully initialized."); 2090c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2091c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mSelectedRoute; 2092c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2093c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2094c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void selectRoute(RouteInfo route) { 209594be6100218126ce6a08bf1f56209578500b361fRoboErik selectRoute(route, MediaRouter.UNSELECT_REASON_ROUTE_CHANGED); 209694be6100218126ce6a08bf1f56209578500b361fRoboErik } 209794be6100218126ce6a08bf1f56209578500b361fRoboErik 209894be6100218126ce6a08bf1f56209578500b361fRoboErik public void selectRoute(RouteInfo route, int unselectReason) { 2099c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (!mRoutes.contains(route)) { 2100c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Log.w(TAG, "Ignoring attempt to select removed route: " + route); 2101c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return; 2102c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2103c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (!route.mEnabled) { 2104c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Log.w(TAG, "Ignoring attempt to select disabled route: " + route); 2105c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return; 2106c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 210794be6100218126ce6a08bf1f56209578500b361fRoboErik setSelectedRouteInternal(route, unselectReason); 2108c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2109c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 211011417b1cfde8f1749905f2d735623af9214148afJeff Brown public boolean isRouteAvailable(MediaRouteSelector selector, int flags) { 2111fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown if (selector.isEmpty()) { 2112fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown return false; 2113fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown } 2114fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown 2115fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown // On low-RAM devices, do not rely on actual discovery results unless asked to. 2116fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown if ((flags & AVAILABILITY_FLAG_REQUIRE_MATCH) == 0 && mLowRam) { 2117fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown return true; 2118fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown } 2119fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown 212011417b1cfde8f1749905f2d735623af9214148afJeff Brown // Check whether any existing routes match the selector. 212111417b1cfde8f1749905f2d735623af9214148afJeff Brown final int routeCount = mRoutes.size(); 212211417b1cfde8f1749905f2d735623af9214148afJeff Brown for (int i = 0; i < routeCount; i++) { 212311417b1cfde8f1749905f2d735623af9214148afJeff Brown RouteInfo route = mRoutes.get(i); 212411417b1cfde8f1749905f2d735623af9214148afJeff Brown if ((flags & AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE) != 0 2125fa19abbe11335366feb6c885f8d75504ab8fe4f9Insun Kang && route.isDefaultOrBluetooth()) { 212611417b1cfde8f1749905f2d735623af9214148afJeff Brown continue; 212711417b1cfde8f1749905f2d735623af9214148afJeff Brown } 212811417b1cfde8f1749905f2d735623af9214148afJeff Brown if (route.matchesSelector(selector)) { 212911417b1cfde8f1749905f2d735623af9214148afJeff Brown return true; 213011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 213111417b1cfde8f1749905f2d735623af9214148afJeff Brown } 213211417b1cfde8f1749905f2d735623af9214148afJeff Brown 213311417b1cfde8f1749905f2d735623af9214148afJeff Brown // It doesn't look like we can find a matching route right now. 213411417b1cfde8f1749905f2d735623af9214148afJeff Brown return false; 213511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 213611417b1cfde8f1749905f2d735623af9214148afJeff Brown 213711417b1cfde8f1749905f2d735623af9214148afJeff Brown public void updateDiscoveryRequest() { 213811417b1cfde8f1749905f2d735623af9214148afJeff Brown // Combine all of the callback selectors and active scan flags. 2139f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown boolean discover = false; 214011417b1cfde8f1749905f2d735623af9214148afJeff Brown boolean activeScan = false; 214111417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder(); 21429fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int i = mRouters.size(); --i >= 0; ) { 21439fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown MediaRouter router = mRouters.get(i).get(); 21449fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown if (router == null) { 21459fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouters.remove(i); 21469fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } else { 21479fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown final int count = router.mCallbackRecords.size(); 21489fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int j = 0; j < count; j++) { 21499fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown CallbackRecord callback = router.mCallbackRecords.get(j); 21509fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown builder.addSelector(callback.mSelector); 2151f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown if ((callback.mFlags & CALLBACK_FLAG_PERFORM_ACTIVE_SCAN) != 0) { 21529fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown activeScan = true; 2153f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown discover = true; // perform active scan implies request discovery 2154f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown } 2155f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown if ((callback.mFlags & CALLBACK_FLAG_REQUEST_DISCOVERY) != 0) { 2156fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown if (!mLowRam) { 2157fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown discover = true; 2158fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown } 2159fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown } 2160fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown if ((callback.mFlags & CALLBACK_FLAG_FORCE_DISCOVERY) != 0) { 2161f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown discover = true; 21629fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } 216311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 216411417b1cfde8f1749905f2d735623af9214148afJeff Brown } 216511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2166f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown MediaRouteSelector selector = discover ? builder.build() : MediaRouteSelector.EMPTY; 216711417b1cfde8f1749905f2d735623af9214148afJeff Brown 216811417b1cfde8f1749905f2d735623af9214148afJeff Brown // Create a new discovery request. 216911417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mDiscoveryRequest != null 217011417b1cfde8f1749905f2d735623af9214148afJeff Brown && mDiscoveryRequest.getSelector().equals(selector) 217111417b1cfde8f1749905f2d735623af9214148afJeff Brown && mDiscoveryRequest.isActiveScan() == activeScan) { 217211417b1cfde8f1749905f2d735623af9214148afJeff Brown return; // no change 217311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 217411417b1cfde8f1749905f2d735623af9214148afJeff Brown if (selector.isEmpty() && !activeScan) { 217511417b1cfde8f1749905f2d735623af9214148afJeff Brown // Discovery is not needed. 217611417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mDiscoveryRequest == null) { 217711417b1cfde8f1749905f2d735623af9214148afJeff Brown return; // no change 217811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 217911417b1cfde8f1749905f2d735623af9214148afJeff Brown mDiscoveryRequest = null; 218011417b1cfde8f1749905f2d735623af9214148afJeff Brown } else { 218111417b1cfde8f1749905f2d735623af9214148afJeff Brown // Discovery is needed. 218211417b1cfde8f1749905f2d735623af9214148afJeff Brown mDiscoveryRequest = new MediaRouteDiscoveryRequest(selector, activeScan); 218311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 218411417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 218511417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Updated discovery request: " + mDiscoveryRequest); 218611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2187fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown if (discover && !activeScan && mLowRam) { 2188fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown Log.i(TAG, "Forcing passive route discovery on a low-RAM device, " 2189fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown + "system performance may be affected. Please consider using " 2190fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown + "CALLBACK_FLAG_REQUEST_DISCOVERY instead of " 2191fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown + "CALLBACK_FLAG_FORCE_DISCOVERY."); 2192fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown } 219311417b1cfde8f1749905f2d735623af9214148afJeff Brown 219411417b1cfde8f1749905f2d735623af9214148afJeff Brown // Notify providers. 219511417b1cfde8f1749905f2d735623af9214148afJeff Brown final int providerCount = mProviders.size(); 219611417b1cfde8f1749905f2d735623af9214148afJeff Brown for (int i = 0; i < providerCount; i++) { 219711417b1cfde8f1749905f2d735623af9214148afJeff Brown mProviders.get(i).mProviderInstance.setDiscoveryRequest(mDiscoveryRequest); 219828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 219928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 220028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 22013efa63d3b896244713e84acbb5945562dce41d77Jeff Brown @Override 2202fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void addProvider(MediaRouteProvider providerInstance) { 2203fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int index = findProviderInfo(providerInstance); 2204c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index < 0) { 2205c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 1. Add the provider to the list. 2206fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo provider = new ProviderInfo(providerInstance); 2207fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mProviders.add(provider); 220811417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 220911417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Provider added: " + provider); 221011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2211fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_ADDED, provider); 2212c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 2. Create the provider's contents. 2213fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown updateProviderContents(provider, providerInstance.getDescriptor()); 2214c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 3. Register the provider callback. 221511417b1cfde8f1749905f2d735623af9214148afJeff Brown providerInstance.setCallback(mProviderCallback); 221611417b1cfde8f1749905f2d735623af9214148afJeff Brown // 4. Set the discovery request. 221711417b1cfde8f1749905f2d735623af9214148afJeff Brown providerInstance.setDiscoveryRequest(mDiscoveryRequest); 2218c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2219c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2220c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 22213efa63d3b896244713e84acbb5945562dce41d77Jeff Brown @Override 2222fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void removeProvider(MediaRouteProvider providerInstance) { 2223fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int index = findProviderInfo(providerInstance); 2224c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index >= 0) { 222511417b1cfde8f1749905f2d735623af9214148afJeff Brown // 1. Unregister the provider callback. 222611417b1cfde8f1749905f2d735623af9214148afJeff Brown providerInstance.setCallback(null); 222711417b1cfde8f1749905f2d735623af9214148afJeff Brown // 2. Clear the discovery request. 222811417b1cfde8f1749905f2d735623af9214148afJeff Brown providerInstance.setDiscoveryRequest(null); 222928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown // 3. Delete the provider's contents. 2230fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo provider = mProviders.get(index); 2231fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown updateProviderContents(provider, null); 223228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown // 4. Remove the provider from the list. 223311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 223411417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Provider removed: " + provider); 223511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2236fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_REMOVED, provider); 2237fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mProviders.remove(index); 2238c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2239c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2240c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2241e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas void updateProviderDescriptor(MediaRouteProvider providerInstance, 224211417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteProviderDescriptor descriptor) { 2243fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int index = findProviderInfo(providerInstance); 2244c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index >= 0) { 2245fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // Update the provider's contents. 2246fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo provider = mProviders.get(index); 2247fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown updateProviderContents(provider, descriptor); 2248c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2249c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2250c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2251fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private int findProviderInfo(MediaRouteProvider providerInstance) { 2252fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown final int count = mProviders.size(); 2253c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown for (int i = 0; i < count; i++) { 2254fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mProviders.get(i).mProviderInstance == providerInstance) { 2255c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return i; 2256c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2257c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2258c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return -1; 2259c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2260c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2261fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private void updateProviderContents(ProviderInfo provider, 226211417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteProviderDescriptor providerDescriptor) { 2263fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (provider.updateDescriptor(providerDescriptor)) { 2264c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Update all existing routes and reorder them to match 2265c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // the order of their descriptors. 2266c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int targetIndex = 0; 2267567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown boolean selectedRouteDescriptorChanged = false; 2268c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (providerDescriptor != null) { 2269fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (providerDescriptor.isValid()) { 227011417b1cfde8f1749905f2d735623af9214148afJeff Brown final List<MediaRouteDescriptor> routeDescriptors = 227111417b1cfde8f1749905f2d735623af9214148afJeff Brown providerDescriptor.getRoutes(); 227211417b1cfde8f1749905f2d735623af9214148afJeff Brown final int routeCount = routeDescriptors.size(); 2273e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo // Updating route group's contents requires all member routes' information. 2274e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo // Add the groups to the lists and update them later. 2275e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo List<Pair<RouteInfo, MediaRouteDescriptor>> addedGroups = new ArrayList<>(); 2276e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo List<Pair<RouteInfo, MediaRouteDescriptor>> updatedGroups = 2277e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo new ArrayList<>(); 227811417b1cfde8f1749905f2d735623af9214148afJeff Brown for (int i = 0; i < routeCount; i++) { 227911417b1cfde8f1749905f2d735623af9214148afJeff Brown final MediaRouteDescriptor routeDescriptor = routeDescriptors.get(i); 2280c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final String id = routeDescriptor.getId(); 2281fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown final int sourceIndex = provider.findRouteByDescriptorId(id); 2282c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (sourceIndex < 0) { 2283c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 1. Add the route to the list. 2284cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown String uniqueId = assignRouteUniqueId(provider, id); 22851bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo boolean isGroup = routeDescriptor.getGroupMemberIds() != null; 2286e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo RouteInfo route = isGroup ? new RouteGroup(provider, id, uniqueId) : 2287e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo new RouteInfo(provider, id, uniqueId); 2288fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown provider.mRoutes.add(targetIndex++, route); 2289c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mRoutes.add(route); 2290c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 2. Create the route's contents. 2291e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (isGroup) { 2292abc73958d264e1eed7fd401a18be1d9ede8304ebAurimas Liutikas addedGroups.add(new Pair<>(route, routeDescriptor)); 2293e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } else { 2294e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo route.maybeUpdateDescriptor(routeDescriptor); 2295e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo // 3. Notify clients about addition. 2296e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (DEBUG) { 2297e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo Log.d(TAG, "Route added: " + route); 2298e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2299e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route); 230011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2301e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 2302fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } else if (sourceIndex < targetIndex) { 2303fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Log.w(TAG, "Ignoring route descriptor with duplicate id: " 2304fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown + routeDescriptor); 2305c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } else { 2306c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 1. Reorder the route within the list. 2307fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown RouteInfo route = provider.mRoutes.get(sourceIndex); 2308fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Collections.swap(provider.mRoutes, 2309c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sourceIndex, targetIndex++); 2310c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 2. Update the route's contents. 2311e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (route instanceof RouteGroup) { 2312abc73958d264e1eed7fd401a18be1d9ede8304ebAurimas Liutikas updatedGroups.add(new Pair<>(route, routeDescriptor)); 2313e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } else { 2314e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo // 3. Notify clients about changes. 2315e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (updateRouteDescriptorAndNotify(route, routeDescriptor) 2316e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo != 0) { 2317e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (route == mSelectedRoute) { 2318e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo selectedRouteDescriptorChanged = true; 2319567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 232011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2321c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2322c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2323c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2324e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo // Update the new and/or existing groups. 2325e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo for (Pair<RouteInfo, MediaRouteDescriptor> pair : addedGroups) { 2326e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo RouteInfo route = pair.first; 2327e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo route.maybeUpdateDescriptor(pair.second); 2328e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (DEBUG) { 2329e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo Log.d(TAG, "Route added: " + route); 2330e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2331e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route); 2332e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2333e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo for (Pair<RouteInfo, MediaRouteDescriptor> pair : updatedGroups) { 2334e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo RouteInfo route = pair.first; 2335e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (updateRouteDescriptorAndNotify(route, pair.second) != 0) { 2336e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (route == mSelectedRoute) { 2337e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo selectedRouteDescriptorChanged = true; 2338e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2339e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2340e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2341fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } else { 2342fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Log.w(TAG, "Ignoring invalid provider descriptor: " + providerDescriptor); 2343c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2344c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2345c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2346c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Dispose all remaining routes that do not have matching descriptors. 2347fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) { 2348fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // 1. Delete the route's contents. 2349fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown RouteInfo route = provider.mRoutes.get(i); 2350e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo route.maybeUpdateDescriptor(null); 2351fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // 2. Remove the route from the list. 235211417b1cfde8f1749905f2d735623af9214148afJeff Brown mRoutes.remove(route); 235335af7c925d660508fe0892f38eec99a3845630e4Chong Zhang } 235435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang 2355567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // Update the selected route if needed. 2356567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown updateSelectedRouteIfNeeded(selectedRouteDescriptorChanged); 235735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang 235835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // Now notify clients about routes that were removed. 235935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // We do this after updating the selected route to ensure 236035af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // that the framework media router observes the new route 236135af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // selection before the removal since removing the currently 236235af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // selected route may have side-effects. 236335af7c925d660508fe0892f38eec99a3845630e4Chong Zhang for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) { 236435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang RouteInfo route = provider.mRoutes.remove(i); 236511417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 236611417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Route removed: " + route); 236711417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2368fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_ROUTE_REMOVED, route); 2369c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2370fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 237111417b1cfde8f1749905f2d735623af9214148afJeff Brown // Notify provider changed. 237211417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 237311417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Provider changed: " + provider); 237411417b1cfde8f1749905f2d735623af9214148afJeff Brown } 237511417b1cfde8f1749905f2d735623af9214148afJeff Brown mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_CHANGED, provider); 2376c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2377c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2378c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2379e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo private int updateRouteDescriptorAndNotify(RouteInfo route, 2380e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo MediaRouteDescriptor routeDescriptor) { 2381e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo int changes = route.maybeUpdateDescriptor(routeDescriptor); 2382e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (changes != 0) { 2383e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if ((changes & RouteInfo.CHANGE_GENERAL) != 0) { 2384e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (DEBUG) { 2385e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo Log.d(TAG, "Route changed: " + route); 2386e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2387e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mCallbackHandler.post( 2388e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo CallbackHandler.MSG_ROUTE_CHANGED, route); 2389e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2390e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if ((changes & RouteInfo.CHANGE_VOLUME) != 0) { 2391e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (DEBUG) { 2392e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo Log.d(TAG, "Route volume changed: " + route); 2393e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2394e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mCallbackHandler.post( 2395e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo CallbackHandler.MSG_ROUTE_VOLUME_CHANGED, route); 2396e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2397e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if ((changes & RouteInfo.CHANGE_PRESENTATION_DISPLAY) != 0) { 2398e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (DEBUG) { 2399e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo Log.d(TAG, "Route presentation display changed: " 2400e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo + route); 2401e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2402e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mCallbackHandler.post(CallbackHandler. 2403e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED, route); 2404e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2405e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2406e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return changes; 2407e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2408e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 2409cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown private String assignRouteUniqueId(ProviderInfo provider, String routeDescriptorId) { 2410cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown // Although route descriptor ids are unique within a provider, it's 2411cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown // possible for there to be two providers with the same package name. 2412cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown // Therefore we must dedupe the composite id. 241338e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo String componentName = provider.getComponentName().flattenToShortString(); 241438e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo String uniqueId = componentName + ":" + routeDescriptorId; 2415cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown if (findRouteByUniqueId(uniqueId) < 0) { 2416abc73958d264e1eed7fd401a18be1d9ede8304ebAurimas Liutikas mUniqueIdMap.put(new Pair<>(componentName, routeDescriptorId), uniqueId); 2417cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return uniqueId; 2418cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 241938e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo Log.w(TAG, "Either " + routeDescriptorId + " isn't unique in " + componentName 2420480052774a9fb375d68b7a0373a5bd8d733f8dc0Jae Seo + " or we're trying to assign a unique ID for an already added route"); 2421cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown for (int i = 2; ; i++) { 2422cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown String newUniqueId = String.format(Locale.US, "%s_%d", uniqueId, i); 2423cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown if (findRouteByUniqueId(newUniqueId) < 0) { 2424abc73958d264e1eed7fd401a18be1d9ede8304ebAurimas Liutikas mUniqueIdMap.put(new Pair<>(componentName, routeDescriptorId), newUniqueId); 2425cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return newUniqueId; 2426cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2427cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2428cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2429cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown 2430cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown private int findRouteByUniqueId(String uniqueId) { 2431cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown final int count = mRoutes.size(); 2432cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown for (int i = 0; i < count; i++) { 2433cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown if (mRoutes.get(i).mUniqueId.equals(uniqueId)) { 2434cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return i; 2435cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2436cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2437cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return -1; 2438cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2439cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown 244038e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo private String getUniqueId(ProviderInfo provider, String routeDescriptorId) { 244138e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo String componentName = provider.getComponentName().flattenToShortString(); 2442abc73958d264e1eed7fd401a18be1d9ede8304ebAurimas Liutikas return mUniqueIdMap.get(new Pair<>(componentName, routeDescriptorId)); 244338e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo } 244438e4efe885198f63fb26b73d79d4636263ba6a5cJae Seo 2445567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private void updateSelectedRouteIfNeeded(boolean selectedRouteDescriptorChanged) { 2446567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // Update default route. 2447567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (mDefaultRoute != null && !isRouteSelectable(mDefaultRoute)) { 244835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang Log.i(TAG, "Clearing the default route because it " 2449567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown + "is no longer selectable: " + mDefaultRoute); 2450c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDefaultRoute = null; 2451c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2452c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mDefaultRoute == null && !mRoutes.isEmpty()) { 2453c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown for (RouteInfo route : mRoutes) { 2454c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (isSystemDefaultRoute(route) && isRouteSelectable(route)) { 2455c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDefaultRoute = route; 2456567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown Log.i(TAG, "Found default route: " + mDefaultRoute); 2457c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 2458c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2459c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2460c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2461567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 24622573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim // Update bluetooth route. 24632573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim if (mBluetoothRoute != null && !isRouteSelectable(mBluetoothRoute)) { 24642573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim Log.i(TAG, "Clearing the bluetooth route because it " 24652573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim + "is no longer selectable: " + mBluetoothRoute); 24662573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim mBluetoothRoute = null; 24672573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim } 24682573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim if (mBluetoothRoute == null && !mRoutes.isEmpty()) { 24692573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim for (RouteInfo route : mRoutes) { 24706adc8ba4e18a8b97b0c6719975d7259f7a2500feSungsoo if (isSystemLiveAudioOnlyRoute(route) && isRouteSelectable(route)) { 24712573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim mBluetoothRoute = route; 24722573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim Log.i(TAG, "Found bluetooth route: " + mBluetoothRoute); 24732573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim break; 24742573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim } 24752573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim } 24762573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim } 24772573ecb4fff9a354dd8fe973bf1c0d5494f83ca3Sungsoo Lim 2478567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // Update selected route. 2479567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (mSelectedRoute != null && !isRouteSelectable(mSelectedRoute)) { 2480567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown Log.i(TAG, "Unselecting the current route because it " 2481567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown + "is no longer selectable: " + mSelectedRoute); 248294be6100218126ce6a08bf1f56209578500b361fRoboErik setSelectedRouteInternal(null, 248394be6100218126ce6a08bf1f56209578500b361fRoboErik MediaRouter.UNSELECT_REASON_UNKNOWN); 2484567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2485c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute == null) { 2486567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // Choose a new route. 2487567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // This will have the side-effect of updating the playback info when 2488567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // the new route is selected. 248994be6100218126ce6a08bf1f56209578500b361fRoboErik setSelectedRouteInternal(chooseFallbackRoute(), 249094be6100218126ce6a08bf1f56209578500b361fRoboErik MediaRouter.UNSELECT_REASON_UNKNOWN); 2491567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } else if (selectedRouteDescriptorChanged) { 2492b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo // In case the selected route is a route group, select/unselect route controllers 2493b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo // for the added/removed route members. 2494b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo if (mSelectedRoute instanceof RouteGroup) { 2495b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo List<RouteInfo> routes = ((RouteGroup) mSelectedRoute).getRoutes(); 2496b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo // Build a set of descriptor IDs for the new route group. 2497abc73958d264e1eed7fd401a18be1d9ede8304ebAurimas Liutikas Set<String> idSet = new HashSet<>(); 2498b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo for (RouteInfo route : routes) { 2499b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo idSet.add(route.mDescriptorId); 2500b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo } 2501b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo // Unselect route controllers for the removed routes. 2502b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo Iterator<Map.Entry<String, RouteController>> iter = 2503b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo mRouteControllerMap.entrySet().iterator(); 2504b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo while (iter.hasNext()) { 2505b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo Map.Entry<String, RouteController> entry = iter.next(); 2506b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo if (!idSet.contains(entry.getKey())) { 2507b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo RouteController controller = entry.getValue(); 2508b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo controller.onUnselect(); 2509b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo controller.onRelease(); 2510b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo iter.remove(); 2511b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo } 2512b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo } 2513b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo // Select route controllers for the added routes. 2514b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo for (RouteInfo route : routes) { 2515b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo if (!mRouteControllerMap.containsKey(route.mDescriptorId)) { 2516b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo RouteController controller = route.getProviderInstance() 2517f29704c9e02cb42d33f8329def3d494b5230a53bSungsoo Lim .onCreateRouteController( 2518f29704c9e02cb42d33f8329def3d494b5230a53bSungsoo Lim route.mDescriptorId, mSelectedRoute.mDescriptorId); 2519b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo controller.onSelect(); 2520b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo mRouteControllerMap.put(route.mDescriptorId, controller); 2521b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo } 2522b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo } 2523b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo } 2524567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // Update the playback info because the properties of the route have changed. 2525567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown updatePlaybackInfoFromSelectedRoute(); 2526c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2527c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2528c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2529beaaa68430d0ad0fcf686e5d4694536f61ee79c1Jae Seo RouteInfo chooseFallbackRoute() { 253035af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // When the current route is removed or no longer selectable, 253135af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // we want to revert to a live audio route if there is 253235af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // one (usually Bluetooth A2DP). Failing that, use 253335af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // the default route. 253435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang for (RouteInfo route : mRoutes) { 253535af7c925d660508fe0892f38eec99a3845630e4Chong Zhang if (route != mDefaultRoute 253635af7c925d660508fe0892f38eec99a3845630e4Chong Zhang && isSystemLiveAudioOnlyRoute(route) 253735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang && isRouteSelectable(route)) { 253835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang return route; 253935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang } 254035af7c925d660508fe0892f38eec99a3845630e4Chong Zhang } 254135af7c925d660508fe0892f38eec99a3845630e4Chong Zhang return mDefaultRoute; 254235af7c925d660508fe0892f38eec99a3845630e4Chong Zhang } 254335af7c925d660508fe0892f38eec99a3845630e4Chong Zhang 254435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang private boolean isSystemLiveAudioOnlyRoute(RouteInfo route) { 254535af7c925d660508fe0892f38eec99a3845630e4Chong Zhang return route.getProviderInstance() == mSystemProvider 254635af7c925d660508fe0892f38eec99a3845630e4Chong Zhang && route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO) 254735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang && !route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO); 254835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang } 254935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang 2550c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private boolean isRouteSelectable(RouteInfo route) { 2551c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This tests whether the route is still valid and enabled. 2552c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // The route descriptor field is set to null when the route is removed. 2553c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return route.mDescriptor != null && route.mEnabled; 2554c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2555c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2556c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private boolean isSystemDefaultRoute(RouteInfo route) { 2557fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return route.getProviderInstance() == mSystemProvider 2558c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown && route.mDescriptorId.equals( 2559c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown SystemMediaRouteProvider.DEFAULT_ROUTE_ID); 2560c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2561c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 256294be6100218126ce6a08bf1f56209578500b361fRoboErik private void setSelectedRouteInternal(RouteInfo route, int unselectReason) { 2563c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute != route) { 2564c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute != null) { 256511417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 256694be6100218126ce6a08bf1f56209578500b361fRoboErik Log.d(TAG, "Route unselected: " + mSelectedRoute + " reason: " 256794be6100218126ce6a08bf1f56209578500b361fRoboErik + unselectReason); 256811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 256934f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho mCallbackHandler.post(CallbackHandler.MSG_ROUTE_UNSELECTED, mSelectedRoute, 257034f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho unselectReason); 2571c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRouteController != null) { 257294be6100218126ce6a08bf1f56209578500b361fRoboErik mSelectedRouteController.onUnselect(unselectReason); 2573129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown mSelectedRouteController.onRelease(); 2574c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSelectedRouteController = null; 2575c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2576b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo if (!mRouteControllerMap.isEmpty()) { 2577b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo for (RouteController controller : mRouteControllerMap.values()) { 25783cb412fab95f51525d1e777e5ae6d12021bb2c93Donghyun Cho controller.onUnselect(unselectReason); 25791bb3dfe1c70801c1981406e93b9ca624a11dcb3bJae Seo controller.onRelease(); 2580bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang } 2581b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo mRouteControllerMap.clear(); 2582bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang } 2583c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2584c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2585c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSelectedRoute = route; 2586c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2587c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute != null) { 2588fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSelectedRouteController = route.getProviderInstance().onCreateRouteController( 2589c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown route.mDescriptorId); 2590c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRouteController != null) { 2591129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown mSelectedRouteController.onSelect(); 2592c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 259311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 259411417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Route selected: " + mSelectedRoute); 259511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2596c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_ROUTE_SELECTED, mSelectedRoute); 2597bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang 2598bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang if (mSelectedRoute instanceof RouteGroup) { 2599b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo List<RouteInfo> routes = ((RouteGroup) mSelectedRoute).getRoutes(); 2600b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo mRouteControllerMap.clear(); 2601b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo for (RouteInfo r : routes) { 26022f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim RouteController controller = 26032f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim r.getProviderInstance().onCreateRouteController( 26042f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim r.mDescriptorId, mSelectedRoute.mDescriptorId); 2605bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang controller.onSelect(); 2606b65ed0711d50e0cf71df299e61725dc5699c52a3Jae Seo mRouteControllerMap.put(r.mDescriptorId, controller); 2607bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang } 2608bc171eedb6cbadf07b5a1f173a9e9efbd554e066Dongwon Kang } 2609c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2610567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2611567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown updatePlaybackInfoFromSelectedRoute(); 2612c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2613c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2614c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2615c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 2616c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getSystemRouteByDescriptorId(String id) { 2617fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int providerIndex = findProviderInfo(mSystemProvider); 2618c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (providerIndex >= 0) { 2619fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo provider = mProviders.get(providerIndex); 2620fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int routeIndex = provider.findRouteByDescriptorId(id); 2621c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (routeIndex >= 0) { 2622fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return provider.mRoutes.get(routeIndex); 2623c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2624c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2625c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return null; 2626c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2627c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2628567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void addRemoteControlClient(Object rcc) { 2629567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown int index = findRemoteControlClientRecord(rcc); 2630567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (index < 0) { 2631567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown RemoteControlClientRecord record = new RemoteControlClientRecord(rcc); 2632567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRemoteControlClients.add(record); 2633567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2634567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2635567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2636567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void removeRemoteControlClient(Object rcc) { 2637567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown int index = findRemoteControlClientRecord(rcc); 2638567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (index >= 0) { 2639567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown RemoteControlClientRecord record = mRemoteControlClients.remove(index); 2640567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown record.disconnect(); 2641567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2642567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2643567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2644bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik public void setMediaSession(Object session) { 26451cc48d3ec09b9afc44ba18910e0e6f59818388d7Donghyun Cho setMediaSessionRecord(session != null ? new MediaSessionRecord(session) : null); 2646bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2647bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 26485c9469e010106467791b47b0fa83efda84491a21RoboErik public void setMediaSessionCompat(final MediaSessionCompat session) { 2649e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik mCompatSession = session; 26505c9469e010106467791b47b0fa83efda84491a21RoboErik if (android.os.Build.VERSION.SDK_INT >= 21) { 26511cc48d3ec09b9afc44ba18910e0e6f59818388d7Donghyun Cho setMediaSessionRecord(session != null ? new MediaSessionRecord(session) : null); 26525c9469e010106467791b47b0fa83efda84491a21RoboErik } else if (android.os.Build.VERSION.SDK_INT >= 14) { 26535c9469e010106467791b47b0fa83efda84491a21RoboErik if (mRccMediaSession != null) { 26545c9469e010106467791b47b0fa83efda84491a21RoboErik removeRemoteControlClient(mRccMediaSession.getRemoteControlClient()); 26555c9469e010106467791b47b0fa83efda84491a21RoboErik mRccMediaSession.removeOnActiveChangeListener(mSessionActiveListener); 26565c9469e010106467791b47b0fa83efda84491a21RoboErik } 26575c9469e010106467791b47b0fa83efda84491a21RoboErik mRccMediaSession = session; 2658d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake if (session != null) { 2659d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake session.addOnActiveChangeListener(mSessionActiveListener); 2660d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake if (session.isActive()) { 2661d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake addRemoteControlClient(session.getRemoteControlClient()); 2662d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake } 26635c9469e010106467791b47b0fa83efda84491a21RoboErik } 26645c9469e010106467791b47b0fa83efda84491a21RoboErik } 26655c9469e010106467791b47b0fa83efda84491a21RoboErik } 26665c9469e010106467791b47b0fa83efda84491a21RoboErik 26671cc48d3ec09b9afc44ba18910e0e6f59818388d7Donghyun Cho private void setMediaSessionRecord(MediaSessionRecord mediaSessionRecord) { 26681cc48d3ec09b9afc44ba18910e0e6f59818388d7Donghyun Cho if (mMediaSession != null) { 26691cc48d3ec09b9afc44ba18910e0e6f59818388d7Donghyun Cho mMediaSession.clearVolumeHandling(); 26701cc48d3ec09b9afc44ba18910e0e6f59818388d7Donghyun Cho } 26711cc48d3ec09b9afc44ba18910e0e6f59818388d7Donghyun Cho mMediaSession = mediaSessionRecord; 26721cc48d3ec09b9afc44ba18910e0e6f59818388d7Donghyun Cho if (mediaSessionRecord != null) { 26731cc48d3ec09b9afc44ba18910e0e6f59818388d7Donghyun Cho updatePlaybackInfoFromSelectedRoute(); 26741cc48d3ec09b9afc44ba18910e0e6f59818388d7Donghyun Cho } 26751cc48d3ec09b9afc44ba18910e0e6f59818388d7Donghyun Cho } 26761cc48d3ec09b9afc44ba18910e0e6f59818388d7Donghyun Cho 267794be6100218126ce6a08bf1f56209578500b361fRoboErik public MediaSessionCompat.Token getMediaSessionToken() { 267894be6100218126ce6a08bf1f56209578500b361fRoboErik if (mMediaSession != null) { 267994be6100218126ce6a08bf1f56209578500b361fRoboErik return mMediaSession.getToken(); 2680e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik } else if (mCompatSession != null) { 2681e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik return mCompatSession.getSessionToken(); 268294be6100218126ce6a08bf1f56209578500b361fRoboErik } 268394be6100218126ce6a08bf1f56209578500b361fRoboErik return null; 268494be6100218126ce6a08bf1f56209578500b361fRoboErik } 268594be6100218126ce6a08bf1f56209578500b361fRoboErik 2686567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private int findRemoteControlClientRecord(Object rcc) { 2687567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown final int count = mRemoteControlClients.size(); 2688567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown for (int i = 0; i < count; i++) { 2689567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown RemoteControlClientRecord record = mRemoteControlClients.get(i); 2690567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (record.getRemoteControlClient() == rcc) { 2691567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown return i; 2692567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2693567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2694567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown return -1; 2695567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2696567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2697567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private void updatePlaybackInfoFromSelectedRoute() { 2698567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (mSelectedRoute != null) { 2699567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mPlaybackInfo.volume = mSelectedRoute.getVolume(); 2700567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mPlaybackInfo.volumeMax = mSelectedRoute.getVolumeMax(); 2701567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mPlaybackInfo.volumeHandling = mSelectedRoute.getVolumeHandling(); 2702567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mPlaybackInfo.playbackStream = mSelectedRoute.getPlaybackStream(); 2703567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mPlaybackInfo.playbackType = mSelectedRoute.getPlaybackType(); 2704567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2705567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown final int count = mRemoteControlClients.size(); 2706567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown for (int i = 0; i < count; i++) { 2707567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown RemoteControlClientRecord record = mRemoteControlClients.get(i); 2708567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown record.updatePlaybackInfo(); 2709567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2710bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik if (mMediaSession != null) { 27117e7e4aba6bfe8841ccda7e2e0bec7e9147dc547aHyundo Moon if (mSelectedRoute == getDefaultRoute() 27127e7e4aba6bfe8841ccda7e2e0bec7e9147dc547aHyundo Moon || mSelectedRoute == getBluetoothRoute()) { 2713f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik // Local route 2714f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik mMediaSession.clearVolumeHandling(); 2715f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik } else { 2716f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake @VolumeProviderCompat.ControlType int controlType = 2717f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake VolumeProviderCompat.VOLUME_CONTROL_FIXED; 2718f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik if (mPlaybackInfo.volumeHandling 2719f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE) { 2720f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik controlType = VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE; 2721f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik } 2722f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik mMediaSession.configureVolume(controlType, mPlaybackInfo.volumeMax, 2723f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik mPlaybackInfo.volume); 2724bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2725f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik } 2726f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik } else { 2727f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik if (mMediaSession != null) { 2728f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik mMediaSession.clearVolumeHandling(); 2729bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2730567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2731567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2732567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2733c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final class ProviderCallback extends MediaRouteProvider.Callback { 2734e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas ProviderCallback() { 2735e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas } 2736e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas 2737c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 2738c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onDescriptorChanged(MediaRouteProvider provider, 273911417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteProviderDescriptor descriptor) { 2740c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown updateProviderDescriptor(provider, descriptor); 2741c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2742c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2743c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2744bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik private final class MediaSessionRecord { 2745bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik private final MediaSessionCompat mMsCompat; 2746bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 2747f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake private @VolumeProviderCompat.ControlType int mControlType; 2748bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik private int mMaxVolume; 2749bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik private VolumeProviderCompat mVpCompat; 2750bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 2751bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik public MediaSessionRecord(Object mediaSession) { 2752dea052cbd7d523a47ce8fff8b4f2fd0c39b132c6Hyundo Moon mMsCompat = MediaSessionCompat.fromMediaSession(mApplicationContext, mediaSession); 2753bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2754bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 27551cc48d3ec09b9afc44ba18910e0e6f59818388d7Donghyun Cho public MediaSessionRecord(MediaSessionCompat mediaSessionCompat) { 27561cc48d3ec09b9afc44ba18910e0e6f59818388d7Donghyun Cho mMsCompat = mediaSessionCompat; 27571cc48d3ec09b9afc44ba18910e0e6f59818388d7Donghyun Cho } 27581cc48d3ec09b9afc44ba18910e0e6f59818388d7Donghyun Cho 2759f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake public void configureVolume(@VolumeProviderCompat.ControlType int controlType, 2760f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake int max, int current) { 2761bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik if (mVpCompat != null && controlType == mControlType && max == mMaxVolume) { 2762bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik // If we haven't changed control type or max just set the 2763bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik // new current volume 2764bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mVpCompat.setCurrentVolume(current); 2765bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } else { 2766bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik // Otherwise create a new provider and update 2767bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mVpCompat = new VolumeProviderCompat(controlType, max, current) { 2768bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik @Override 2769a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik public void onSetVolumeTo(final int volume) { 2770a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik mCallbackHandler.post(new Runnable() { 2771a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik @Override 2772a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik public void run() { 2773a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik if (mSelectedRoute != null) { 2774a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik mSelectedRoute.requestSetVolume(volume); 2775a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik } 2776a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik } 2777a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik }); 2778bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2779bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 2780bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik @Override 2781a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik public void onAdjustVolume(final int direction) { 2782a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik mCallbackHandler.post(new Runnable() { 2783a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik @Override 2784a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik public void run() { 2785a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik if (mSelectedRoute != null) { 2786a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik mSelectedRoute.requestUpdateVolume(direction); 2787a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik } 2788a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik } 2789a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik }); 2790bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2791bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik }; 2792bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mMsCompat.setPlaybackToRemote(mVpCompat); 2793bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2794bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2795bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 2796bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik public void clearVolumeHandling() { 2797bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mMsCompat.setPlaybackToLocal(mPlaybackInfo.playbackStream); 2798bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mVpCompat = null; 2799bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2800bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 280194be6100218126ce6a08bf1f56209578500b361fRoboErik public MediaSessionCompat.Token getToken() { 280294be6100218126ce6a08bf1f56209578500b361fRoboErik return mMsCompat.getSessionToken(); 280394be6100218126ce6a08bf1f56209578500b361fRoboErik } 2804bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2805bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 2806567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private final class RemoteControlClientRecord 2807567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown implements RemoteControlClientCompat.VolumeCallback { 2808567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private final RemoteControlClientCompat mRccCompat; 2809567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private boolean mDisconnected; 2810567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2811567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public RemoteControlClientRecord(Object rcc) { 2812567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRccCompat = RemoteControlClientCompat.obtain(mApplicationContext, rcc); 2813567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRccCompat.setVolumeCallback(this); 2814567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown updatePlaybackInfo(); 2815567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2816567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2817567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public Object getRemoteControlClient() { 2818567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown return mRccCompat.getRemoteControlClient(); 2819567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2820567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2821567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void disconnect() { 2822567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mDisconnected = true; 2823567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRccCompat.setVolumeCallback(null); 2824567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2825567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2826567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void updatePlaybackInfo() { 2827567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRccCompat.setPlaybackInfo(mPlaybackInfo); 2828567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2829567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2830567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown @Override 2831567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void onVolumeSetRequest(int volume) { 2832567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (!mDisconnected && mSelectedRoute != null) { 2833567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mSelectedRoute.requestSetVolume(volume); 2834567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2835567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2836567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2837567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown @Override 2838567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void onVolumeUpdateRequest(int direction) { 2839567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (!mDisconnected && mSelectedRoute != null) { 2840567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mSelectedRoute.requestUpdateVolume(direction); 2841567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2842567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2843567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2844567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2845c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final class CallbackHandler extends Handler { 28469fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown private final ArrayList<CallbackRecord> mTempCallbackRecords = 28479fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown new ArrayList<CallbackRecord>(); 2848c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 284911417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final int MSG_TYPE_MASK = 0xff00; 285011417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final int MSG_TYPE_ROUTE = 0x0100; 285111417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final int MSG_TYPE_PROVIDER = 0x0200; 285211417b1cfde8f1749905f2d735623af9214148afJeff Brown 285311417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_ADDED = MSG_TYPE_ROUTE | 1; 285411417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_REMOVED = MSG_TYPE_ROUTE | 2; 285511417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_CHANGED = MSG_TYPE_ROUTE | 3; 285611417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_VOLUME_CHANGED = MSG_TYPE_ROUTE | 4; 285711417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED = MSG_TYPE_ROUTE | 5; 285811417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_SELECTED = MSG_TYPE_ROUTE | 6; 285911417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_UNSELECTED = MSG_TYPE_ROUTE | 7; 286011417b1cfde8f1749905f2d735623af9214148afJeff Brown 286111417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_PROVIDER_ADDED = MSG_TYPE_PROVIDER | 1; 286211417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_PROVIDER_REMOVED = MSG_TYPE_PROVIDER | 2; 286311417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_PROVIDER_CHANGED = MSG_TYPE_PROVIDER | 3; 2864c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2865e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas CallbackHandler() { 2866e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas } 2867e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas 2868fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void post(int msg, Object obj) { 2869fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown obtainMessage(msg, obj).sendToTarget(); 2870c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2871c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 287234f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho public void post(int msg, Object obj, int arg) { 287334f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho Message message = obtainMessage(msg, obj); 287434f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho message.arg1 = arg; 287534f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho message.sendToTarget(); 287634f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho } 287734f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho 2878c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 2879c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void handleMessage(Message msg) { 2880c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final int what = msg.what; 2881fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown final Object obj = msg.obj; 288234f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho final int arg = msg.arg1; 2883c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2884d38d8790c68d4d82a278d42e7f2a1382d005c637Sungsoo if (what == MSG_ROUTE_CHANGED 2885d38d8790c68d4d82a278d42e7f2a1382d005c637Sungsoo && getSelectedRoute().getId().equals(((RouteInfo) obj).getId())) { 2886d38d8790c68d4d82a278d42e7f2a1382d005c637Sungsoo updateSelectedRouteIfNeeded(true); 2887d38d8790c68d4d82a278d42e7f2a1382d005c637Sungsoo } 2888d38d8790c68d4d82a278d42e7f2a1382d005c637Sungsoo 2889c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Synchronize state with the system media router. 2890fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown syncWithSystemProvider(what, obj); 2891c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2892c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Invoke all registered callbacks. 28939fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown // Build a list of callbacks before invoking them in case callbacks 28949fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown // are added or removed during dispatch. 2895c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown try { 28969fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int i = mRouters.size(); --i >= 0; ) { 28979fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown MediaRouter router = mRouters.get(i).get(); 28989fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown if (router == null) { 28999fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouters.remove(i); 29009fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } else { 29019fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mTempCallbackRecords.addAll(router.mCallbackRecords); 2902c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2903c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 29049fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown 29059fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown final int callbackCount = mTempCallbackRecords.size(); 29069fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int i = 0; i < callbackCount; i++) { 290734f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho invokeCallback(mTempCallbackRecords.get(i), what, obj, arg); 29089fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } 2909c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } finally { 29109fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mTempCallbackRecords.clear(); 2911c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2912c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2913c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2914fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private void syncWithSystemProvider(int what, Object obj) { 2915c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown switch (what) { 2916c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_ADDED: 2917fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSystemProvider.onSyncRouteAdded((RouteInfo)obj); 2918c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 2919c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_REMOVED: 2920fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSystemProvider.onSyncRouteRemoved((RouteInfo)obj); 2921c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 2922c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_CHANGED: 2923fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSystemProvider.onSyncRouteChanged((RouteInfo)obj); 2924c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 2925c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_SELECTED: 2926fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSystemProvider.onSyncRouteSelected((RouteInfo)obj); 2927c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 2928c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2929c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2930c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 293134f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho private void invokeCallback(CallbackRecord record, int what, Object obj, int arg) { 29329fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown final MediaRouter router = record.mRouter; 293311417b1cfde8f1749905f2d735623af9214148afJeff Brown final MediaRouter.Callback callback = record.mCallback; 293411417b1cfde8f1749905f2d735623af9214148afJeff Brown switch (what & MSG_TYPE_MASK) { 293511417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_TYPE_ROUTE: { 293611417b1cfde8f1749905f2d735623af9214148afJeff Brown final RouteInfo route = (RouteInfo)obj; 293711417b1cfde8f1749905f2d735623af9214148afJeff Brown if (!record.filterRouteEvent(route)) { 293811417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 293911417b1cfde8f1749905f2d735623af9214148afJeff Brown } 294011417b1cfde8f1749905f2d735623af9214148afJeff Brown switch (what) { 294111417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_ADDED: 294211417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteAdded(router, route); 294311417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 294411417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_REMOVED: 294511417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteRemoved(router, route); 294611417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 294711417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_CHANGED: 294811417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteChanged(router, route); 294911417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 295011417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_VOLUME_CHANGED: 295111417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteVolumeChanged(router, route); 295211417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 295311417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED: 295411417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRoutePresentationDisplayChanged(router, route); 295511417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 295611417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_SELECTED: 295711417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteSelected(router, route); 295811417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 295911417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_UNSELECTED: 296034f02b24b3bc24d10b509c41ba221cc7a3205d12Donghyun Cho callback.onRouteUnselected(router, route, arg); 296111417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 296211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2963c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 296411417b1cfde8f1749905f2d735623af9214148afJeff Brown } 296511417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_TYPE_PROVIDER: { 296611417b1cfde8f1749905f2d735623af9214148afJeff Brown final ProviderInfo provider = (ProviderInfo)obj; 296711417b1cfde8f1749905f2d735623af9214148afJeff Brown switch (what) { 296811417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_PROVIDER_ADDED: 296911417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onProviderAdded(router, provider); 297011417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 297111417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_PROVIDER_REMOVED: 297211417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onProviderRemoved(router, provider); 297311417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 297411417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_PROVIDER_CHANGED: 297511417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onProviderChanged(router, provider); 297611417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 297711417b1cfde8f1749905f2d735623af9214148afJeff Brown } 297811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2979c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2980c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2981c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2982c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2983c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown} 2984