MediaRouter.java revision ef3c9e9b057a5aac2d0d012e8e6385660478e203
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;
20ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackbornimport android.app.ActivityThread;
218e37a85bf3dc39519942698dc90a3951306b934bAdam Powellimport android.content.BroadcastReceiver;
229a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.content.Context;
238e37a85bf3dc39519942698dc90a3951306b934bAdam Powellimport android.content.Intent;
248e37a85bf3dc39519942698dc90a3951306b934bAdam Powellimport android.content.IntentFilter;
25af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brownimport android.content.pm.PackageManager;
26b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackbornimport android.content.res.Resources;
27ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powellimport android.graphics.drawable.Drawable;
28705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.hardware.display.DisplayManager;
29705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.hardware.display.WifiDisplay;
30705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.hardware.display.WifiDisplayStatus;
315d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErikimport android.media.session.MediaSession;
329a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.os.Handler;
33632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.os.IBinder;
34af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brownimport android.os.Process;
35632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.os.RemoteException;
36632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.os.ServiceManager;
3769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.os.UserHandle;
38632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.text.TextUtils;
399a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.util.Log;
40705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.view.Display;
419a1de308cea2d160778fd977825f10a07b49d738Adam Powell
429a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport java.util.ArrayList;
439a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport java.util.HashMap;
44d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powellimport java.util.List;
45e6585b32ea586743258a5457e2184ffc087f2d2fKenny Rootimport java.util.Objects;
4639d5c6172503620ac3761148adac5fd7fa20d02dAdam Powellimport java.util.concurrent.CopyOnWriteArrayList;
479a1de308cea2d160778fd977825f10a07b49d738Adam Powell
489a1de308cea2d160778fd977825f10a07b49d738Adam Powell/**
499a1de308cea2d160778fd977825f10a07b49d738Adam Powell * MediaRouter allows applications to control the routing of media channels
509a1de308cea2d160778fd977825f10a07b49d738Adam Powell * and streams from the current device to external speakers and destination devices.
519a1de308cea2d160778fd977825f10a07b49d738Adam Powell *
52b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * <p>A MediaRouter is retrieved through {@link Context#getSystemService(String)
53b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * Context.getSystemService()} of a {@link Context#MEDIA_ROUTER_SERVICE
54b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * Context.MEDIA_ROUTER_SERVICE}.
55b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn *
56b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * <p>The media router API is not thread-safe; all interactions with it must be
57b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * done from the main thread of the process.</p>
589a1de308cea2d160778fd977825f10a07b49d738Adam Powell */
599a1de308cea2d160778fd977825f10a07b49d738Adam Powellpublic class MediaRouter {
609a1de308cea2d160778fd977825f10a07b49d738Adam Powell    private static final String TAG = "MediaRouter";
6169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
629a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown    static class Static implements DisplayManager.DisplayListener {
6469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final Context mAppContext;
65b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final Resources mResources;
66632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn        final IAudioService mAudioService;
67705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final DisplayManager mDisplayService;
6869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final IMediaRouterService mMediaRouterService;
69b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final Handler mHandler;
7039d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        final CopyOnWriteArrayList<CallbackInfo> mCallbacks =
7139d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell                new CopyOnWriteArrayList<CallbackInfo>();
72b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
73b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
74b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final ArrayList<RouteCategory> mCategories = new ArrayList<RouteCategory>();
75b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
76b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final RouteCategory mSystemCategory;
77632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn
78705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
79b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
80705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        RouteInfo mDefaultAudioVideo;
81b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        RouteInfo mBluetoothA2dpRoute;
82b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
83b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        RouteInfo mSelectedRoute;
849a1de308cea2d160778fd977825f10a07b49d738Adam Powell
85af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        final boolean mCanConfigureWifiDisplays;
8666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        boolean mActivelyScanningWifiDisplays;
87ce468a35b388ca46578934706b38dbae94941643Jeff Brown        String mPreviousActiveWifiDisplayAddress;
88705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
8969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        int mDiscoveryRequestRouteTypes;
9069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        boolean mDiscoverRequestActiveScan;
9169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
9269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        int mCurrentUserId = -1;
9369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        IMediaRouterClient mClient;
9469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        MediaRouterClientState mClientState;
9569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
96705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() {
9766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            @Override
98632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
99632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                mHandler.post(new Runnable() {
100632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    @Override public void run() {
101705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                        updateAudioRoutes(newRoutes);
102632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    }
103632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                });
104632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
105632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn        };
106632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn
107b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        Static(Context appContext) {
10869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mAppContext = appContext;
109b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            mResources = Resources.getSystem();
110b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            mHandler = new Handler(appContext.getMainLooper());
1119a1de308cea2d160778fd977825f10a07b49d738Adam Powell
112632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
113632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            mAudioService = IAudioService.Stub.asInterface(b);
1149a1de308cea2d160778fd977825f10a07b49d738Adam Powell
115705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            mDisplayService = (DisplayManager) appContext.getSystemService(Context.DISPLAY_SERVICE);
116705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
11769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mMediaRouterService = IMediaRouterService.Stub.asInterface(
11869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
11969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
120dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell            mSystemCategory = new RouteCategory(
121dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell                    com.android.internal.R.string.default_audio_route_category_name,
122705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO, false);
123705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            mSystemCategory.mIsSystem = true;
124af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
125af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Only the system can configure wifi displays.  The display manager
126af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // enforces this with a permission check.  Set a flag here so that we
127af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // know whether this process is actually allowed to scan and connect.
128af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            mCanConfigureWifiDisplays = appContext.checkPermission(
129af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    Manifest.permission.CONFIGURE_WIFI_DISPLAY,
130af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    Process.myPid(), Process.myUid()) == PackageManager.PERMISSION_GRANTED;
131b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        }
132b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
133b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        // Called after sStatic is initialized
1348e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        void startMonitoringRoutes(Context appContext) {
135705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            mDefaultAudioVideo = new RouteInfo(mSystemCategory);
136705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            mDefaultAudioVideo.mNameResId = com.android.internal.R.string.default_audio_route_name;
137705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            mDefaultAudioVideo.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO;
1385830b0b33618940d65197cec99d697b21908fec8Chong Zhang            mDefaultAudioVideo.updatePresentationDisplay();
1392ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            addRouteStatic(mDefaultAudioVideo);
140632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn
1412ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            // This will select the active wifi display route if there is one.
1422ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            updateWifiDisplayStatus(mDisplayService.getWifiDisplayStatus());
1432ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell
1442ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            appContext.registerReceiver(new WifiDisplayStatusChangedReceiver(),
1452ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell                    new IntentFilter(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED));
1468e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            appContext.registerReceiver(new VolumeChangeReceiver(),
1478e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
1488e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
14992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            mDisplayService.registerDisplayListener(this, mHandler);
15092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
151705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            AudioRoutesInfo newAudioRoutes = null;
152632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            try {
153705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
154632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            } catch (RemoteException e) {
155632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
156705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (newAudioRoutes != null) {
1572ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell                // This will select the active BT route if there is one and the current
1582ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell                // selected route is the default system route, or if there is no selected
1592ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell                // route yet.
160705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                updateAudioRoutes(newAudioRoutes);
161632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
162705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
16369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Bind to the media router service.
16469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            rebindAsUser(UserHandle.myUserId());
16569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
1662ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            // Select the default route if the above didn't sync us up
1672ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            // appropriately with relevant system state.
1682ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            if (mSelectedRoute == null) {
16969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                selectDefaultRouteStatic();
1702ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            }
171632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn        }
172632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn
173705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        void updateAudioRoutes(AudioRoutesInfo newRoutes) {
174705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (newRoutes.mMainType != mCurAudioRoutesInfo.mMainType) {
175705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                mCurAudioRoutesInfo.mMainType = newRoutes.mMainType;
176632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                int name;
177632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                if ((newRoutes.mMainType&AudioRoutesInfo.MAIN_HEADPHONES) != 0
178632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        || (newRoutes.mMainType&AudioRoutesInfo.MAIN_HEADSET) != 0) {
179632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    name = com.android.internal.R.string.default_audio_route_name_headphones;
180632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                } else if ((newRoutes.mMainType&AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
181632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
182632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                } else if ((newRoutes.mMainType&AudioRoutesInfo.MAIN_HDMI) != 0) {
1834131a37366d59b5e61f55c4e48d2b22ee0c4cad4Adam Powell                    name = com.android.internal.R.string.default_media_route_name_hdmi;
184632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                } else {
185632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    name = com.android.internal.R.string.default_audio_route_name;
186632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                }
187705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                sStatic.mDefaultAudioVideo.mNameResId = name;
188705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                dispatchRouteChanged(sStatic.mDefaultAudioVideo);
189632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
190bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell
1913f369684e13dfea0ba8ea134f3e95930b0dd7df0Adam Powell            final int mainType = mCurAudioRoutesInfo.mMainType;
1923f369684e13dfea0ba8ea134f3e95930b0dd7df0Adam Powell
193bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell            boolean a2dpEnabled;
194bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell            try {
195bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell                a2dpEnabled = mAudioService.isBluetoothA2dpOn();
196bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell            } catch (RemoteException e) {
197bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell                Log.e(TAG, "Error querying Bluetooth A2DP state", e);
198bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell                a2dpEnabled = false;
199bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell            }
200bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell
201705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (!TextUtils.equals(newRoutes.mBluetoothName, mCurAudioRoutesInfo.mBluetoothName)) {
202705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                mCurAudioRoutesInfo.mBluetoothName = newRoutes.mBluetoothName;
203705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                if (mCurAudioRoutesInfo.mBluetoothName != null) {
204632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    if (sStatic.mBluetoothA2dpRoute == null) {
205632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        final RouteInfo info = new RouteInfo(sStatic.mSystemCategory);
206705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                        info.mName = mCurAudioRoutesInfo.mBluetoothName;
20756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown                        info.mDescription = sStatic.mResources.getText(
20856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown                                com.android.internal.R.string.bluetooth_a2dp_audio_route_name);
209632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        info.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO;
210632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        sStatic.mBluetoothA2dpRoute = info;
2112ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell                        addRouteStatic(sStatic.mBluetoothA2dpRoute);
212632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    } else {
213705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                        sStatic.mBluetoothA2dpRoute.mName = mCurAudioRoutesInfo.mBluetoothName;
214632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        dispatchRouteChanged(sStatic.mBluetoothA2dpRoute);
215632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    }
216632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                } else if (sStatic.mBluetoothA2dpRoute != null) {
21769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    removeRouteStatic(sStatic.mBluetoothA2dpRoute);
218632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    sStatic.mBluetoothA2dpRoute = null;
219632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                }
220632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
221bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell
222bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell            if (mBluetoothA2dpRoute != null) {
2233f369684e13dfea0ba8ea134f3e95930b0dd7df0Adam Powell                if (mainType != AudioRoutesInfo.MAIN_SPEAKER &&
2243f369684e13dfea0ba8ea134f3e95930b0dd7df0Adam Powell                        mSelectedRoute == mBluetoothA2dpRoute && !a2dpEnabled) {
22569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mDefaultAudioVideo, false);
2263f369684e13dfea0ba8ea134f3e95930b0dd7df0Adam Powell                } else if ((mSelectedRoute == mDefaultAudioVideo || mSelectedRoute == null) &&
2272ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell                        a2dpEnabled) {
22869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mBluetoothA2dpRoute, false);
229bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell                }
230bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell            }
231b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        }
23292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
23369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void updateDiscoveryRequest() {
23469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // What are we looking for today?
23569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int routeTypes = 0;
23669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int passiveRouteTypes = 0;
23769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean activeScan = false;
23869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean activeScanWifiDisplay = false;
23969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int count = mCallbacks.size();
24069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < count; i++) {
24169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                CallbackInfo cbi = mCallbacks.get(i);
24269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if ((cbi.flags & (CALLBACK_FLAG_PERFORM_ACTIVE_SCAN
24369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        | CALLBACK_FLAG_REQUEST_DISCOVERY)) != 0) {
24469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Discovery explicitly requested.
24569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    routeTypes |= cbi.type;
24669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else if ((cbi.flags & CALLBACK_FLAG_PASSIVE_DISCOVERY) != 0) {
24769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Discovery only passively requested.
24869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    passiveRouteTypes |= cbi.type;
24969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else {
25069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Legacy case since applications don't specify the discovery flag.
25169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Unfortunately we just have to assume they always need discovery
25269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // whenever they have a callback registered.
25369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    routeTypes |= cbi.type;
25469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
25569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if ((cbi.flags & CALLBACK_FLAG_PERFORM_ACTIVE_SCAN) != 0) {
25669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    activeScan = true;
257af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    if ((cbi.type & ROUTE_TYPE_REMOTE_DISPLAY) != 0) {
25869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        activeScanWifiDisplay = true;
25969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
26069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
26169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
26269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (routeTypes != 0 || activeScan) {
26369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // If someone else requests discovery then enable the passive listeners.
26469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // This is used by the MediaRouteButton and MediaRouteActionProvider since
26569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // they don't receive lifecycle callbacks from the Activity.
26669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                routeTypes |= passiveRouteTypes;
26769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
26869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
26969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Update wifi display scanning.
270ce468a35b388ca46578934706b38dbae94941643Jeff Brown            // TODO: All of this should be managed by the media router service.
271ce468a35b388ca46578934706b38dbae94941643Jeff Brown            if (mCanConfigureWifiDisplays) {
272ce468a35b388ca46578934706b38dbae94941643Jeff Brown                if (mSelectedRoute != null
273ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        && mSelectedRoute.matchesTypes(ROUTE_TYPE_REMOTE_DISPLAY)) {
274ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    // Don't scan while already connected to a remote display since
275ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    // it may interfere with the ongoing transmission.
276ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    activeScanWifiDisplay = false;
27766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown                }
278ce468a35b388ca46578934706b38dbae94941643Jeff Brown                if (activeScanWifiDisplay) {
279ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    if (!mActivelyScanningWifiDisplays) {
280ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mActivelyScanningWifiDisplays = true;
281ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mDisplayService.startWifiDisplayScan();
282ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    }
283ce468a35b388ca46578934706b38dbae94941643Jeff Brown                } else {
284ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    if (mActivelyScanningWifiDisplays) {
285ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mActivelyScanningWifiDisplays = false;
286ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mDisplayService.stopWifiDisplayScan();
287ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    }
28866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown                }
28966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            }
29066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
29169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Tell the media router service all about it.
29269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (routeTypes != mDiscoveryRequestRouteTypes
29369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    || activeScan != mDiscoverRequestActiveScan) {
29469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mDiscoveryRequestRouteTypes = routeTypes;
29569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mDiscoverRequestActiveScan = activeScan;
29669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                publishClientDiscoveryRequest();
29766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            }
29866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
29966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
30092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        @Override
30192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onDisplayAdded(int displayId) {
30292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            updatePresentationDisplays(displayId);
30392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
30492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
30592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        @Override
30692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onDisplayChanged(int displayId) {
30792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            updatePresentationDisplays(displayId);
30892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
30992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
31092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        @Override
31192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onDisplayRemoved(int displayId) {
31292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            updatePresentationDisplays(displayId);
31392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
31492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
31592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public Display[] getAllPresentationDisplays() {
31692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
31792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
31892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
31992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        private void updatePresentationDisplays(int changedDisplayId) {
32092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            final int count = mRoutes.size();
32192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            for (int i = 0; i < count; i++) {
3225830b0b33618940d65197cec99d697b21908fec8Chong Zhang                final RouteInfo route = mRoutes.get(i);
3235830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (route.updatePresentationDisplay() || (route.mPresentationDisplay != null
3245830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        && route.mPresentationDisplay.getDisplayId() == changedDisplayId)) {
3255830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    dispatchRoutePresentationDisplayChanged(route);
32692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                }
32792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            }
32892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
32969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
33069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void setSelectedRoute(RouteInfo info, boolean explicit) {
33169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Must be non-reentrant.
33269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mSelectedRoute = info;
33369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            publishClientSelectedRoute(explicit);
33469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
33569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
33669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void rebindAsUser(int userId) {
33769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mCurrentUserId != userId || userId < 0 || mClient == null) {
33869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (mClient != null) {
33969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    try {
34069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        mMediaRouterService.unregisterClient(mClient);
34169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    } catch (RemoteException ex) {
34269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        Log.e(TAG, "Unable to unregister media router client.", ex);
34369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
34469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClient = null;
34569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
34669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
34769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mCurrentUserId = userId;
34869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
34969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
35069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Client client = new Client();
35169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.registerClientAsUser(client,
35269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mAppContext.getPackageName(), userId);
35369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClient = client;
35469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
35569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to register media router client.", ex);
35669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
35769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
35869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                publishClientDiscoveryRequest();
35969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                publishClientSelectedRoute(false);
36069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                updateClientState();
36169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
36269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
36369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
36469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void publishClientDiscoveryRequest() {
36569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mClient != null) {
36669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
36769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.setDiscoveryRequest(mClient,
36869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mDiscoveryRequestRouteTypes, mDiscoverRequestActiveScan);
36969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
37069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to publish media router client discovery request.", ex);
37169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
37269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
37369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
37469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
37569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void publishClientSelectedRoute(boolean explicit) {
37669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mClient != null) {
37769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
37869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.setSelectedRoute(mClient,
37969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mSelectedRoute != null ? mSelectedRoute.mGlobalRouteId : null,
38069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            explicit);
38169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
38269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to publish media router client selected route.", ex);
38369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
38469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
38569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
38669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
38769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void updateClientState() {
38869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Update the client state.
38969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mClientState = null;
39069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mClient != null) {
39169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
39269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClientState = mMediaRouterService.getState(mClient);
39369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
39469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to retrieve media router client state.", ex);
39569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
39669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
39769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final ArrayList<MediaRouterClientState.RouteInfo> globalRoutes =
39869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClientState != null ? mClientState.routes : null;
39969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final String globallySelectedRouteId = mClientState != null ?
40069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClientState.globallySelectedRouteId : null;
40169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
40269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Add or update routes.
40369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int globalRouteCount = globalRoutes != null ? globalRoutes.size() : 0;
40469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < globalRouteCount; i++) {
40569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final MediaRouterClientState.RouteInfo globalRoute = globalRoutes.get(i);
40669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                RouteInfo route = findGlobalRoute(globalRoute.id);
40769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (route == null) {
40869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    route = makeGlobalRoute(globalRoute);
40969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    addRouteStatic(route);
41069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else {
41169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    updateGlobalRoute(route, globalRoute);
41269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
41369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
41469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
41569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Synchronize state with the globally selected route.
41669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (globallySelectedRouteId != null) {
41769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final RouteInfo route = findGlobalRoute(globallySelectedRouteId);
41869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (route == null) {
41969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.w(TAG, "Could not find new globally selected route: "
42069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            + globallySelectedRouteId);
42169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else if (route != mSelectedRoute) {
42269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    if (DEBUG) {
42369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        Log.d(TAG, "Selecting new globally selected route: " + route);
42469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
42569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    selectRouteStatic(route.mSupportedTypes, route, false);
42669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
42769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            } else if (mSelectedRoute != null && mSelectedRoute.mGlobalRouteId != null) {
42869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (DEBUG) {
42969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.d(TAG, "Unselecting previous globally selected route: " + mSelectedRoute);
43069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
43169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                selectDefaultRouteStatic();
43269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
43369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
43469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Remove defunct routes.
43569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            outer: for (int i = mRoutes.size(); i-- > 0; ) {
43669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final RouteInfo route = mRoutes.get(i);
43769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final String globalRouteId = route.mGlobalRouteId;
43869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (globalRouteId != null) {
43969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    for (int j = 0; j < globalRouteCount; j++) {
44069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        MediaRouterClientState.RouteInfo globalRoute = globalRoutes.get(j);
44169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (globalRouteId.equals(globalRoute.id)) {
44269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            continue outer; // found
44369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
44469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
44569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // not found
44669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    removeRouteStatic(route);
44769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
44869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
44969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
45069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
45169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void requestSetVolume(RouteInfo route, int volume) {
45269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mGlobalRouteId != null && mClient != null) {
45369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
45469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.requestSetVolume(mClient,
45569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            route.mGlobalRouteId, volume);
45669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
45769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.w(TAG, "Unable to request volume change.", ex);
45869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
45969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
46069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
46169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
46269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void requestUpdateVolume(RouteInfo route, int direction) {
46369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mGlobalRouteId != null && mClient != null) {
46469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
46569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.requestUpdateVolume(mClient,
46669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            route.mGlobalRouteId, direction);
46769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
46869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.w(TAG, "Unable to request volume change.", ex);
46969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
47069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
47169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
47269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
47369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        RouteInfo makeGlobalRoute(MediaRouterClientState.RouteInfo globalRoute) {
47469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            RouteInfo route = new RouteInfo(sStatic.mSystemCategory);
47569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mGlobalRouteId = globalRoute.id;
47669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mName = globalRoute.name;
47769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mDescription = globalRoute.description;
47869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mSupportedTypes = globalRoute.supportedTypes;
47969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mEnabled = globalRoute.enabled;
48039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            route.setRealStatusCode(globalRoute.statusCode);
48169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mPlaybackType = globalRoute.playbackType;
48269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mPlaybackStream = globalRoute.playbackStream;
48369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mVolume = globalRoute.volume;
48469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mVolumeMax = globalRoute.volumeMax;
48569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mVolumeHandling = globalRoute.volumeHandling;
4865830b0b33618940d65197cec99d697b21908fec8Chong Zhang            route.mPresentationDisplayId = globalRoute.presentationDisplayId;
4875830b0b33618940d65197cec99d697b21908fec8Chong Zhang            route.updatePresentationDisplay();
48869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            return route;
48969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
49069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
49169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void updateGlobalRoute(RouteInfo route, MediaRouterClientState.RouteInfo globalRoute) {
49269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean changed = false;
49369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean volumeChanged = false;
49469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean presentationDisplayChanged = false;
49569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
496e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root            if (!Objects.equals(route.mName, globalRoute.name)) {
49769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mName = globalRoute.name;
49869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
49969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
500e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root            if (!Objects.equals(route.mDescription, globalRoute.description)) {
50169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mDescription = globalRoute.description;
50269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
50369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
504af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            final int oldSupportedTypes = route.mSupportedTypes;
505af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (oldSupportedTypes != globalRoute.supportedTypes) {
50669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mSupportedTypes = globalRoute.supportedTypes;
50769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
50869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
50969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mEnabled != globalRoute.enabled) {
51069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mEnabled = globalRoute.enabled;
51169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
51269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
51339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (route.mRealStatusCode != globalRoute.statusCode) {
51439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                route.setRealStatusCode(globalRoute.statusCode);
51569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
51669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
51769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mPlaybackType != globalRoute.playbackType) {
51869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mPlaybackType = globalRoute.playbackType;
51969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
52069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
52169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mPlaybackStream != globalRoute.playbackStream) {
52269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mPlaybackStream = globalRoute.playbackStream;
52369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
52469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
52569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mVolume != globalRoute.volume) {
52669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mVolume = globalRoute.volume;
52769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
52869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                volumeChanged = true;
52969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
53069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mVolumeMax != globalRoute.volumeMax) {
53169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mVolumeMax = globalRoute.volumeMax;
53269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
53369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                volumeChanged = true;
53469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
53569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mVolumeHandling != globalRoute.volumeHandling) {
53669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mVolumeHandling = globalRoute.volumeHandling;
53769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
53869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                volumeChanged = true;
53969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
5405830b0b33618940d65197cec99d697b21908fec8Chong Zhang            if (route.mPresentationDisplayId != globalRoute.presentationDisplayId) {
5415830b0b33618940d65197cec99d697b21908fec8Chong Zhang                route.mPresentationDisplayId = globalRoute.presentationDisplayId;
5425830b0b33618940d65197cec99d697b21908fec8Chong Zhang                route.updatePresentationDisplay();
54369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
54469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                presentationDisplayChanged = true;
54569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
54669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
54769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (changed) {
548af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                dispatchRouteChanged(route, oldSupportedTypes);
54969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
55069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (volumeChanged) {
55169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                dispatchRouteVolumeChanged(route);
55269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
55369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (presentationDisplayChanged) {
55469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                dispatchRoutePresentationDisplayChanged(route);
55569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
55669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
55769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
55869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        RouteInfo findGlobalRoute(String globalRouteId) {
55969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int count = mRoutes.size();
56069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < count; i++) {
56169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final RouteInfo route = mRoutes.get(i);
56269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (globalRouteId.equals(route.mGlobalRouteId)) {
56369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    return route;
56469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
56569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
56669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            return null;
56769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
56869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
56969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final class Client extends IMediaRouterClient.Stub {
57069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            @Override
57169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public void onStateChanged() {
57269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mHandler.post(new Runnable() {
57369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    @Override
57469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    public void run() {
57569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (Client.this == mClient) {
57669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            updateClientState();
57769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
57869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
57969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                });
58069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
58169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
582b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
5839a1de308cea2d160778fd977825f10a07b49d738Adam Powell
584b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static Static sStatic;
5859a1de308cea2d160778fd977825f10a07b49d738Adam Powell
5869a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
5879a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Route type flag for live audio.
5889a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
5899a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>A device that supports live audio routing will allow the media audio stream
5909a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * to be routed to supported destinations. This can include internal speakers or
5919a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * audio jacks on the device itself, A2DP devices, and more.</p>
5929a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
5939a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>Once initiated this routing is transparent to the application. All audio
5949a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * played on the media stream will be routed to the selected destination.</p>
5959a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
59669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_LIVE_AUDIO = 1 << 0;
5979a1de308cea2d160778fd977825f10a07b49d738Adam Powell
5989a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
599705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * Route type flag for live video.
600705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     *
601705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * <p>A device that supports live video routing will allow a mirrored version
602705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * of the device's primary display or a customized
603705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * {@link android.app.Presentation Presentation} to be routed to supported destinations.</p>
604705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     *
605705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * <p>Once initiated, display mirroring is transparent to the application.
606705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * While remote routing is active the application may use a
607705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * {@link android.app.Presentation Presentation} to replace the mirrored view
608705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * on the external display with different content.</p>
60992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown     *
61092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown     * @see RouteInfo#getPresentationDisplay()
61192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown     * @see android.app.Presentation
612705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     */
61369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_LIVE_VIDEO = 1 << 1;
61469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
61569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
61669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Temporary interop constant to identify remote displays.
61769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide To be removed when media router API is updated.
61869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
61969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_REMOTE_DISPLAY = 1 << 2;
620705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
621705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    /**
6229a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Route type flag for application-specific usage.
6239a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
6249a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>Unlike other media route types, user routes are managed by the application.
6259a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * The MediaRouter will manage and dispatch events for user routes, but the application
6269a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * is expected to interpret the meaning of these events and perform the requested
6279a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * routing tasks.</p>
6289a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
62969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_USER = 1 << 23;
63069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
63169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static final int ROUTE_TYPE_ANY = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO
63269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            | ROUTE_TYPE_REMOTE_DISPLAY | ROUTE_TYPE_USER;
6339a1de308cea2d160778fd977825f10a07b49d738Adam Powell
63466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    /**
63566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Flag for {@link #addCallback}: Actively scan for routes while this callback
63666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * is registered.
63766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
63866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * When this flag is specified, the media router will actively scan for new
63966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * routes.  Certain routes, such as wifi display routes, may not be discoverable
64066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * except when actively scanning.  This flag is typically used when the route picker
64166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * dialog has been opened by the user to ensure that the route information is
64266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * up to date.
64366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p><p>
64466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Active scanning may consume a significant amount of power and may have intrusive
64566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * effects on wireless connectivity.  Therefore it is important that active scanning
64666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * only be requested when it is actually needed to satisfy a user request to
64766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * discover and select a new route.
64866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
64966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     */
65014507e257af5d71577574e25cbd690c4b54c9272Jeff Brown    public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1 << 0;
65166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
65266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    /**
65366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Flag for {@link #addCallback}: Do not filter route events.
65466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
65566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * When this flag is specified, the callback will be invoked for event that affect any
65669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * route even if they do not match the callback's filter.
65766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
65866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     */
65966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 1 << 1;
66066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
66169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
66269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Explicitly requests discovery.
66369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     *
66469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide Future API ported from support library.  Revisit this later.
66569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
66669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 1 << 2;
66769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
66869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
66969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Requests that discovery be performed but only if there is some other active
67069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * callback already registered.
67169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     *
67269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide Compatibility workaround for the fact that applications do not currently
67369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * request discovery explicitly (except when using the support library API).
67469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
67569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int CALLBACK_FLAG_PASSIVE_DISCOVERY = 1 << 3;
67669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
6770abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    /**
6780abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * Flag for {@link #isRouteAvailable}: Ignore the default route.
6790abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * <p>
6800abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * This flag is used to determine whether a matching non-default route is available.
6810abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * This constraint may be used to decide whether to offer the route chooser dialog
6820abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * to the user.  There is no point offering the chooser if there are no
6830abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * non-default choices.
6840abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * </p>
6850abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     *
6860abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @hide Future API ported from support library.  Revisit this later.
6870abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     */
6880abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1 << 0;
6890abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
6909a1de308cea2d160778fd977825f10a07b49d738Adam Powell    // Maps application contexts
6919a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static final HashMap<Context, MediaRouter> sRouters = new HashMap<Context, MediaRouter>();
6929a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6939a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static String typesToString(int types) {
6949a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final StringBuilder result = new StringBuilder();
6959a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if ((types & ROUTE_TYPE_LIVE_AUDIO) != 0) {
6969a1de308cea2d160778fd977825f10a07b49d738Adam Powell            result.append("ROUTE_TYPE_LIVE_AUDIO ");
6979a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
6982bb7c122ef6ce8539dfbaeb3292adcd942185f82Bryan Mawhinney        if ((types & ROUTE_TYPE_LIVE_VIDEO) != 0) {
6992bb7c122ef6ce8539dfbaeb3292adcd942185f82Bryan Mawhinney            result.append("ROUTE_TYPE_LIVE_VIDEO ");
7002bb7c122ef6ce8539dfbaeb3292adcd942185f82Bryan Mawhinney        }
70169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if ((types & ROUTE_TYPE_REMOTE_DISPLAY) != 0) {
70269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            result.append("ROUTE_TYPE_REMOTE_DISPLAY ");
70369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
7049a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if ((types & ROUTE_TYPE_USER) != 0) {
7059a1de308cea2d160778fd977825f10a07b49d738Adam Powell            result.append("ROUTE_TYPE_USER ");
7069a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
7079a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return result.toString();
7089a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
7099a1de308cea2d160778fd977825f10a07b49d738Adam Powell
710b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    /** @hide */
711b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public MediaRouter(Context context) {
712b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        synchronized (Static.class) {
713b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            if (sStatic == null) {
7148e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final Context appContext = context.getApplicationContext();
7158e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                sStatic = new Static(appContext);
7168e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                sStatic.startMonitoringRoutes(appContext);
717b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            }
7189a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
7199a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
7209a1de308cea2d160778fd977825f10a07b49d738Adam Powell
721690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    /**
7223afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * Gets the default route for playing media content on the system.
7233afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * <p>
7243afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * The system always provides a default route.
7253afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * </p>
7263afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     *
7273afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * @return The default route, which is guaranteed to never be null.
728690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     */
7293afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown    public RouteInfo getDefaultRoute() {
730705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return sStatic.mDefaultAudioVideo;
731690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
732690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
733690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    /**
7344599696591f745b3a546197d2ba7e5cfc5562484Adam Powell     * @hide for use by framework routing UI
7354599696591f745b3a546197d2ba7e5cfc5562484Adam Powell     */
7363afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown    public RouteCategory getSystemCategory() {
7374599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        return sStatic.mSystemCategory;
7384599696591f745b3a546197d2ba7e5cfc5562484Adam Powell    }
7394599696591f745b3a546197d2ba7e5cfc5562484Adam Powell
7400abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    /** @hide */
7410abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public RouteInfo getSelectedRoute() {
7420abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        return getSelectedRoute(ROUTE_TYPE_ANY);
7430abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    }
7440abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
7454599696591f745b3a546197d2ba7e5cfc5562484Adam Powell    /**
7461cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell     * Return the currently selected route for any of the given types
747690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     *
748690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * @param type route types
749690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * @return the selected route
750690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     */
751690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public RouteInfo getSelectedRoute(int type) {
7521cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        if (sStatic.mSelectedRoute != null &&
7531cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell                (sStatic.mSelectedRoute.mSupportedTypes & type) != 0) {
7541cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // If the selected route supports any of the types supplied, it's still considered
7551cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // 'selected' for that type.
7561cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            return sStatic.mSelectedRoute;
7571cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        } else if (type == ROUTE_TYPE_USER) {
7581cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // The caller specifically asked for a user route and the currently selected route
7591cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // doesn't qualify.
7601cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            return null;
7611cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        }
7621cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        // If the above didn't match and we're not specifically asking for a user route,
7631cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        // consider the default selected.
7641cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        return sStatic.mDefaultAudioVideo;
765690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
766690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
7679a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
7680abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * Returns true if there is a route that matches the specified types.
7690abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * <p>
7700abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * This method returns true if there are any available routes that match the types
7710abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * regardless of whether they are enabled or disabled.  If the
7720abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} flag is specified, then
7730abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * the method will only consider non-default routes.
7740abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * </p>
7750abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     *
7760abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @param types The types to match.
7770abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @param flags Flags to control the determination of whether a route may be available.
7780abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * May be zero or {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE}.
7790abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @return True if a matching route may be available.
7800abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     *
7810abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @hide Future API ported from support library.  Revisit this later.
7820abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     */
7830abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public boolean isRouteAvailable(int types, int flags) {
7840abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        final int count = sStatic.mRoutes.size();
7850abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        for (int i = 0; i < count; i++) {
7860abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            RouteInfo route = sStatic.mRoutes.get(i);
7870abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            if (route.matchesTypes(types)) {
7880abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                if ((flags & AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE) == 0
7890abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                        || route != sStatic.mDefaultAudioVideo) {
7900abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                    return true;
7910abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                }
7920abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            }
7930abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
7940abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
7950abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        // It doesn't look like we can find a matching route right now.
7960abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        return false;
7970abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    }
7980abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
7990abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    /**
8009a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Add a callback to listen to events about specific kinds of media routes.
8019a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * If the specified callback is already registered, its registration will be updated for any
8029a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * additional route types specified.
80366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
80466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * This is a convenience method that has the same effect as calling
80566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * {@link #addCallback(int, Callback, int)} without flags.
80666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
8079a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
8089a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param types Types of routes this callback is interested in
8099a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param cb Callback to add
8109a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
8119a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void addCallback(int types, Callback cb) {
81266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        addCallback(types, cb, 0);
81366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    }
81466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
81566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    /**
81666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Add a callback to listen to events about specific kinds of media routes.
81766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * If the specified callback is already registered, its registration will be updated for any
81866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * additional route types specified.
81966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
82066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * By default, the callback will only be invoked for events that affect routes
82166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * that match the specified selector.  The filtering may be disabled by specifying
82266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * the {@link #CALLBACK_FLAG_UNFILTERED_EVENTS} flag.
82366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
82466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     *
82566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @param types Types of routes this callback is interested in
82666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @param cb Callback to add
82766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @param flags Flags to control the behavior of the callback.
82814507e257af5d71577574e25cbd690c4b54c9272Jeff Brown     * May be zero or a combination of {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and
82966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * {@link #CALLBACK_FLAG_UNFILTERED_EVENTS}.
83066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     */
83166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    public void addCallback(int types, Callback cb, int flags) {
83266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        CallbackInfo info;
83366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        int index = findCallbackInfo(cb);
83466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        if (index >= 0) {
83566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info = sStatic.mCallbacks.get(index);
83666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info.type |= types;
83766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info.flags |= flags;
83866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        } else {
83966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info = new CallbackInfo(cb, types, flags, this);
84066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            sStatic.mCallbacks.add(info);
84166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
84269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        sStatic.updateDiscoveryRequest();
8439a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
8449a1de308cea2d160778fd977825f10a07b49d738Adam Powell
8459a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
8469a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Remove the specified callback. It will no longer receive events about media routing.
8479a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
8489a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param cb Callback to remove
8499a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
8509a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void removeCallback(Callback cb) {
85166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        int index = findCallbackInfo(cb);
85266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        if (index >= 0) {
85369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            sStatic.mCallbacks.remove(index);
85469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            sStatic.updateDiscoveryRequest();
85566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        } else {
85666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            Log.w(TAG, "removeCallback(" + cb + "): callback not registered");
85766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
85866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    }
85966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
86066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    private int findCallbackInfo(Callback cb) {
861b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        final int count = sStatic.mCallbacks.size();
8629a1de308cea2d160778fd977825f10a07b49d738Adam Powell        for (int i = 0; i < count; i++) {
86366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            final CallbackInfo info = sStatic.mCallbacks.get(i);
86466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (info.cb == cb) {
86566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown                return i;
8669a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
8679a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
86866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        return -1;
8699a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
8709a1de308cea2d160778fd977825f10a07b49d738Adam Powell
871d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell    /**
872d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     * Select the specified route to use for output of the given media types.
8733afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * <p class="note">
8743afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * As API version 18, this function may be used to select any route.
8753afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * In prior versions, this function could only be used to select user
8763afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * routes and would ignore any attempt to select a system route.
8773afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * </p>
878d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     *
879d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     * @param types type flags indicating which types this route should be used for.
880d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     *              The route must support at least a subset.
881d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     * @param route Route to select
882d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     */
8839a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void selectRoute(int types, RouteInfo route) {
88469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        selectRouteStatic(types, route, true);
8850d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    }
88669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
8870d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    /**
8880d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @hide internal use
8890d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     */
89069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void selectRouteInt(int types, RouteInfo route, boolean explicit) {
89169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        selectRouteStatic(types, route, explicit);
892b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
893b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
89469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static void selectRouteStatic(int types, RouteInfo route, boolean explicit) {
895705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final RouteInfo oldRoute = sStatic.mSelectedRoute;
896705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (oldRoute == route) return;
8970abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        if (!route.matchesTypes(types)) {
8980d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            Log.w(TAG, "selectRoute ignored; cannot select route with supported types " +
8990d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                    typesToString(route.getSupportedTypes()) + " into route types " +
9000d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                    typesToString(types));
9014ee1f55ce0f4909a7430ab44563a81852f335071Adam Powell            return;
9020d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
9039a1de308cea2d160778fd977825f10a07b49d738Adam Powell
904dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell        final RouteInfo btRoute = sStatic.mBluetoothA2dpRoute;
905dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell        if (btRoute != null && (types & ROUTE_TYPE_LIVE_AUDIO) != 0 &&
906705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                (route == btRoute || route == sStatic.mDefaultAudioVideo)) {
907dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell            try {
908dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell                sStatic.mAudioService.setBluetoothA2dpOn(route == btRoute);
909dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell            } catch (RemoteException e) {
910dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell                Log.e(TAG, "Error changing Bluetooth A2DP state", e);
911dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell            }
912dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell        }
913dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell
914705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final WifiDisplay activeDisplay =
915705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                sStatic.mDisplayService.getWifiDisplayStatus().getActiveDisplay();
916705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final boolean oldRouteHasAddress = oldRoute != null && oldRoute.mDeviceAddress != null;
917705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final boolean newRouteHasAddress = route != null && route.mDeviceAddress != null;
918705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (activeDisplay != null || oldRouteHasAddress || newRouteHasAddress) {
919705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (newRouteHasAddress && !matchesDeviceAddress(activeDisplay, route)) {
920af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (sStatic.mCanConfigureWifiDisplays) {
921af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    sStatic.mDisplayService.connectWifiDisplay(route.mDeviceAddress);
922af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                } else {
923af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    Log.e(TAG, "Cannot connect to wifi displays because this process "
924af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                            + "is not allowed to do so.");
925af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
926705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            } else if (activeDisplay != null && !newRouteHasAddress) {
927705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                sStatic.mDisplayService.disconnectWifiDisplay();
928705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
929705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
930705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
93169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        sStatic.setSelectedRoute(route, explicit);
93269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
933705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (oldRoute != null) {
934705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            dispatchRouteUnselected(types & oldRoute.getSupportedTypes(), oldRoute);
93539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (oldRoute.resolveStatusCode()) {
93639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                dispatchRouteChanged(oldRoute);
93739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
9389a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
9399a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if (route != null) {
94039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (route.resolveStatusCode()) {
94139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                dispatchRouteChanged(route);
94239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
9439a1de308cea2d160778fd977825f10a07b49d738Adam Powell            dispatchRouteSelected(types & route.getSupportedTypes(), route);
9449a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
945ce468a35b388ca46578934706b38dbae94941643Jeff Brown
946ce468a35b388ca46578934706b38dbae94941643Jeff Brown        // The behavior of active scans may depend on the currently selected route.
947ce468a35b388ca46578934706b38dbae94941643Jeff Brown        sStatic.updateDiscoveryRequest();
9489a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
9499a1de308cea2d160778fd977825f10a07b49d738Adam Powell
95069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static void selectDefaultRouteStatic() {
95169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // TODO: Be smarter about the route types here; this selects for all valid.
95269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (sStatic.mSelectedRoute != sStatic.mBluetoothA2dpRoute
95369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                && sStatic.mBluetoothA2dpRoute != null) {
95469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mBluetoothA2dpRoute, false);
95569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        } else {
95669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mDefaultAudioVideo, false);
95769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
95869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
95969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
9609a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
961705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * Compare the device address of a display and a route.
962705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * Nulls/no device address will match another null/no address.
963705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     */
964705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    static boolean matchesDeviceAddress(WifiDisplay display, RouteInfo info) {
965705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final boolean routeHasAddress = info != null && info.mDeviceAddress != null;
966705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (display == null && !routeHasAddress) {
967705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return true;
968705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
969705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
970705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (display != null && routeHasAddress) {
971705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return display.getDeviceAddress().equals(info.mDeviceAddress);
972705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
973705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return false;
974705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
975705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
976705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    /**
9779a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Add an app-specified route for media to the MediaRouter.
9789a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * App-specified route definitions are created using {@link #createUserRoute(RouteCategory)}
9799a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
9809a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param info Definition of the route to add
9813afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * @see #createUserRoute(RouteCategory)
9829a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #removeUserRoute(UserRouteInfo)
9839a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
9849a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void addUserRoute(UserRouteInfo info) {
9852ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        addRouteStatic(info);
9869a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
9879a1de308cea2d160778fd977825f10a07b49d738Adam Powell
988d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    /**
989d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     * @hide Framework use only
990d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     */
991d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    public void addRouteInt(RouteInfo info) {
9922ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        addRouteStatic(info);
993d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    }
994d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
9952ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell    static void addRouteStatic(RouteInfo info) {
9969a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final RouteCategory cat = info.getCategory();
997b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        if (!sStatic.mCategories.contains(cat)) {
998b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            sStatic.mCategories.add(cat);
9999a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
1000d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        if (cat.isGroupable() && !(info instanceof RouteGroup)) {
10019a1de308cea2d160778fd977825f10a07b49d738Adam Powell            // Enforce that any added route in a groupable category must be in a group.
10029a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final RouteGroup group = new RouteGroup(info.getCategory());
1003dbbfa702a09f6d2d36dee1b552442d04a4673f89Adam Powell            group.mSupportedTypes = info.mSupportedTypes;
1004b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            sStatic.mRoutes.add(group);
1005d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteAdded(group);
1006b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            group.addRoute(info);
1007d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
10089a1de308cea2d160778fd977825f10a07b49d738Adam Powell            info = group;
1009d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        } else {
1010b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            sStatic.mRoutes.add(info);
1011d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteAdded(info);
10129a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
10139a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
10149a1de308cea2d160778fd977825f10a07b49d738Adam Powell
10159a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
10169a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Remove an app-specified route for media from the MediaRouter.
10179a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
10189a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param info Definition of the route to remove
10199a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #addUserRoute(UserRouteInfo)
10209a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
10219a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void removeUserRoute(UserRouteInfo info) {
102269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        removeRouteStatic(info);
10239a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
10249a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1025690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    /**
1026690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * Remove all app-specified routes from the MediaRouter.
1027690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     *
1028690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * @see #removeUserRoute(UserRouteInfo)
1029690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     */
1030690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public void clearUserRoutes() {
1031b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        for (int i = 0; i < sStatic.mRoutes.size(); i++) {
1032b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final RouteInfo info = sStatic.mRoutes.get(i);
1033d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            // TODO Right now, RouteGroups only ever contain user routes.
1034d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            // The code below will need to change if this assumption does.
1035d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            if (info instanceof UserRouteInfo || info instanceof RouteGroup) {
103669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                removeRouteStatic(info);
1037690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell                i--;
1038690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            }
1039690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
1040690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
1041690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
1042d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    /**
1043d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     * @hide internal use only
1044d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     */
1045d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    public void removeRouteInt(RouteInfo info) {
104669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        removeRouteStatic(info);
1047d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    }
1048d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
104969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static void removeRouteStatic(RouteInfo info) {
1050b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        if (sStatic.mRoutes.remove(info)) {
10519a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final RouteCategory removingCat = info.getCategory();
1052b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final int count = sStatic.mRoutes.size();
10539a1de308cea2d160778fd977825f10a07b49d738Adam Powell            boolean found = false;
10549a1de308cea2d160778fd977825f10a07b49d738Adam Powell            for (int i = 0; i < count; i++) {
1055b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                final RouteCategory cat = sStatic.mRoutes.get(i).getCategory();
10569a1de308cea2d160778fd977825f10a07b49d738Adam Powell                if (removingCat == cat) {
10579a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    found = true;
10589a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    break;
10599a1de308cea2d160778fd977825f10a07b49d738Adam Powell                }
10609a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
10610abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            if (info.isSelected()) {
1062d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                // Removing the currently selected route? Select the default before we remove it.
106369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                selectDefaultRouteStatic();
1064d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            }
1065690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            if (!found) {
1066b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                sStatic.mCategories.remove(removingCat);
1067690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            }
1068690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            dispatchRouteRemoved(info);
1069690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
1070690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
1071690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
10729a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
10739a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the number of {@link MediaRouter.RouteCategory categories} currently
10749a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * represented by routes known to this MediaRouter.
10759a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
10769a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the number of unique categories represented by this MediaRouter's known routes
10779a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
10789a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public int getCategoryCount() {
1079b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mCategories.size();
10809a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
10819a1de308cea2d160778fd977825f10a07b49d738Adam Powell
10829a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
10839a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the {@link MediaRouter.RouteCategory category} at the given index.
10849a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Valid indices are in the range [0-getCategoryCount).
10859a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
10869a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param index which category to return
10879a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the category at index
10889a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
10899a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteCategory getCategoryAt(int index) {
1090b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mCategories.get(index);
10919a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
10929a1de308cea2d160778fd977825f10a07b49d738Adam Powell
10939a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
10949a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the number of {@link MediaRouter.RouteInfo routes} currently known
10959a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * to this MediaRouter.
10969a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
10979a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the number of routes tracked by this router
10989a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
10999a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public int getRouteCount() {
1100b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.size();
11019a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11029a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11039a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11049a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the route at the specified index.
11059a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11069a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param index index of the route to return
11079a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the route at index
11089a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11099a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteInfo getRouteAt(int index) {
1110b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.get(index);
1111b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
1112b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
1113b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static int getRouteCountStatic() {
1114b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.size();
1115b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
1116b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
1117b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static RouteInfo getRouteAtStatic(int index) {
1118b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.get(index);
11199a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11209a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11219a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11229a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Create a new user route that may be modified and registered for use by the application.
11239a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11249a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param category The category the new route will belong to
11259a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return A new UserRouteInfo for use by the application
11269a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11279a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #addUserRoute(UserRouteInfo)
11289a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #removeUserRoute(UserRouteInfo)
112969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @see #createRouteCategory(CharSequence, boolean)
11309a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11319a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public UserRouteInfo createUserRoute(RouteCategory category) {
11329a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return new UserRouteInfo(category);
11339a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11349a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11359a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11369a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Create a new route category. Each route must belong to a category.
11379a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11389a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param name Name of the new category
11399a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param isGroupable true if routes in this category may be grouped with one another
11409a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the new RouteCategory
11419a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11429a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteCategory createRouteCategory(CharSequence name, boolean isGroupable) {
11439a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return new RouteCategory(name, ROUTE_TYPE_USER, isGroupable);
11449a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11455d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
11460d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    /**
11470d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * Create a new route category. Each route must belong to a category.
11480d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     *
11490d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @param nameResId Resource ID of the name of the new category
11500d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @param isGroupable true if routes in this category may be grouped with one another
11510d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @return the new RouteCategory
11520d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     */
11530d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    public RouteCategory createRouteCategory(int nameResId, boolean isGroupable) {
11540d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        return new RouteCategory(nameResId, ROUTE_TYPE_USER, isGroupable);
11550d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    }
11569a1de308cea2d160778fd977825f10a07b49d738Adam Powell
115769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
115869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Rebinds the media router to handle routes that belong to the specified user.
115969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Requires the interact across users permission to access the routes of another user.
116069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * <p>
116169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * This method is a complete hack to work around the singleton nature of the
116269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * media router when running inside of singleton processes like QuickSettings.
116369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * This mechanism should be burned to the ground when MediaRouter is redesigned.
116469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Ideally the current user would be pulled from the Context but we need to break
116569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * down MediaRouter.Static before we can get there.
116669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * </p>
116769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     *
116869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide
116969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
117069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void rebindAsUser(int userId) {
117169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        sStatic.rebindAsUser(userId);
117269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
117369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
1174b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void updateRoute(final RouteInfo info) {
11759a1de308cea2d160778fd977825f10a07b49d738Adam Powell        dispatchRouteChanged(info);
11769a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11779a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1178b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteSelected(int type, RouteInfo info) {
117939d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
118066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1181b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteSelected(cbi.router, type, info);
11829a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
11839a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
11849a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11859a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1186b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteUnselected(int type, RouteInfo info) {
118739d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
118866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1189b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteUnselected(cbi.router, type, info);
11909a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
11919a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
11929a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11939a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1194b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteChanged(RouteInfo info) {
1195af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        dispatchRouteChanged(info, info.mSupportedTypes);
1196af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown    }
1197af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
1198af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown    static void dispatchRouteChanged(RouteInfo info, int oldSupportedTypes) {
1199af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        final int newSupportedTypes = info.mSupportedTypes;
120039d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
1201af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Reconstruct some of the history for callbacks that may not have observed
1202af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // all of the events needed to correctly interpret the current state.
1203af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // FIXME: This is a strong signal that we should deprecate route type filtering
1204af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // completely in the future because it can lead to inconsistencies in
1205af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // applications.
1206af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            final boolean oldVisibility = cbi.filterRouteEvent(oldSupportedTypes);
1207af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            final boolean newVisibility = cbi.filterRouteEvent(newSupportedTypes);
1208af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (!oldVisibility && newVisibility) {
1209af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                cbi.cb.onRouteAdded(cbi.router, info);
1210af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (info.isSelected()) {
1211af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    cbi.cb.onRouteSelected(cbi.router, newSupportedTypes, info);
1212af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
1213af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            }
1214af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (oldVisibility || newVisibility) {
1215b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteChanged(cbi.router, info);
12169a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
1217af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (oldVisibility && !newVisibility) {
1218af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (info.isSelected()) {
1219af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    cbi.cb.onRouteUnselected(cbi.router, oldSupportedTypes, info);
1220af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
1221af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                cbi.cb.onRouteRemoved(cbi.router, info);
1222af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            }
12239a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12249a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12259a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1226b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteAdded(RouteInfo info) {
122739d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
122866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1229b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteAdded(cbi.router, info);
12309a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
12319a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12329a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12339a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1234b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteRemoved(RouteInfo info) {
123539d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
123666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1237b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteRemoved(cbi.router, info);
12389a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
12399a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12409a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12419a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1242b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteGrouped(RouteInfo info, RouteGroup group, int index) {
124339d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
124466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(group)) {
1245b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteGrouped(cbi.router, info, group, index);
1246d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            }
1247d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        }
1248d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell    }
1249d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
1250b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteUngrouped(RouteInfo info, RouteGroup group) {
125139d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
125266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(group)) {
1253b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteUngrouped(cbi.router, info, group);
12549a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
12559a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12569a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12579a1de308cea2d160778fd977825f10a07b49d738Adam Powell
12588e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    static void dispatchRouteVolumeChanged(RouteInfo info) {
12598e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
126066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
12618e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                cbi.cb.onRouteVolumeChanged(cbi.router, info);
12628e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
12638e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
12648e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    }
12658e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
126692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown    static void dispatchRoutePresentationDisplayChanged(RouteInfo info) {
126792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        for (CallbackInfo cbi : sStatic.mCallbacks) {
126866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
126992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                cbi.cb.onRoutePresentationDisplayChanged(cbi.router, info);
127092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            }
127192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
127292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown    }
127392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
12748e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    static void systemVolumeChanged(int newValue) {
12758e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        final RouteInfo selectedRoute = sStatic.mSelectedRoute;
12768e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        if (selectedRoute == null) return;
12778e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
12788e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        if (selectedRoute == sStatic.mBluetoothA2dpRoute ||
1279705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                selectedRoute == sStatic.mDefaultAudioVideo) {
12808e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            dispatchRouteVolumeChanged(selectedRoute);
12818e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        } else if (sStatic.mBluetoothA2dpRoute != null) {
12828e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            try {
12838e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                dispatchRouteVolumeChanged(sStatic.mAudioService.isBluetoothA2dpOn() ?
1284705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                        sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo);
12858e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            } catch (RemoteException e) {
12868e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                Log.e(TAG, "Error checking Bluetooth A2DP state to report volume change", e);
12878e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
12888e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        } else {
1289705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            dispatchRouteVolumeChanged(sStatic.mDefaultAudioVideo);
1290705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1291705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1292705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
129375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    static void updateWifiDisplayStatus(WifiDisplayStatus status) {
129475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        WifiDisplay[] displays;
1295615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell        WifiDisplay activeDisplay;
129675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        if (status.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) {
129775af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            displays = status.getDisplays();
129875af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            activeDisplay = status.getActiveDisplay();
1299af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
1300af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Only the system is able to connect to wifi display routes.
1301af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // The display manager will enforce this with a permission check but it
1302af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // still publishes information about all available displays.
1303af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Filter the list down to just the active display.
1304af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (!sStatic.mCanConfigureWifiDisplays) {
1305af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (activeDisplay != null) {
1306af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    displays = new WifiDisplay[] { activeDisplay };
1307af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                } else {
1308af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    displays = WifiDisplay.EMPTY_ARRAY;
1309af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
1310af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            }
1311615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell        } else {
131275af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            displays = WifiDisplay.EMPTY_ARRAY;
1313615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell            activeDisplay = null;
1314615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell        }
1315ce468a35b388ca46578934706b38dbae94941643Jeff Brown        String activeDisplayAddress = activeDisplay != null ?
1316ce468a35b388ca46578934706b38dbae94941643Jeff Brown                activeDisplay.getDeviceAddress() : null;
1317705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
131875af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        // Add or update routes.
131975af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        for (int i = 0; i < displays.length; i++) {
132075af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            final WifiDisplay d = displays[i];
132175af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            if (shouldShowWifiDisplay(d, activeDisplay)) {
1322ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                RouteInfo route = findWifiDisplayRoute(d);
1323ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                if (route == null) {
132475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                    route = makeWifiDisplayRoute(d, status);
1325ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                    addRouteStatic(route);
1326ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                } else {
1327ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    String address = d.getDeviceAddress();
1328ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    boolean disconnected = !address.equals(activeDisplayAddress)
1329ce468a35b388ca46578934706b38dbae94941643Jeff Brown                            && address.equals(sStatic.mPreviousActiveWifiDisplayAddress);
1330ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    updateWifiDisplayRoute(route, d, status, disconnected);
1331ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                }
1332ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                if (d.equals(activeDisplay)) {
133369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    selectRouteStatic(route.getSupportedTypes(), route, false);
1334ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                }
13352ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            }
1336705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
133775af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown
133875af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        // Remove stale routes.
133975af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        for (int i = sStatic.mRoutes.size(); i-- > 0; ) {
134075af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            RouteInfo route = sStatic.mRoutes.get(i);
134175af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            if (route.mDeviceAddress != null) {
134275af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                WifiDisplay d = findWifiDisplay(displays, route.mDeviceAddress);
134375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                if (d == null || !shouldShowWifiDisplay(d, activeDisplay)) {
134475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                    removeRouteStatic(route);
1345ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                }
1346705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1347705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1348ce468a35b388ca46578934706b38dbae94941643Jeff Brown
1349ce468a35b388ca46578934706b38dbae94941643Jeff Brown        // Remember the current active wifi display address so that we can infer disconnections.
1350ce468a35b388ca46578934706b38dbae94941643Jeff Brown        // TODO: This hack will go away once all of this is moved into the media router service.
1351ce468a35b388ca46578934706b38dbae94941643Jeff Brown        sStatic.mPreviousActiveWifiDisplayAddress = activeDisplayAddress;
135275af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    }
1353705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
135475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    private static boolean shouldShowWifiDisplay(WifiDisplay d, WifiDisplay activeDisplay) {
135575af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        return d.isRemembered() || d.equals(activeDisplay);
1356705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1357705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1358ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    static int getWifiDisplayStatusCode(WifiDisplay d, WifiDisplayStatus wfdStatus) {
135969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        int newStatus;
1360ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        if (wfdStatus.getScanState() == WifiDisplayStatus.SCAN_STATE_SCANNING) {
1361ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang            newStatus = RouteInfo.STATUS_SCANNING;
1362ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        } else if (d.isAvailable()) {
1363ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang            newStatus = d.canConnect() ?
1364ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                    RouteInfo.STATUS_AVAILABLE: RouteInfo.STATUS_IN_USE;
1365705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        } else {
1366705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            newStatus = RouteInfo.STATUS_NOT_AVAILABLE;
1367705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1368705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1369ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        if (d.equals(wfdStatus.getActiveDisplay())) {
1370ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang            final int activeState = wfdStatus.getActiveDisplayState();
1371705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            switch (activeState) {
1372705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                case WifiDisplayStatus.DISPLAY_STATE_CONNECTED:
137369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    newStatus = RouteInfo.STATUS_CONNECTED;
1374705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    break;
1375705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                case WifiDisplayStatus.DISPLAY_STATE_CONNECTING:
1376705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    newStatus = RouteInfo.STATUS_CONNECTING;
1377705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    break;
1378705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                case WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED:
1379705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    Log.e(TAG, "Active display is not connected!");
1380705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    break;
1381705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1382705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1383705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1384ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        return newStatus;
1385ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    }
1386ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
1387ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    static boolean isWifiDisplayEnabled(WifiDisplay d, WifiDisplayStatus wfdStatus) {
1388ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        return d.isAvailable() && (d.canConnect() || d.equals(wfdStatus.getActiveDisplay()));
1389ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    }
1390ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
1391ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    static RouteInfo makeWifiDisplayRoute(WifiDisplay display, WifiDisplayStatus wfdStatus) {
1392ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        final RouteInfo newRoute = new RouteInfo(sStatic.mSystemCategory);
1393ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mDeviceAddress = display.getDeviceAddress();
139469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        newRoute.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO
139569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                | ROUTE_TYPE_REMOTE_DISPLAY;
1396ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mVolumeHandling = RouteInfo.PLAYBACK_VOLUME_FIXED;
1397ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mPlaybackType = RouteInfo.PLAYBACK_TYPE_REMOTE;
1398ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
139939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        newRoute.setRealStatusCode(getWifiDisplayStatusCode(display, wfdStatus));
1400ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mEnabled = isWifiDisplayEnabled(display, wfdStatus);
1401ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mName = display.getFriendlyDisplayName();
1402ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mDescription = sStatic.mResources.getText(
1403ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                com.android.internal.R.string.wireless_display_route_description);
14045830b0b33618940d65197cec99d697b21908fec8Chong Zhang        newRoute.updatePresentationDisplay();
1405ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        return newRoute;
1406ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    }
1407ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
1408ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    private static void updateWifiDisplayRoute(
1409ce468a35b388ca46578934706b38dbae94941643Jeff Brown            RouteInfo route, WifiDisplay display, WifiDisplayStatus wfdStatus,
1410ce468a35b388ca46578934706b38dbae94941643Jeff Brown            boolean disconnected) {
1411ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        boolean changed = false;
14122444ae7e2b8658a4a90f996e678423558744b4a2Jeff Brown        final String newName = display.getFriendlyDisplayName();
14132444ae7e2b8658a4a90f996e678423558744b4a2Jeff Brown        if (!route.getName().equals(newName)) {
1414705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            route.mName = newName;
1415705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            changed = true;
1416705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1417705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1418ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        boolean enabled = isWifiDisplayEnabled(display, wfdStatus);
1419ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        changed |= route.mEnabled != enabled;
1420ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        route.mEnabled = enabled;
1421705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
142239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        changed |= route.setRealStatusCode(getWifiDisplayStatusCode(display, wfdStatus));
1423705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1424705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (changed) {
1425705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            dispatchRouteChanged(route);
1426705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1427705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1428ce468a35b388ca46578934706b38dbae94941643Jeff Brown        if ((!enabled || disconnected) && route.isSelected()) {
1429705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            // Oops, no longer available. Reselect the default.
143069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            selectDefaultRouteStatic();
1431705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1432705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1433705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
143475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    private static WifiDisplay findWifiDisplay(WifiDisplay[] displays, String deviceAddress) {
1435705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        for (int i = 0; i < displays.length; i++) {
143675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            final WifiDisplay d = displays[i];
143775af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            if (d.getDeviceAddress().equals(deviceAddress)) {
143875af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                return d;
1439705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1440705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1441705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return null;
1442705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1443705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1444705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    private static RouteInfo findWifiDisplayRoute(WifiDisplay d) {
1445705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final int count = sStatic.mRoutes.size();
1446705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        for (int i = 0; i < count; i++) {
1447705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            final RouteInfo info = sStatic.mRoutes.get(i);
1448705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (d.getDeviceAddress().equals(info.mDeviceAddress)) {
1449705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                return info;
1450705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1451705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1452705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return null;
14538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    }
14548e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
14559a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
14569a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a media route.
14579a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
1458b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class RouteInfo {
14599a1de308cea2d160778fd977825f10a07b49d738Adam Powell        CharSequence mName;
14600d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        int mNameResId;
146156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        CharSequence mDescription;
14629a1de308cea2d160778fd977825f10a07b49d738Adam Powell        private CharSequence mStatus;
14639a1de308cea2d160778fd977825f10a07b49d738Adam Powell        int mSupportedTypes;
14649a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteGroup mGroup;
14659a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final RouteCategory mCategory;
1466ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        Drawable mIcon;
14671357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        // playback information
14681357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mPlaybackType = PLAYBACK_TYPE_LOCAL;
14691357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
14701357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
14711357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
14721357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mPlaybackStream = AudioManager.STREAM_MUSIC;
14731357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        VolumeCallbackInfo mVcb;
147492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        Display mPresentationDisplay;
14755830b0b33618940d65197cec99d697b21908fec8Chong Zhang        int mPresentationDisplayId = -1;
14769a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1477705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        String mDeviceAddress;
1478705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        boolean mEnabled = true;
1479705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
148069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // An id by which the route is known to the media router service.
148169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // Null if this route only exists as an artifact within this process.
148269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        String mGlobalRouteId;
148369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
1484705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        // A predetermined connection status that can override mStatus
148539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private int mRealStatusCode;
148639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private int mResolvedStatusCode;
1487705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
14882ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_NONE = 0;
14892ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_SCANNING = 1;
14902ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_CONNECTING = 2;
14912ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_AVAILABLE = 3;
14922ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_NOT_AVAILABLE = 4;
1493ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        /** @hide */ public static final int STATUS_IN_USE = 5;
149469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        /** @hide */ public static final int STATUS_CONNECTED = 6;
1495705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1496b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        private Object mTag;
1497b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
14981357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
14991357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * The default playback type, "local", indicating the presentation of the media is happening
15001357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * on the same device (e.g. a phone, a tablet) as where it is controlled from.
150169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getPlaybackType()
15021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
15031357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_TYPE_LOCAL = 0;
15041357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
15051357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * A playback type indicating the presentation of the media is happening on
15061357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * a different device (i.e. the remote device) than where it is controlled from.
150769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getPlaybackType()
15081357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
15091357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_TYPE_REMOTE = 1;
15101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
15111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Playback information indicating the playback volume is fixed, i.e. it cannot be
15121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * controlled from this object. An example of fixed playback volume is a remote player,
15131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
15141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * than attenuate at the source.
151569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getVolumeHandling()
15161357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
15171357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_VOLUME_FIXED = 0;
15181357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
15191357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Playback information indicating the playback volume is variable and can be controlled
15201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * from this object.
152169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getVolumeHandling()
15221357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
15231357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_VOLUME_VARIABLE = 1;
15241357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
15259a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteInfo(RouteCategory category) {
15269a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mCategory = category;
15279a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
15289a1de308cea2d160778fd977825f10a07b49d738Adam Powell
15299a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
153056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Gets the user-visible name of the route.
153156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
153256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route name identifies the destination represented by the route.
153356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied name, an alias, or device serial number.
153456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
153556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
153656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The user-visible name of a media route.  This is the string presented
15379a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * to users who may select this as the active route.
15389a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
15399a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getName() {
15400d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(sStatic.mResources);
15410d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
154256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
15430d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        /**
154456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Return the properly localized/resource user-visible name of this route.
154556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
154656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route name identifies the destination represented by the route.
154756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied name, an alias, or device serial number.
154856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
154956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
15500d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @param context Context used to resolve the correct configuration to load
155156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The user-visible name of a media route.  This is the string presented
15520d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * to users who may select this as the active route.
15530d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         */
15540d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public CharSequence getName(Context context) {
15550d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(context.getResources());
15560d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
155756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
15580d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        CharSequence getName(Resources res) {
15590d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            if (mNameResId != 0) {
15600d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                return mName = res.getText(mNameResId);
15610d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            }
15629a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mName;
15639a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
15649a1de308cea2d160778fd977825f10a07b49d738Adam Powell
15659a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
156656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Gets the user-visible description of the route.
156756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
156856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route description describes the kind of destination represented by the route.
156956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied string, a model number or brand of device.
157056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
157156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
157256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The description of the route, or null if none.
157356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         */
157456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        public CharSequence getDescription() {
157556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown            return mDescription;
157656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        }
157756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
157856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        /**
157956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The user-visible status for a media route. This may include a description
15809a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * of the currently playing media, if available.
15819a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
15829a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getStatus() {
15839a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mStatus;
15849a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
15859a1de308cea2d160778fd977825f10a07b49d738Adam Powell
15869a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
1587705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * Set this route's status by predetermined status code. If the caller
1588705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * should dispatch a route changed event this call will return true;
1589705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         */
159039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        boolean setRealStatusCode(int statusCode) {
159139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (mRealStatusCode != statusCode) {
159239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                mRealStatusCode = statusCode;
159339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                return resolveStatusCode();
159439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
159539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return false;
159639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        }
159739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown
159839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        /**
159939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown         * Resolves the status code whenever the real status code or selection state
160039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown         * changes.
160139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown         */
160239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        boolean resolveStatusCode() {
160339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            int statusCode = mRealStatusCode;
160439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (isSelected()) {
1605705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                switch (statusCode) {
160639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // If the route is selected and its status appears to be between states
160739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // then report it as connecting even though it has not yet had a chance
160839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // to officially move into the CONNECTING state.  Note that routes in
160939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // the NONE state are assumed to not require an explicit connection
161039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // lifecycle whereas those that are AVAILABLE are assumed to have
161139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // to eventually proceed to CONNECTED.
1612705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    case STATUS_AVAILABLE:
161339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    case STATUS_SCANNING:
161439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                        statusCode = STATUS_CONNECTING;
161569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        break;
1616705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                }
1617705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
161839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (mResolvedStatusCode == statusCode) {
161939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                return false;
162039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
162139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown
162239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            mResolvedStatusCode = statusCode;
162339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            int resId;
162439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            switch (statusCode) {
162539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_SCANNING:
162639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_scanning;
162739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
162839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_CONNECTING:
162939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_connecting;
163039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
163139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_AVAILABLE:
163239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_available;
163339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
163439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_NOT_AVAILABLE:
163539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_not_available;
163639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
163739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_IN_USE:
163839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_in_use;
163939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
164039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_CONNECTED:
164139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_NONE:
164239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                default:
164339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = 0;
164439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
164539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
164639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            mStatus = resId != 0 ? sStatic.mResources.getText(resId) : null;
164739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return true;
1648705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1649705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1650705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        /**
16512ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell         * @hide
16522ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell         */
16532ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        public int getStatusCode() {
165439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return mResolvedStatusCode;
16552ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        }
16562ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell
16572ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /**
16589a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return A media type flag set describing which types this route supports.
16599a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
16609a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int getSupportedTypes() {
16619a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mSupportedTypes;
16629a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
16639a1de308cea2d160778fd977825f10a07b49d738Adam Powell
16640abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
16650abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public boolean matchesTypes(int types) {
16660abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            return (mSupportedTypes & types) != 0;
16670abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
16680abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
16699a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
16709a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return The group that this route belongs to.
16719a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
16729a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public RouteGroup getGroup() {
16739a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mGroup;
16749a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
16759a1de308cea2d160778fd977825f10a07b49d738Adam Powell
16769a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
16779a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return the category this route belongs to.
16789a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
16799a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public RouteCategory getCategory() {
16809a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mCategory;
16819a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
16829a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1683ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
1684ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Get the icon representing this route.
1685ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * This icon will be used in picker UIs if available.
1686ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
1687ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @return the icon representing this route or null if no icon is available
1688ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
1689ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public Drawable getIconDrawable() {
1690ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            return mIcon;
1691ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
1692ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
1693b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        /**
1694b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * Set an application-specific tag object for this route.
1695b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * The application may use this to store arbitrary data associated with the
1696b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * route for internal tracking.
1697b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         *
1698b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * <p>Note that the lifespan of a route may be well past the lifespan of
1699b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * an Activity or other Context; take care that objects you store here
1700b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * will not keep more data in memory alive than you intend.</p>
1701b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         *
1702b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * @param tag Arbitrary, app-specific data for this route to hold for later use
1703b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         */
1704b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        public void setTag(Object tag) {
1705b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            mTag = tag;
1706130b4572d1f3df702e5b296a655d15a41f6d4c66Adam Powell            routeUpdated();
1707b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        }
1708b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
1709b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        /**
1710b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * @return The tag object previously set by the application
1711b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * @see #setTag(Object)
1712b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         */
1713b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        public Object getTag() {
1714b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            return mTag;
1715b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        }
1716b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
17171357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
17181357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the type of playback associated with this route
17191357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setPlaybackType(int)
17201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
17211357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getPlaybackType() {
17221357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            return mPlaybackType;
17231357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
17241357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
17251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
17261357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the stream over which the playback associated with this route is performed
17271357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setPlaybackStream(int)
17281357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
17291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getPlaybackStream() {
17301357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            return mPlaybackStream;
17311357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
17321357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
17331357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
17348e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Return the current volume for this route. Depending on the route, this may only
17358e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * be valid if the route is currently selected.
17368e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         *
17371357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the volume at which the playback associated with this route is performed
17381357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setVolume(int)
17391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
17401357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getVolume() {
17411357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
17421357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                int vol = 0;
17431357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                try {
17441357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    vol = sStatic.mAudioService.getStreamVolume(mPlaybackStream);
17451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                } catch (RemoteException e) {
17461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    Log.e(TAG, "Error getting local stream volume", e);
17471357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                }
17481357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return vol;
17491357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            } else {
17501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return mVolume;
17511357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
17521357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
17531357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
17541357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
17558e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Request a volume change for this route.
17568e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param volume value between 0 and getVolumeMax
17578e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         */
17588e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestSetVolume(int volume) {
17598e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
17608e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                try {
1761ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                    sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0,
1762ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                            ActivityThread.currentPackageName());
17638e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                } catch (RemoteException e) {
17648e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Error setting local stream volume", e);
17658e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
17668e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            } else {
176769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                sStatic.requestSetVolume(this, volume);
17688e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
17698e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
17708e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
17718e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        /**
17728e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Request an incremental volume update for this route.
17738e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param direction Delta to apply to the current volume
17748e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         */
17758e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestUpdateVolume(int direction) {
17768e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
17778e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                try {
17788e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    final int volume =
17798e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                            Math.max(0, Math.min(getVolume() + direction, getVolumeMax()));
1780ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                    sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0,
1781ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                            ActivityThread.currentPackageName());
17828e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                } catch (RemoteException e) {
17838e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Error setting local stream volume", e);
17848e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
17858e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            } else {
178669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                sStatic.requestUpdateVolume(this, direction);
17878e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
17888e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
17898e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
17908e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        /**
17911357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the maximum volume at which the playback associated with this route is performed
17921357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setVolumeMax(int)
17931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
17941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getVolumeMax() {
17951357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
17961357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                int volMax = 0;
17971357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                try {
17981357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    volMax = sStatic.mAudioService.getStreamMaxVolume(mPlaybackStream);
17991357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                } catch (RemoteException e) {
18001357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    Log.e(TAG, "Error getting local stream volume", e);
18011357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                }
18021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return volMax;
18031357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            } else {
18041357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return mVolumeMax;
18051357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
18061357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
18071357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
18081357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
18091357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return how volume is handling on the route
18101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setVolumeHandling(int)
18111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
18121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getVolumeHandling() {
18131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            return mVolumeHandling;
18141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
18151357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
1816705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        /**
181792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * Gets the {@link Display} that should be used by the application to show
181892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * a {@link android.app.Presentation} on an external display when this route is selected.
181992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * Depending on the route, this may only be valid if the route is currently
182092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * selected.
182192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * <p>
182292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * The preferred presentation display may change independently of the route
182392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * being selected or unselected.  For example, the presentation display
182492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * of the default system route may change when an external HDMI display is connected
182592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * or disconnected even though the route itself has not changed.
182692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p><p>
182792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * This method may return null if there is no external display associated with
182892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * the route or if the display is not ready to show UI yet.
182992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p><p>
183092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * The application should listen for changes to the presentation display
183192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * using the {@link Callback#onRoutePresentationDisplayChanged} callback and
183292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * show or dismiss its {@link android.app.Presentation} accordingly when the display
183392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * becomes available or is removed.
183492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p><p>
183592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * This method only makes sense for {@link #ROUTE_TYPE_LIVE_VIDEO live video} routes.
183692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p>
183792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
183892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @return The preferred presentation display to use when this route is
183992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * selected or null if none.
184092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
184192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @see #ROUTE_TYPE_LIVE_VIDEO
184292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @see android.app.Presentation
184392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         */
184492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public Display getPresentationDisplay() {
184592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            return mPresentationDisplay;
184692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
184792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
18485830b0b33618940d65197cec99d697b21908fec8Chong Zhang        boolean updatePresentationDisplay() {
18495830b0b33618940d65197cec99d697b21908fec8Chong Zhang            Display display = choosePresentationDisplay();
18505830b0b33618940d65197cec99d697b21908fec8Chong Zhang            if (mPresentationDisplay != display) {
18515830b0b33618940d65197cec99d697b21908fec8Chong Zhang                mPresentationDisplay = display;
18525830b0b33618940d65197cec99d697b21908fec8Chong Zhang                return true;
18535830b0b33618940d65197cec99d697b21908fec8Chong Zhang            }
18545830b0b33618940d65197cec99d697b21908fec8Chong Zhang            return false;
18555830b0b33618940d65197cec99d697b21908fec8Chong Zhang        }
18565830b0b33618940d65197cec99d697b21908fec8Chong Zhang
18575830b0b33618940d65197cec99d697b21908fec8Chong Zhang        private Display choosePresentationDisplay() {
18585830b0b33618940d65197cec99d697b21908fec8Chong Zhang            if ((mSupportedTypes & ROUTE_TYPE_LIVE_VIDEO) != 0) {
18595830b0b33618940d65197cec99d697b21908fec8Chong Zhang                Display[] displays = sStatic.getAllPresentationDisplays();
18605830b0b33618940d65197cec99d697b21908fec8Chong Zhang
18615830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // Ensure that the specified display is valid for presentations.
18625830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // This check will normally disallow the default display unless it was
18635830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // configured as a presentation display for some reason.
18645830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (mPresentationDisplayId >= 0) {
18655830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    for (Display display : displays) {
18665830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        if (display.getDisplayId() == mPresentationDisplayId) {
18675830b0b33618940d65197cec99d697b21908fec8Chong Zhang                            return display;
18685830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        }
18695830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    }
18705830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    return null;
18715830b0b33618940d65197cec99d697b21908fec8Chong Zhang                }
18725830b0b33618940d65197cec99d697b21908fec8Chong Zhang
18735830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // Find the indicated Wifi display by its address.
18745830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (mDeviceAddress != null) {
18755830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    for (Display display : displays) {
18765830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        if (display.getType() == Display.TYPE_WIFI
18775830b0b33618940d65197cec99d697b21908fec8Chong Zhang                                && mDeviceAddress.equals(display.getAddress())) {
18785830b0b33618940d65197cec99d697b21908fec8Chong Zhang                            return display;
18795830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        }
18805830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    }
18815830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    return null;
18825830b0b33618940d65197cec99d697b21908fec8Chong Zhang                }
18835830b0b33618940d65197cec99d697b21908fec8Chong Zhang
18845830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // For the default route, choose the first presentation display from the list.
18855830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (this == sStatic.mDefaultAudioVideo && displays.length > 0) {
18865830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    return displays[0];
18875830b0b33618940d65197cec99d697b21908fec8Chong Zhang                }
18885830b0b33618940d65197cec99d697b21908fec8Chong Zhang            }
18895830b0b33618940d65197cec99d697b21908fec8Chong Zhang            return null;
18905830b0b33618940d65197cec99d697b21908fec8Chong Zhang        }
18915830b0b33618940d65197cec99d697b21908fec8Chong Zhang
189275af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        /** @hide */
189375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        public String getDeviceAddress() {
189475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            return mDeviceAddress;
189575af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        }
189675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown
189792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        /**
1898a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * Returns true if this route is enabled and may be selected.
1899a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         *
1900a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * @return True if this route is enabled.
1901705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         */
1902705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        public boolean isEnabled() {
1903705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return mEnabled;
1904705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1905705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1906a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown        /**
1907a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * Returns true if the route is in the process of connecting and is not
1908a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * yet ready for use.
1909a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         *
1910a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * @return True if this route is in the process of connecting.
1911a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         */
1912a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown        public boolean isConnecting() {
191339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return mResolvedStatusCode == STATUS_CONNECTING;
19140abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
19150abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
19160abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
19170abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public boolean isSelected() {
19180abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            return this == sStatic.mSelectedRoute;
19190abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
19200abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
19210abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
19220abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public boolean isDefault() {
19230abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            return this == sStatic.mDefaultAudioVideo;
19240abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
19250abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
19260abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
19270abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public void select() {
19280abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            selectRouteStatic(mSupportedTypes, this, true);
1929a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown        }
1930a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown
19319a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void setStatusInt(CharSequence status) {
19329a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (!status.equals(mStatus)) {
19339a1de308cea2d160778fd977825f10a07b49d738Adam Powell                mStatus = status;
19349a1de308cea2d160778fd977825f10a07b49d738Adam Powell                if (mGroup != null) {
19359a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    mGroup.memberStatusChanged(this, status);
19369a1de308cea2d160778fd977825f10a07b49d738Adam Powell                }
19379a1de308cea2d160778fd977825f10a07b49d738Adam Powell                routeUpdated();
19389a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
19399a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
19409a1de308cea2d160778fd977825f10a07b49d738Adam Powell
19411357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        final IRemoteVolumeObserver.Stub mRemoteVolObserver = new IRemoteVolumeObserver.Stub() {
194269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            @Override
19431357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            public void dispatchRemoteVolumeUpdate(final int direction, final int value) {
19441357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                sStatic.mHandler.post(new Runnable() {
19451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    @Override
19461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    public void run() {
19471357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                        if (mVcb != null) {
19481357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                            if (direction != 0) {
19491357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                                mVcb.vcb.onVolumeUpdateRequest(mVcb.route, direction);
19501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                            } else {
19511357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                                mVcb.vcb.onVolumeSetRequest(mVcb.route, value);
19521357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                            }
19531357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                        }
19541357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    }
19551357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                });
19561357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
19571357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        };
19581357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
19599a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void routeUpdated() {
19609a1de308cea2d160778fd977825f10a07b49d738Adam Powell            updateRoute(this);
19619a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
19629a1de308cea2d160778fd977825f10a07b49d738Adam Powell
19639a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
19649a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public String toString() {
1965d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            String supportedTypes = typesToString(getSupportedTypes());
196692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            return getClass().getSimpleName() + "{ name=" + getName() +
196756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown                    ", description=" + getDescription() +
196892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                    ", status=" + getStatus() +
196992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                    ", category=" + getCategory() +
197092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                    ", supportedTypes=" + supportedTypes +
197169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    ", presentationDisplay=" + mPresentationDisplay + " }";
19729a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
19739a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
19749a1de308cea2d160778fd977825f10a07b49d738Adam Powell
19759a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
19769a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a route that the application may define and modify.
19778e37a85bf3dc39519942698dc90a3951306b934bAdam Powell     * A user route defaults to {@link RouteInfo#PLAYBACK_TYPE_REMOTE} and
19788e37a85bf3dc39519942698dc90a3951306b934bAdam Powell     * {@link RouteInfo#PLAYBACK_VOLUME_FIXED}.
19799a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
19809a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see MediaRouter.RouteInfo
19819a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
1982b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class UserRouteInfo extends RouteInfo {
1983ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        RemoteControlClient mRcc;
19845d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        SessionVolumeProvider mSvp;
19859a1de308cea2d160778fd977825f10a07b49d738Adam Powell
19869a1de308cea2d160778fd977825f10a07b49d738Adam Powell        UserRouteInfo(RouteCategory category) {
19879a1de308cea2d160778fd977825f10a07b49d738Adam Powell            super(category);
19889a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mSupportedTypes = ROUTE_TYPE_USER;
19898e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mPlaybackType = PLAYBACK_TYPE_REMOTE;
19908e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeHandling = PLAYBACK_VOLUME_FIXED;
19919a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
19929a1de308cea2d160778fd977825f10a07b49d738Adam Powell
19939a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
19949a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Set the user-visible name of this route.
19959a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param name Name to display to the user to describe this route
19969a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
19979a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void setName(CharSequence name) {
19989a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = name;
19999a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
20009a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
20015d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
20020d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        /**
20030d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * Set the user-visible name of this route.
200456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
200556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route name identifies the destination represented by the route.
200656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied name, an alias, or device serial number.
200756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
200856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
20090d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @param resId Resource ID of the name to display to the user to describe this route
20100d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         */
20110d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public void setName(int resId) {
20120d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mNameResId = resId;
20130d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mName = null;
20140d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            routeUpdated();
20150d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
20169a1de308cea2d160778fd977825f10a07b49d738Adam Powell
20179a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
201856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Set the user-visible description of this route.
201956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
202056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route description describes the kind of destination represented by the route.
202156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied string, a model number or brand of device.
202256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
202356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
202456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @param description The description of the route, or null if none.
202556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         */
202656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        public void setDescription(CharSequence description) {
202756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown            mDescription = description;
202856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown            routeUpdated();
202956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        }
203056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
203156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        /**
20329a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Set the current user-visible status for this route.
20339a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param status Status to display to the user to describe what the endpoint
20349a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * of this route is currently doing
20359a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
20369a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void setStatus(CharSequence status) {
20379a1de308cea2d160778fd977825f10a07b49d738Adam Powell            setStatusInt(status);
20389a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2039ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2040ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2041ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set the RemoteControlClient responsible for reporting playback info for this
2042ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * user route.
2043ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
2044ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * <p>If this route manages remote playback, the data exposed by this
2045ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * RemoteControlClient will be used to reflect and update information
2046ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * such as route volume info in related UIs.</p>
2047ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
20481357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * <p>The RemoteControlClient must have been previously registered with
20491357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}.</p>
20501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *
2051ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @param rcc RemoteControlClient associated with this route
2052ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
2053ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setRemoteControlClient(RemoteControlClient rcc) {
2054ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            mRcc = rcc;
20551357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            updatePlaybackInfoOnRcc();
2056ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2057ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2058ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
20594599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         * Retrieve the RemoteControlClient associated with this route, if one has been set.
20604599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         *
20614599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         * @return the RemoteControlClient associated with this route
20624599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         * @see #setRemoteControlClient(RemoteControlClient)
20634599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         */
20644599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        public RemoteControlClient getRemoteControlClient() {
20654599696591f745b3a546197d2ba7e5cfc5562484Adam Powell            return mRcc;
20664599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        }
20674599696591f745b3a546197d2ba7e5cfc5562484Adam Powell
20684599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        /**
2069ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this route.
2070ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2071ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
2072ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @param icon icon drawable to use to represent this route
2073ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
2074ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setIconDrawable(Drawable icon) {
2075ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            mIcon = icon;
2076ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2077ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2078ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2079ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this route.
2080ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2081ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
208271c69897ad0a55d590698bfa399bfe99c763b9dbAdam Powell         * @param resId Resource ID of an icon drawable to use to represent this route
2083ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
2084ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setIconResource(int resId) {
2085ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            setIconDrawable(sStatic.mResources.getDrawable(resId));
2086ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
20871357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
20881357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
20891357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Set a callback to be notified of volume update requests
20901357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param vcb
20911357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
20921357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolumeCallback(VolumeCallback vcb) {
20931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            mVcb = new VolumeCallbackInfo(vcb, this);
20941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
20951357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
20961357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
20971357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines whether playback associated with this route is "local"
20981357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *    ({@link RouteInfo#PLAYBACK_TYPE_LOCAL}) or "remote"
20991357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *    ({@link RouteInfo#PLAYBACK_TYPE_REMOTE}).
21001357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param type
21011357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
21021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setPlaybackType(int type) {
21031357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackType != type) {
21041357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mPlaybackType = type;
2105430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
21061357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
21071357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
21081357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
21091357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
21101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines whether volume for the playback associated with this route is fixed
21111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * ({@link RouteInfo#PLAYBACK_VOLUME_FIXED}) or can modified
21121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * ({@link RouteInfo#PLAYBACK_VOLUME_VARIABLE}).
21131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volumeHandling
21141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
21151357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolumeHandling(int volumeHandling) {
21161357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mVolumeHandling != volumeHandling) {
21171357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mVolumeHandling = volumeHandling;
2118430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
21191357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
21201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
21211357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
21221357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
21231357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines at what volume the playback associated with this route is performed (for user
21241357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * feedback purposes). This information is only used when the playback is not local.
21251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volume
21261357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
21271357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolume(int volume) {
21288e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            volume = Math.max(0, Math.min(volume, getVolumeMax()));
21291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mVolume != volume) {
21301357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mVolume = volume;
2131430fc48865e5a371b08f180390946b96d73848feRoboErik                if (mSvp != null) {
2132430fc48865e5a371b08f180390946b96d73848feRoboErik                    mSvp.notifyVolumeChanged();
21335d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
21348e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                dispatchRouteVolumeChanged(this);
2135f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                if (mGroup != null) {
2136f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                    mGroup.memberVolumeChanged(this);
2137f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                }
21388e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
21398e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
21408e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
21418e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
21428e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestSetVolume(int volume) {
21438e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mVolumeHandling == PLAYBACK_VOLUME_VARIABLE) {
21448e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (mVcb == null) {
21458e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Cannot requestSetVolume on user route - no volume callback set");
21468e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    return;
21478e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
21488e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVcb.vcb.onVolumeSetRequest(this, volume);
21498e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
21508e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
21518e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
21528e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
21538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestUpdateVolume(int direction) {
21548e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mVolumeHandling == PLAYBACK_VOLUME_VARIABLE) {
21558e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (mVcb == null) {
21568e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Cannot requestChangeVolume on user route - no volumec callback set");
21578e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    return;
21588e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
21598e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVcb.vcb.onVolumeUpdateRequest(this, direction);
21601357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
21611357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
21621357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
21631357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
21641357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines the maximum volume at which the playback associated with this route is performed
21651357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * (for user feedback purposes). This information is only used when the playback is not
21661357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * local.
21671357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volumeMax
21681357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
21691357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolumeMax(int volumeMax) {
21701357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mVolumeMax != volumeMax) {
21711357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mVolumeMax = volumeMax;
2172430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
21731357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
21741357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
21751357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
21761357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
21771357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines over what stream type the media is presented.
21781357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param stream
21791357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
21801357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setPlaybackStream(int stream) {
21811357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackStream != stream) {
21821357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mPlaybackStream = stream;
2183430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
21841357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
21851357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
21861357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
21871357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        private void updatePlaybackInfoOnRcc() {
2188430fc48865e5a371b08f180390946b96d73848feRoboErik            configureSessionVolume();
21895d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        }
21905d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
21915d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        private void configureSessionVolume() {
21925d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            if (mRcc == null) {
21935d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                if (DEBUG) {
21945d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    Log.d(TAG, "No Rcc to configure volume for route " + mName);
21955d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
21965d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                return;
21975d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
21985d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            MediaSession session = mRcc.getMediaSession();
21995d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            if (session == null) {
22005d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                if (DEBUG) {
22015d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    Log.d(TAG, "Rcc has no session to configure volume");
22025d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
22035d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                return;
22045d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
22055d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            if (mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
2206ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik                int volumeControl = VolumeProvider.VOLUME_CONTROL_FIXED;
22075d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                switch (mVolumeHandling) {
22085d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    case RemoteControlClient.PLAYBACK_VOLUME_VARIABLE:
2209ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik                        volumeControl = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
22105d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        break;
22115d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    case RemoteControlClient.PLAYBACK_VOLUME_FIXED:
22125d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    default:
22135d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        break;
22141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                }
22155d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                // Only register a new listener if necessary
22165d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                if (mSvp == null || mSvp.getVolumeControl() != volumeControl
22175d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        || mSvp.getMaxVolume() != mVolumeMax) {
22185d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    mSvp = new SessionVolumeProvider(volumeControl, mVolumeMax);
22195d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    session.setPlaybackToRemote(mSvp);
22205d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
22215d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            } else {
22225d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                // We only know how to handle local and remote, fall back to local if not remote.
22235d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                session.setPlaybackToLocal(mPlaybackStream);
22245d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                mSvp = null;
22251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
22261357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
22271357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
2228ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        class SessionVolumeProvider extends VolumeProvider {
22295d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
22305d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            public SessionVolumeProvider(int volumeControl, int maxVolume) {
22315d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                super(volumeControl, maxVolume);
22325d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
22335d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
22345d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            @Override
22355d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            public int onGetCurrentVolume() {
22365d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                return mVcb == null ? 0 : mVcb.route.mVolume;
22375d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
22385d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
22395d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            @Override
22405d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            public void onSetVolumeTo(final int volume) {
22415d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                sStatic.mHandler.post(new Runnable() {
22425d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    @Override
22435d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    public void run() {
22445d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        if (mVcb != null) {
22455d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                            mVcb.vcb.onVolumeSetRequest(mVcb.route, volume);
22465d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        }
22475d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    }
22485d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                });
22495d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
22505d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
22515d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            @Override
22525d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            public void onAdjustVolumeBy(final int delta) {
22535d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                sStatic.mHandler.post(new Runnable() {
22545d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    @Override
22555d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    public void run() {
22565d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        if (mVcb != null) {
22575d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                            mVcb.vcb.onVolumeUpdateRequest(mVcb.route, delta);
22585d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        }
22595d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    }
22605d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                });
22615d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
22625d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        }
22639a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
22649a1de308cea2d160778fd977825f10a07b49d738Adam Powell
22659a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
22669a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a route that consists of multiple other routes in a group.
22679a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
2268b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class RouteGroup extends RouteInfo {
22699a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
22709a1de308cea2d160778fd977825f10a07b49d738Adam Powell        private boolean mUpdateName;
22719a1de308cea2d160778fd977825f10a07b49d738Adam Powell
22729a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteGroup(RouteCategory category) {
22739a1de308cea2d160778fd977825f10a07b49d738Adam Powell            super(category);
22749a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mGroup = this;
22758e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeHandling = PLAYBACK_VOLUME_FIXED;
22769a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
22779a1de308cea2d160778fd977825f10a07b49d738Adam Powell
227869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
22790d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        CharSequence getName(Resources res) {
22809a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (mUpdateName) updateName();
22810d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return super.getName(res);
22829a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
22839a1de308cea2d160778fd977825f10a07b49d738Adam Powell
22849a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
22859a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Add a route to this group. The route must not currently belong to another group.
22869a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
22879a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to add to this group
22889a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
22899a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void addRoute(RouteInfo route) {
22909a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != null) {
22919a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalStateException("Route " + route + " is already part of a group.");
22929a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
22939a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getCategory() != mCategory) {
22949a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException(
22959a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        "Route cannot be added to a group with a different category. " +
22969a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            "(Route category=" + route.getCategory() +
22979a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            " group category=" + mCategory + ")");
22989a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
2299d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            final int at = mRoutes.size();
23009a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.add(route);
2301d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = this;
23029a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2303f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
23049a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
2305f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell            dispatchRouteGrouped(route, this, at);
23069a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
23079a1de308cea2d160778fd977825f10a07b49d738Adam Powell
23089a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
23099a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Add a route to this group before the specified index.
23109a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
23119a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to add
23129a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param insertAt insert the new route before this index
23139a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
23149a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void addRoute(RouteInfo route, int insertAt) {
23159a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != null) {
23169a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalStateException("Route " + route + " is already part of a group.");
23179a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
23189a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getCategory() != mCategory) {
23199a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException(
23209a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        "Route cannot be added to a group with a different category. " +
23219a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            "(Route category=" + route.getCategory() +
23229a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            " group category=" + mCategory + ")");
23239a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
23249a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.add(insertAt, route);
2325d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = this;
23269a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2327f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
23289a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
2329f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell            dispatchRouteGrouped(route, this, insertAt);
23309a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
23319a1de308cea2d160778fd977825f10a07b49d738Adam Powell
23329a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
23339a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Remove a route from this group.
23349a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
23359a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to remove
23369a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
23379a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void removeRoute(RouteInfo route) {
23389a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != this) {
23399a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException("Route " + route +
23409a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        " is not a member of this group.");
23419a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
23429a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.remove(route);
2343d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = null;
23449a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2345f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
2346d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteUngrouped(route, this);
23479a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
23489a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
23499a1de308cea2d160778fd977825f10a07b49d738Adam Powell
23509a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
23519a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Remove the route at the specified index from this group.
23529a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
23539a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param index index of the route to remove
23549a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
23559a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void removeRoute(int index) {
2356d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            RouteInfo route = mRoutes.remove(index);
2357d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = null;
23589a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2359f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
2360d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteUngrouped(route, this);
23619a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
23629a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
23639a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2364d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2365d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @return The number of routes in this group
2366d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
2367d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public int getRouteCount() {
2368d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            return mRoutes.size();
2369d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        }
2370d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2371d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2372d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Return the route in this group at the specified index
2373d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2374d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param index Index to fetch
2375d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @return The route at index
2376d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
2377d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public RouteInfo getRouteAt(int index) {
2378d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            return mRoutes.get(index);
2379d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        }
2380d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2381ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2382ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this group.
2383ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2384ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
2385ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @param icon icon drawable to use to represent this group
2386ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
2387ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setIconDrawable(Drawable icon) {
2388ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            mIcon = icon;
2389ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2390ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2391ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2392ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this group.
2393ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2394ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
239571c69897ad0a55d590698bfa399bfe99c763b9dbAdam Powell         * @param resId Resource ID of an icon drawable to use to represent this group
2396ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
2397ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setIconResource(int resId) {
2398ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            setIconDrawable(sStatic.mResources.getDrawable(resId));
2399ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2400ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
24018e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
24028e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestSetVolume(int volume) {
24038e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int maxVol = getVolumeMax();
24048e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (maxVol == 0) {
24058e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                return;
24068e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
24078e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
24088e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final float scaledVolume = (float) volume / maxVol;
24098e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int routeCount = getRouteCount();
24108e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            for (int i = 0; i < routeCount; i++) {
24118e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final RouteInfo route = getRouteAt(i);
24128e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int routeVol = (int) (scaledVolume * route.getVolumeMax());
24138e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                route.requestSetVolume(routeVol);
24148e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
24158e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (volume != mVolume) {
24168e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVolume = volume;
24178e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                dispatchRouteVolumeChanged(this);
24188e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
24198e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
24208e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
24218e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
24228e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestUpdateVolume(int direction) {
24238e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int maxVol = getVolumeMax();
24248e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (maxVol == 0) {
24258e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                return;
24268e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
24278e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
24288e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int routeCount = getRouteCount();
2429f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            int volume = 0;
24308e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            for (int i = 0; i < routeCount; i++) {
24318e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final RouteInfo route = getRouteAt(i);
24328e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                route.requestUpdateVolume(direction);
2433f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                final int routeVol = route.getVolume();
2434f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                if (routeVol > volume) {
2435f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                    volume = routeVol;
2436f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                }
24378e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
24388e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (volume != mVolume) {
24398e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVolume = volume;
24408e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                dispatchRouteVolumeChanged(this);
24418e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
24428e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
24438e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
24449a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void memberNameChanged(RouteInfo info, CharSequence name) {
24459a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
24469a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
24479a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
24489a1de308cea2d160778fd977825f10a07b49d738Adam Powell
24499a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void memberStatusChanged(RouteInfo info, CharSequence status) {
24509a1de308cea2d160778fd977825f10a07b49d738Adam Powell            setStatusInt(status);
24519a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
24529a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2453f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        void memberVolumeChanged(RouteInfo info) {
2454f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
2455f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        }
2456f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell
2457f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        void updateVolume() {
2458f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            // A group always represents the highest component volume value.
2459f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            final int routeCount = getRouteCount();
2460f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            int volume = 0;
2461f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            for (int i = 0; i < routeCount; i++) {
2462f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                final int routeVol = getRouteAt(i).getVolume();
2463f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                if (routeVol > volume) {
2464f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                    volume = routeVol;
2465f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                }
2466f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            }
2467f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            if (volume != mVolume) {
2468f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                mVolume = volume;
2469f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                dispatchRouteVolumeChanged(this);
2470f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            }
2471f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        }
2472f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell
2473d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        @Override
2474d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        void routeUpdated() {
2475d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            int types = 0;
2476d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            final int count = mRoutes.size();
2477b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            if (count == 0) {
2478b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell                // Don't keep empty groups in the router.
247969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                MediaRouter.removeRouteStatic(this);
2480b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell                return;
2481b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            }
2482b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
24838e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            int maxVolume = 0;
24848e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            boolean isLocal = true;
24858e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            boolean isFixedVolume = true;
2486d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            for (int i = 0; i < count; i++) {
24878e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final RouteInfo route = mRoutes.get(i);
24888e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                types |= route.mSupportedTypes;
24898e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int routeMaxVolume = route.getVolumeMax();
24908e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (routeMaxVolume > maxVolume) {
24918e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    maxVolume = routeMaxVolume;
24928e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
24938e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                isLocal &= route.getPlaybackType() == PLAYBACK_TYPE_LOCAL;
24948e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                isFixedVolume &= route.getVolumeHandling() == PLAYBACK_VOLUME_FIXED;
2495d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            }
24968e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mPlaybackType = isLocal ? PLAYBACK_TYPE_LOCAL : PLAYBACK_TYPE_REMOTE;
24978e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeHandling = isFixedVolume ? PLAYBACK_VOLUME_FIXED : PLAYBACK_VOLUME_VARIABLE;
2498d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            mSupportedTypes = types;
24998e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeMax = maxVolume;
2500d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            mIcon = count == 1 ? mRoutes.get(0).getIconDrawable() : null;
2501d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            super.routeUpdated();
2502d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        }
2503d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
25049a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void updateName() {
25059a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final StringBuilder sb = new StringBuilder();
25069a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final int count = mRoutes.size();
25079a1de308cea2d160778fd977825f10a07b49d738Adam Powell            for (int i = 0; i < count; i++) {
25089a1de308cea2d160778fd977825f10a07b49d738Adam Powell                final RouteInfo info = mRoutes.get(i);
2509b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell                // TODO: There's probably a much more correct way to localize this.
25109a1de308cea2d160778fd977825f10a07b49d738Adam Powell                if (i > 0) sb.append(", ");
25119a1de308cea2d160778fd977825f10a07b49d738Adam Powell                sb.append(info.mName);
25129a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
25139a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = sb.toString();
25149a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = false;
25159a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2516d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
2517d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        @Override
2518d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        public String toString() {
2519d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            StringBuilder sb = new StringBuilder(super.toString());
2520d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            sb.append('[');
2521d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            final int count = mRoutes.size();
2522d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            for (int i = 0; i < count; i++) {
2523d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                if (i > 0) sb.append(", ");
2524d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                sb.append(mRoutes.get(i));
2525d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            }
2526d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            sb.append(']');
2527d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            return sb.toString();
2528d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        }
25299a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
25309a1de308cea2d160778fd977825f10a07b49d738Adam Powell
25319a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
25329a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Definition of a category of routes. All routes belong to a category.
25339a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
2534b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class RouteCategory {
25359a1de308cea2d160778fd977825f10a07b49d738Adam Powell        CharSequence mName;
25360d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        int mNameResId;
25379a1de308cea2d160778fd977825f10a07b49d738Adam Powell        int mTypes;
25389a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final boolean mGroupable;
2539705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        boolean mIsSystem;
25409a1de308cea2d160778fd977825f10a07b49d738Adam Powell
25419a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteCategory(CharSequence name, int types, boolean groupable) {
25429a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = name;
25439a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mTypes = types;
25449a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mGroupable = groupable;
25459a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
25469a1de308cea2d160778fd977825f10a07b49d738Adam Powell
25470d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        RouteCategory(int nameResId, int types, boolean groupable) {
25480d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mNameResId = nameResId;
25490d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mTypes = types;
25500d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mGroupable = groupable;
25510d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
25520d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell
25539a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
25549a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return the name of this route category
25559a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
25569a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getName() {
25570d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(sStatic.mResources);
25580d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
25595d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
25600d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        /**
25610d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * Return the properly localized/configuration dependent name of this RouteCategory.
25625d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik         *
25630d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @param context Context to resolve name resources
25640d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @return the name of this route category
25650d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         */
25660d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public CharSequence getName(Context context) {
25670d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(context.getResources());
25680d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
25695d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
25700d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        CharSequence getName(Resources res) {
25710d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            if (mNameResId != 0) {
25720d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                return res.getText(mNameResId);
25730d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            }
25749a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mName;
25759a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
25769a1de308cea2d160778fd977825f10a07b49d738Adam Powell
25779a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
2578d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Return the current list of routes in this category that have been added
2579d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * to the MediaRouter.
25809a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2581d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * <p>This list will not include routes that are nested within RouteGroups.
2582d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * A RouteGroup is treated as a single route within its category.</p>
2583d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2584d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param out a List to fill with the routes in this category. If this parameter is
2585d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *            non-null, it will be cleared, filled with the current routes with this
2586d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *            category, and returned. If this parameter is null, a new List will be
2587d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *            allocated to report the category's current routes.
2588d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @return A list with the routes in this category that have been added to the MediaRouter.
25899a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
2590d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public List<RouteInfo> getRoutes(List<RouteInfo> out) {
2591d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            if (out == null) {
2592d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                out = new ArrayList<RouteInfo>();
2593d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            } else {
2594d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                out.clear();
2595d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            }
2596d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2597b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final int count = getRouteCountStatic();
2598d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            for (int i = 0; i < count; i++) {
2599b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                final RouteInfo route = getRouteAtStatic(i);
2600d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                if (route.mCategory == this) {
2601d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                    out.add(route);
2602d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                }
2603d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            }
2604d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            return out;
26059a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
26069a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26079a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
26089a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return Flag set describing the route types supported by this category
26099a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
26109a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int getSupportedTypes() {
26119a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mTypes;
26129a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
26139a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26149a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
26159a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Return whether or not this category supports grouping.
26169a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
26179a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * <p>If this method returns true, all routes obtained from this category
2618d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * via calls to {@link #getRouteAt(int)} will be {@link MediaRouter.RouteGroup}s.</p>
26199a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
26209a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return true if this category supports
26219a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
26229a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public boolean isGroupable() {
26239a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mGroupable;
26249a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
26259a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2626705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        /**
2627705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * @return true if this is the category reserved for system routes.
2628705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * @hide
2629705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         */
2630705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        public boolean isSystem() {
2631705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return mIsSystem;
2632705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
2633705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
263469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
26359a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public String toString() {
26369a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return "RouteCategory{ name=" + mName + " types=" + typesToString(mTypes) +
2637d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                    " groupable=" + mGroupable + " }";
26389a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
26399a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
26409a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26419a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static class CallbackInfo {
26429a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int type;
264366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        public int flags;
2644b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        public final Callback cb;
2645b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        public final MediaRouter router;
26469a1de308cea2d160778fd977825f10a07b49d738Adam Powell
264766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        public CallbackInfo(Callback cb, int type, int flags, MediaRouter router) {
26489a1de308cea2d160778fd977825f10a07b49d738Adam Powell            this.cb = cb;
26499a1de308cea2d160778fd977825f10a07b49d738Adam Powell            this.type = type;
265066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            this.flags = flags;
2651b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            this.router = router;
26529a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
265366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
265466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        public boolean filterRouteEvent(RouteInfo route) {
2655af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            return filterRouteEvent(route.mSupportedTypes);
2656af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        }
2657af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
2658af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        public boolean filterRouteEvent(int supportedTypes) {
265966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            return (flags & CALLBACK_FLAG_UNFILTERED_EVENTS) != 0
2660af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    || (type & supportedTypes) != 0;
266166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
26629a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
26639a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26649a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
26659a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Interface for receiving events about media routing changes.
26669a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * All methods of this interface will be called from the application's main thread.
266766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
266866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * A Callback will only receive events relevant to routes that the callback
266966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * was registered for unless the {@link MediaRouter#CALLBACK_FLAG_UNFILTERED_EVENTS}
267066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * flag was specified in {@link MediaRouter#addCallback(int, Callback, int)}.
267166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
26729a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
267366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @see MediaRouter#addCallback(int, Callback, int)
26749a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see MediaRouter#removeCallback(Callback)
26759a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
26760d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    public static abstract class Callback {
26779a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
26789a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when the supplied route becomes selected as the active route
26799a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * for the given route type.
26809a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2681d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
26829a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param type Type flag set indicating the routes that have been selected
26839a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been selected for the given route types
26849a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
26850d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteSelected(MediaRouter router, int type, RouteInfo info);
26869a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26879a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
26889a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when the supplied route becomes unselected as the active route
26899a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * for the given route type.
26909a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2691d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
26929a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param type Type flag set indicating the routes that have been unselected
26939a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been unselected for the given route types
26949a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
26950d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteUnselected(MediaRouter router, int type, RouteInfo info);
26969a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26979a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
26989a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when a route for the specified type was added.
26999a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2700d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
27019a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has become available for use
27029a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
27030d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteAdded(MediaRouter router, RouteInfo info);
27049a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27059a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
27069a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when a route for the specified type was removed.
27079a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2708d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
27099a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been removed from availability
27109a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
27110d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteRemoved(MediaRouter router, RouteInfo info);
27129a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27139a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
27149a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when an aspect of the indicated route has changed.
27159a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
27169a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * <p>This will not indicate that the types supported by this route have
27179a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * changed, only that cosmetic info such as name or status have been updated.</p>
27189a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2719d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
27209a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info The route that was changed
27219a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
27220d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteChanged(MediaRouter router, RouteInfo info);
2723d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2724d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2725d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Called when a route is added to a group.
2726d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2727d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
2728d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param info The route that was added
2729d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param group The group the route was added to
2730d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param index The route index within group that info was added at
2731d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
27320d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
27330d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                int index);
2734d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2735d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2736d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Called when a route is removed from a group.
2737d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2738d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
2739d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param info The route that was removed
2740d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param group The group the route was removed from
2741d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
27420d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group);
27438e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
27448e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        /**
27458e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Called when a route's volume changes.
27468e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         *
27478e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param router the MediaRouter reporting the event
27488e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param info The route with altered volume
27498e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         */
27508e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public abstract void onRouteVolumeChanged(MediaRouter router, RouteInfo info);
275192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
275292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        /**
275392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * Called when a route's presentation display changes.
275492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * <p>
275592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * This method is called whenever the route's presentation display becomes
275692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * available, is removes or has changes to some of its properties (such as its size).
275792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p>
275892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
275992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @param router the MediaRouter reporting the event
276092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @param info The route whose presentation display changed
276192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
276292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @see RouteInfo#getPresentationDisplay()
276392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         */
276492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
276592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
27669a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
27679a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27689a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
27690d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * Stub implementation of {@link MediaRouter.Callback}.
27700d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * Each abstract method is defined as a no-op. Override just the ones
27719a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * you need.
27729a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
27730d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    public static class SimpleCallback extends Callback {
27749a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27759a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2776d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
27779a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27789a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27799a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2780d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
27819a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27829a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27839a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2784d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteAdded(MediaRouter router, RouteInfo info) {
27859a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27869a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27879a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2788d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteRemoved(MediaRouter router, RouteInfo info) {
27899a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27909a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27919a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2792d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteChanged(MediaRouter router, RouteInfo info) {
27939a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27949a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27959a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2796d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
2797d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                int index) {
27989a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27999a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28009a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2801d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
28029a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2803d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
28048e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
28058e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void onRouteVolumeChanged(MediaRouter router, RouteInfo info) {
28068e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
28079a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
28081357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
28091357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    static class VolumeCallbackInfo {
28101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final VolumeCallback vcb;
28111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final RouteInfo route;
28121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
28131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public VolumeCallbackInfo(VolumeCallback vcb, RouteInfo route) {
28141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            this.vcb = vcb;
28151357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            this.route = route;
28161357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
28171357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    }
28181357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
28191357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    /**
28201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * Interface for receiving events about volume changes.
28211357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * All methods of this interface will be called from the application's main thread.
28221357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     *
28231357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * <p>A VolumeCallback will only receive events relevant to routes that the callback
28241357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * was registered for.</p>
28251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     *
28261357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * @see UserRouteInfo#setVolumeCallback(VolumeCallback)
28271357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     */
28281357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    public static abstract class VolumeCallback {
28291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
28301357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Called when the volume for the route should be increased or decreased.
28311357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param info the route affected by this event
28321357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param direction an integer indicating whether the volume is to be increased
28331357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     (positive value) or decreased (negative value).
28341357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     For bundled changes, the absolute value indicates the number of changes
28351357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     in the same direction, e.g. +3 corresponds to three "volume up" changes.
28361357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
28371357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public abstract void onVolumeUpdateRequest(RouteInfo info, int direction);
28381357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
28391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Called when the volume for the route should be set to the given value
28401357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param info the route affected by this event
28411357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volume an integer indicating the new volume value that should be used, always
28421357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     between 0 and the value set by {@link UserRouteInfo#setVolumeMax(int)}.
28431357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
28441357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public abstract void onVolumeSetRequest(RouteInfo info, int volume);
28451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    }
28461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
28478e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    static class VolumeChangeReceiver extends BroadcastReceiver {
28488e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
28498e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void onReceive(Context context, Intent intent) {
28508e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (intent.getAction().equals(AudioManager.VOLUME_CHANGED_ACTION)) {
28518e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
28528e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                        -1);
28538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (streamType != AudioManager.STREAM_MUSIC) {
28548e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    return;
28558e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
28568e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
28578e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int newVolume = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
28588e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int oldVolume = intent.getIntExtra(
28598e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                        AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0);
28608e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (newVolume != oldVolume) {
28618e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    systemVolumeChanged(newVolume);
28628e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
28638e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
28648e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
2865705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
28668e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
2867705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    static class WifiDisplayStatusChangedReceiver extends BroadcastReceiver {
2868705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        @Override
2869705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        public void onReceive(Context context, Intent intent) {
2870705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (intent.getAction().equals(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)) {
2871705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                updateWifiDisplayStatus((WifiDisplayStatus) intent.getParcelableExtra(
2872705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                        DisplayManager.EXTRA_WIFI_DISPLAY_STATUS));
2873705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
2874705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
28758e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    }
28769a1de308cea2d160778fd977825f10a07b49d738Adam Powell}
2877