MediaRouter.java revision fa6f70e225a7370c1ecb21ee7076c9086ecb9e9a
19a1de308cea2d160778fd977825f10a07b49d738Adam Powell/*
29a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Copyright (C) 2012 The Android Open Source Project
39a1de308cea2d160778fd977825f10a07b49d738Adam Powell *
49a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Licensed under the Apache License, Version 2.0 (the "License");
59a1de308cea2d160778fd977825f10a07b49d738Adam Powell * you may not use this file except in compliance with the License.
69a1de308cea2d160778fd977825f10a07b49d738Adam Powell * You may obtain a copy of the License at
79a1de308cea2d160778fd977825f10a07b49d738Adam Powell *
89a1de308cea2d160778fd977825f10a07b49d738Adam Powell *      http://www.apache.org/licenses/LICENSE-2.0
99a1de308cea2d160778fd977825f10a07b49d738Adam Powell *
109a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Unless required by applicable law or agreed to in writing, software
119a1de308cea2d160778fd977825f10a07b49d738Adam Powell * distributed under the License is distributed on an "AS IS" BASIS,
129a1de308cea2d160778fd977825f10a07b49d738Adam Powell * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139a1de308cea2d160778fd977825f10a07b49d738Adam Powell * See the License for the specific language governing permissions and
149a1de308cea2d160778fd977825f10a07b49d738Adam Powell * limitations under the License.
159a1de308cea2d160778fd977825f10a07b49d738Adam Powell */
169a1de308cea2d160778fd977825f10a07b49d738Adam Powell
179a1de308cea2d160778fd977825f10a07b49d738Adam Powellpackage android.media;
189a1de308cea2d160778fd977825f10a07b49d738Adam Powell
19af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brownimport android.Manifest;
207b9c912f536925ac6ec43935d6e97506851b33d6Tor Norbyeimport android.annotation.DrawableRes;
219dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kangimport android.annotation.IntDef;
229d93a378c5158d14f80d46af70433234330ec568P.Y. Laligandimport android.annotation.NonNull;
23ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackbornimport android.app.ActivityThread;
248e37a85bf3dc39519942698dc90a3951306b934bAdam Powellimport android.content.BroadcastReceiver;
259a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.content.Context;
268e37a85bf3dc39519942698dc90a3951306b934bAdam Powellimport android.content.Intent;
278e37a85bf3dc39519942698dc90a3951306b934bAdam Powellimport android.content.IntentFilter;
28af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brownimport android.content.pm.PackageManager;
29b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackbornimport android.content.res.Resources;
30ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powellimport android.graphics.drawable.Drawable;
31705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.hardware.display.DisplayManager;
32705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.hardware.display.WifiDisplay;
33705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.hardware.display.WifiDisplayStatus;
345d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErikimport android.media.session.MediaSession;
359a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.os.Handler;
36632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.os.IBinder;
37af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brownimport android.os.Process;
38632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.os.RemoteException;
39632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.os.ServiceManager;
4069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.os.UserHandle;
41632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.text.TextUtils;
429a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.util.Log;
43705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.view.Display;
449a1de308cea2d160778fd977825f10a07b49d738Adam Powell
459dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kangimport java.lang.annotation.Retention;
469dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kangimport java.lang.annotation.RetentionPolicy;
479a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport java.util.ArrayList;
489a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport java.util.HashMap;
49d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powellimport java.util.List;
50e6585b32ea586743258a5457e2184ffc087f2d2fKenny Rootimport java.util.Objects;
5139d5c6172503620ac3761148adac5fd7fa20d02dAdam Powellimport java.util.concurrent.CopyOnWriteArrayList;
529a1de308cea2d160778fd977825f10a07b49d738Adam Powell
539a1de308cea2d160778fd977825f10a07b49d738Adam Powell/**
549a1de308cea2d160778fd977825f10a07b49d738Adam Powell * MediaRouter allows applications to control the routing of media channels
559a1de308cea2d160778fd977825f10a07b49d738Adam Powell * and streams from the current device to external speakers and destination devices.
569a1de308cea2d160778fd977825f10a07b49d738Adam Powell *
57b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * <p>A MediaRouter is retrieved through {@link Context#getSystemService(String)
58b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * Context.getSystemService()} of a {@link Context#MEDIA_ROUTER_SERVICE
59b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * Context.MEDIA_ROUTER_SERVICE}.
60b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn *
61b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * <p>The media router API is not thread-safe; all interactions with it must be
62b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * done from the main thread of the process.</p>
639a1de308cea2d160778fd977825f10a07b49d738Adam Powell */
649a1de308cea2d160778fd977825f10a07b49d738Adam Powellpublic class MediaRouter {
659a1de308cea2d160778fd977825f10a07b49d738Adam Powell    private static final String TAG = "MediaRouter";
6669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
679a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown    static class Static implements DisplayManager.DisplayListener {
6969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final Context mAppContext;
70b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final Resources mResources;
71632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn        final IAudioService mAudioService;
72705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final DisplayManager mDisplayService;
7369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final IMediaRouterService mMediaRouterService;
74b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final Handler mHandler;
7539d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        final CopyOnWriteArrayList<CallbackInfo> mCallbacks =
7639d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell                new CopyOnWriteArrayList<CallbackInfo>();
77b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
78b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
79b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final ArrayList<RouteCategory> mCategories = new ArrayList<RouteCategory>();
80b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
81b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final RouteCategory mSystemCategory;
82632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn
83705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
84b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
85705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        RouteInfo mDefaultAudioVideo;
86b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        RouteInfo mBluetoothA2dpRoute;
87b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
88b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        RouteInfo mSelectedRoute;
899a1de308cea2d160778fd977825f10a07b49d738Adam Powell
90af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        final boolean mCanConfigureWifiDisplays;
9166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        boolean mActivelyScanningWifiDisplays;
92ce468a35b388ca46578934706b38dbae94941643Jeff Brown        String mPreviousActiveWifiDisplayAddress;
93705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
9469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        int mDiscoveryRequestRouteTypes;
9569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        boolean mDiscoverRequestActiveScan;
9669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
9769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        int mCurrentUserId = -1;
9869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        IMediaRouterClient mClient;
9969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        MediaRouterClientState mClientState;
10069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
101705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() {
10266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            @Override
103632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
104632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                mHandler.post(new Runnable() {
105632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    @Override public void run() {
106705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                        updateAudioRoutes(newRoutes);
107632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    }
108632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                });
109632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
110632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn        };
111632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn
112b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        Static(Context appContext) {
11369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mAppContext = appContext;
114b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            mResources = Resources.getSystem();
115b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            mHandler = new Handler(appContext.getMainLooper());
1169a1de308cea2d160778fd977825f10a07b49d738Adam Powell
117632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
118632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            mAudioService = IAudioService.Stub.asInterface(b);
1199a1de308cea2d160778fd977825f10a07b49d738Adam Powell
120705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            mDisplayService = (DisplayManager) appContext.getSystemService(Context.DISPLAY_SERVICE);
121705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
12269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mMediaRouterService = IMediaRouterService.Stub.asInterface(
12369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
12469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
125dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell            mSystemCategory = new RouteCategory(
126dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell                    com.android.internal.R.string.default_audio_route_category_name,
127705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO, false);
128705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            mSystemCategory.mIsSystem = true;
129af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
130af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Only the system can configure wifi displays.  The display manager
131af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // enforces this with a permission check.  Set a flag here so that we
132af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // know whether this process is actually allowed to scan and connect.
133af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            mCanConfigureWifiDisplays = appContext.checkPermission(
134af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    Manifest.permission.CONFIGURE_WIFI_DISPLAY,
135af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    Process.myPid(), Process.myUid()) == PackageManager.PERMISSION_GRANTED;
136b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        }
137b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
138b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        // Called after sStatic is initialized
1398e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        void startMonitoringRoutes(Context appContext) {
140705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            mDefaultAudioVideo = new RouteInfo(mSystemCategory);
141705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            mDefaultAudioVideo.mNameResId = com.android.internal.R.string.default_audio_route_name;
142705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            mDefaultAudioVideo.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO;
1435830b0b33618940d65197cec99d697b21908fec8Chong Zhang            mDefaultAudioVideo.updatePresentationDisplay();
1442ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            addRouteStatic(mDefaultAudioVideo);
145632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn
1462ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            // This will select the active wifi display route if there is one.
1472ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            updateWifiDisplayStatus(mDisplayService.getWifiDisplayStatus());
1482ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell
1492ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            appContext.registerReceiver(new WifiDisplayStatusChangedReceiver(),
1502ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell                    new IntentFilter(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED));
1518e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            appContext.registerReceiver(new VolumeChangeReceiver(),
1528e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
1538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
15492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            mDisplayService.registerDisplayListener(this, mHandler);
15592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
156705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            AudioRoutesInfo newAudioRoutes = null;
157632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            try {
158705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
159632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            } catch (RemoteException e) {
160632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
161705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (newAudioRoutes != null) {
1622ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell                // This will select the active BT route if there is one and the current
1632ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell                // selected route is the default system route, or if there is no selected
1642ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell                // route yet.
165705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                updateAudioRoutes(newAudioRoutes);
166632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
167705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
16869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Bind to the media router service.
16969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            rebindAsUser(UserHandle.myUserId());
17069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
1712ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            // Select the default route if the above didn't sync us up
1722ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            // appropriately with relevant system state.
1732ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            if (mSelectedRoute == null) {
17469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                selectDefaultRouteStatic();
1752ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            }
176632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn        }
177632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn
178705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        void updateAudioRoutes(AudioRoutesInfo newRoutes) {
17918687b7a275b3782e63d52cdbe8ee9df191046e7Jae Seo            Log.v(TAG, "Updating audio routes: " + newRoutes);
1806156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock            if (newRoutes.mainType != mCurAudioRoutesInfo.mainType) {
1816156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                mCurAudioRoutesInfo.mainType = newRoutes.mainType;
182632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                int name;
1836156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                if ((newRoutes.mainType&AudioRoutesInfo.MAIN_HEADPHONES) != 0
1846156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                        || (newRoutes.mainType&AudioRoutesInfo.MAIN_HEADSET) != 0) {
185632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    name = com.android.internal.R.string.default_audio_route_name_headphones;
1866156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                } else if ((newRoutes.mainType&AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
187632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
1886156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                } else if ((newRoutes.mainType&AudioRoutesInfo.MAIN_HDMI) != 0) {
1894131a37366d59b5e61f55c4e48d2b22ee0c4cad4Adam Powell                    name = com.android.internal.R.string.default_media_route_name_hdmi;
190632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                } else {
191632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    name = com.android.internal.R.string.default_audio_route_name;
192632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                }
193705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                sStatic.mDefaultAudioVideo.mNameResId = name;
194705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                dispatchRouteChanged(sStatic.mDefaultAudioVideo);
195632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
196bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell
1976156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock            final int mainType = mCurAudioRoutesInfo.mainType;
1983f369684e13dfea0ba8ea134f3e95930b0dd7df0Adam Powell
1996156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock            if (!TextUtils.equals(newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) {
2006156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                mCurAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName;
2016156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                if (mCurAudioRoutesInfo.bluetoothName != null) {
202632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    if (sStatic.mBluetoothA2dpRoute == null) {
203632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        final RouteInfo info = new RouteInfo(sStatic.mSystemCategory);
2046156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                        info.mName = mCurAudioRoutesInfo.bluetoothName;
20556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown                        info.mDescription = sStatic.mResources.getText(
20656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown                                com.android.internal.R.string.bluetooth_a2dp_audio_route_name);
207632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        info.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO;
2089dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang                        info.mDeviceType = RouteInfo.DEVICE_TYPE_BLUETOOTH;
209632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        sStatic.mBluetoothA2dpRoute = info;
2102ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell                        addRouteStatic(sStatic.mBluetoothA2dpRoute);
211632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    } else {
2126156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                        sStatic.mBluetoothA2dpRoute.mName = mCurAudioRoutesInfo.bluetoothName;
213632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        dispatchRouteChanged(sStatic.mBluetoothA2dpRoute);
214632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    }
215632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                } else if (sStatic.mBluetoothA2dpRoute != null) {
21669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    removeRouteStatic(sStatic.mBluetoothA2dpRoute);
217632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    sStatic.mBluetoothA2dpRoute = null;
218632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                }
219632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
220bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell
221bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell            if (mBluetoothA2dpRoute != null) {
222fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                final boolean a2dpEnabled = isBluetoothA2dpOn();
223fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                if (mainType != AudioRoutesInfo.MAIN_SPEAKER &&
224fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                        mSelectedRoute == mBluetoothA2dpRoute && !a2dpEnabled) {
225fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                    selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mDefaultAudioVideo, false);
226fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                } else if ((mSelectedRoute == mDefaultAudioVideo || mSelectedRoute == null) &&
227fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                        a2dpEnabled) {
22869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mBluetoothA2dpRoute, false);
229bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell                }
230bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell            }
231b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        }
23292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
233fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo        boolean isBluetoothA2dpOn() {
234fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            try {
235fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                return mAudioService.isBluetoothA2dpOn();
236fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            } catch (RemoteException e) {
237fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                Log.e(TAG, "Error querying Bluetooth A2DP state", e);
238fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                return false;
239fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            }
240fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo        }
241fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo
24269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void updateDiscoveryRequest() {
24369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // What are we looking for today?
24469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int routeTypes = 0;
24569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int passiveRouteTypes = 0;
24669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean activeScan = false;
24769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean activeScanWifiDisplay = false;
24869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int count = mCallbacks.size();
24969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < count; i++) {
25069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                CallbackInfo cbi = mCallbacks.get(i);
25169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if ((cbi.flags & (CALLBACK_FLAG_PERFORM_ACTIVE_SCAN
25269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        | CALLBACK_FLAG_REQUEST_DISCOVERY)) != 0) {
25369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Discovery explicitly requested.
25469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    routeTypes |= cbi.type;
25569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else if ((cbi.flags & CALLBACK_FLAG_PASSIVE_DISCOVERY) != 0) {
25669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Discovery only passively requested.
25769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    passiveRouteTypes |= cbi.type;
25869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else {
25969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Legacy case since applications don't specify the discovery flag.
26069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Unfortunately we just have to assume they always need discovery
26169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // whenever they have a callback registered.
26269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    routeTypes |= cbi.type;
26369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
26469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if ((cbi.flags & CALLBACK_FLAG_PERFORM_ACTIVE_SCAN) != 0) {
26569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    activeScan = true;
266af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    if ((cbi.type & ROUTE_TYPE_REMOTE_DISPLAY) != 0) {
26769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        activeScanWifiDisplay = true;
26869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
26969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
27069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
27169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (routeTypes != 0 || activeScan) {
27269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // If someone else requests discovery then enable the passive listeners.
27369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // This is used by the MediaRouteButton and MediaRouteActionProvider since
27469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // they don't receive lifecycle callbacks from the Activity.
27569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                routeTypes |= passiveRouteTypes;
27669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
27769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
27869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Update wifi display scanning.
279ce468a35b388ca46578934706b38dbae94941643Jeff Brown            // TODO: All of this should be managed by the media router service.
280ce468a35b388ca46578934706b38dbae94941643Jeff Brown            if (mCanConfigureWifiDisplays) {
281ce468a35b388ca46578934706b38dbae94941643Jeff Brown                if (mSelectedRoute != null
282ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        && mSelectedRoute.matchesTypes(ROUTE_TYPE_REMOTE_DISPLAY)) {
283ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    // Don't scan while already connected to a remote display since
284ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    // it may interfere with the ongoing transmission.
285ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    activeScanWifiDisplay = false;
28666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown                }
287ce468a35b388ca46578934706b38dbae94941643Jeff Brown                if (activeScanWifiDisplay) {
288ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    if (!mActivelyScanningWifiDisplays) {
289ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mActivelyScanningWifiDisplays = true;
290ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mDisplayService.startWifiDisplayScan();
291ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    }
292ce468a35b388ca46578934706b38dbae94941643Jeff Brown                } else {
293ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    if (mActivelyScanningWifiDisplays) {
294ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mActivelyScanningWifiDisplays = false;
295ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mDisplayService.stopWifiDisplayScan();
296ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    }
29766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown                }
29866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            }
29966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
30069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Tell the media router service all about it.
30169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (routeTypes != mDiscoveryRequestRouteTypes
30269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    || activeScan != mDiscoverRequestActiveScan) {
30369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mDiscoveryRequestRouteTypes = routeTypes;
30469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mDiscoverRequestActiveScan = activeScan;
30569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                publishClientDiscoveryRequest();
30666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            }
30766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
30866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
30992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        @Override
31092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onDisplayAdded(int displayId) {
31192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            updatePresentationDisplays(displayId);
31292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
31392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
31492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        @Override
31592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onDisplayChanged(int displayId) {
31692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            updatePresentationDisplays(displayId);
31792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
31892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
31992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        @Override
32092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onDisplayRemoved(int displayId) {
32192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            updatePresentationDisplays(displayId);
32292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
32392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
32492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public Display[] getAllPresentationDisplays() {
32592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
32692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
32792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
32892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        private void updatePresentationDisplays(int changedDisplayId) {
32992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            final int count = mRoutes.size();
33092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            for (int i = 0; i < count; i++) {
3315830b0b33618940d65197cec99d697b21908fec8Chong Zhang                final RouteInfo route = mRoutes.get(i);
3325830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (route.updatePresentationDisplay() || (route.mPresentationDisplay != null
3335830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        && route.mPresentationDisplay.getDisplayId() == changedDisplayId)) {
3345830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    dispatchRoutePresentationDisplayChanged(route);
33592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                }
33692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            }
33792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
33869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
33969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void setSelectedRoute(RouteInfo info, boolean explicit) {
34069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Must be non-reentrant.
34169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mSelectedRoute = info;
34269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            publishClientSelectedRoute(explicit);
34369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
34469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
34569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void rebindAsUser(int userId) {
34669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mCurrentUserId != userId || userId < 0 || mClient == null) {
34769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (mClient != null) {
34869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    try {
34969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        mMediaRouterService.unregisterClient(mClient);
35069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    } catch (RemoteException ex) {
35169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        Log.e(TAG, "Unable to unregister media router client.", ex);
35269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
35369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClient = null;
35469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
35569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
35669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mCurrentUserId = userId;
35769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
35869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
35969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Client client = new Client();
36069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.registerClientAsUser(client,
36169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mAppContext.getPackageName(), userId);
36269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClient = client;
36369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
36469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to register media router client.", ex);
36569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
36669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
36769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                publishClientDiscoveryRequest();
36869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                publishClientSelectedRoute(false);
36969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                updateClientState();
37069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
37169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
37269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
37369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void publishClientDiscoveryRequest() {
37469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mClient != null) {
37569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
37669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.setDiscoveryRequest(mClient,
37769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mDiscoveryRequestRouteTypes, mDiscoverRequestActiveScan);
37869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
37969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to publish media router client discovery request.", ex);
38069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
38169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
38269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
38369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
38469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void publishClientSelectedRoute(boolean explicit) {
38569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mClient != null) {
38669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
38769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.setSelectedRoute(mClient,
38869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mSelectedRoute != null ? mSelectedRoute.mGlobalRouteId : null,
38969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            explicit);
39069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
39169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to publish media router client selected route.", ex);
39269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
39369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
39469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
39569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
39669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void updateClientState() {
39769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Update the client state.
39869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mClientState = null;
39969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mClient != null) {
40069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
40169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClientState = mMediaRouterService.getState(mClient);
40269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
40369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to retrieve media router client state.", ex);
40469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
40569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
40669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final ArrayList<MediaRouterClientState.RouteInfo> globalRoutes =
40769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClientState != null ? mClientState.routes : null;
40869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final String globallySelectedRouteId = mClientState != null ?
40969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClientState.globallySelectedRouteId : null;
41069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
41169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Add or update routes.
41269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int globalRouteCount = globalRoutes != null ? globalRoutes.size() : 0;
41369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < globalRouteCount; i++) {
41469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final MediaRouterClientState.RouteInfo globalRoute = globalRoutes.get(i);
41569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                RouteInfo route = findGlobalRoute(globalRoute.id);
41669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (route == null) {
41769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    route = makeGlobalRoute(globalRoute);
41869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    addRouteStatic(route);
41969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else {
42069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    updateGlobalRoute(route, globalRoute);
42169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
42269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
42369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
42469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Synchronize state with the globally selected route.
42569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (globallySelectedRouteId != null) {
42669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final RouteInfo route = findGlobalRoute(globallySelectedRouteId);
42769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (route == null) {
42869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.w(TAG, "Could not find new globally selected route: "
42969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            + globallySelectedRouteId);
43069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else if (route != mSelectedRoute) {
43169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    if (DEBUG) {
43269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        Log.d(TAG, "Selecting new globally selected route: " + route);
43369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
43469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    selectRouteStatic(route.mSupportedTypes, route, false);
43569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
43669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            } else if (mSelectedRoute != null && mSelectedRoute.mGlobalRouteId != null) {
43769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (DEBUG) {
43869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.d(TAG, "Unselecting previous globally selected route: " + mSelectedRoute);
43969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
44069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                selectDefaultRouteStatic();
44169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
44269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
44369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Remove defunct routes.
44469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            outer: for (int i = mRoutes.size(); i-- > 0; ) {
44569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final RouteInfo route = mRoutes.get(i);
44669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final String globalRouteId = route.mGlobalRouteId;
44769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (globalRouteId != null) {
44869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    for (int j = 0; j < globalRouteCount; j++) {
44969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        MediaRouterClientState.RouteInfo globalRoute = globalRoutes.get(j);
45069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (globalRouteId.equals(globalRoute.id)) {
45169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            continue outer; // found
45269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
45369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
45469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // not found
45569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    removeRouteStatic(route);
45669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
45769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
45869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
45969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
46069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void requestSetVolume(RouteInfo route, int volume) {
46169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mGlobalRouteId != null && mClient != null) {
46269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
46369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.requestSetVolume(mClient,
46469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            route.mGlobalRouteId, volume);
46569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
46669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.w(TAG, "Unable to request volume change.", ex);
46769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
46869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
46969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
47069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
47169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void requestUpdateVolume(RouteInfo route, int direction) {
47269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mGlobalRouteId != null && mClient != null) {
47369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
47469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.requestUpdateVolume(mClient,
47569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            route.mGlobalRouteId, direction);
47669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
47769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.w(TAG, "Unable to request volume change.", ex);
47869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
47969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
48069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
48169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
48269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        RouteInfo makeGlobalRoute(MediaRouterClientState.RouteInfo globalRoute) {
48369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            RouteInfo route = new RouteInfo(sStatic.mSystemCategory);
48469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mGlobalRouteId = globalRoute.id;
48569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mName = globalRoute.name;
48669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mDescription = globalRoute.description;
48769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mSupportedTypes = globalRoute.supportedTypes;
4889dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang            route.mDeviceType = globalRoute.deviceType;
48969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mEnabled = globalRoute.enabled;
49039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            route.setRealStatusCode(globalRoute.statusCode);
49169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mPlaybackType = globalRoute.playbackType;
49269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mPlaybackStream = globalRoute.playbackStream;
49369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mVolume = globalRoute.volume;
49469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mVolumeMax = globalRoute.volumeMax;
49569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mVolumeHandling = globalRoute.volumeHandling;
4965830b0b33618940d65197cec99d697b21908fec8Chong Zhang            route.mPresentationDisplayId = globalRoute.presentationDisplayId;
4975830b0b33618940d65197cec99d697b21908fec8Chong Zhang            route.updatePresentationDisplay();
49869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            return route;
49969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
50069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
50169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void updateGlobalRoute(RouteInfo route, MediaRouterClientState.RouteInfo globalRoute) {
50269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean changed = false;
50369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean volumeChanged = false;
50469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean presentationDisplayChanged = false;
50569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
506e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root            if (!Objects.equals(route.mName, globalRoute.name)) {
50769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mName = globalRoute.name;
50869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
50969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
510e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root            if (!Objects.equals(route.mDescription, globalRoute.description)) {
51169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mDescription = globalRoute.description;
51269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
51369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
514af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            final int oldSupportedTypes = route.mSupportedTypes;
515af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (oldSupportedTypes != globalRoute.supportedTypes) {
51669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mSupportedTypes = globalRoute.supportedTypes;
51769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
51869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
51969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mEnabled != globalRoute.enabled) {
52069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mEnabled = globalRoute.enabled;
52169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
52269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
52339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (route.mRealStatusCode != globalRoute.statusCode) {
52439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                route.setRealStatusCode(globalRoute.statusCode);
52569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
52669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
52769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mPlaybackType != globalRoute.playbackType) {
52869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mPlaybackType = globalRoute.playbackType;
52969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
53069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
53169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mPlaybackStream != globalRoute.playbackStream) {
53269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mPlaybackStream = globalRoute.playbackStream;
53369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
53469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
53569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mVolume != globalRoute.volume) {
53669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mVolume = globalRoute.volume;
53769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
53869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                volumeChanged = true;
53969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
54069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mVolumeMax != globalRoute.volumeMax) {
54169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mVolumeMax = globalRoute.volumeMax;
54269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
54369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                volumeChanged = true;
54469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
54569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mVolumeHandling != globalRoute.volumeHandling) {
54669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mVolumeHandling = globalRoute.volumeHandling;
54769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
54869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                volumeChanged = true;
54969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
5505830b0b33618940d65197cec99d697b21908fec8Chong Zhang            if (route.mPresentationDisplayId != globalRoute.presentationDisplayId) {
5515830b0b33618940d65197cec99d697b21908fec8Chong Zhang                route.mPresentationDisplayId = globalRoute.presentationDisplayId;
5525830b0b33618940d65197cec99d697b21908fec8Chong Zhang                route.updatePresentationDisplay();
55369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
55469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                presentationDisplayChanged = true;
55569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
55669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
55769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (changed) {
558af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                dispatchRouteChanged(route, oldSupportedTypes);
55969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
56069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (volumeChanged) {
56169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                dispatchRouteVolumeChanged(route);
56269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
56369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (presentationDisplayChanged) {
56469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                dispatchRoutePresentationDisplayChanged(route);
56569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
56669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
56769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
56869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        RouteInfo findGlobalRoute(String globalRouteId) {
56969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int count = mRoutes.size();
57069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < count; i++) {
57169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final RouteInfo route = mRoutes.get(i);
57269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (globalRouteId.equals(route.mGlobalRouteId)) {
57369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    return route;
57469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
57569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
57669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            return null;
57769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
57869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
57969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final class Client extends IMediaRouterClient.Stub {
58069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            @Override
58169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public void onStateChanged() {
58269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mHandler.post(new Runnable() {
58369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    @Override
58469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    public void run() {
58569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (Client.this == mClient) {
58669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            updateClientState();
58769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
58869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
58969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                });
59069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
59169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
592b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
5939a1de308cea2d160778fd977825f10a07b49d738Adam Powell
594b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static Static sStatic;
5959a1de308cea2d160778fd977825f10a07b49d738Adam Powell
5969a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
5979a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Route type flag for live audio.
5989a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
5999a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>A device that supports live audio routing will allow the media audio stream
6009a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * to be routed to supported destinations. This can include internal speakers or
6019a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * audio jacks on the device itself, A2DP devices, and more.</p>
6029a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
6039a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>Once initiated this routing is transparent to the application. All audio
6049a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * played on the media stream will be routed to the selected destination.</p>
6059a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
60669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_LIVE_AUDIO = 1 << 0;
6079a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6089a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
609705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * Route type flag for live video.
610705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     *
611705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * <p>A device that supports live video routing will allow a mirrored version
612705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * of the device's primary display or a customized
613705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * {@link android.app.Presentation Presentation} to be routed to supported destinations.</p>
614705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     *
615705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * <p>Once initiated, display mirroring is transparent to the application.
616705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * While remote routing is active the application may use a
617705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * {@link android.app.Presentation Presentation} to replace the mirrored view
618705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * on the external display with different content.</p>
61992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown     *
62092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown     * @see RouteInfo#getPresentationDisplay()
62192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown     * @see android.app.Presentation
622705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     */
62369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_LIVE_VIDEO = 1 << 1;
62469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
62569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
62669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Temporary interop constant to identify remote displays.
62769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide To be removed when media router API is updated.
62869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
62969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_REMOTE_DISPLAY = 1 << 2;
630705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
631705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    /**
6329a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Route type flag for application-specific usage.
6339a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
6349a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>Unlike other media route types, user routes are managed by the application.
6359a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * The MediaRouter will manage and dispatch events for user routes, but the application
6369a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * is expected to interpret the meaning of these events and perform the requested
6379a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * routing tasks.</p>
6389a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
63969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_USER = 1 << 23;
64069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
64169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static final int ROUTE_TYPE_ANY = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO
64269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            | ROUTE_TYPE_REMOTE_DISPLAY | ROUTE_TYPE_USER;
6439a1de308cea2d160778fd977825f10a07b49d738Adam Powell
64466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    /**
64566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Flag for {@link #addCallback}: Actively scan for routes while this callback
64666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * is registered.
64766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
64866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * When this flag is specified, the media router will actively scan for new
64966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * routes.  Certain routes, such as wifi display routes, may not be discoverable
65066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * except when actively scanning.  This flag is typically used when the route picker
65166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * dialog has been opened by the user to ensure that the route information is
65266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * up to date.
65366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p><p>
65466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Active scanning may consume a significant amount of power and may have intrusive
65566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * effects on wireless connectivity.  Therefore it is important that active scanning
65666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * only be requested when it is actually needed to satisfy a user request to
65766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * discover and select a new route.
65866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
65966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     */
66014507e257af5d71577574e25cbd690c4b54c9272Jeff Brown    public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1 << 0;
66166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
66266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    /**
66366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Flag for {@link #addCallback}: Do not filter route events.
66466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
66566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * When this flag is specified, the callback will be invoked for event that affect any
66669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * route even if they do not match the callback's filter.
66766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
66866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     */
66966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 1 << 1;
67066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
67169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
67269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Explicitly requests discovery.
67369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     *
67469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide Future API ported from support library.  Revisit this later.
67569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
67669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 1 << 2;
67769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
67869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
67969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Requests that discovery be performed but only if there is some other active
68069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * callback already registered.
68169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     *
68269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide Compatibility workaround for the fact that applications do not currently
68369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * request discovery explicitly (except when using the support library API).
68469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
68569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int CALLBACK_FLAG_PASSIVE_DISCOVERY = 1 << 3;
68669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
6870abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    /**
6880abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * Flag for {@link #isRouteAvailable}: Ignore the default route.
6890abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * <p>
6900abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * This flag is used to determine whether a matching non-default route is available.
6910abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * This constraint may be used to decide whether to offer the route chooser dialog
6920abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * to the user.  There is no point offering the chooser if there are no
6930abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * non-default choices.
6940abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * </p>
6950abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     *
6960abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @hide Future API ported from support library.  Revisit this later.
6970abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     */
6980abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1 << 0;
6990abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
7009a1de308cea2d160778fd977825f10a07b49d738Adam Powell    // Maps application contexts
7019a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static final HashMap<Context, MediaRouter> sRouters = new HashMap<Context, MediaRouter>();
7029a1de308cea2d160778fd977825f10a07b49d738Adam Powell
7039a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static String typesToString(int types) {
7049a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final StringBuilder result = new StringBuilder();
7059a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if ((types & ROUTE_TYPE_LIVE_AUDIO) != 0) {
7069a1de308cea2d160778fd977825f10a07b49d738Adam Powell            result.append("ROUTE_TYPE_LIVE_AUDIO ");
7079a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
7082bb7c122ef6ce8539dfbaeb3292adcd942185f82Bryan Mawhinney        if ((types & ROUTE_TYPE_LIVE_VIDEO) != 0) {
7092bb7c122ef6ce8539dfbaeb3292adcd942185f82Bryan Mawhinney            result.append("ROUTE_TYPE_LIVE_VIDEO ");
7102bb7c122ef6ce8539dfbaeb3292adcd942185f82Bryan Mawhinney        }
71169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if ((types & ROUTE_TYPE_REMOTE_DISPLAY) != 0) {
71269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            result.append("ROUTE_TYPE_REMOTE_DISPLAY ");
71369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
7149a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if ((types & ROUTE_TYPE_USER) != 0) {
7159a1de308cea2d160778fd977825f10a07b49d738Adam Powell            result.append("ROUTE_TYPE_USER ");
7169a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
7179a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return result.toString();
7189a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
7199a1de308cea2d160778fd977825f10a07b49d738Adam Powell
720b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    /** @hide */
721b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public MediaRouter(Context context) {
722b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        synchronized (Static.class) {
723b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            if (sStatic == null) {
7248e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final Context appContext = context.getApplicationContext();
7258e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                sStatic = new Static(appContext);
7268e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                sStatic.startMonitoringRoutes(appContext);
727b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            }
7289a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
7299a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
7309a1de308cea2d160778fd977825f10a07b49d738Adam Powell
731690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    /**
7323afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * Gets the default route for playing media content on the system.
7333afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * <p>
7343afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * The system always provides a default route.
7353afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * </p>
7363afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     *
7373afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * @return The default route, which is guaranteed to never be null.
738690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     */
7393afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown    public RouteInfo getDefaultRoute() {
740705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return sStatic.mDefaultAudioVideo;
741690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
742690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
743690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    /**
7444599696591f745b3a546197d2ba7e5cfc5562484Adam Powell     * @hide for use by framework routing UI
7454599696591f745b3a546197d2ba7e5cfc5562484Adam Powell     */
7463afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown    public RouteCategory getSystemCategory() {
7474599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        return sStatic.mSystemCategory;
7484599696591f745b3a546197d2ba7e5cfc5562484Adam Powell    }
7494599696591f745b3a546197d2ba7e5cfc5562484Adam Powell
7500abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    /** @hide */
7510abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public RouteInfo getSelectedRoute() {
7520abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        return getSelectedRoute(ROUTE_TYPE_ANY);
7530abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    }
7540abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
7554599696591f745b3a546197d2ba7e5cfc5562484Adam Powell    /**
7561cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell     * Return the currently selected route for any of the given types
757690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     *
758690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * @param type route types
759690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * @return the selected route
760690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     */
761690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public RouteInfo getSelectedRoute(int type) {
7621cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        if (sStatic.mSelectedRoute != null &&
7631cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell                (sStatic.mSelectedRoute.mSupportedTypes & type) != 0) {
7641cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // If the selected route supports any of the types supplied, it's still considered
7651cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // 'selected' for that type.
7661cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            return sStatic.mSelectedRoute;
7671cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        } else if (type == ROUTE_TYPE_USER) {
7681cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // The caller specifically asked for a user route and the currently selected route
7691cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // doesn't qualify.
7701cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            return null;
7711cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        }
7721cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        // If the above didn't match and we're not specifically asking for a user route,
7731cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        // consider the default selected.
7741cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        return sStatic.mDefaultAudioVideo;
775690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
776690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
7779a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
7780abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * Returns true if there is a route that matches the specified types.
7790abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * <p>
7800abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * This method returns true if there are any available routes that match the types
7810abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * regardless of whether they are enabled or disabled.  If the
7820abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} flag is specified, then
7830abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * the method will only consider non-default routes.
7840abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * </p>
7850abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     *
7860abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @param types The types to match.
7870abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @param flags Flags to control the determination of whether a route may be available.
7880abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * May be zero or {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE}.
7890abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @return True if a matching route may be available.
7900abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     *
7910abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @hide Future API ported from support library.  Revisit this later.
7920abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     */
7930abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public boolean isRouteAvailable(int types, int flags) {
7940abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        final int count = sStatic.mRoutes.size();
7950abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        for (int i = 0; i < count; i++) {
7960abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            RouteInfo route = sStatic.mRoutes.get(i);
7970abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            if (route.matchesTypes(types)) {
7980abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                if ((flags & AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE) == 0
7990abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                        || route != sStatic.mDefaultAudioVideo) {
8000abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                    return true;
8010abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                }
8020abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            }
8030abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
8040abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
8050abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        // It doesn't look like we can find a matching route right now.
8060abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        return false;
8070abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    }
8080abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
8090abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    /**
8109a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Add a callback to listen to events about specific kinds of media routes.
8119a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * If the specified callback is already registered, its registration will be updated for any
8129a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * additional route types specified.
81366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
81466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * This is a convenience method that has the same effect as calling
81566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * {@link #addCallback(int, Callback, int)} without flags.
81666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
8179a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
8189a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param types Types of routes this callback is interested in
8199a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param cb Callback to add
8209a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
8219a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void addCallback(int types, Callback cb) {
82266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        addCallback(types, cb, 0);
82366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    }
82466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
82566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    /**
82666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Add a callback to listen to events about specific kinds of media routes.
82766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * If the specified callback is already registered, its registration will be updated for any
82866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * additional route types specified.
82966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
83066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * By default, the callback will only be invoked for events that affect routes
83166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * that match the specified selector.  The filtering may be disabled by specifying
83266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * the {@link #CALLBACK_FLAG_UNFILTERED_EVENTS} flag.
83366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
83466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     *
83566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @param types Types of routes this callback is interested in
83666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @param cb Callback to add
83766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @param flags Flags to control the behavior of the callback.
83814507e257af5d71577574e25cbd690c4b54c9272Jeff Brown     * May be zero or a combination of {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and
83966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * {@link #CALLBACK_FLAG_UNFILTERED_EVENTS}.
84066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     */
84166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    public void addCallback(int types, Callback cb, int flags) {
84266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        CallbackInfo info;
84366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        int index = findCallbackInfo(cb);
84466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        if (index >= 0) {
84566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info = sStatic.mCallbacks.get(index);
84666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info.type |= types;
84766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info.flags |= flags;
84866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        } else {
84966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info = new CallbackInfo(cb, types, flags, this);
85066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            sStatic.mCallbacks.add(info);
85166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
85269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        sStatic.updateDiscoveryRequest();
8539a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
8549a1de308cea2d160778fd977825f10a07b49d738Adam Powell
8559a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
8569a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Remove the specified callback. It will no longer receive events about media routing.
8579a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
8589a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param cb Callback to remove
8599a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
8609a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void removeCallback(Callback cb) {
86166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        int index = findCallbackInfo(cb);
86266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        if (index >= 0) {
86369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            sStatic.mCallbacks.remove(index);
86469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            sStatic.updateDiscoveryRequest();
86566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        } else {
86666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            Log.w(TAG, "removeCallback(" + cb + "): callback not registered");
86766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
86866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    }
86966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
87066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    private int findCallbackInfo(Callback cb) {
871b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        final int count = sStatic.mCallbacks.size();
8729a1de308cea2d160778fd977825f10a07b49d738Adam Powell        for (int i = 0; i < count; i++) {
87366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            final CallbackInfo info = sStatic.mCallbacks.get(i);
87466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (info.cb == cb) {
87566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown                return i;
8769a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
8779a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
87866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        return -1;
8799a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
8809a1de308cea2d160778fd977825f10a07b49d738Adam Powell
881d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell    /**
882d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     * Select the specified route to use for output of the given media types.
8833afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * <p class="note">
8843afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * As API version 18, this function may be used to select any route.
8853afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * In prior versions, this function could only be used to select user
8863afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * routes and would ignore any attempt to select a system route.
8873afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * </p>
888d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     *
889d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     * @param types type flags indicating which types this route should be used for.
890d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     *              The route must support at least a subset.
891d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     * @param route Route to select
8929d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand     * @throws IllegalArgumentException if the given route is {@code null}
893d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     */
8949d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand    public void selectRoute(int types, @NonNull RouteInfo route) {
8959d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand        if (route == null) {
8969d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand            throw new IllegalArgumentException("Route cannot be null.");
8979d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand        }
89869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        selectRouteStatic(types, route, true);
8990d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    }
90069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
9010d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    /**
9020d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @hide internal use
9030d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     */
90469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void selectRouteInt(int types, RouteInfo route, boolean explicit) {
90569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        selectRouteStatic(types, route, explicit);
906b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
907b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
9089d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand    static void selectRouteStatic(int types, @NonNull RouteInfo route, boolean explicit) {
90918687b7a275b3782e63d52cdbe8ee9df191046e7Jae Seo        Log.v(TAG, "Selecting route: " + route);
9109d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand        assert(route != null);
911705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final RouteInfo oldRoute = sStatic.mSelectedRoute;
912705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (oldRoute == route) return;
9130abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        if (!route.matchesTypes(types)) {
9140d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            Log.w(TAG, "selectRoute ignored; cannot select route with supported types " +
9150d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                    typesToString(route.getSupportedTypes()) + " into route types " +
9160d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                    typesToString(types));
9174ee1f55ce0f4909a7430ab44563a81852f335071Adam Powell            return;
9180d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
9199a1de308cea2d160778fd977825f10a07b49d738Adam Powell
920fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo        final RouteInfo btRoute = sStatic.mBluetoothA2dpRoute;
921fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo        if (btRoute != null && (types & ROUTE_TYPE_LIVE_AUDIO) != 0 &&
922fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                (route == btRoute || route == sStatic.mDefaultAudioVideo)) {
923fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            try {
924fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                sStatic.mAudioService.setBluetoothA2dpOn(route == btRoute);
925fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            } catch (RemoteException e) {
926fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                Log.e(TAG, "Error changing Bluetooth A2DP state", e);
927fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            }
928fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo        }
929fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo
930705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final WifiDisplay activeDisplay =
931705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                sStatic.mDisplayService.getWifiDisplayStatus().getActiveDisplay();
932705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final boolean oldRouteHasAddress = oldRoute != null && oldRoute.mDeviceAddress != null;
9339d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand        final boolean newRouteHasAddress = route.mDeviceAddress != null;
934705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (activeDisplay != null || oldRouteHasAddress || newRouteHasAddress) {
935705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (newRouteHasAddress && !matchesDeviceAddress(activeDisplay, route)) {
936af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (sStatic.mCanConfigureWifiDisplays) {
937af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    sStatic.mDisplayService.connectWifiDisplay(route.mDeviceAddress);
938af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                } else {
939af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    Log.e(TAG, "Cannot connect to wifi displays because this process "
940af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                            + "is not allowed to do so.");
941af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
942705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            } else if (activeDisplay != null && !newRouteHasAddress) {
943705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                sStatic.mDisplayService.disconnectWifiDisplay();
944705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
945705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
946705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
94769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        sStatic.setSelectedRoute(route, explicit);
94869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
949705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (oldRoute != null) {
950705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            dispatchRouteUnselected(types & oldRoute.getSupportedTypes(), oldRoute);
95139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (oldRoute.resolveStatusCode()) {
95239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                dispatchRouteChanged(oldRoute);
95339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
9549a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
9559a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if (route != null) {
95639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (route.resolveStatusCode()) {
95739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                dispatchRouteChanged(route);
95839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
9599a1de308cea2d160778fd977825f10a07b49d738Adam Powell            dispatchRouteSelected(types & route.getSupportedTypes(), route);
9609a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
961ce468a35b388ca46578934706b38dbae94941643Jeff Brown
962ce468a35b388ca46578934706b38dbae94941643Jeff Brown        // The behavior of active scans may depend on the currently selected route.
963ce468a35b388ca46578934706b38dbae94941643Jeff Brown        sStatic.updateDiscoveryRequest();
9649a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
9659a1de308cea2d160778fd977825f10a07b49d738Adam Powell
96669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static void selectDefaultRouteStatic() {
96769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // TODO: Be smarter about the route types here; this selects for all valid.
96869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (sStatic.mSelectedRoute != sStatic.mBluetoothA2dpRoute
969fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                && sStatic.mBluetoothA2dpRoute != null && sStatic.isBluetoothA2dpOn()) {
97069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mBluetoothA2dpRoute, false);
97169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        } else {
97269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mDefaultAudioVideo, false);
97369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
97469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
97569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
9769a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
977705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * Compare the device address of a display and a route.
978705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * Nulls/no device address will match another null/no address.
979705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     */
980705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    static boolean matchesDeviceAddress(WifiDisplay display, RouteInfo info) {
981705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final boolean routeHasAddress = info != null && info.mDeviceAddress != null;
982705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (display == null && !routeHasAddress) {
983705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return true;
984705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
985705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
986705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (display != null && routeHasAddress) {
987705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return display.getDeviceAddress().equals(info.mDeviceAddress);
988705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
989705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return false;
990705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
991705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
992705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    /**
9939a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Add an app-specified route for media to the MediaRouter.
9949a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * App-specified route definitions are created using {@link #createUserRoute(RouteCategory)}
9959a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
9969a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param info Definition of the route to add
9973afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * @see #createUserRoute(RouteCategory)
9989a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #removeUserRoute(UserRouteInfo)
9999a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
10009a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void addUserRoute(UserRouteInfo info) {
10012ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        addRouteStatic(info);
10029a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
10039a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1004d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    /**
1005d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     * @hide Framework use only
1006d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     */
1007d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    public void addRouteInt(RouteInfo info) {
10082ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        addRouteStatic(info);
1009d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    }
1010d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
10112ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell    static void addRouteStatic(RouteInfo info) {
101218687b7a275b3782e63d52cdbe8ee9df191046e7Jae Seo        Log.v(TAG, "Adding route: " + info);
10139a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final RouteCategory cat = info.getCategory();
1014b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        if (!sStatic.mCategories.contains(cat)) {
1015b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            sStatic.mCategories.add(cat);
10169a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
1017d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        if (cat.isGroupable() && !(info instanceof RouteGroup)) {
10189a1de308cea2d160778fd977825f10a07b49d738Adam Powell            // Enforce that any added route in a groupable category must be in a group.
10199a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final RouteGroup group = new RouteGroup(info.getCategory());
1020dbbfa702a09f6d2d36dee1b552442d04a4673f89Adam Powell            group.mSupportedTypes = info.mSupportedTypes;
1021b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            sStatic.mRoutes.add(group);
1022d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteAdded(group);
1023b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            group.addRoute(info);
1024d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
10259a1de308cea2d160778fd977825f10a07b49d738Adam Powell            info = group;
1026d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        } else {
1027b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            sStatic.mRoutes.add(info);
1028d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteAdded(info);
10299a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
10309a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
10319a1de308cea2d160778fd977825f10a07b49d738Adam Powell
10329a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
10339a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Remove an app-specified route for media from the MediaRouter.
10349a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
10359a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param info Definition of the route to remove
10369a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #addUserRoute(UserRouteInfo)
10379a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
10389a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void removeUserRoute(UserRouteInfo info) {
103969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        removeRouteStatic(info);
10409a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
10419a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1042690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    /**
1043690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * Remove all app-specified routes from the MediaRouter.
1044690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     *
1045690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * @see #removeUserRoute(UserRouteInfo)
1046690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     */
1047690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public void clearUserRoutes() {
1048b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        for (int i = 0; i < sStatic.mRoutes.size(); i++) {
1049b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final RouteInfo info = sStatic.mRoutes.get(i);
1050d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            // TODO Right now, RouteGroups only ever contain user routes.
1051d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            // The code below will need to change if this assumption does.
1052d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            if (info instanceof UserRouteInfo || info instanceof RouteGroup) {
105369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                removeRouteStatic(info);
1054690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell                i--;
1055690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            }
1056690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
1057690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
1058690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
1059d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    /**
1060d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     * @hide internal use only
1061d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     */
1062d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    public void removeRouteInt(RouteInfo info) {
106369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        removeRouteStatic(info);
1064d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    }
1065d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
106669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static void removeRouteStatic(RouteInfo info) {
106718687b7a275b3782e63d52cdbe8ee9df191046e7Jae Seo        Log.v(TAG, "Removing route: " + info);
1068b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        if (sStatic.mRoutes.remove(info)) {
10699a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final RouteCategory removingCat = info.getCategory();
1070b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final int count = sStatic.mRoutes.size();
10719a1de308cea2d160778fd977825f10a07b49d738Adam Powell            boolean found = false;
10729a1de308cea2d160778fd977825f10a07b49d738Adam Powell            for (int i = 0; i < count; i++) {
1073b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                final RouteCategory cat = sStatic.mRoutes.get(i).getCategory();
10749a1de308cea2d160778fd977825f10a07b49d738Adam Powell                if (removingCat == cat) {
10759a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    found = true;
10769a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    break;
10779a1de308cea2d160778fd977825f10a07b49d738Adam Powell                }
10789a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
10790abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            if (info.isSelected()) {
1080d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                // Removing the currently selected route? Select the default before we remove it.
108169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                selectDefaultRouteStatic();
1082d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            }
1083690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            if (!found) {
1084b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                sStatic.mCategories.remove(removingCat);
1085690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            }
1086690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            dispatchRouteRemoved(info);
1087690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
1088690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
1089690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
10909a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
10919a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the number of {@link MediaRouter.RouteCategory categories} currently
10929a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * represented by routes known to this MediaRouter.
10939a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
10949a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the number of unique categories represented by this MediaRouter's known routes
10959a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
10969a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public int getCategoryCount() {
1097b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mCategories.size();
10989a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
10999a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11009a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11019a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the {@link MediaRouter.RouteCategory category} at the given index.
11029a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Valid indices are in the range [0-getCategoryCount).
11039a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11049a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param index which category to return
11059a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the category at index
11069a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11079a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteCategory getCategoryAt(int index) {
1108b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mCategories.get(index);
11099a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11109a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11119a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11129a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the number of {@link MediaRouter.RouteInfo routes} currently known
11139a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * to this MediaRouter.
11149a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11159a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the number of routes tracked by this router
11169a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11179a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public int getRouteCount() {
1118b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.size();
11199a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11209a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11219a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11229a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the route at the specified index.
11239a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11249a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param index index of the route to return
11259a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the route at index
11269a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11279a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteInfo getRouteAt(int index) {
1128b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.get(index);
1129b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
1130b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
1131b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static int getRouteCountStatic() {
1132b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.size();
1133b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
1134b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
1135b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static RouteInfo getRouteAtStatic(int index) {
1136b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.get(index);
11379a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11389a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11399a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11409a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Create a new user route that may be modified and registered for use by the application.
11419a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11429a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param category The category the new route will belong to
11439a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return A new UserRouteInfo for use by the application
11449a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11459a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #addUserRoute(UserRouteInfo)
11469a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #removeUserRoute(UserRouteInfo)
114769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @see #createRouteCategory(CharSequence, boolean)
11489a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11499a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public UserRouteInfo createUserRoute(RouteCategory category) {
11509a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return new UserRouteInfo(category);
11519a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11529a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11539a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11549a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Create a new route category. Each route must belong to a category.
11559a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11569a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param name Name of the new category
11579a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param isGroupable true if routes in this category may be grouped with one another
11589a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the new RouteCategory
11599a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11609a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteCategory createRouteCategory(CharSequence name, boolean isGroupable) {
11619a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return new RouteCategory(name, ROUTE_TYPE_USER, isGroupable);
11629a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11635d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
11640d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    /**
11650d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * Create a new route category. Each route must belong to a category.
11660d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     *
11670d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @param nameResId Resource ID of the name of the new category
11680d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @param isGroupable true if routes in this category may be grouped with one another
11690d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @return the new RouteCategory
11700d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     */
11710d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    public RouteCategory createRouteCategory(int nameResId, boolean isGroupable) {
11720d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        return new RouteCategory(nameResId, ROUTE_TYPE_USER, isGroupable);
11730d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    }
11749a1de308cea2d160778fd977825f10a07b49d738Adam Powell
117569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
117669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Rebinds the media router to handle routes that belong to the specified user.
117769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Requires the interact across users permission to access the routes of another user.
117869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * <p>
117969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * This method is a complete hack to work around the singleton nature of the
118069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * media router when running inside of singleton processes like QuickSettings.
118169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * This mechanism should be burned to the ground when MediaRouter is redesigned.
118269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Ideally the current user would be pulled from the Context but we need to break
118369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * down MediaRouter.Static before we can get there.
118469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * </p>
118569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     *
118669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide
118769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
118869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void rebindAsUser(int userId) {
118969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        sStatic.rebindAsUser(userId);
119069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
119169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
1192b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void updateRoute(final RouteInfo info) {
11939a1de308cea2d160778fd977825f10a07b49d738Adam Powell        dispatchRouteChanged(info);
11949a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11959a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1196b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteSelected(int type, RouteInfo info) {
119739d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
119866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1199b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteSelected(cbi.router, type, info);
12009a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
12019a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12029a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12039a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1204b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteUnselected(int type, RouteInfo info) {
120539d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
120666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1207b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteUnselected(cbi.router, type, info);
12089a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
12099a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12109a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12119a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1212b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteChanged(RouteInfo info) {
1213af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        dispatchRouteChanged(info, info.mSupportedTypes);
1214af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown    }
1215af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
1216af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown    static void dispatchRouteChanged(RouteInfo info, int oldSupportedTypes) {
121718687b7a275b3782e63d52cdbe8ee9df191046e7Jae Seo        Log.v(TAG, "Dispatching route change: " + info);
1218af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        final int newSupportedTypes = info.mSupportedTypes;
121939d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
1220af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Reconstruct some of the history for callbacks that may not have observed
1221af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // all of the events needed to correctly interpret the current state.
1222af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // FIXME: This is a strong signal that we should deprecate route type filtering
1223af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // completely in the future because it can lead to inconsistencies in
1224af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // applications.
1225af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            final boolean oldVisibility = cbi.filterRouteEvent(oldSupportedTypes);
1226af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            final boolean newVisibility = cbi.filterRouteEvent(newSupportedTypes);
1227af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (!oldVisibility && newVisibility) {
1228af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                cbi.cb.onRouteAdded(cbi.router, info);
1229af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (info.isSelected()) {
1230af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    cbi.cb.onRouteSelected(cbi.router, newSupportedTypes, info);
1231af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
1232af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            }
1233af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (oldVisibility || newVisibility) {
1234b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteChanged(cbi.router, info);
12359a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
1236af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (oldVisibility && !newVisibility) {
1237af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (info.isSelected()) {
1238af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    cbi.cb.onRouteUnselected(cbi.router, oldSupportedTypes, info);
1239af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
1240af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                cbi.cb.onRouteRemoved(cbi.router, info);
1241af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            }
12429a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12439a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12449a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1245b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteAdded(RouteInfo info) {
124639d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
124766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1248b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteAdded(cbi.router, info);
12499a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
12509a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12519a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12529a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1253b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteRemoved(RouteInfo info) {
125439d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
125566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1256b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteRemoved(cbi.router, info);
12579a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
12589a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12599a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12609a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1261b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteGrouped(RouteInfo info, RouteGroup group, int index) {
126239d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
126366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(group)) {
1264b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteGrouped(cbi.router, info, group, index);
1265d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            }
1266d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        }
1267d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell    }
1268d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
1269b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteUngrouped(RouteInfo info, RouteGroup group) {
127039d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
127166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(group)) {
1272b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteUngrouped(cbi.router, info, group);
12739a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
12749a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12759a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12769a1de308cea2d160778fd977825f10a07b49d738Adam Powell
12778e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    static void dispatchRouteVolumeChanged(RouteInfo info) {
12788e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
127966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
12808e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                cbi.cb.onRouteVolumeChanged(cbi.router, info);
12818e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
12828e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
12838e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    }
12848e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
128592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown    static void dispatchRoutePresentationDisplayChanged(RouteInfo info) {
128692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        for (CallbackInfo cbi : sStatic.mCallbacks) {
128766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
128892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                cbi.cb.onRoutePresentationDisplayChanged(cbi.router, info);
128992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            }
129092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
129192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown    }
129292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
12938e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    static void systemVolumeChanged(int newValue) {
12948e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        final RouteInfo selectedRoute = sStatic.mSelectedRoute;
12958e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        if (selectedRoute == null) return;
12968e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
12978e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        if (selectedRoute == sStatic.mBluetoothA2dpRoute ||
1298705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                selectedRoute == sStatic.mDefaultAudioVideo) {
12998e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            dispatchRouteVolumeChanged(selectedRoute);
13008e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        } else if (sStatic.mBluetoothA2dpRoute != null) {
1301fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            try {
1302fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                dispatchRouteVolumeChanged(sStatic.mAudioService.isBluetoothA2dpOn() ?
1303fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                        sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo);
1304fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            } catch (RemoteException e) {
1305fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                Log.e(TAG, "Error checking Bluetooth A2DP state to report volume change", e);
1306fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            }
13078e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        } else {
1308705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            dispatchRouteVolumeChanged(sStatic.mDefaultAudioVideo);
1309705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1310705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1311705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
131275af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    static void updateWifiDisplayStatus(WifiDisplayStatus status) {
131375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        WifiDisplay[] displays;
1314615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell        WifiDisplay activeDisplay;
131575af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        if (status.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) {
131675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            displays = status.getDisplays();
131775af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            activeDisplay = status.getActiveDisplay();
1318af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
1319af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Only the system is able to connect to wifi display routes.
1320af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // The display manager will enforce this with a permission check but it
1321af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // still publishes information about all available displays.
1322af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Filter the list down to just the active display.
1323af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (!sStatic.mCanConfigureWifiDisplays) {
1324af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (activeDisplay != null) {
1325af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    displays = new WifiDisplay[] { activeDisplay };
1326af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                } else {
1327af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    displays = WifiDisplay.EMPTY_ARRAY;
1328af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
1329af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            }
1330615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell        } else {
133175af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            displays = WifiDisplay.EMPTY_ARRAY;
1332615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell            activeDisplay = null;
1333615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell        }
1334ce468a35b388ca46578934706b38dbae94941643Jeff Brown        String activeDisplayAddress = activeDisplay != null ?
1335ce468a35b388ca46578934706b38dbae94941643Jeff Brown                activeDisplay.getDeviceAddress() : null;
1336705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
133775af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        // Add or update routes.
133875af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        for (int i = 0; i < displays.length; i++) {
133975af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            final WifiDisplay d = displays[i];
134075af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            if (shouldShowWifiDisplay(d, activeDisplay)) {
1341ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                RouteInfo route = findWifiDisplayRoute(d);
1342ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                if (route == null) {
134375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                    route = makeWifiDisplayRoute(d, status);
1344ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                    addRouteStatic(route);
1345ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                } else {
1346ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    String address = d.getDeviceAddress();
1347ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    boolean disconnected = !address.equals(activeDisplayAddress)
1348ce468a35b388ca46578934706b38dbae94941643Jeff Brown                            && address.equals(sStatic.mPreviousActiveWifiDisplayAddress);
1349ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    updateWifiDisplayRoute(route, d, status, disconnected);
1350ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                }
1351ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                if (d.equals(activeDisplay)) {
135269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    selectRouteStatic(route.getSupportedTypes(), route, false);
1353ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                }
13542ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            }
1355705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
135675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown
135775af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        // Remove stale routes.
135875af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        for (int i = sStatic.mRoutes.size(); i-- > 0; ) {
135975af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            RouteInfo route = sStatic.mRoutes.get(i);
136075af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            if (route.mDeviceAddress != null) {
136175af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                WifiDisplay d = findWifiDisplay(displays, route.mDeviceAddress);
136275af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                if (d == null || !shouldShowWifiDisplay(d, activeDisplay)) {
136375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                    removeRouteStatic(route);
1364ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                }
1365705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1366705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1367ce468a35b388ca46578934706b38dbae94941643Jeff Brown
1368ce468a35b388ca46578934706b38dbae94941643Jeff Brown        // Remember the current active wifi display address so that we can infer disconnections.
1369ce468a35b388ca46578934706b38dbae94941643Jeff Brown        // TODO: This hack will go away once all of this is moved into the media router service.
1370ce468a35b388ca46578934706b38dbae94941643Jeff Brown        sStatic.mPreviousActiveWifiDisplayAddress = activeDisplayAddress;
137175af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    }
1372705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
137375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    private static boolean shouldShowWifiDisplay(WifiDisplay d, WifiDisplay activeDisplay) {
137475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        return d.isRemembered() || d.equals(activeDisplay);
1375705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1376705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1377ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    static int getWifiDisplayStatusCode(WifiDisplay d, WifiDisplayStatus wfdStatus) {
137869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        int newStatus;
1379ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        if (wfdStatus.getScanState() == WifiDisplayStatus.SCAN_STATE_SCANNING) {
1380ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang            newStatus = RouteInfo.STATUS_SCANNING;
1381ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        } else if (d.isAvailable()) {
1382ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang            newStatus = d.canConnect() ?
1383ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                    RouteInfo.STATUS_AVAILABLE: RouteInfo.STATUS_IN_USE;
1384705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        } else {
1385705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            newStatus = RouteInfo.STATUS_NOT_AVAILABLE;
1386705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1387705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1388ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        if (d.equals(wfdStatus.getActiveDisplay())) {
1389ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang            final int activeState = wfdStatus.getActiveDisplayState();
1390705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            switch (activeState) {
1391705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                case WifiDisplayStatus.DISPLAY_STATE_CONNECTED:
139269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    newStatus = RouteInfo.STATUS_CONNECTED;
1393705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    break;
1394705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                case WifiDisplayStatus.DISPLAY_STATE_CONNECTING:
1395705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    newStatus = RouteInfo.STATUS_CONNECTING;
1396705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    break;
1397705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                case WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED:
1398705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    Log.e(TAG, "Active display is not connected!");
1399705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    break;
1400705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1401705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1402705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1403ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        return newStatus;
1404ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    }
1405ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
1406ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    static boolean isWifiDisplayEnabled(WifiDisplay d, WifiDisplayStatus wfdStatus) {
1407ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        return d.isAvailable() && (d.canConnect() || d.equals(wfdStatus.getActiveDisplay()));
1408ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    }
1409ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
1410ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    static RouteInfo makeWifiDisplayRoute(WifiDisplay display, WifiDisplayStatus wfdStatus) {
1411ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        final RouteInfo newRoute = new RouteInfo(sStatic.mSystemCategory);
1412ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mDeviceAddress = display.getDeviceAddress();
141369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        newRoute.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO
141469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                | ROUTE_TYPE_REMOTE_DISPLAY;
1415ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mVolumeHandling = RouteInfo.PLAYBACK_VOLUME_FIXED;
1416ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mPlaybackType = RouteInfo.PLAYBACK_TYPE_REMOTE;
1417ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
141839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        newRoute.setRealStatusCode(getWifiDisplayStatusCode(display, wfdStatus));
1419ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mEnabled = isWifiDisplayEnabled(display, wfdStatus);
1420ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mName = display.getFriendlyDisplayName();
1421ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mDescription = sStatic.mResources.getText(
1422ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                com.android.internal.R.string.wireless_display_route_description);
14235830b0b33618940d65197cec99d697b21908fec8Chong Zhang        newRoute.updatePresentationDisplay();
14249dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        newRoute.mDeviceType = RouteInfo.DEVICE_TYPE_TV;
1425ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        return newRoute;
1426ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    }
1427ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
1428ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    private static void updateWifiDisplayRoute(
1429ce468a35b388ca46578934706b38dbae94941643Jeff Brown            RouteInfo route, WifiDisplay display, WifiDisplayStatus wfdStatus,
1430ce468a35b388ca46578934706b38dbae94941643Jeff Brown            boolean disconnected) {
1431ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        boolean changed = false;
14322444ae7e2b8658a4a90f996e678423558744b4a2Jeff Brown        final String newName = display.getFriendlyDisplayName();
14332444ae7e2b8658a4a90f996e678423558744b4a2Jeff Brown        if (!route.getName().equals(newName)) {
1434705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            route.mName = newName;
1435705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            changed = true;
1436705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1437705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1438ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        boolean enabled = isWifiDisplayEnabled(display, wfdStatus);
1439ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        changed |= route.mEnabled != enabled;
1440ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        route.mEnabled = enabled;
1441705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
144239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        changed |= route.setRealStatusCode(getWifiDisplayStatusCode(display, wfdStatus));
1443705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1444705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (changed) {
1445705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            dispatchRouteChanged(route);
1446705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1447705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1448ce468a35b388ca46578934706b38dbae94941643Jeff Brown        if ((!enabled || disconnected) && route.isSelected()) {
1449705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            // Oops, no longer available. Reselect the default.
145069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            selectDefaultRouteStatic();
1451705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1452705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1453705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
145475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    private static WifiDisplay findWifiDisplay(WifiDisplay[] displays, String deviceAddress) {
1455705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        for (int i = 0; i < displays.length; i++) {
145675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            final WifiDisplay d = displays[i];
145775af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            if (d.getDeviceAddress().equals(deviceAddress)) {
145875af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                return d;
1459705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1460705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1461705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return null;
1462705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1463705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1464705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    private static RouteInfo findWifiDisplayRoute(WifiDisplay d) {
1465705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final int count = sStatic.mRoutes.size();
1466705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        for (int i = 0; i < count; i++) {
1467705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            final RouteInfo info = sStatic.mRoutes.get(i);
1468705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (d.getDeviceAddress().equals(info.mDeviceAddress)) {
1469705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                return info;
1470705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1471705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1472705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return null;
14738e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    }
14748e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
14759a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
14769a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a media route.
14779a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
1478b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class RouteInfo {
14799a1de308cea2d160778fd977825f10a07b49d738Adam Powell        CharSequence mName;
14800d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        int mNameResId;
148156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        CharSequence mDescription;
14829a1de308cea2d160778fd977825f10a07b49d738Adam Powell        private CharSequence mStatus;
14839a1de308cea2d160778fd977825f10a07b49d738Adam Powell        int mSupportedTypes;
14849dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        int mDeviceType;
14859a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteGroup mGroup;
14869a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final RouteCategory mCategory;
1487ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        Drawable mIcon;
14881357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        // playback information
14891357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mPlaybackType = PLAYBACK_TYPE_LOCAL;
14901357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
14911357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
14921357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
14931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mPlaybackStream = AudioManager.STREAM_MUSIC;
14941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        VolumeCallbackInfo mVcb;
149592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        Display mPresentationDisplay;
14965830b0b33618940d65197cec99d697b21908fec8Chong Zhang        int mPresentationDisplayId = -1;
14979a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1498705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        String mDeviceAddress;
1499705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        boolean mEnabled = true;
1500705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
150169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // An id by which the route is known to the media router service.
150269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // Null if this route only exists as an artifact within this process.
150369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        String mGlobalRouteId;
150469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
1505705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        // A predetermined connection status that can override mStatus
150639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private int mRealStatusCode;
150739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private int mResolvedStatusCode;
1508705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
15092ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_NONE = 0;
15102ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_SCANNING = 1;
15112ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_CONNECTING = 2;
15122ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_AVAILABLE = 3;
15132ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_NOT_AVAILABLE = 4;
1514ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        /** @hide */ public static final int STATUS_IN_USE = 5;
151569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        /** @hide */ public static final int STATUS_CONNECTED = 6;
1516705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
15179dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /** @hide */
15189dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        @IntDef({DEVICE_TYPE_UNKNOWN, DEVICE_TYPE_TV, DEVICE_TYPE_SPEAKER, DEVICE_TYPE_BLUETOOTH})
15199dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        @Retention(RetentionPolicy.SOURCE)
15209dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public @interface DeviceType {}
15219dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
15229dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /**
15239dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * The default receiver device type of the route indicating the type is unknown.
15249dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         *
15259dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * @see #getDeviceType
15269dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         */
15279dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public static final int DEVICE_TYPE_UNKNOWN = 0;
15289dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
15299dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /**
15309dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * A receiver device type of the route indicating the presentation of the media is happening
15319dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * on a TV.
15329dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         *
15339dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * @see #getDeviceType
15349dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         */
15359dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public static final int DEVICE_TYPE_TV = 1;
15369dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
15379dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /**
15389dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * A receiver device type of the route indicating the presentation of the media is happening
15399dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * on a speaker.
15409dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         *
15419dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * @see #getDeviceType
15429dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         */
15439dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public static final int DEVICE_TYPE_SPEAKER = 2;
15449dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
15459dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /**
15469dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * A receiver device type of the route indicating the presentation of the media is happening
15479dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * on a bluetooth device such as a bluetooth speaker.
15489dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         *
15499dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * @see #getDeviceType
15509dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         */
15519dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public static final int DEVICE_TYPE_BLUETOOTH = 3;
15529dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
1553b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        private Object mTag;
1554b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
15557c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        /** @hide */
15567c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        @IntDef({PLAYBACK_TYPE_LOCAL, PLAYBACK_TYPE_REMOTE})
15577c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        @Retention(RetentionPolicy.SOURCE)
15587c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        public @interface PlaybackType {}
15597c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang
15601357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
15611357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * The default playback type, "local", indicating the presentation of the media is happening
1562bc4cf00dc5bf6e0e3c01206d5c46e64306df260aJohn Spurlock         * on the same device (e&#46;g&#46; a phone, a tablet) as where it is controlled from.
156369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getPlaybackType()
15641357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
15651357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_TYPE_LOCAL = 0;
15667c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang
15671357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
15681357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * A playback type indicating the presentation of the media is happening on
1569bc4cf00dc5bf6e0e3c01206d5c46e64306df260aJohn Spurlock         * a different device (i&#46;e&#46; the remote device) than where it is controlled from.
157069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getPlaybackType()
15711357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
15721357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_TYPE_REMOTE = 1;
15737c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang
15747c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        /** @hide */
15757c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang         @IntDef({PLAYBACK_VOLUME_FIXED,PLAYBACK_VOLUME_VARIABLE})
15767c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang         @Retention(RetentionPolicy.SOURCE)
15777c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang         private @interface PlaybackVolume {}
15787c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang
15791357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
1580bc4cf00dc5bf6e0e3c01206d5c46e64306df260aJohn Spurlock         * Playback information indicating the playback volume is fixed, i&#46;e&#46; it cannot be
15811357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * controlled from this object. An example of fixed playback volume is a remote player,
15821357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
15831357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * than attenuate at the source.
158469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getVolumeHandling()
15851357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
15861357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_VOLUME_FIXED = 0;
15871357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
15881357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Playback information indicating the playback volume is variable and can be controlled
15891357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * from this object.
159069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getVolumeHandling()
15911357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
15921357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_VOLUME_VARIABLE = 1;
15931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
15949a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteInfo(RouteCategory category) {
15959a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mCategory = category;
15969dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang            mDeviceType = DEVICE_TYPE_UNKNOWN;
15979a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
15989a1de308cea2d160778fd977825f10a07b49d738Adam Powell
15999a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
160056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Gets the user-visible name of the route.
160156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
160256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route name identifies the destination represented by the route.
160356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied name, an alias, or device serial number.
160456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
160556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
160656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The user-visible name of a media route.  This is the string presented
16079a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * to users who may select this as the active route.
16089a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
16099a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getName() {
16100d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(sStatic.mResources);
16110d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
161256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
16130d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        /**
161456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Return the properly localized/resource user-visible name of this route.
161556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
161656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route name identifies the destination represented by the route.
161756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied name, an alias, or device serial number.
161856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
161956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
16200d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @param context Context used to resolve the correct configuration to load
162156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The user-visible name of a media route.  This is the string presented
16220d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * to users who may select this as the active route.
16230d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         */
16240d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public CharSequence getName(Context context) {
16250d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(context.getResources());
16260d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
162756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
16280d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        CharSequence getName(Resources res) {
16290d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            if (mNameResId != 0) {
16300d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                return mName = res.getText(mNameResId);
16310d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            }
16329a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mName;
16339a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
16349a1de308cea2d160778fd977825f10a07b49d738Adam Powell
16359a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
163656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Gets the user-visible description of the route.
163756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
163856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route description describes the kind of destination represented by the route.
163956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied string, a model number or brand of device.
164056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
164156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
164256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The description of the route, or null if none.
164356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         */
164456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        public CharSequence getDescription() {
164556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown            return mDescription;
164656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        }
164756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
164856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        /**
164956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The user-visible status for a media route. This may include a description
16509a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * of the currently playing media, if available.
16519a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
16529a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getStatus() {
16539a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mStatus;
16549a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
16559a1de308cea2d160778fd977825f10a07b49d738Adam Powell
16569a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
1657705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * Set this route's status by predetermined status code. If the caller
1658705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * should dispatch a route changed event this call will return true;
1659705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         */
166039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        boolean setRealStatusCode(int statusCode) {
166139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (mRealStatusCode != statusCode) {
166239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                mRealStatusCode = statusCode;
166339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                return resolveStatusCode();
166439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
166539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return false;
166639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        }
166739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown
166839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        /**
166939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown         * Resolves the status code whenever the real status code or selection state
167039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown         * changes.
167139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown         */
167239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        boolean resolveStatusCode() {
167339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            int statusCode = mRealStatusCode;
167439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (isSelected()) {
1675705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                switch (statusCode) {
167639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // If the route is selected and its status appears to be between states
167739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // then report it as connecting even though it has not yet had a chance
167839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // to officially move into the CONNECTING state.  Note that routes in
167939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // the NONE state are assumed to not require an explicit connection
168039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // lifecycle whereas those that are AVAILABLE are assumed to have
168139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // to eventually proceed to CONNECTED.
1682705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    case STATUS_AVAILABLE:
168339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    case STATUS_SCANNING:
168439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                        statusCode = STATUS_CONNECTING;
168569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        break;
1686705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                }
1687705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
168839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (mResolvedStatusCode == statusCode) {
168939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                return false;
169039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
169139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown
169239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            mResolvedStatusCode = statusCode;
169339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            int resId;
169439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            switch (statusCode) {
169539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_SCANNING:
169639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_scanning;
169739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
169839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_CONNECTING:
169939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_connecting;
170039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
170139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_AVAILABLE:
170239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_available;
170339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
170439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_NOT_AVAILABLE:
170539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_not_available;
170639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
170739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_IN_USE:
170839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_in_use;
170939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
171039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_CONNECTED:
171139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_NONE:
171239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                default:
171339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = 0;
171439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
171539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
171639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            mStatus = resId != 0 ? sStatic.mResources.getText(resId) : null;
171739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return true;
1718705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1719705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1720705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        /**
17212ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell         * @hide
17222ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell         */
17232ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        public int getStatusCode() {
172439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return mResolvedStatusCode;
17252ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        }
17262ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell
17272ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /**
17289a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return A media type flag set describing which types this route supports.
17299a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
17309a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int getSupportedTypes() {
17319a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mSupportedTypes;
17329a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
17339a1de308cea2d160778fd977825f10a07b49d738Adam Powell
17349dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /**
17359dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * Gets the type of the receiver device associated with this route.
17369dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         *
17379dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * @return The type of the receiver device associated with this route:
17389dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * {@link #DEVICE_TYPE_BLUETOOTH}, {@link #DEVICE_TYPE_TV}, {@link #DEVICE_TYPE_SPEAKER},
17399dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * or {@link #DEVICE_TYPE_UNKNOWN}.
17409dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         */
17419dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        @DeviceType
17429dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public int getDeviceType() {
17439dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang            return mDeviceType;
17449dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        }
17459dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
17460abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
17470abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public boolean matchesTypes(int types) {
17480abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            return (mSupportedTypes & types) != 0;
17490abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
17500abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
17519a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
17529a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return The group that this route belongs to.
17539a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
17549a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public RouteGroup getGroup() {
17559a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mGroup;
17569a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
17579a1de308cea2d160778fd977825f10a07b49d738Adam Powell
17589a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
17599a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return the category this route belongs to.
17609a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
17619a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public RouteCategory getCategory() {
17629a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mCategory;
17639a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
17649a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1765ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
1766ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Get the icon representing this route.
1767ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * This icon will be used in picker UIs if available.
1768ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
1769ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @return the icon representing this route or null if no icon is available
1770ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
1771ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public Drawable getIconDrawable() {
1772ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            return mIcon;
1773ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
1774ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
1775b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        /**
1776b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * Set an application-specific tag object for this route.
1777b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * The application may use this to store arbitrary data associated with the
1778b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * route for internal tracking.
1779b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         *
1780b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * <p>Note that the lifespan of a route may be well past the lifespan of
1781b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * an Activity or other Context; take care that objects you store here
1782b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * will not keep more data in memory alive than you intend.</p>
1783b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         *
1784b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * @param tag Arbitrary, app-specific data for this route to hold for later use
1785b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         */
1786b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        public void setTag(Object tag) {
1787b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            mTag = tag;
1788130b4572d1f3df702e5b296a655d15a41f6d4c66Adam Powell            routeUpdated();
1789b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        }
1790b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
1791b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        /**
1792b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * @return The tag object previously set by the application
1793b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * @see #setTag(Object)
1794b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         */
1795b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        public Object getTag() {
1796b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            return mTag;
1797b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        }
1798b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
17991357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
18001357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the type of playback associated with this route
18011357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setPlaybackType(int)
18021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
18037c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        @PlaybackType
18041357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getPlaybackType() {
18051357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            return mPlaybackType;
18061357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
18071357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
18081357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
18091357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the stream over which the playback associated with this route is performed
18101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setPlaybackStream(int)
18111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
18121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getPlaybackStream() {
18131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            return mPlaybackStream;
18141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
18151357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
18161357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
18178e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Return the current volume for this route. Depending on the route, this may only
18188e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * be valid if the route is currently selected.
18198e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         *
18201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the volume at which the playback associated with this route is performed
18211357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setVolume(int)
18221357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
18231357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getVolume() {
18241357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
18251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                int vol = 0;
18261357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                try {
18271357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    vol = sStatic.mAudioService.getStreamVolume(mPlaybackStream);
18281357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                } catch (RemoteException e) {
18291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    Log.e(TAG, "Error getting local stream volume", e);
18301357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                }
18311357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return vol;
18321357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            } else {
18331357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return mVolume;
18341357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
18351357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
18361357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
18371357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
18388e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Request a volume change for this route.
18398e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param volume value between 0 and getVolumeMax
18408e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         */
18418e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestSetVolume(int volume) {
18428e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
18438e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                try {
1844ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                    sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0,
1845ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                            ActivityThread.currentPackageName());
18468e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                } catch (RemoteException e) {
18478e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Error setting local stream volume", e);
18488e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
18498e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            } else {
185069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                sStatic.requestSetVolume(this, volume);
18518e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
18528e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
18538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
18548e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        /**
18558e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Request an incremental volume update for this route.
18568e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param direction Delta to apply to the current volume
18578e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         */
18588e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestUpdateVolume(int direction) {
18598e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
18608e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                try {
18618e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    final int volume =
18628e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                            Math.max(0, Math.min(getVolume() + direction, getVolumeMax()));
1863ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                    sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0,
1864ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                            ActivityThread.currentPackageName());
18658e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                } catch (RemoteException e) {
18668e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Error setting local stream volume", e);
18678e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
18688e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            } else {
186969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                sStatic.requestUpdateVolume(this, direction);
18708e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
18718e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
18728e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
18738e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        /**
18741357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the maximum volume at which the playback associated with this route is performed
18751357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setVolumeMax(int)
18761357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
18771357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getVolumeMax() {
18781357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
18791357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                int volMax = 0;
18801357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                try {
18811357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    volMax = sStatic.mAudioService.getStreamMaxVolume(mPlaybackStream);
18821357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                } catch (RemoteException e) {
18831357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    Log.e(TAG, "Error getting local stream volume", e);
18841357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                }
18851357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return volMax;
18861357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            } else {
18871357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return mVolumeMax;
18881357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
18891357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
18901357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
18911357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
18921357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return how volume is handling on the route
18931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setVolumeHandling(int)
18941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
18957c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        @PlaybackVolume
18961357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getVolumeHandling() {
18971357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            return mVolumeHandling;
18981357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
18991357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
1900705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        /**
190192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * Gets the {@link Display} that should be used by the application to show
190292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * a {@link android.app.Presentation} on an external display when this route is selected.
190392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * Depending on the route, this may only be valid if the route is currently
190492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * selected.
190592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * <p>
190692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * The preferred presentation display may change independently of the route
190792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * being selected or unselected.  For example, the presentation display
190892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * of the default system route may change when an external HDMI display is connected
190992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * or disconnected even though the route itself has not changed.
191092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p><p>
191192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * This method may return null if there is no external display associated with
191292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * the route or if the display is not ready to show UI yet.
191392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p><p>
191492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * The application should listen for changes to the presentation display
191592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * using the {@link Callback#onRoutePresentationDisplayChanged} callback and
191692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * show or dismiss its {@link android.app.Presentation} accordingly when the display
191792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * becomes available or is removed.
191892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p><p>
191992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * This method only makes sense for {@link #ROUTE_TYPE_LIVE_VIDEO live video} routes.
192092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p>
192192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
192292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @return The preferred presentation display to use when this route is
192392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * selected or null if none.
192492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
192592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @see #ROUTE_TYPE_LIVE_VIDEO
192692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @see android.app.Presentation
192792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         */
192892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public Display getPresentationDisplay() {
192992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            return mPresentationDisplay;
193092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
193192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
19325830b0b33618940d65197cec99d697b21908fec8Chong Zhang        boolean updatePresentationDisplay() {
19335830b0b33618940d65197cec99d697b21908fec8Chong Zhang            Display display = choosePresentationDisplay();
19345830b0b33618940d65197cec99d697b21908fec8Chong Zhang            if (mPresentationDisplay != display) {
19355830b0b33618940d65197cec99d697b21908fec8Chong Zhang                mPresentationDisplay = display;
19365830b0b33618940d65197cec99d697b21908fec8Chong Zhang                return true;
19375830b0b33618940d65197cec99d697b21908fec8Chong Zhang            }
19385830b0b33618940d65197cec99d697b21908fec8Chong Zhang            return false;
19395830b0b33618940d65197cec99d697b21908fec8Chong Zhang        }
19405830b0b33618940d65197cec99d697b21908fec8Chong Zhang
19415830b0b33618940d65197cec99d697b21908fec8Chong Zhang        private Display choosePresentationDisplay() {
19425830b0b33618940d65197cec99d697b21908fec8Chong Zhang            if ((mSupportedTypes & ROUTE_TYPE_LIVE_VIDEO) != 0) {
19435830b0b33618940d65197cec99d697b21908fec8Chong Zhang                Display[] displays = sStatic.getAllPresentationDisplays();
19445830b0b33618940d65197cec99d697b21908fec8Chong Zhang
19455830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // Ensure that the specified display is valid for presentations.
19465830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // This check will normally disallow the default display unless it was
19475830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // configured as a presentation display for some reason.
19485830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (mPresentationDisplayId >= 0) {
19495830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    for (Display display : displays) {
19505830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        if (display.getDisplayId() == mPresentationDisplayId) {
19515830b0b33618940d65197cec99d697b21908fec8Chong Zhang                            return display;
19525830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        }
19535830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    }
19545830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    return null;
19555830b0b33618940d65197cec99d697b21908fec8Chong Zhang                }
19565830b0b33618940d65197cec99d697b21908fec8Chong Zhang
19575830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // Find the indicated Wifi display by its address.
19585830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (mDeviceAddress != null) {
19595830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    for (Display display : displays) {
19605830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        if (display.getType() == Display.TYPE_WIFI
19615830b0b33618940d65197cec99d697b21908fec8Chong Zhang                                && mDeviceAddress.equals(display.getAddress())) {
19625830b0b33618940d65197cec99d697b21908fec8Chong Zhang                            return display;
19635830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        }
19645830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    }
19655830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    return null;
19665830b0b33618940d65197cec99d697b21908fec8Chong Zhang                }
19675830b0b33618940d65197cec99d697b21908fec8Chong Zhang
19685830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // For the default route, choose the first presentation display from the list.
19695830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (this == sStatic.mDefaultAudioVideo && displays.length > 0) {
19705830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    return displays[0];
19715830b0b33618940d65197cec99d697b21908fec8Chong Zhang                }
19725830b0b33618940d65197cec99d697b21908fec8Chong Zhang            }
19735830b0b33618940d65197cec99d697b21908fec8Chong Zhang            return null;
19745830b0b33618940d65197cec99d697b21908fec8Chong Zhang        }
19755830b0b33618940d65197cec99d697b21908fec8Chong Zhang
197675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        /** @hide */
197775af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        public String getDeviceAddress() {
197875af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            return mDeviceAddress;
197975af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        }
198075af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown
198192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        /**
1982a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * Returns true if this route is enabled and may be selected.
1983a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         *
1984a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * @return True if this route is enabled.
1985705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         */
1986705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        public boolean isEnabled() {
1987705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return mEnabled;
1988705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1989705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1990a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown        /**
1991a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * Returns true if the route is in the process of connecting and is not
1992a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * yet ready for use.
1993a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         *
1994a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * @return True if this route is in the process of connecting.
1995a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         */
1996a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown        public boolean isConnecting() {
199739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return mResolvedStatusCode == STATUS_CONNECTING;
19980abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
19990abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
20000abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
20010abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public boolean isSelected() {
20020abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            return this == sStatic.mSelectedRoute;
20030abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
20040abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
20050abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
20060abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public boolean isDefault() {
20070abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            return this == sStatic.mDefaultAudioVideo;
20080abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
20090abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
20100abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
20110abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public void select() {
20120abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            selectRouteStatic(mSupportedTypes, this, true);
2013a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown        }
2014a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown
20159a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void setStatusInt(CharSequence status) {
20169a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (!status.equals(mStatus)) {
20179a1de308cea2d160778fd977825f10a07b49d738Adam Powell                mStatus = status;
20189a1de308cea2d160778fd977825f10a07b49d738Adam Powell                if (mGroup != null) {
20199a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    mGroup.memberStatusChanged(this, status);
20209a1de308cea2d160778fd977825f10a07b49d738Adam Powell                }
20219a1de308cea2d160778fd977825f10a07b49d738Adam Powell                routeUpdated();
20229a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
20239a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
20249a1de308cea2d160778fd977825f10a07b49d738Adam Powell
20251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        final IRemoteVolumeObserver.Stub mRemoteVolObserver = new IRemoteVolumeObserver.Stub() {
202669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            @Override
20271357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            public void dispatchRemoteVolumeUpdate(final int direction, final int value) {
20281357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                sStatic.mHandler.post(new Runnable() {
20291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    @Override
20301357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    public void run() {
20311357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                        if (mVcb != null) {
20321357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                            if (direction != 0) {
20331357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                                mVcb.vcb.onVolumeUpdateRequest(mVcb.route, direction);
20341357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                            } else {
20351357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                                mVcb.vcb.onVolumeSetRequest(mVcb.route, value);
20361357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                            }
20371357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                        }
20381357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    }
20391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                });
20401357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
20411357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        };
20421357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
20439a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void routeUpdated() {
20449a1de308cea2d160778fd977825f10a07b49d738Adam Powell            updateRoute(this);
20459a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
20469a1de308cea2d160778fd977825f10a07b49d738Adam Powell
20479a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
20489a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public String toString() {
2049d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            String supportedTypes = typesToString(getSupportedTypes());
205092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            return getClass().getSimpleName() + "{ name=" + getName() +
205156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown                    ", description=" + getDescription() +
205292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                    ", status=" + getStatus() +
205392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                    ", category=" + getCategory() +
205492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                    ", supportedTypes=" + supportedTypes +
205569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    ", presentationDisplay=" + mPresentationDisplay + " }";
20569a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
20579a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
20589a1de308cea2d160778fd977825f10a07b49d738Adam Powell
20599a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
20609a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a route that the application may define and modify.
20618e37a85bf3dc39519942698dc90a3951306b934bAdam Powell     * A user route defaults to {@link RouteInfo#PLAYBACK_TYPE_REMOTE} and
20628e37a85bf3dc39519942698dc90a3951306b934bAdam Powell     * {@link RouteInfo#PLAYBACK_VOLUME_FIXED}.
20639a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
20649a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see MediaRouter.RouteInfo
20659a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
2066b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class UserRouteInfo extends RouteInfo {
2067ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        RemoteControlClient mRcc;
20685d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        SessionVolumeProvider mSvp;
20699a1de308cea2d160778fd977825f10a07b49d738Adam Powell
20709a1de308cea2d160778fd977825f10a07b49d738Adam Powell        UserRouteInfo(RouteCategory category) {
20719a1de308cea2d160778fd977825f10a07b49d738Adam Powell            super(category);
20729a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mSupportedTypes = ROUTE_TYPE_USER;
20738e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mPlaybackType = PLAYBACK_TYPE_REMOTE;
20748e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeHandling = PLAYBACK_VOLUME_FIXED;
20759a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
20769a1de308cea2d160778fd977825f10a07b49d738Adam Powell
20779a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
20789a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Set the user-visible name of this route.
20799a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param name Name to display to the user to describe this route
20809a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
20819a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void setName(CharSequence name) {
20829a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = name;
20839a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
20849a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
20855d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
20860d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        /**
20870d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * Set the user-visible name of this route.
208856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
208956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route name identifies the destination represented by the route.
209056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied name, an alias, or device serial number.
209156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
209256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
20930d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @param resId Resource ID of the name to display to the user to describe this route
20940d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         */
20950d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public void setName(int resId) {
20960d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mNameResId = resId;
20970d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mName = null;
20980d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            routeUpdated();
20990d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
21009a1de308cea2d160778fd977825f10a07b49d738Adam Powell
21019a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
210256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Set the user-visible description of this route.
210356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
210456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route description describes the kind of destination represented by the route.
210556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied string, a model number or brand of device.
210656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
210756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
210856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @param description The description of the route, or null if none.
210956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         */
211056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        public void setDescription(CharSequence description) {
211156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown            mDescription = description;
211256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown            routeUpdated();
211356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        }
211456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
211556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        /**
21169a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Set the current user-visible status for this route.
21179a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param status Status to display to the user to describe what the endpoint
21189a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * of this route is currently doing
21199a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
21209a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void setStatus(CharSequence status) {
21219a1de308cea2d160778fd977825f10a07b49d738Adam Powell            setStatusInt(status);
21229a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2123ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2124ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2125ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set the RemoteControlClient responsible for reporting playback info for this
2126ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * user route.
2127ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
2128ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * <p>If this route manages remote playback, the data exposed by this
2129ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * RemoteControlClient will be used to reflect and update information
2130ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * such as route volume info in related UIs.</p>
2131ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
21321357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * <p>The RemoteControlClient must have been previously registered with
21331357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}.</p>
21341357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *
2135ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @param rcc RemoteControlClient associated with this route
2136ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
2137ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setRemoteControlClient(RemoteControlClient rcc) {
2138ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            mRcc = rcc;
21391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            updatePlaybackInfoOnRcc();
2140ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2141ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2142ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
21434599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         * Retrieve the RemoteControlClient associated with this route, if one has been set.
21444599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         *
21454599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         * @return the RemoteControlClient associated with this route
21464599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         * @see #setRemoteControlClient(RemoteControlClient)
21474599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         */
21484599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        public RemoteControlClient getRemoteControlClient() {
21494599696591f745b3a546197d2ba7e5cfc5562484Adam Powell            return mRcc;
21504599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        }
21514599696591f745b3a546197d2ba7e5cfc5562484Adam Powell
21524599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        /**
2153ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this route.
2154ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2155ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
2156ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @param icon icon drawable to use to represent this route
2157ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
2158ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setIconDrawable(Drawable icon) {
2159ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            mIcon = icon;
2160ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2161ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2162ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2163ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this route.
2164ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2165ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
216671c69897ad0a55d590698bfa399bfe99c763b9dbAdam Powell         * @param resId Resource ID of an icon drawable to use to represent this route
2167ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
21687b9c912f536925ac6ec43935d6e97506851b33d6Tor Norbye        public void setIconResource(@DrawableRes int resId) {
2169ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            setIconDrawable(sStatic.mResources.getDrawable(resId));
2170ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
21711357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
21721357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
21731357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Set a callback to be notified of volume update requests
21741357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param vcb
21751357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
21761357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolumeCallback(VolumeCallback vcb) {
21771357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            mVcb = new VolumeCallbackInfo(vcb, this);
21781357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
21791357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
21801357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
21811357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines whether playback associated with this route is "local"
21821357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *    ({@link RouteInfo#PLAYBACK_TYPE_LOCAL}) or "remote"
21831357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *    ({@link RouteInfo#PLAYBACK_TYPE_REMOTE}).
21841357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param type
21851357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
21867c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        public void setPlaybackType(@RouteInfo.PlaybackType int type) {
21871357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackType != type) {
21881357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mPlaybackType = type;
2189430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
21901357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
21911357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
21921357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
21931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
21941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines whether volume for the playback associated with this route is fixed
21951357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * ({@link RouteInfo#PLAYBACK_VOLUME_FIXED}) or can modified
21961357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * ({@link RouteInfo#PLAYBACK_VOLUME_VARIABLE}).
21971357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volumeHandling
21981357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
21997c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        public void setVolumeHandling(@RouteInfo.PlaybackVolume int volumeHandling) {
22001357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mVolumeHandling != volumeHandling) {
22011357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mVolumeHandling = volumeHandling;
2202430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
22031357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
22041357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
22051357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
22061357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
22071357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines at what volume the playback associated with this route is performed (for user
22081357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * feedback purposes). This information is only used when the playback is not local.
22091357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volume
22101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
22111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolume(int volume) {
22128e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            volume = Math.max(0, Math.min(volume, getVolumeMax()));
22131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mVolume != volume) {
22141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mVolume = volume;
2215430fc48865e5a371b08f180390946b96d73848feRoboErik                if (mSvp != null) {
22160d0f67f5ee5f939a1b611bc4583212707afd9beeRoboErik                    mSvp.setCurrentVolume(mVolume);
22175d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
22188e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                dispatchRouteVolumeChanged(this);
2219f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                if (mGroup != null) {
2220f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                    mGroup.memberVolumeChanged(this);
2221f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                }
22228e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
22238e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
22248e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
22258e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
22268e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestSetVolume(int volume) {
22278e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mVolumeHandling == PLAYBACK_VOLUME_VARIABLE) {
22288e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (mVcb == null) {
22298e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Cannot requestSetVolume on user route - no volume callback set");
22308e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    return;
22318e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
22328e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVcb.vcb.onVolumeSetRequest(this, volume);
22338e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
22348e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
22358e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
22368e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
22378e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestUpdateVolume(int direction) {
22388e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mVolumeHandling == PLAYBACK_VOLUME_VARIABLE) {
22398e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (mVcb == null) {
22408e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Cannot requestChangeVolume on user route - no volumec callback set");
22418e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    return;
22428e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
22438e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVcb.vcb.onVolumeUpdateRequest(this, direction);
22441357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
22451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
22461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
22471357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
22481357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines the maximum volume at which the playback associated with this route is performed
22491357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * (for user feedback purposes). This information is only used when the playback is not
22501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * local.
22511357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volumeMax
22521357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
22531357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolumeMax(int volumeMax) {
22541357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mVolumeMax != volumeMax) {
22551357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mVolumeMax = volumeMax;
2256430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
22571357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
22581357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
22591357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
22601357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
22611357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines over what stream type the media is presented.
22621357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param stream
22631357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
22641357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setPlaybackStream(int stream) {
22651357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackStream != stream) {
22661357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mPlaybackStream = stream;
2267430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
22681357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
22691357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
22701357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
22711357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        private void updatePlaybackInfoOnRcc() {
2272430fc48865e5a371b08f180390946b96d73848feRoboErik            configureSessionVolume();
22735d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        }
22745d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
22755d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        private void configureSessionVolume() {
22765d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            if (mRcc == null) {
22775d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                if (DEBUG) {
22785d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    Log.d(TAG, "No Rcc to configure volume for route " + mName);
22795d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
22805d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                return;
22815d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
22825d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            MediaSession session = mRcc.getMediaSession();
22835d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            if (session == null) {
22845d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                if (DEBUG) {
22855d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    Log.d(TAG, "Rcc has no session to configure volume");
22865d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
22875d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                return;
22885d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
22895d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            if (mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
22907c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang                @VolumeProvider.ControlType int volumeControl =
22917c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang                        VolumeProvider.VOLUME_CONTROL_FIXED;
22925d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                switch (mVolumeHandling) {
22935d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    case RemoteControlClient.PLAYBACK_VOLUME_VARIABLE:
2294ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik                        volumeControl = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
22955d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        break;
22965d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    case RemoteControlClient.PLAYBACK_VOLUME_FIXED:
22975d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    default:
22985d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        break;
22991357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                }
23005d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                // Only register a new listener if necessary
23015d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                if (mSvp == null || mSvp.getVolumeControl() != volumeControl
23025d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        || mSvp.getMaxVolume() != mVolumeMax) {
23030d0f67f5ee5f939a1b611bc4583212707afd9beeRoboErik                    mSvp = new SessionVolumeProvider(volumeControl, mVolumeMax, mVolume);
23045d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    session.setPlaybackToRemote(mSvp);
23055d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
23065d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            } else {
23075d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                // We only know how to handle local and remote, fall back to local if not remote.
23089db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik                AudioAttributes.Builder bob = new AudioAttributes.Builder();
23099db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik                bob.setLegacyStreamType(mPlaybackStream);
23109db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik                session.setPlaybackToLocal(bob.build());
23115d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                mSvp = null;
23121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
23131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
23141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
2315ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        class SessionVolumeProvider extends VolumeProvider {
23165d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
23177c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang            public SessionVolumeProvider(@VolumeProvider.ControlType int volumeControl,
23187c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang                    int maxVolume, int currentVolume) {
23190d0f67f5ee5f939a1b611bc4583212707afd9beeRoboErik                super(volumeControl, maxVolume, currentVolume);
23205d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
23215d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
23225d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            @Override
23235d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            public void onSetVolumeTo(final int volume) {
23245d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                sStatic.mHandler.post(new Runnable() {
23255d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    @Override
23265d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    public void run() {
23275d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        if (mVcb != null) {
23285d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                            mVcb.vcb.onVolumeSetRequest(mVcb.route, volume);
23295d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        }
23305d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    }
23315d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                });
23325d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
23335d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
23345d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            @Override
23351ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik            public void onAdjustVolume(final int direction) {
23365d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                sStatic.mHandler.post(new Runnable() {
23375d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    @Override
23385d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    public void run() {
23395d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        if (mVcb != null) {
23401ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik                            mVcb.vcb.onVolumeUpdateRequest(mVcb.route, direction);
23415d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        }
23425d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    }
23435d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                });
23445d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
23455d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        }
23469a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
23479a1de308cea2d160778fd977825f10a07b49d738Adam Powell
23489a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
23499a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a route that consists of multiple other routes in a group.
23509a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
2351b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class RouteGroup extends RouteInfo {
23529a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
23539a1de308cea2d160778fd977825f10a07b49d738Adam Powell        private boolean mUpdateName;
23549a1de308cea2d160778fd977825f10a07b49d738Adam Powell
23559a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteGroup(RouteCategory category) {
23569a1de308cea2d160778fd977825f10a07b49d738Adam Powell            super(category);
23579a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mGroup = this;
23588e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeHandling = PLAYBACK_VOLUME_FIXED;
23599a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
23609a1de308cea2d160778fd977825f10a07b49d738Adam Powell
236169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
23620d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        CharSequence getName(Resources res) {
23639a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (mUpdateName) updateName();
23640d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return super.getName(res);
23659a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
23669a1de308cea2d160778fd977825f10a07b49d738Adam Powell
23679a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
23689a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Add a route to this group. The route must not currently belong to another group.
23699a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
23709a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to add to this group
23719a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
23729a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void addRoute(RouteInfo route) {
23739a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != null) {
23749a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalStateException("Route " + route + " is already part of a group.");
23759a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
23769a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getCategory() != mCategory) {
23779a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException(
23789a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        "Route cannot be added to a group with a different category. " +
23799a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            "(Route category=" + route.getCategory() +
23809a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            " group category=" + mCategory + ")");
23819a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
2382d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            final int at = mRoutes.size();
23839a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.add(route);
2384d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = this;
23859a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2386f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
23879a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
2388f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell            dispatchRouteGrouped(route, this, at);
23899a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
23909a1de308cea2d160778fd977825f10a07b49d738Adam Powell
23919a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
23929a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Add a route to this group before the specified index.
23939a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
23949a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to add
23959a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param insertAt insert the new route before this index
23969a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
23979a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void addRoute(RouteInfo route, int insertAt) {
23989a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != null) {
23999a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalStateException("Route " + route + " is already part of a group.");
24009a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
24019a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getCategory() != mCategory) {
24029a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException(
24039a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        "Route cannot be added to a group with a different category. " +
24049a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            "(Route category=" + route.getCategory() +
24059a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            " group category=" + mCategory + ")");
24069a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
24079a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.add(insertAt, route);
2408d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = this;
24099a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2410f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
24119a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
2412f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell            dispatchRouteGrouped(route, this, insertAt);
24139a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
24149a1de308cea2d160778fd977825f10a07b49d738Adam Powell
24159a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
24169a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Remove a route from this group.
24179a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
24189a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to remove
24199a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
24209a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void removeRoute(RouteInfo route) {
24219a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != this) {
24229a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException("Route " + route +
24239a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        " is not a member of this group.");
24249a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
24259a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.remove(route);
2426d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = null;
24279a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2428f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
2429d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteUngrouped(route, this);
24309a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
24319a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
24329a1de308cea2d160778fd977825f10a07b49d738Adam Powell
24339a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
24349a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Remove the route at the specified index from this group.
24359a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
24369a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param index index of the route to remove
24379a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
24389a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void removeRoute(int index) {
2439d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            RouteInfo route = mRoutes.remove(index);
2440d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = null;
24419a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2442f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
2443d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteUngrouped(route, this);
24449a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
24459a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
24469a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2447d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2448d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @return The number of routes in this group
2449d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
2450d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public int getRouteCount() {
2451d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            return mRoutes.size();
2452d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        }
2453d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2454d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2455d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Return the route in this group at the specified index
2456d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2457d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param index Index to fetch
2458d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @return The route at index
2459d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
2460d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public RouteInfo getRouteAt(int index) {
2461d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            return mRoutes.get(index);
2462d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        }
2463d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2464ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2465ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this group.
2466ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2467ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
2468ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @param icon icon drawable to use to represent this group
2469ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
2470ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setIconDrawable(Drawable icon) {
2471ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            mIcon = icon;
2472ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2473ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2474ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2475ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this group.
2476ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2477ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
247871c69897ad0a55d590698bfa399bfe99c763b9dbAdam Powell         * @param resId Resource ID of an icon drawable to use to represent this group
2479ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
24807b9c912f536925ac6ec43935d6e97506851b33d6Tor Norbye        public void setIconResource(@DrawableRes int resId) {
2481ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            setIconDrawable(sStatic.mResources.getDrawable(resId));
2482ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2483ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
24848e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
24858e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestSetVolume(int volume) {
24868e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int maxVol = getVolumeMax();
24878e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (maxVol == 0) {
24888e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                return;
24898e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
24908e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
24918e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final float scaledVolume = (float) volume / maxVol;
24928e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int routeCount = getRouteCount();
24938e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            for (int i = 0; i < routeCount; i++) {
24948e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final RouteInfo route = getRouteAt(i);
24958e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int routeVol = (int) (scaledVolume * route.getVolumeMax());
24968e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                route.requestSetVolume(routeVol);
24978e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
24988e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (volume != mVolume) {
24998e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVolume = volume;
25008e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                dispatchRouteVolumeChanged(this);
25018e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
25028e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
25038e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
25048e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
25058e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestUpdateVolume(int direction) {
25068e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int maxVol = getVolumeMax();
25078e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (maxVol == 0) {
25088e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                return;
25098e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
25108e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
25118e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int routeCount = getRouteCount();
2512f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            int volume = 0;
25138e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            for (int i = 0; i < routeCount; i++) {
25148e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final RouteInfo route = getRouteAt(i);
25158e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                route.requestUpdateVolume(direction);
2516f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                final int routeVol = route.getVolume();
2517f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                if (routeVol > volume) {
2518f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                    volume = routeVol;
2519f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                }
25208e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
25218e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (volume != mVolume) {
25228e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVolume = volume;
25238e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                dispatchRouteVolumeChanged(this);
25248e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
25258e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
25268e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
25279a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void memberNameChanged(RouteInfo info, CharSequence name) {
25289a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
25299a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
25309a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
25319a1de308cea2d160778fd977825f10a07b49d738Adam Powell
25329a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void memberStatusChanged(RouteInfo info, CharSequence status) {
25339a1de308cea2d160778fd977825f10a07b49d738Adam Powell            setStatusInt(status);
25349a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
25359a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2536f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        void memberVolumeChanged(RouteInfo info) {
2537f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
2538f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        }
2539f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell
2540f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        void updateVolume() {
2541f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            // A group always represents the highest component volume value.
2542f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            final int routeCount = getRouteCount();
2543f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            int volume = 0;
2544f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            for (int i = 0; i < routeCount; i++) {
2545f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                final int routeVol = getRouteAt(i).getVolume();
2546f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                if (routeVol > volume) {
2547f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                    volume = routeVol;
2548f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                }
2549f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            }
2550f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            if (volume != mVolume) {
2551f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                mVolume = volume;
2552f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                dispatchRouteVolumeChanged(this);
2553f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            }
2554f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        }
2555f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell
2556d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        @Override
2557d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        void routeUpdated() {
2558d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            int types = 0;
2559d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            final int count = mRoutes.size();
2560b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            if (count == 0) {
2561b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell                // Don't keep empty groups in the router.
256269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                MediaRouter.removeRouteStatic(this);
2563b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell                return;
2564b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            }
2565b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
25668e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            int maxVolume = 0;
25678e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            boolean isLocal = true;
25688e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            boolean isFixedVolume = true;
2569d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            for (int i = 0; i < count; i++) {
25708e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final RouteInfo route = mRoutes.get(i);
25718e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                types |= route.mSupportedTypes;
25728e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int routeMaxVolume = route.getVolumeMax();
25738e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (routeMaxVolume > maxVolume) {
25748e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    maxVolume = routeMaxVolume;
25758e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
25768e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                isLocal &= route.getPlaybackType() == PLAYBACK_TYPE_LOCAL;
25778e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                isFixedVolume &= route.getVolumeHandling() == PLAYBACK_VOLUME_FIXED;
2578d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            }
25798e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mPlaybackType = isLocal ? PLAYBACK_TYPE_LOCAL : PLAYBACK_TYPE_REMOTE;
25808e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeHandling = isFixedVolume ? PLAYBACK_VOLUME_FIXED : PLAYBACK_VOLUME_VARIABLE;
2581d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            mSupportedTypes = types;
25828e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeMax = maxVolume;
2583d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            mIcon = count == 1 ? mRoutes.get(0).getIconDrawable() : null;
2584d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            super.routeUpdated();
2585d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        }
2586d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
25879a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void updateName() {
25889a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final StringBuilder sb = new StringBuilder();
25899a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final int count = mRoutes.size();
25909a1de308cea2d160778fd977825f10a07b49d738Adam Powell            for (int i = 0; i < count; i++) {
25919a1de308cea2d160778fd977825f10a07b49d738Adam Powell                final RouteInfo info = mRoutes.get(i);
2592b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell                // TODO: There's probably a much more correct way to localize this.
25939a1de308cea2d160778fd977825f10a07b49d738Adam Powell                if (i > 0) sb.append(", ");
25949a1de308cea2d160778fd977825f10a07b49d738Adam Powell                sb.append(info.mName);
25959a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
25969a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = sb.toString();
25979a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = false;
25989a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2599d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
2600d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        @Override
2601d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        public String toString() {
2602d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            StringBuilder sb = new StringBuilder(super.toString());
2603d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            sb.append('[');
2604d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            final int count = mRoutes.size();
2605d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            for (int i = 0; i < count; i++) {
2606d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                if (i > 0) sb.append(", ");
2607d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                sb.append(mRoutes.get(i));
2608d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            }
2609d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            sb.append(']');
2610d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            return sb.toString();
2611d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        }
26129a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
26139a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26149a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
26159a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Definition of a category of routes. All routes belong to a category.
26169a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
2617b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class RouteCategory {
26189a1de308cea2d160778fd977825f10a07b49d738Adam Powell        CharSequence mName;
26190d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        int mNameResId;
26209a1de308cea2d160778fd977825f10a07b49d738Adam Powell        int mTypes;
26219a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final boolean mGroupable;
2622705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        boolean mIsSystem;
26239a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26249a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteCategory(CharSequence name, int types, boolean groupable) {
26259a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = name;
26269a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mTypes = types;
26279a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mGroupable = groupable;
26289a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
26299a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26300d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        RouteCategory(int nameResId, int types, boolean groupable) {
26310d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mNameResId = nameResId;
26320d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mTypes = types;
26330d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mGroupable = groupable;
26340d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
26350d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell
26369a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
26379a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return the name of this route category
26389a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
26399a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getName() {
26400d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(sStatic.mResources);
26410d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
26425d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
26430d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        /**
26440d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * Return the properly localized/configuration dependent name of this RouteCategory.
26455d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik         *
26460d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @param context Context to resolve name resources
26470d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @return the name of this route category
26480d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         */
26490d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public CharSequence getName(Context context) {
26500d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(context.getResources());
26510d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
26525d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
26530d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        CharSequence getName(Resources res) {
26540d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            if (mNameResId != 0) {
26550d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                return res.getText(mNameResId);
26560d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            }
26579a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mName;
26589a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
26599a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26609a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
2661d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Return the current list of routes in this category that have been added
2662d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * to the MediaRouter.
26639a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2664d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * <p>This list will not include routes that are nested within RouteGroups.
2665d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * A RouteGroup is treated as a single route within its category.</p>
2666d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2667d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param out a List to fill with the routes in this category. If this parameter is
2668d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *            non-null, it will be cleared, filled with the current routes with this
2669d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *            category, and returned. If this parameter is null, a new List will be
2670d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *            allocated to report the category's current routes.
2671d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @return A list with the routes in this category that have been added to the MediaRouter.
26729a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
2673d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public List<RouteInfo> getRoutes(List<RouteInfo> out) {
2674d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            if (out == null) {
2675d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                out = new ArrayList<RouteInfo>();
2676d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            } else {
2677d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                out.clear();
2678d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            }
2679d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2680b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final int count = getRouteCountStatic();
2681d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            for (int i = 0; i < count; i++) {
2682b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                final RouteInfo route = getRouteAtStatic(i);
2683d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                if (route.mCategory == this) {
2684d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                    out.add(route);
2685d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                }
2686d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            }
2687d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            return out;
26889a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
26899a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26909a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
26919a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return Flag set describing the route types supported by this category
26929a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
26939a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int getSupportedTypes() {
26949a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mTypes;
26959a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
26969a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26979a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
26989a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Return whether or not this category supports grouping.
26999a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
27009a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * <p>If this method returns true, all routes obtained from this category
2701d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * via calls to {@link #getRouteAt(int)} will be {@link MediaRouter.RouteGroup}s.</p>
27029a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
27039a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return true if this category supports
27049a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
27059a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public boolean isGroupable() {
27069a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mGroupable;
27079a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27089a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2709705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        /**
2710705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * @return true if this is the category reserved for system routes.
2711705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * @hide
2712705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         */
2713705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        public boolean isSystem() {
2714705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return mIsSystem;
2715705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
2716705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
271769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
27189a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public String toString() {
27199a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return "RouteCategory{ name=" + mName + " types=" + typesToString(mTypes) +
2720d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                    " groupable=" + mGroupable + " }";
27219a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27229a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
27239a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27249a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static class CallbackInfo {
27259a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int type;
272666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        public int flags;
2727b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        public final Callback cb;
2728b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        public final MediaRouter router;
27299a1de308cea2d160778fd977825f10a07b49d738Adam Powell
273066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        public CallbackInfo(Callback cb, int type, int flags, MediaRouter router) {
27319a1de308cea2d160778fd977825f10a07b49d738Adam Powell            this.cb = cb;
27329a1de308cea2d160778fd977825f10a07b49d738Adam Powell            this.type = type;
273366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            this.flags = flags;
2734b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            this.router = router;
27359a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
273666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
273766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        public boolean filterRouteEvent(RouteInfo route) {
2738af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            return filterRouteEvent(route.mSupportedTypes);
2739af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        }
2740af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
2741af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        public boolean filterRouteEvent(int supportedTypes) {
274266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            return (flags & CALLBACK_FLAG_UNFILTERED_EVENTS) != 0
2743af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    || (type & supportedTypes) != 0;
274466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
27459a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
27469a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27479a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
27489a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Interface for receiving events about media routing changes.
27499a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * All methods of this interface will be called from the application's main thread.
275066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
275166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * A Callback will only receive events relevant to routes that the callback
275266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * was registered for unless the {@link MediaRouter#CALLBACK_FLAG_UNFILTERED_EVENTS}
275366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * flag was specified in {@link MediaRouter#addCallback(int, Callback, int)}.
275466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
27559a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
275666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @see MediaRouter#addCallback(int, Callback, int)
27579a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see MediaRouter#removeCallback(Callback)
27589a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
27590d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    public static abstract class Callback {
27609a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
27619a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when the supplied route becomes selected as the active route
27629a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * for the given route type.
27639a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2764d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
27659a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param type Type flag set indicating the routes that have been selected
27669a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been selected for the given route types
27679a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
27680d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteSelected(MediaRouter router, int type, RouteInfo info);
27699a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27709a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
27719a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when the supplied route becomes unselected as the active route
27729a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * for the given route type.
27739a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2774d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
27759a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param type Type flag set indicating the routes that have been unselected
27769a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been unselected for the given route types
27779a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
27780d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteUnselected(MediaRouter router, int type, RouteInfo info);
27799a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27809a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
27819a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when a route for the specified type was added.
27829a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2783d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
27849a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has become available for use
27859a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
27860d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteAdded(MediaRouter router, RouteInfo info);
27879a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27889a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
27899a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when a route for the specified type was removed.
27909a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2791d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
27929a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been removed from availability
27939a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
27940d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteRemoved(MediaRouter router, RouteInfo info);
27959a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27969a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
27979a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when an aspect of the indicated route has changed.
27989a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
27999a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * <p>This will not indicate that the types supported by this route have
28009a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * changed, only that cosmetic info such as name or status have been updated.</p>
28019a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2802d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
28039a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info The route that was changed
28049a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
28050d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteChanged(MediaRouter router, RouteInfo info);
2806d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2807d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2808d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Called when a route is added to a group.
2809d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2810d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
2811d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param info The route that was added
2812d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param group The group the route was added to
2813d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param index The route index within group that info was added at
2814d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
28150d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
28160d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                int index);
2817d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2818d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2819d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Called when a route is removed from a group.
2820d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2821d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
2822d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param info The route that was removed
2823d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param group The group the route was removed from
2824d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
28250d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group);
28268e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
28278e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        /**
28288e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Called when a route's volume changes.
28298e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         *
28308e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param router the MediaRouter reporting the event
28318e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param info The route with altered volume
28328e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         */
28338e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public abstract void onRouteVolumeChanged(MediaRouter router, RouteInfo info);
283492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
283592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        /**
283692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * Called when a route's presentation display changes.
283792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * <p>
283892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * This method is called whenever the route's presentation display becomes
283992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * available, is removes or has changes to some of its properties (such as its size).
284092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p>
284192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
284292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @param router the MediaRouter reporting the event
284392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @param info The route whose presentation display changed
284492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
284592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @see RouteInfo#getPresentationDisplay()
284692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         */
284792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
284892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
28499a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
28509a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28519a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
28520d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * Stub implementation of {@link MediaRouter.Callback}.
28530d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * Each abstract method is defined as a no-op. Override just the ones
28549a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * you need.
28559a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
28560d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    public static class SimpleCallback extends Callback {
28579a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28589a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2859d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
28609a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
28619a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28629a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2863d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
28649a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
28659a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28669a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2867d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteAdded(MediaRouter router, RouteInfo info) {
28689a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
28699a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28709a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2871d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteRemoved(MediaRouter router, RouteInfo info) {
28729a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
28739a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28749a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2875d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteChanged(MediaRouter router, RouteInfo info) {
28769a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
28779a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28789a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2879d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
2880d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                int index) {
28819a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
28829a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28839a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2884d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
28859a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2886d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
28878e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
28888e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void onRouteVolumeChanged(MediaRouter router, RouteInfo info) {
28898e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
28909a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
28911357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
28921357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    static class VolumeCallbackInfo {
28931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final VolumeCallback vcb;
28941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final RouteInfo route;
28951357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
28961357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public VolumeCallbackInfo(VolumeCallback vcb, RouteInfo route) {
28971357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            this.vcb = vcb;
28981357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            this.route = route;
28991357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
29001357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    }
29011357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
29021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    /**
29031357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * Interface for receiving events about volume changes.
29041357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * All methods of this interface will be called from the application's main thread.
29051357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     *
29061357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * <p>A VolumeCallback will only receive events relevant to routes that the callback
29071357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * was registered for.</p>
29081357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     *
29091357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * @see UserRouteInfo#setVolumeCallback(VolumeCallback)
29101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     */
29111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    public static abstract class VolumeCallback {
29121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
29131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Called when the volume for the route should be increased or decreased.
29141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param info the route affected by this event
29151357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param direction an integer indicating whether the volume is to be increased
29161357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     (positive value) or decreased (negative value).
29171357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     For bundled changes, the absolute value indicates the number of changes
29181357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     in the same direction, e.g. +3 corresponds to three "volume up" changes.
29191357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
29201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public abstract void onVolumeUpdateRequest(RouteInfo info, int direction);
29211357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
29221357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Called when the volume for the route should be set to the given value
29231357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param info the route affected by this event
29241357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volume an integer indicating the new volume value that should be used, always
29251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     between 0 and the value set by {@link UserRouteInfo#setVolumeMax(int)}.
29261357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
29271357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public abstract void onVolumeSetRequest(RouteInfo info, int volume);
29281357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    }
29291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
29308e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    static class VolumeChangeReceiver extends BroadcastReceiver {
29318e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
29328e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void onReceive(Context context, Intent intent) {
29338e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (intent.getAction().equals(AudioManager.VOLUME_CHANGED_ACTION)) {
29348e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
29358e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                        -1);
29368e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (streamType != AudioManager.STREAM_MUSIC) {
29378e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    return;
29388e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
29398e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
29408e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int newVolume = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
29418e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int oldVolume = intent.getIntExtra(
29428e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                        AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0);
29438e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (newVolume != oldVolume) {
29448e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    systemVolumeChanged(newVolume);
29458e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
29468e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
29478e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
2948705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
29498e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
2950705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    static class WifiDisplayStatusChangedReceiver extends BroadcastReceiver {
2951705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        @Override
2952705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        public void onReceive(Context context, Intent intent) {
2953705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (intent.getAction().equals(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)) {
2954705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                updateWifiDisplayStatus((WifiDisplayStatus) intent.getParcelableExtra(
2955705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                        DisplayManager.EXTRA_WIFI_DISPLAY_STATUS));
2956705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
2957705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
29588e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    }
29599a1de308cea2d160778fd977825f10a07b49d738Adam Powell}
2960