MediaRouter.java revision 0d0f67f5ee5f939a1b611bc4583212707afd9bee
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
193705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (!TextUtils.equals(newRoutes.mBluetoothName, mCurAudioRoutesInfo.mBluetoothName)) {
194705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                mCurAudioRoutesInfo.mBluetoothName = newRoutes.mBluetoothName;
195705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                if (mCurAudioRoutesInfo.mBluetoothName != null) {
196632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    if (sStatic.mBluetoothA2dpRoute == null) {
197632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        final RouteInfo info = new RouteInfo(sStatic.mSystemCategory);
198705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                        info.mName = mCurAudioRoutesInfo.mBluetoothName;
19956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown                        info.mDescription = sStatic.mResources.getText(
20056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown                                com.android.internal.R.string.bluetooth_a2dp_audio_route_name);
201632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        info.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO;
202632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        sStatic.mBluetoothA2dpRoute = info;
2032ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell                        addRouteStatic(sStatic.mBluetoothA2dpRoute);
204632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    } else {
205705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                        sStatic.mBluetoothA2dpRoute.mName = mCurAudioRoutesInfo.mBluetoothName;
206632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        dispatchRouteChanged(sStatic.mBluetoothA2dpRoute);
207632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    }
208632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                } else if (sStatic.mBluetoothA2dpRoute != null) {
20969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    removeRouteStatic(sStatic.mBluetoothA2dpRoute);
210632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    sStatic.mBluetoothA2dpRoute = null;
211632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                }
212632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
213bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell
214bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell            if (mBluetoothA2dpRoute != null) {
215f1b2335a36fa6f2b9926f309a7c4ed29a1404ec4Jeff Brown                final boolean a2dpEnabled = isBluetoothA2dpOn();
2163f369684e13dfea0ba8ea134f3e95930b0dd7df0Adam Powell                if (mainType != AudioRoutesInfo.MAIN_SPEAKER &&
2173f369684e13dfea0ba8ea134f3e95930b0dd7df0Adam Powell                        mSelectedRoute == mBluetoothA2dpRoute && !a2dpEnabled) {
21869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mDefaultAudioVideo, false);
2193f369684e13dfea0ba8ea134f3e95930b0dd7df0Adam Powell                } else if ((mSelectedRoute == mDefaultAudioVideo || mSelectedRoute == null) &&
2202ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell                        a2dpEnabled) {
22169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mBluetoothA2dpRoute, false);
222bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell                }
223bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell            }
224b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        }
22592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
226f1b2335a36fa6f2b9926f309a7c4ed29a1404ec4Jeff Brown        boolean isBluetoothA2dpOn() {
227f1b2335a36fa6f2b9926f309a7c4ed29a1404ec4Jeff Brown            try {
228f1b2335a36fa6f2b9926f309a7c4ed29a1404ec4Jeff Brown                return mAudioService.isBluetoothA2dpOn();
229f1b2335a36fa6f2b9926f309a7c4ed29a1404ec4Jeff Brown            } catch (RemoteException e) {
230f1b2335a36fa6f2b9926f309a7c4ed29a1404ec4Jeff Brown                Log.e(TAG, "Error querying Bluetooth A2DP state", e);
231f1b2335a36fa6f2b9926f309a7c4ed29a1404ec4Jeff Brown                return false;
232f1b2335a36fa6f2b9926f309a7c4ed29a1404ec4Jeff Brown            }
233f1b2335a36fa6f2b9926f309a7c4ed29a1404ec4Jeff Brown        }
234f1b2335a36fa6f2b9926f309a7c4ed29a1404ec4Jeff Brown
23569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void updateDiscoveryRequest() {
23669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // What are we looking for today?
23769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int routeTypes = 0;
23869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int passiveRouteTypes = 0;
23969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean activeScan = false;
24069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean activeScanWifiDisplay = false;
24169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int count = mCallbacks.size();
24269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < count; i++) {
24369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                CallbackInfo cbi = mCallbacks.get(i);
24469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if ((cbi.flags & (CALLBACK_FLAG_PERFORM_ACTIVE_SCAN
24569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        | CALLBACK_FLAG_REQUEST_DISCOVERY)) != 0) {
24669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Discovery explicitly requested.
24769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    routeTypes |= cbi.type;
24869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else if ((cbi.flags & CALLBACK_FLAG_PASSIVE_DISCOVERY) != 0) {
24969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Discovery only passively requested.
25069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    passiveRouteTypes |= cbi.type;
25169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else {
25269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Legacy case since applications don't specify the discovery flag.
25369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Unfortunately we just have to assume they always need discovery
25469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // whenever they have a callback registered.
25569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    routeTypes |= cbi.type;
25669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
25769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if ((cbi.flags & CALLBACK_FLAG_PERFORM_ACTIVE_SCAN) != 0) {
25869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    activeScan = true;
259af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    if ((cbi.type & ROUTE_TYPE_REMOTE_DISPLAY) != 0) {
26069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        activeScanWifiDisplay = true;
26169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
26269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
26369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
26469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (routeTypes != 0 || activeScan) {
26569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // If someone else requests discovery then enable the passive listeners.
26669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // This is used by the MediaRouteButton and MediaRouteActionProvider since
26769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // they don't receive lifecycle callbacks from the Activity.
26869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                routeTypes |= passiveRouteTypes;
26969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
27069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
27169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Update wifi display scanning.
272ce468a35b388ca46578934706b38dbae94941643Jeff Brown            // TODO: All of this should be managed by the media router service.
273ce468a35b388ca46578934706b38dbae94941643Jeff Brown            if (mCanConfigureWifiDisplays) {
274ce468a35b388ca46578934706b38dbae94941643Jeff Brown                if (mSelectedRoute != null
275ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        && mSelectedRoute.matchesTypes(ROUTE_TYPE_REMOTE_DISPLAY)) {
276ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    // Don't scan while already connected to a remote display since
277ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    // it may interfere with the ongoing transmission.
278ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    activeScanWifiDisplay = false;
27966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown                }
280ce468a35b388ca46578934706b38dbae94941643Jeff Brown                if (activeScanWifiDisplay) {
281ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    if (!mActivelyScanningWifiDisplays) {
282ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mActivelyScanningWifiDisplays = true;
283ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mDisplayService.startWifiDisplayScan();
284ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    }
285ce468a35b388ca46578934706b38dbae94941643Jeff Brown                } else {
286ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    if (mActivelyScanningWifiDisplays) {
287ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mActivelyScanningWifiDisplays = false;
288ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mDisplayService.stopWifiDisplayScan();
289ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    }
29066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown                }
29166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            }
29266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
29369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Tell the media router service all about it.
29469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (routeTypes != mDiscoveryRequestRouteTypes
29569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    || activeScan != mDiscoverRequestActiveScan) {
29669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mDiscoveryRequestRouteTypes = routeTypes;
29769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mDiscoverRequestActiveScan = activeScan;
29869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                publishClientDiscoveryRequest();
29966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            }
30066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
30166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
30292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        @Override
30392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onDisplayAdded(int displayId) {
30492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            updatePresentationDisplays(displayId);
30592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
30692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
30792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        @Override
30892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onDisplayChanged(int displayId) {
30992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            updatePresentationDisplays(displayId);
31092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
31192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
31292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        @Override
31392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onDisplayRemoved(int displayId) {
31492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            updatePresentationDisplays(displayId);
31592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
31692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
31792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public Display[] getAllPresentationDisplays() {
31892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
31992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
32092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
32192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        private void updatePresentationDisplays(int changedDisplayId) {
32292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            final int count = mRoutes.size();
32392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            for (int i = 0; i < count; i++) {
3245830b0b33618940d65197cec99d697b21908fec8Chong Zhang                final RouteInfo route = mRoutes.get(i);
3255830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (route.updatePresentationDisplay() || (route.mPresentationDisplay != null
3265830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        && route.mPresentationDisplay.getDisplayId() == changedDisplayId)) {
3275830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    dispatchRoutePresentationDisplayChanged(route);
32892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                }
32992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            }
33092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
33169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
33269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void setSelectedRoute(RouteInfo info, boolean explicit) {
33369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Must be non-reentrant.
33469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mSelectedRoute = info;
33569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            publishClientSelectedRoute(explicit);
33669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
33769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
33869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void rebindAsUser(int userId) {
33969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mCurrentUserId != userId || userId < 0 || mClient == null) {
34069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (mClient != null) {
34169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    try {
34269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        mMediaRouterService.unregisterClient(mClient);
34369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    } catch (RemoteException ex) {
34469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        Log.e(TAG, "Unable to unregister media router client.", ex);
34569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
34669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClient = null;
34769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
34869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
34969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mCurrentUserId = userId;
35069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
35169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
35269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Client client = new Client();
35369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.registerClientAsUser(client,
35469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mAppContext.getPackageName(), userId);
35569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClient = client;
35669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
35769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to register media router client.", ex);
35869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
35969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
36069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                publishClientDiscoveryRequest();
36169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                publishClientSelectedRoute(false);
36269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                updateClientState();
36369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
36469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
36569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
36669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void publishClientDiscoveryRequest() {
36769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mClient != null) {
36869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
36969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.setDiscoveryRequest(mClient,
37069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mDiscoveryRequestRouteTypes, mDiscoverRequestActiveScan);
37169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
37269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to publish media router client discovery request.", ex);
37369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
37469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
37569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
37669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
37769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void publishClientSelectedRoute(boolean explicit) {
37869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mClient != null) {
37969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
38069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.setSelectedRoute(mClient,
38169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mSelectedRoute != null ? mSelectedRoute.mGlobalRouteId : null,
38269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            explicit);
38369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
38469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to publish media router client selected route.", ex);
38569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
38669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
38769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
38869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
38969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void updateClientState() {
39069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Update the client state.
39169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mClientState = null;
39269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mClient != null) {
39369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
39469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClientState = mMediaRouterService.getState(mClient);
39569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
39669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to retrieve media router client state.", ex);
39769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
39869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
39969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final ArrayList<MediaRouterClientState.RouteInfo> globalRoutes =
40069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClientState != null ? mClientState.routes : null;
40169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final String globallySelectedRouteId = mClientState != null ?
40269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClientState.globallySelectedRouteId : null;
40369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
40469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Add or update routes.
40569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int globalRouteCount = globalRoutes != null ? globalRoutes.size() : 0;
40669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < globalRouteCount; i++) {
40769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final MediaRouterClientState.RouteInfo globalRoute = globalRoutes.get(i);
40869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                RouteInfo route = findGlobalRoute(globalRoute.id);
40969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (route == null) {
41069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    route = makeGlobalRoute(globalRoute);
41169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    addRouteStatic(route);
41269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else {
41369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    updateGlobalRoute(route, globalRoute);
41469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
41569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
41669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
41769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Synchronize state with the globally selected route.
41869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (globallySelectedRouteId != null) {
41969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final RouteInfo route = findGlobalRoute(globallySelectedRouteId);
42069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (route == null) {
42169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.w(TAG, "Could not find new globally selected route: "
42269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            + globallySelectedRouteId);
42369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else if (route != mSelectedRoute) {
42469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    if (DEBUG) {
42569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        Log.d(TAG, "Selecting new globally selected route: " + route);
42669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
42769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    selectRouteStatic(route.mSupportedTypes, route, false);
42869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
42969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            } else if (mSelectedRoute != null && mSelectedRoute.mGlobalRouteId != null) {
43069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (DEBUG) {
43169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.d(TAG, "Unselecting previous globally selected route: " + mSelectedRoute);
43269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
43369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                selectDefaultRouteStatic();
43469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
43569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
43669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Remove defunct routes.
43769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            outer: for (int i = mRoutes.size(); i-- > 0; ) {
43869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final RouteInfo route = mRoutes.get(i);
43969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final String globalRouteId = route.mGlobalRouteId;
44069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (globalRouteId != null) {
44169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    for (int j = 0; j < globalRouteCount; j++) {
44269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        MediaRouterClientState.RouteInfo globalRoute = globalRoutes.get(j);
44369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (globalRouteId.equals(globalRoute.id)) {
44469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            continue outer; // found
44569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
44669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
44769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // not found
44869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    removeRouteStatic(route);
44969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
45069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
45169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
45269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
45369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void requestSetVolume(RouteInfo route, int volume) {
45469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mGlobalRouteId != null && mClient != null) {
45569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
45669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.requestSetVolume(mClient,
45769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            route.mGlobalRouteId, volume);
45869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
45969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.w(TAG, "Unable to request volume change.", ex);
46069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
46169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
46269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
46369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
46469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void requestUpdateVolume(RouteInfo route, int direction) {
46569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mGlobalRouteId != null && mClient != null) {
46669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
46769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.requestUpdateVolume(mClient,
46869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            route.mGlobalRouteId, direction);
46969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
47069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.w(TAG, "Unable to request volume change.", ex);
47169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
47269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
47369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
47469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
47569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        RouteInfo makeGlobalRoute(MediaRouterClientState.RouteInfo globalRoute) {
47669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            RouteInfo route = new RouteInfo(sStatic.mSystemCategory);
47769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mGlobalRouteId = globalRoute.id;
47869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mName = globalRoute.name;
47969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mDescription = globalRoute.description;
48069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mSupportedTypes = globalRoute.supportedTypes;
48169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mEnabled = globalRoute.enabled;
48239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            route.setRealStatusCode(globalRoute.statusCode);
48369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mPlaybackType = globalRoute.playbackType;
48469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mPlaybackStream = globalRoute.playbackStream;
48569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mVolume = globalRoute.volume;
48669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mVolumeMax = globalRoute.volumeMax;
48769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mVolumeHandling = globalRoute.volumeHandling;
4885830b0b33618940d65197cec99d697b21908fec8Chong Zhang            route.mPresentationDisplayId = globalRoute.presentationDisplayId;
4895830b0b33618940d65197cec99d697b21908fec8Chong Zhang            route.updatePresentationDisplay();
49069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            return route;
49169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
49269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
49369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void updateGlobalRoute(RouteInfo route, MediaRouterClientState.RouteInfo globalRoute) {
49469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean changed = false;
49569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean volumeChanged = false;
49669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean presentationDisplayChanged = false;
49769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
498e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root            if (!Objects.equals(route.mName, globalRoute.name)) {
49969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mName = globalRoute.name;
50069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
50169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
502e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root            if (!Objects.equals(route.mDescription, globalRoute.description)) {
50369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mDescription = globalRoute.description;
50469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
50569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
506af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            final int oldSupportedTypes = route.mSupportedTypes;
507af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (oldSupportedTypes != globalRoute.supportedTypes) {
50869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mSupportedTypes = globalRoute.supportedTypes;
50969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
51069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
51169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mEnabled != globalRoute.enabled) {
51269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mEnabled = globalRoute.enabled;
51369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
51469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
51539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (route.mRealStatusCode != globalRoute.statusCode) {
51639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                route.setRealStatusCode(globalRoute.statusCode);
51769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
51869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
51969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mPlaybackType != globalRoute.playbackType) {
52069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mPlaybackType = globalRoute.playbackType;
52169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
52269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
52369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mPlaybackStream != globalRoute.playbackStream) {
52469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mPlaybackStream = globalRoute.playbackStream;
52569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
52669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
52769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mVolume != globalRoute.volume) {
52869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mVolume = globalRoute.volume;
52969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
53069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                volumeChanged = true;
53169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
53269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mVolumeMax != globalRoute.volumeMax) {
53369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mVolumeMax = globalRoute.volumeMax;
53469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
53569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                volumeChanged = true;
53669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
53769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mVolumeHandling != globalRoute.volumeHandling) {
53869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mVolumeHandling = globalRoute.volumeHandling;
53969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
54069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                volumeChanged = true;
54169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
5425830b0b33618940d65197cec99d697b21908fec8Chong Zhang            if (route.mPresentationDisplayId != globalRoute.presentationDisplayId) {
5435830b0b33618940d65197cec99d697b21908fec8Chong Zhang                route.mPresentationDisplayId = globalRoute.presentationDisplayId;
5445830b0b33618940d65197cec99d697b21908fec8Chong Zhang                route.updatePresentationDisplay();
54569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
54669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                presentationDisplayChanged = true;
54769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
54869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
54969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (changed) {
550af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                dispatchRouteChanged(route, oldSupportedTypes);
55169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
55269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (volumeChanged) {
55369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                dispatchRouteVolumeChanged(route);
55469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
55569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (presentationDisplayChanged) {
55669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                dispatchRoutePresentationDisplayChanged(route);
55769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
55869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
55969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
56069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        RouteInfo findGlobalRoute(String globalRouteId) {
56169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int count = mRoutes.size();
56269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < count; i++) {
56369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final RouteInfo route = mRoutes.get(i);
56469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (globalRouteId.equals(route.mGlobalRouteId)) {
56569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    return route;
56669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
56769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
56869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            return null;
56969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
57069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
57169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final class Client extends IMediaRouterClient.Stub {
57269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            @Override
57369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public void onStateChanged() {
57469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mHandler.post(new Runnable() {
57569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    @Override
57669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    public void run() {
57769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (Client.this == mClient) {
57869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            updateClientState();
57969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
58069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
58169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                });
58269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
58369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
584b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
5859a1de308cea2d160778fd977825f10a07b49d738Adam Powell
586b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static Static sStatic;
5879a1de308cea2d160778fd977825f10a07b49d738Adam Powell
5889a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
5899a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Route type flag for live audio.
5909a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
5919a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>A device that supports live audio routing will allow the media audio stream
5929a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * to be routed to supported destinations. This can include internal speakers or
5939a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * audio jacks on the device itself, A2DP devices, and more.</p>
5949a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
5959a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>Once initiated this routing is transparent to the application. All audio
5969a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * played on the media stream will be routed to the selected destination.</p>
5979a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
59869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_LIVE_AUDIO = 1 << 0;
5999a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6009a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
601705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * Route type flag for live video.
602705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     *
603705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * <p>A device that supports live video routing will allow a mirrored version
604705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * of the device's primary display or a customized
605705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * {@link android.app.Presentation Presentation} to be routed to supported destinations.</p>
606705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     *
607705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * <p>Once initiated, display mirroring is transparent to the application.
608705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * While remote routing is active the application may use a
609705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * {@link android.app.Presentation Presentation} to replace the mirrored view
610705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * on the external display with different content.</p>
61192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown     *
61292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown     * @see RouteInfo#getPresentationDisplay()
61392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown     * @see android.app.Presentation
614705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     */
61569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_LIVE_VIDEO = 1 << 1;
61669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
61769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
61869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Temporary interop constant to identify remote displays.
61969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide To be removed when media router API is updated.
62069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
62169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_REMOTE_DISPLAY = 1 << 2;
622705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
623705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    /**
6249a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Route type flag for application-specific usage.
6259a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
6269a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>Unlike other media route types, user routes are managed by the application.
6279a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * The MediaRouter will manage and dispatch events for user routes, but the application
6289a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * is expected to interpret the meaning of these events and perform the requested
6299a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * routing tasks.</p>
6309a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
63169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_USER = 1 << 23;
63269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
63369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static final int ROUTE_TYPE_ANY = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO
63469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            | ROUTE_TYPE_REMOTE_DISPLAY | ROUTE_TYPE_USER;
6359a1de308cea2d160778fd977825f10a07b49d738Adam Powell
63666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    /**
63766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Flag for {@link #addCallback}: Actively scan for routes while this callback
63866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * is registered.
63966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
64066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * When this flag is specified, the media router will actively scan for new
64166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * routes.  Certain routes, such as wifi display routes, may not be discoverable
64266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * except when actively scanning.  This flag is typically used when the route picker
64366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * dialog has been opened by the user to ensure that the route information is
64466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * up to date.
64566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p><p>
64666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Active scanning may consume a significant amount of power and may have intrusive
64766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * effects on wireless connectivity.  Therefore it is important that active scanning
64866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * only be requested when it is actually needed to satisfy a user request to
64966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * discover and select a new route.
65066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
65166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     */
65214507e257af5d71577574e25cbd690c4b54c9272Jeff Brown    public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1 << 0;
65366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
65466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    /**
65566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Flag for {@link #addCallback}: Do not filter route events.
65666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
65766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * When this flag is specified, the callback will be invoked for event that affect any
65869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * route even if they do not match the callback's filter.
65966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
66066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     */
66166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 1 << 1;
66266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
66369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
66469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Explicitly requests discovery.
66569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     *
66669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide Future API ported from support library.  Revisit this later.
66769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
66869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 1 << 2;
66969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
67069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
67169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Requests that discovery be performed but only if there is some other active
67269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * callback already registered.
67369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     *
67469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide Compatibility workaround for the fact that applications do not currently
67569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * request discovery explicitly (except when using the support library API).
67669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
67769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int CALLBACK_FLAG_PASSIVE_DISCOVERY = 1 << 3;
67869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
6790abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    /**
6800abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * Flag for {@link #isRouteAvailable}: Ignore the default route.
6810abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * <p>
6820abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * This flag is used to determine whether a matching non-default route is available.
6830abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * This constraint may be used to decide whether to offer the route chooser dialog
6840abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * to the user.  There is no point offering the chooser if there are no
6850abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * non-default choices.
6860abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * </p>
6870abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     *
6880abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @hide Future API ported from support library.  Revisit this later.
6890abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     */
6900abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1 << 0;
6910abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
6929a1de308cea2d160778fd977825f10a07b49d738Adam Powell    // Maps application contexts
6939a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static final HashMap<Context, MediaRouter> sRouters = new HashMap<Context, MediaRouter>();
6949a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6959a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static String typesToString(int types) {
6969a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final StringBuilder result = new StringBuilder();
6979a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if ((types & ROUTE_TYPE_LIVE_AUDIO) != 0) {
6989a1de308cea2d160778fd977825f10a07b49d738Adam Powell            result.append("ROUTE_TYPE_LIVE_AUDIO ");
6999a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
7002bb7c122ef6ce8539dfbaeb3292adcd942185f82Bryan Mawhinney        if ((types & ROUTE_TYPE_LIVE_VIDEO) != 0) {
7012bb7c122ef6ce8539dfbaeb3292adcd942185f82Bryan Mawhinney            result.append("ROUTE_TYPE_LIVE_VIDEO ");
7022bb7c122ef6ce8539dfbaeb3292adcd942185f82Bryan Mawhinney        }
70369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if ((types & ROUTE_TYPE_REMOTE_DISPLAY) != 0) {
70469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            result.append("ROUTE_TYPE_REMOTE_DISPLAY ");
70569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
7069a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if ((types & ROUTE_TYPE_USER) != 0) {
7079a1de308cea2d160778fd977825f10a07b49d738Adam Powell            result.append("ROUTE_TYPE_USER ");
7089a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
7099a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return result.toString();
7109a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
7119a1de308cea2d160778fd977825f10a07b49d738Adam Powell
712b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    /** @hide */
713b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public MediaRouter(Context context) {
714b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        synchronized (Static.class) {
715b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            if (sStatic == null) {
7168e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final Context appContext = context.getApplicationContext();
7178e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                sStatic = new Static(appContext);
7188e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                sStatic.startMonitoringRoutes(appContext);
719b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            }
7209a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
7219a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
7229a1de308cea2d160778fd977825f10a07b49d738Adam Powell
723690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    /**
7243afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * Gets the default route for playing media content on the system.
7253afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * <p>
7263afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * The system always provides a default route.
7273afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * </p>
7283afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     *
7293afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * @return The default route, which is guaranteed to never be null.
730690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     */
7313afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown    public RouteInfo getDefaultRoute() {
732705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return sStatic.mDefaultAudioVideo;
733690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
734690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
735690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    /**
7364599696591f745b3a546197d2ba7e5cfc5562484Adam Powell     * @hide for use by framework routing UI
7374599696591f745b3a546197d2ba7e5cfc5562484Adam Powell     */
7383afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown    public RouteCategory getSystemCategory() {
7394599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        return sStatic.mSystemCategory;
7404599696591f745b3a546197d2ba7e5cfc5562484Adam Powell    }
7414599696591f745b3a546197d2ba7e5cfc5562484Adam Powell
7420abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    /** @hide */
7430abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public RouteInfo getSelectedRoute() {
7440abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        return getSelectedRoute(ROUTE_TYPE_ANY);
7450abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    }
7460abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
7474599696591f745b3a546197d2ba7e5cfc5562484Adam Powell    /**
7481cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell     * Return the currently selected route for any of the given types
749690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     *
750690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * @param type route types
751690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * @return the selected route
752690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     */
753690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public RouteInfo getSelectedRoute(int type) {
7541cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        if (sStatic.mSelectedRoute != null &&
7551cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell                (sStatic.mSelectedRoute.mSupportedTypes & type) != 0) {
7561cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // If the selected route supports any of the types supplied, it's still considered
7571cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // 'selected' for that type.
7581cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            return sStatic.mSelectedRoute;
7591cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        } else if (type == ROUTE_TYPE_USER) {
7601cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // The caller specifically asked for a user route and the currently selected route
7611cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // doesn't qualify.
7621cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            return null;
7631cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        }
7641cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        // If the above didn't match and we're not specifically asking for a user route,
7651cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        // consider the default selected.
7661cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        return sStatic.mDefaultAudioVideo;
767690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
768690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
7699a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
7700abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * Returns true if there is a route that matches the specified types.
7710abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * <p>
7720abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * This method returns true if there are any available routes that match the types
7730abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * regardless of whether they are enabled or disabled.  If the
7740abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} flag is specified, then
7750abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * the method will only consider non-default routes.
7760abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * </p>
7770abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     *
7780abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @param types The types to match.
7790abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @param flags Flags to control the determination of whether a route may be available.
7800abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * May be zero or {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE}.
7810abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @return True if a matching route may be available.
7820abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     *
7830abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @hide Future API ported from support library.  Revisit this later.
7840abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     */
7850abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public boolean isRouteAvailable(int types, int flags) {
7860abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        final int count = sStatic.mRoutes.size();
7870abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        for (int i = 0; i < count; i++) {
7880abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            RouteInfo route = sStatic.mRoutes.get(i);
7890abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            if (route.matchesTypes(types)) {
7900abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                if ((flags & AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE) == 0
7910abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                        || route != sStatic.mDefaultAudioVideo) {
7920abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                    return true;
7930abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                }
7940abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            }
7950abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
7960abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
7970abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        // It doesn't look like we can find a matching route right now.
7980abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        return false;
7990abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    }
8000abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
8010abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    /**
8029a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Add a callback to listen to events about specific kinds of media routes.
8039a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * If the specified callback is already registered, its registration will be updated for any
8049a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * additional route types specified.
80566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
80666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * This is a convenience method that has the same effect as calling
80766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * {@link #addCallback(int, Callback, int)} without flags.
80866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
8099a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
8109a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param types Types of routes this callback is interested in
8119a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param cb Callback to add
8129a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
8139a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void addCallback(int types, Callback cb) {
81466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        addCallback(types, cb, 0);
81566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    }
81666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
81766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    /**
81866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Add a callback to listen to events about specific kinds of media routes.
81966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * If the specified callback is already registered, its registration will be updated for any
82066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * additional route types specified.
82166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
82266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * By default, the callback will only be invoked for events that affect routes
82366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * that match the specified selector.  The filtering may be disabled by specifying
82466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * the {@link #CALLBACK_FLAG_UNFILTERED_EVENTS} flag.
82566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
82666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     *
82766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @param types Types of routes this callback is interested in
82866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @param cb Callback to add
82966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @param flags Flags to control the behavior of the callback.
83014507e257af5d71577574e25cbd690c4b54c9272Jeff Brown     * May be zero or a combination of {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and
83166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * {@link #CALLBACK_FLAG_UNFILTERED_EVENTS}.
83266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     */
83366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    public void addCallback(int types, Callback cb, int flags) {
83466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        CallbackInfo info;
83566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        int index = findCallbackInfo(cb);
83666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        if (index >= 0) {
83766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info = sStatic.mCallbacks.get(index);
83866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info.type |= types;
83966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info.flags |= flags;
84066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        } else {
84166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info = new CallbackInfo(cb, types, flags, this);
84266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            sStatic.mCallbacks.add(info);
84366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
84469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        sStatic.updateDiscoveryRequest();
8459a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
8469a1de308cea2d160778fd977825f10a07b49d738Adam Powell
8479a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
8489a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Remove the specified callback. It will no longer receive events about media routing.
8499a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
8509a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param cb Callback to remove
8519a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
8529a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void removeCallback(Callback cb) {
85366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        int index = findCallbackInfo(cb);
85466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        if (index >= 0) {
85569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            sStatic.mCallbacks.remove(index);
85669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            sStatic.updateDiscoveryRequest();
85766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        } else {
85866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            Log.w(TAG, "removeCallback(" + cb + "): callback not registered");
85966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
86066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    }
86166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
86266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    private int findCallbackInfo(Callback cb) {
863b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        final int count = sStatic.mCallbacks.size();
8649a1de308cea2d160778fd977825f10a07b49d738Adam Powell        for (int i = 0; i < count; i++) {
86566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            final CallbackInfo info = sStatic.mCallbacks.get(i);
86666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (info.cb == cb) {
86766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown                return i;
8689a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
8699a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
87066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        return -1;
8719a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
8729a1de308cea2d160778fd977825f10a07b49d738Adam Powell
873d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell    /**
874d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     * Select the specified route to use for output of the given media types.
8753afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * <p class="note">
8763afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * As API version 18, this function may be used to select any route.
8773afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * In prior versions, this function could only be used to select user
8783afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * routes and would ignore any attempt to select a system route.
8793afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * </p>
880d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     *
881d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     * @param types type flags indicating which types this route should be used for.
882d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     *              The route must support at least a subset.
883d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     * @param route Route to select
884d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     */
8859a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void selectRoute(int types, RouteInfo route) {
88669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        selectRouteStatic(types, route, true);
8870d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    }
88869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
8890d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    /**
8900d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @hide internal use
8910d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     */
89269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void selectRouteInt(int types, RouteInfo route, boolean explicit) {
89369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        selectRouteStatic(types, route, explicit);
894b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
895b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
89669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static void selectRouteStatic(int types, RouteInfo route, boolean explicit) {
897705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final RouteInfo oldRoute = sStatic.mSelectedRoute;
898705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (oldRoute == route) return;
8990abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        if (!route.matchesTypes(types)) {
9000d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            Log.w(TAG, "selectRoute ignored; cannot select route with supported types " +
9010d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                    typesToString(route.getSupportedTypes()) + " into route types " +
9020d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                    typesToString(types));
9034ee1f55ce0f4909a7430ab44563a81852f335071Adam Powell            return;
9040d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
9059a1de308cea2d160778fd977825f10a07b49d738Adam Powell
906dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell        final RouteInfo btRoute = sStatic.mBluetoothA2dpRoute;
907dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell        if (btRoute != null && (types & ROUTE_TYPE_LIVE_AUDIO) != 0 &&
908705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                (route == btRoute || route == sStatic.mDefaultAudioVideo)) {
909dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell            try {
910dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell                sStatic.mAudioService.setBluetoothA2dpOn(route == btRoute);
911dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell            } catch (RemoteException e) {
912dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell                Log.e(TAG, "Error changing Bluetooth A2DP state", e);
913dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell            }
914dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell        }
915dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell
916705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final WifiDisplay activeDisplay =
917705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                sStatic.mDisplayService.getWifiDisplayStatus().getActiveDisplay();
918705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final boolean oldRouteHasAddress = oldRoute != null && oldRoute.mDeviceAddress != null;
919705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final boolean newRouteHasAddress = route != null && route.mDeviceAddress != null;
920705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (activeDisplay != null || oldRouteHasAddress || newRouteHasAddress) {
921705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (newRouteHasAddress && !matchesDeviceAddress(activeDisplay, route)) {
922af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (sStatic.mCanConfigureWifiDisplays) {
923af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    sStatic.mDisplayService.connectWifiDisplay(route.mDeviceAddress);
924af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                } else {
925af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    Log.e(TAG, "Cannot connect to wifi displays because this process "
926af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                            + "is not allowed to do so.");
927af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
928705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            } else if (activeDisplay != null && !newRouteHasAddress) {
929705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                sStatic.mDisplayService.disconnectWifiDisplay();
930705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
931705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
932705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
93369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        sStatic.setSelectedRoute(route, explicit);
93469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
935705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (oldRoute != null) {
936705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            dispatchRouteUnselected(types & oldRoute.getSupportedTypes(), oldRoute);
93739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (oldRoute.resolveStatusCode()) {
93839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                dispatchRouteChanged(oldRoute);
93939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
9409a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
9419a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if (route != null) {
94239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (route.resolveStatusCode()) {
94339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                dispatchRouteChanged(route);
94439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
9459a1de308cea2d160778fd977825f10a07b49d738Adam Powell            dispatchRouteSelected(types & route.getSupportedTypes(), route);
9469a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
947ce468a35b388ca46578934706b38dbae94941643Jeff Brown
948ce468a35b388ca46578934706b38dbae94941643Jeff Brown        // The behavior of active scans may depend on the currently selected route.
949ce468a35b388ca46578934706b38dbae94941643Jeff Brown        sStatic.updateDiscoveryRequest();
9509a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
9519a1de308cea2d160778fd977825f10a07b49d738Adam Powell
95269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static void selectDefaultRouteStatic() {
95369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // TODO: Be smarter about the route types here; this selects for all valid.
95469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (sStatic.mSelectedRoute != sStatic.mBluetoothA2dpRoute
955f1b2335a36fa6f2b9926f309a7c4ed29a1404ec4Jeff Brown                && sStatic.mBluetoothA2dpRoute != null && sStatic.isBluetoothA2dpOn()) {
95669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mBluetoothA2dpRoute, false);
95769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        } else {
95869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mDefaultAudioVideo, false);
95969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
96069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
96169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
9629a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
963705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * Compare the device address of a display and a route.
964705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * Nulls/no device address will match another null/no address.
965705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     */
966705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    static boolean matchesDeviceAddress(WifiDisplay display, RouteInfo info) {
967705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final boolean routeHasAddress = info != null && info.mDeviceAddress != null;
968705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (display == null && !routeHasAddress) {
969705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return true;
970705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
971705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
972705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (display != null && routeHasAddress) {
973705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return display.getDeviceAddress().equals(info.mDeviceAddress);
974705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
975705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return false;
976705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
977705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
978705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    /**
9799a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Add an app-specified route for media to the MediaRouter.
9809a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * App-specified route definitions are created using {@link #createUserRoute(RouteCategory)}
9819a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
9829a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param info Definition of the route to add
9833afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * @see #createUserRoute(RouteCategory)
9849a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #removeUserRoute(UserRouteInfo)
9859a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
9869a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void addUserRoute(UserRouteInfo info) {
9872ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        addRouteStatic(info);
9889a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
9899a1de308cea2d160778fd977825f10a07b49d738Adam Powell
990d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    /**
991d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     * @hide Framework use only
992d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     */
993d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    public void addRouteInt(RouteInfo info) {
9942ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        addRouteStatic(info);
995d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    }
996d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
9972ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell    static void addRouteStatic(RouteInfo info) {
9989a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final RouteCategory cat = info.getCategory();
999b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        if (!sStatic.mCategories.contains(cat)) {
1000b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            sStatic.mCategories.add(cat);
10019a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
1002d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        if (cat.isGroupable() && !(info instanceof RouteGroup)) {
10039a1de308cea2d160778fd977825f10a07b49d738Adam Powell            // Enforce that any added route in a groupable category must be in a group.
10049a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final RouteGroup group = new RouteGroup(info.getCategory());
1005dbbfa702a09f6d2d36dee1b552442d04a4673f89Adam Powell            group.mSupportedTypes = info.mSupportedTypes;
1006b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            sStatic.mRoutes.add(group);
1007d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteAdded(group);
1008b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            group.addRoute(info);
1009d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
10109a1de308cea2d160778fd977825f10a07b49d738Adam Powell            info = group;
1011d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        } else {
1012b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            sStatic.mRoutes.add(info);
1013d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteAdded(info);
10149a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
10159a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
10169a1de308cea2d160778fd977825f10a07b49d738Adam Powell
10179a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
10189a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Remove an app-specified route for media from the MediaRouter.
10199a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
10209a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param info Definition of the route to remove
10219a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #addUserRoute(UserRouteInfo)
10229a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
10239a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void removeUserRoute(UserRouteInfo info) {
102469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        removeRouteStatic(info);
10259a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
10269a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1027690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    /**
1028690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * Remove all app-specified routes from the MediaRouter.
1029690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     *
1030690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * @see #removeUserRoute(UserRouteInfo)
1031690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     */
1032690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public void clearUserRoutes() {
1033b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        for (int i = 0; i < sStatic.mRoutes.size(); i++) {
1034b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final RouteInfo info = sStatic.mRoutes.get(i);
1035d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            // TODO Right now, RouteGroups only ever contain user routes.
1036d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            // The code below will need to change if this assumption does.
1037d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            if (info instanceof UserRouteInfo || info instanceof RouteGroup) {
103869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                removeRouteStatic(info);
1039690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell                i--;
1040690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            }
1041690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
1042690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
1043690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
1044d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    /**
1045d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     * @hide internal use only
1046d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     */
1047d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    public void removeRouteInt(RouteInfo info) {
104869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        removeRouteStatic(info);
1049d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    }
1050d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
105169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static void removeRouteStatic(RouteInfo info) {
1052b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        if (sStatic.mRoutes.remove(info)) {
10539a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final RouteCategory removingCat = info.getCategory();
1054b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final int count = sStatic.mRoutes.size();
10559a1de308cea2d160778fd977825f10a07b49d738Adam Powell            boolean found = false;
10569a1de308cea2d160778fd977825f10a07b49d738Adam Powell            for (int i = 0; i < count; i++) {
1057b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                final RouteCategory cat = sStatic.mRoutes.get(i).getCategory();
10589a1de308cea2d160778fd977825f10a07b49d738Adam Powell                if (removingCat == cat) {
10599a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    found = true;
10609a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    break;
10619a1de308cea2d160778fd977825f10a07b49d738Adam Powell                }
10629a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
10630abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            if (info.isSelected()) {
1064d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                // Removing the currently selected route? Select the default before we remove it.
106569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                selectDefaultRouteStatic();
1066d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            }
1067690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            if (!found) {
1068b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                sStatic.mCategories.remove(removingCat);
1069690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            }
1070690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            dispatchRouteRemoved(info);
1071690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
1072690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
1073690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
10749a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
10759a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the number of {@link MediaRouter.RouteCategory categories} currently
10769a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * represented by routes known to this MediaRouter.
10779a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
10789a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the number of unique categories represented by this MediaRouter's known routes
10799a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
10809a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public int getCategoryCount() {
1081b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mCategories.size();
10829a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
10839a1de308cea2d160778fd977825f10a07b49d738Adam Powell
10849a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
10859a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the {@link MediaRouter.RouteCategory category} at the given index.
10869a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Valid indices are in the range [0-getCategoryCount).
10879a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
10889a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param index which category to return
10899a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the category at index
10909a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
10919a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteCategory getCategoryAt(int index) {
1092b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mCategories.get(index);
10939a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
10949a1de308cea2d160778fd977825f10a07b49d738Adam Powell
10959a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
10969a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the number of {@link MediaRouter.RouteInfo routes} currently known
10979a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * to this MediaRouter.
10989a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
10999a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the number of routes tracked by this router
11009a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11019a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public int getRouteCount() {
1102b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.size();
11039a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11049a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11059a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11069a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the route at the specified index.
11079a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11089a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param index index of the route to return
11099a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the route at index
11109a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11119a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteInfo getRouteAt(int index) {
1112b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.get(index);
1113b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
1114b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
1115b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static int getRouteCountStatic() {
1116b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.size();
1117b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
1118b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
1119b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static RouteInfo getRouteAtStatic(int index) {
1120b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.get(index);
11219a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11229a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11239a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11249a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Create a new user route that may be modified and registered for use by the application.
11259a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11269a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param category The category the new route will belong to
11279a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return A new UserRouteInfo for use by the application
11289a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11299a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #addUserRoute(UserRouteInfo)
11309a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #removeUserRoute(UserRouteInfo)
113169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @see #createRouteCategory(CharSequence, boolean)
11329a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11339a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public UserRouteInfo createUserRoute(RouteCategory category) {
11349a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return new UserRouteInfo(category);
11359a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11369a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11379a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11389a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Create a new route category. Each route must belong to a category.
11399a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11409a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param name Name of the new category
11419a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param isGroupable true if routes in this category may be grouped with one another
11429a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the new RouteCategory
11439a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11449a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteCategory createRouteCategory(CharSequence name, boolean isGroupable) {
11459a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return new RouteCategory(name, ROUTE_TYPE_USER, isGroupable);
11469a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11475d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
11480d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    /**
11490d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * Create a new route category. Each route must belong to a category.
11500d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     *
11510d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @param nameResId Resource ID of the name of the new category
11520d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @param isGroupable true if routes in this category may be grouped with one another
11530d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @return the new RouteCategory
11540d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     */
11550d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    public RouteCategory createRouteCategory(int nameResId, boolean isGroupable) {
11560d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        return new RouteCategory(nameResId, ROUTE_TYPE_USER, isGroupable);
11570d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    }
11589a1de308cea2d160778fd977825f10a07b49d738Adam Powell
115969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
116069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Rebinds the media router to handle routes that belong to the specified user.
116169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Requires the interact across users permission to access the routes of another user.
116269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * <p>
116369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * This method is a complete hack to work around the singleton nature of the
116469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * media router when running inside of singleton processes like QuickSettings.
116569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * This mechanism should be burned to the ground when MediaRouter is redesigned.
116669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Ideally the current user would be pulled from the Context but we need to break
116769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * down MediaRouter.Static before we can get there.
116869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * </p>
116969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     *
117069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide
117169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
117269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void rebindAsUser(int userId) {
117369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        sStatic.rebindAsUser(userId);
117469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
117569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
1176b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void updateRoute(final RouteInfo info) {
11779a1de308cea2d160778fd977825f10a07b49d738Adam Powell        dispatchRouteChanged(info);
11789a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11799a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1180b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteSelected(int type, RouteInfo info) {
118139d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
118266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1183b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteSelected(cbi.router, type, info);
11849a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
11859a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
11869a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11879a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1188b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteUnselected(int type, RouteInfo info) {
118939d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
119066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1191b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteUnselected(cbi.router, type, info);
11929a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
11939a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
11949a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11959a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1196b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteChanged(RouteInfo info) {
1197af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        dispatchRouteChanged(info, info.mSupportedTypes);
1198af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown    }
1199af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
1200af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown    static void dispatchRouteChanged(RouteInfo info, int oldSupportedTypes) {
1201af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        final int newSupportedTypes = info.mSupportedTypes;
120239d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
1203af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Reconstruct some of the history for callbacks that may not have observed
1204af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // all of the events needed to correctly interpret the current state.
1205af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // FIXME: This is a strong signal that we should deprecate route type filtering
1206af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // completely in the future because it can lead to inconsistencies in
1207af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // applications.
1208af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            final boolean oldVisibility = cbi.filterRouteEvent(oldSupportedTypes);
1209af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            final boolean newVisibility = cbi.filterRouteEvent(newSupportedTypes);
1210af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (!oldVisibility && newVisibility) {
1211af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                cbi.cb.onRouteAdded(cbi.router, info);
1212af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (info.isSelected()) {
1213af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    cbi.cb.onRouteSelected(cbi.router, newSupportedTypes, info);
1214af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
1215af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            }
1216af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (oldVisibility || newVisibility) {
1217b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteChanged(cbi.router, info);
12189a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
1219af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (oldVisibility && !newVisibility) {
1220af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (info.isSelected()) {
1221af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    cbi.cb.onRouteUnselected(cbi.router, oldSupportedTypes, info);
1222af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
1223af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                cbi.cb.onRouteRemoved(cbi.router, info);
1224af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            }
12259a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12269a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12279a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1228b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteAdded(RouteInfo info) {
122939d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
123066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1231b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteAdded(cbi.router, info);
12329a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
12339a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12349a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12359a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1236b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteRemoved(RouteInfo info) {
123739d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
123866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1239b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteRemoved(cbi.router, info);
12409a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
12419a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12429a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12439a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1244b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteGrouped(RouteInfo info, RouteGroup group, int index) {
124539d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
124666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(group)) {
1247b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteGrouped(cbi.router, info, group, index);
1248d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            }
1249d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        }
1250d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell    }
1251d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
1252b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteUngrouped(RouteInfo info, RouteGroup group) {
125339d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
125466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(group)) {
1255b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteUngrouped(cbi.router, info, group);
12569a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
12579a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12589a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12599a1de308cea2d160778fd977825f10a07b49d738Adam Powell
12608e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    static void dispatchRouteVolumeChanged(RouteInfo info) {
12618e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
126266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
12638e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                cbi.cb.onRouteVolumeChanged(cbi.router, info);
12648e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
12658e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
12668e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    }
12678e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
126892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown    static void dispatchRoutePresentationDisplayChanged(RouteInfo info) {
126992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        for (CallbackInfo cbi : sStatic.mCallbacks) {
127066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
127192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                cbi.cb.onRoutePresentationDisplayChanged(cbi.router, info);
127292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            }
127392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
127492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown    }
127592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
12768e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    static void systemVolumeChanged(int newValue) {
12778e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        final RouteInfo selectedRoute = sStatic.mSelectedRoute;
12788e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        if (selectedRoute == null) return;
12798e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
12808e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        if (selectedRoute == sStatic.mBluetoothA2dpRoute ||
1281705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                selectedRoute == sStatic.mDefaultAudioVideo) {
12828e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            dispatchRouteVolumeChanged(selectedRoute);
12838e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        } else if (sStatic.mBluetoothA2dpRoute != null) {
12848e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            try {
12858e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                dispatchRouteVolumeChanged(sStatic.mAudioService.isBluetoothA2dpOn() ?
1286705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                        sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo);
12878e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            } catch (RemoteException e) {
12888e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                Log.e(TAG, "Error checking Bluetooth A2DP state to report volume change", e);
12898e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
12908e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        } else {
1291705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            dispatchRouteVolumeChanged(sStatic.mDefaultAudioVideo);
1292705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1293705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1294705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
129575af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    static void updateWifiDisplayStatus(WifiDisplayStatus status) {
129675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        WifiDisplay[] displays;
1297615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell        WifiDisplay activeDisplay;
129875af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        if (status.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) {
129975af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            displays = status.getDisplays();
130075af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            activeDisplay = status.getActiveDisplay();
1301af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
1302af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Only the system is able to connect to wifi display routes.
1303af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // The display manager will enforce this with a permission check but it
1304af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // still publishes information about all available displays.
1305af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Filter the list down to just the active display.
1306af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (!sStatic.mCanConfigureWifiDisplays) {
1307af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (activeDisplay != null) {
1308af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    displays = new WifiDisplay[] { activeDisplay };
1309af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                } else {
1310af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    displays = WifiDisplay.EMPTY_ARRAY;
1311af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
1312af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            }
1313615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell        } else {
131475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            displays = WifiDisplay.EMPTY_ARRAY;
1315615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell            activeDisplay = null;
1316615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell        }
1317ce468a35b388ca46578934706b38dbae94941643Jeff Brown        String activeDisplayAddress = activeDisplay != null ?
1318ce468a35b388ca46578934706b38dbae94941643Jeff Brown                activeDisplay.getDeviceAddress() : null;
1319705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
132075af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        // Add or update routes.
132175af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        for (int i = 0; i < displays.length; i++) {
132275af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            final WifiDisplay d = displays[i];
132375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            if (shouldShowWifiDisplay(d, activeDisplay)) {
1324ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                RouteInfo route = findWifiDisplayRoute(d);
1325ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                if (route == null) {
132675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                    route = makeWifiDisplayRoute(d, status);
1327ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                    addRouteStatic(route);
1328ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                } else {
1329ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    String address = d.getDeviceAddress();
1330ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    boolean disconnected = !address.equals(activeDisplayAddress)
1331ce468a35b388ca46578934706b38dbae94941643Jeff Brown                            && address.equals(sStatic.mPreviousActiveWifiDisplayAddress);
1332ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    updateWifiDisplayRoute(route, d, status, disconnected);
1333ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                }
1334ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                if (d.equals(activeDisplay)) {
133569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    selectRouteStatic(route.getSupportedTypes(), route, false);
1336ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                }
13372ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            }
1338705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
133975af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown
134075af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        // Remove stale routes.
134175af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        for (int i = sStatic.mRoutes.size(); i-- > 0; ) {
134275af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            RouteInfo route = sStatic.mRoutes.get(i);
134375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            if (route.mDeviceAddress != null) {
134475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                WifiDisplay d = findWifiDisplay(displays, route.mDeviceAddress);
134575af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                if (d == null || !shouldShowWifiDisplay(d, activeDisplay)) {
134675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                    removeRouteStatic(route);
1347ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                }
1348705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1349705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1350ce468a35b388ca46578934706b38dbae94941643Jeff Brown
1351ce468a35b388ca46578934706b38dbae94941643Jeff Brown        // Remember the current active wifi display address so that we can infer disconnections.
1352ce468a35b388ca46578934706b38dbae94941643Jeff Brown        // TODO: This hack will go away once all of this is moved into the media router service.
1353ce468a35b388ca46578934706b38dbae94941643Jeff Brown        sStatic.mPreviousActiveWifiDisplayAddress = activeDisplayAddress;
135475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    }
1355705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
135675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    private static boolean shouldShowWifiDisplay(WifiDisplay d, WifiDisplay activeDisplay) {
135775af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        return d.isRemembered() || d.equals(activeDisplay);
1358705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1359705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1360ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    static int getWifiDisplayStatusCode(WifiDisplay d, WifiDisplayStatus wfdStatus) {
136169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        int newStatus;
1362ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        if (wfdStatus.getScanState() == WifiDisplayStatus.SCAN_STATE_SCANNING) {
1363ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang            newStatus = RouteInfo.STATUS_SCANNING;
1364ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        } else if (d.isAvailable()) {
1365ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang            newStatus = d.canConnect() ?
1366ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                    RouteInfo.STATUS_AVAILABLE: RouteInfo.STATUS_IN_USE;
1367705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        } else {
1368705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            newStatus = RouteInfo.STATUS_NOT_AVAILABLE;
1369705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1370705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1371ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        if (d.equals(wfdStatus.getActiveDisplay())) {
1372ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang            final int activeState = wfdStatus.getActiveDisplayState();
1373705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            switch (activeState) {
1374705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                case WifiDisplayStatus.DISPLAY_STATE_CONNECTED:
137569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    newStatus = RouteInfo.STATUS_CONNECTED;
1376705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    break;
1377705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                case WifiDisplayStatus.DISPLAY_STATE_CONNECTING:
1378705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    newStatus = RouteInfo.STATUS_CONNECTING;
1379705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    break;
1380705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                case WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED:
1381705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    Log.e(TAG, "Active display is not connected!");
1382705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    break;
1383705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1384705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1385705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1386ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        return newStatus;
1387ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    }
1388ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
1389ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    static boolean isWifiDisplayEnabled(WifiDisplay d, WifiDisplayStatus wfdStatus) {
1390ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        return d.isAvailable() && (d.canConnect() || d.equals(wfdStatus.getActiveDisplay()));
1391ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    }
1392ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
1393ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    static RouteInfo makeWifiDisplayRoute(WifiDisplay display, WifiDisplayStatus wfdStatus) {
1394ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        final RouteInfo newRoute = new RouteInfo(sStatic.mSystemCategory);
1395ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mDeviceAddress = display.getDeviceAddress();
139669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        newRoute.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO
139769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                | ROUTE_TYPE_REMOTE_DISPLAY;
1398ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mVolumeHandling = RouteInfo.PLAYBACK_VOLUME_FIXED;
1399ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mPlaybackType = RouteInfo.PLAYBACK_TYPE_REMOTE;
1400ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
140139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        newRoute.setRealStatusCode(getWifiDisplayStatusCode(display, wfdStatus));
1402ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mEnabled = isWifiDisplayEnabled(display, wfdStatus);
1403ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mName = display.getFriendlyDisplayName();
1404ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mDescription = sStatic.mResources.getText(
1405ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                com.android.internal.R.string.wireless_display_route_description);
14065830b0b33618940d65197cec99d697b21908fec8Chong Zhang        newRoute.updatePresentationDisplay();
1407ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        return newRoute;
1408ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    }
1409ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
1410ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    private static void updateWifiDisplayRoute(
1411ce468a35b388ca46578934706b38dbae94941643Jeff Brown            RouteInfo route, WifiDisplay display, WifiDisplayStatus wfdStatus,
1412ce468a35b388ca46578934706b38dbae94941643Jeff Brown            boolean disconnected) {
1413ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        boolean changed = false;
14142444ae7e2b8658a4a90f996e678423558744b4a2Jeff Brown        final String newName = display.getFriendlyDisplayName();
14152444ae7e2b8658a4a90f996e678423558744b4a2Jeff Brown        if (!route.getName().equals(newName)) {
1416705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            route.mName = newName;
1417705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            changed = true;
1418705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1419705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1420ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        boolean enabled = isWifiDisplayEnabled(display, wfdStatus);
1421ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        changed |= route.mEnabled != enabled;
1422ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        route.mEnabled = enabled;
1423705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
142439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        changed |= route.setRealStatusCode(getWifiDisplayStatusCode(display, wfdStatus));
1425705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1426705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (changed) {
1427705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            dispatchRouteChanged(route);
1428705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1429705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1430ce468a35b388ca46578934706b38dbae94941643Jeff Brown        if ((!enabled || disconnected) && route.isSelected()) {
1431705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            // Oops, no longer available. Reselect the default.
143269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            selectDefaultRouteStatic();
1433705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1434705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1435705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
143675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    private static WifiDisplay findWifiDisplay(WifiDisplay[] displays, String deviceAddress) {
1437705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        for (int i = 0; i < displays.length; i++) {
143875af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            final WifiDisplay d = displays[i];
143975af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            if (d.getDeviceAddress().equals(deviceAddress)) {
144075af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                return d;
1441705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1442705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1443705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return null;
1444705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1445705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1446705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    private static RouteInfo findWifiDisplayRoute(WifiDisplay d) {
1447705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final int count = sStatic.mRoutes.size();
1448705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        for (int i = 0; i < count; i++) {
1449705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            final RouteInfo info = sStatic.mRoutes.get(i);
1450705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (d.getDeviceAddress().equals(info.mDeviceAddress)) {
1451705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                return info;
1452705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1453705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1454705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return null;
14558e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    }
14568e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
14579a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
14589a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a media route.
14599a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
1460b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class RouteInfo {
14619a1de308cea2d160778fd977825f10a07b49d738Adam Powell        CharSequence mName;
14620d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        int mNameResId;
146356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        CharSequence mDescription;
14649a1de308cea2d160778fd977825f10a07b49d738Adam Powell        private CharSequence mStatus;
14659a1de308cea2d160778fd977825f10a07b49d738Adam Powell        int mSupportedTypes;
14669a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteGroup mGroup;
14679a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final RouteCategory mCategory;
1468ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        Drawable mIcon;
14691357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        // playback information
14701357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mPlaybackType = PLAYBACK_TYPE_LOCAL;
14711357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
14721357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
14731357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
14741357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mPlaybackStream = AudioManager.STREAM_MUSIC;
14751357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        VolumeCallbackInfo mVcb;
147692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        Display mPresentationDisplay;
14775830b0b33618940d65197cec99d697b21908fec8Chong Zhang        int mPresentationDisplayId = -1;
14789a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1479705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        String mDeviceAddress;
1480705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        boolean mEnabled = true;
1481705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
148269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // An id by which the route is known to the media router service.
148369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // Null if this route only exists as an artifact within this process.
148469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        String mGlobalRouteId;
148569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
1486705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        // A predetermined connection status that can override mStatus
148739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private int mRealStatusCode;
148839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private int mResolvedStatusCode;
1489705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
14902ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_NONE = 0;
14912ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_SCANNING = 1;
14922ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_CONNECTING = 2;
14932ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_AVAILABLE = 3;
14942ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_NOT_AVAILABLE = 4;
1495ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        /** @hide */ public static final int STATUS_IN_USE = 5;
149669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        /** @hide */ public static final int STATUS_CONNECTED = 6;
1497705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1498b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        private Object mTag;
1499b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
15001357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
15011357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * The default playback type, "local", indicating the presentation of the media is happening
15021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * on the same device (e.g. a phone, a tablet) as where it is controlled from.
150369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getPlaybackType()
15041357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
15051357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_TYPE_LOCAL = 0;
15061357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
15071357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * A playback type indicating the presentation of the media is happening on
15081357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * a different device (i.e. the remote device) than where it is controlled from.
150969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getPlaybackType()
15101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
15111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_TYPE_REMOTE = 1;
15121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
15131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Playback information indicating the playback volume is fixed, i.e. it cannot be
15141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * controlled from this object. An example of fixed playback volume is a remote player,
15151357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
15161357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * than attenuate at the source.
151769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getVolumeHandling()
15181357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
15191357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_VOLUME_FIXED = 0;
15201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
15211357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Playback information indicating the playback volume is variable and can be controlled
15221357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * from this object.
152369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getVolumeHandling()
15241357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
15251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_VOLUME_VARIABLE = 1;
15261357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
15279a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteInfo(RouteCategory category) {
15289a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mCategory = category;
15299a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
15309a1de308cea2d160778fd977825f10a07b49d738Adam Powell
15319a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
153256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Gets the user-visible name of the route.
153356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
153456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route name identifies the destination represented by the route.
153556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied name, an alias, or device serial number.
153656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
153756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
153856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The user-visible name of a media route.  This is the string presented
15399a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * to users who may select this as the active route.
15409a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
15419a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getName() {
15420d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(sStatic.mResources);
15430d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
154456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
15450d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        /**
154656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Return the properly localized/resource user-visible name of this route.
154756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
154856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route name identifies the destination represented by the route.
154956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied name, an alias, or device serial number.
155056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
155156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
15520d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @param context Context used to resolve the correct configuration to load
155356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The user-visible name of a media route.  This is the string presented
15540d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * to users who may select this as the active route.
15550d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         */
15560d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public CharSequence getName(Context context) {
15570d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(context.getResources());
15580d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
155956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
15600d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        CharSequence getName(Resources res) {
15610d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            if (mNameResId != 0) {
15620d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                return mName = res.getText(mNameResId);
15630d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            }
15649a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mName;
15659a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
15669a1de308cea2d160778fd977825f10a07b49d738Adam Powell
15679a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
156856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Gets the user-visible description of the route.
156956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
157056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route description describes the kind of destination represented by the route.
157156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied string, a model number or brand of device.
157256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
157356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
157456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The description of the route, or null if none.
157556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         */
157656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        public CharSequence getDescription() {
157756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown            return mDescription;
157856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        }
157956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
158056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        /**
158156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The user-visible status for a media route. This may include a description
15829a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * of the currently playing media, if available.
15839a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
15849a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getStatus() {
15859a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mStatus;
15869a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
15879a1de308cea2d160778fd977825f10a07b49d738Adam Powell
15889a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
1589705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * Set this route's status by predetermined status code. If the caller
1590705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * should dispatch a route changed event this call will return true;
1591705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         */
159239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        boolean setRealStatusCode(int statusCode) {
159339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (mRealStatusCode != statusCode) {
159439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                mRealStatusCode = statusCode;
159539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                return resolveStatusCode();
159639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
159739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return false;
159839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        }
159939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown
160039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        /**
160139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown         * Resolves the status code whenever the real status code or selection state
160239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown         * changes.
160339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown         */
160439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        boolean resolveStatusCode() {
160539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            int statusCode = mRealStatusCode;
160639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (isSelected()) {
1607705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                switch (statusCode) {
160839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // If the route is selected and its status appears to be between states
160939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // then report it as connecting even though it has not yet had a chance
161039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // to officially move into the CONNECTING state.  Note that routes in
161139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // the NONE state are assumed to not require an explicit connection
161239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // lifecycle whereas those that are AVAILABLE are assumed to have
161339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // to eventually proceed to CONNECTED.
1614705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    case STATUS_AVAILABLE:
161539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    case STATUS_SCANNING:
161639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                        statusCode = STATUS_CONNECTING;
161769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        break;
1618705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                }
1619705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
162039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (mResolvedStatusCode == statusCode) {
162139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                return false;
162239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
162339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown
162439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            mResolvedStatusCode = statusCode;
162539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            int resId;
162639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            switch (statusCode) {
162739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_SCANNING:
162839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_scanning;
162939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
163039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_CONNECTING:
163139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_connecting;
163239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
163339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_AVAILABLE:
163439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_available;
163539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
163639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_NOT_AVAILABLE:
163739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_not_available;
163839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
163939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_IN_USE:
164039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_in_use;
164139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
164239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_CONNECTED:
164339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_NONE:
164439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                default:
164539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = 0;
164639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
164739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
164839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            mStatus = resId != 0 ? sStatic.mResources.getText(resId) : null;
164939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return true;
1650705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1651705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1652705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        /**
16532ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell         * @hide
16542ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell         */
16552ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        public int getStatusCode() {
165639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return mResolvedStatusCode;
16572ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        }
16582ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell
16592ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /**
16609a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return A media type flag set describing which types this route supports.
16619a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
16629a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int getSupportedTypes() {
16639a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mSupportedTypes;
16649a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
16659a1de308cea2d160778fd977825f10a07b49d738Adam Powell
16660abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
16670abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public boolean matchesTypes(int types) {
16680abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            return (mSupportedTypes & types) != 0;
16690abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
16700abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
16719a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
16729a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return The group that this route belongs to.
16739a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
16749a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public RouteGroup getGroup() {
16759a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mGroup;
16769a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
16779a1de308cea2d160778fd977825f10a07b49d738Adam Powell
16789a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
16799a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return the category this route belongs to.
16809a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
16819a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public RouteCategory getCategory() {
16829a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mCategory;
16839a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
16849a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1685ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
1686ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Get the icon representing this route.
1687ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * This icon will be used in picker UIs if available.
1688ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
1689ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @return the icon representing this route or null if no icon is available
1690ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
1691ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public Drawable getIconDrawable() {
1692ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            return mIcon;
1693ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
1694ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
1695b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        /**
1696b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * Set an application-specific tag object for this route.
1697b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * The application may use this to store arbitrary data associated with the
1698b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * route for internal tracking.
1699b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         *
1700b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * <p>Note that the lifespan of a route may be well past the lifespan of
1701b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * an Activity or other Context; take care that objects you store here
1702b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * will not keep more data in memory alive than you intend.</p>
1703b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         *
1704b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * @param tag Arbitrary, app-specific data for this route to hold for later use
1705b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         */
1706b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        public void setTag(Object tag) {
1707b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            mTag = tag;
1708130b4572d1f3df702e5b296a655d15a41f6d4c66Adam Powell            routeUpdated();
1709b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        }
1710b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
1711b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        /**
1712b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * @return The tag object previously set by the application
1713b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * @see #setTag(Object)
1714b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         */
1715b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        public Object getTag() {
1716b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            return mTag;
1717b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        }
1718b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
17191357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
17201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the type of playback associated with this route
17211357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setPlaybackType(int)
17221357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
17231357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getPlaybackType() {
17241357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            return mPlaybackType;
17251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
17261357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
17271357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
17281357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the stream over which the playback associated with this route is performed
17291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setPlaybackStream(int)
17301357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
17311357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getPlaybackStream() {
17321357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            return mPlaybackStream;
17331357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
17341357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
17351357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
17368e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Return the current volume for this route. Depending on the route, this may only
17378e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * be valid if the route is currently selected.
17388e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         *
17391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the volume at which the playback associated with this route is performed
17401357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setVolume(int)
17411357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
17421357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getVolume() {
17431357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
17441357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                int vol = 0;
17451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                try {
17461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    vol = sStatic.mAudioService.getStreamVolume(mPlaybackStream);
17471357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                } catch (RemoteException e) {
17481357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    Log.e(TAG, "Error getting local stream volume", e);
17491357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                }
17501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return vol;
17511357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            } else {
17521357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return mVolume;
17531357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
17541357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
17551357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
17561357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
17578e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Request a volume change for this route.
17588e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param volume value between 0 and getVolumeMax
17598e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         */
17608e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestSetVolume(int volume) {
17618e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
17628e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                try {
1763ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                    sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0,
1764ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                            ActivityThread.currentPackageName());
17658e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                } catch (RemoteException e) {
17668e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Error setting local stream volume", e);
17678e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
17688e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            } else {
176969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                sStatic.requestSetVolume(this, volume);
17708e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
17718e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
17728e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
17738e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        /**
17748e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Request an incremental volume update for this route.
17758e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param direction Delta to apply to the current volume
17768e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         */
17778e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestUpdateVolume(int direction) {
17788e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
17798e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                try {
17808e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    final int volume =
17818e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                            Math.max(0, Math.min(getVolume() + direction, getVolumeMax()));
1782ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                    sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0,
1783ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                            ActivityThread.currentPackageName());
17848e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                } catch (RemoteException e) {
17858e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Error setting local stream volume", e);
17868e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
17878e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            } else {
178869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                sStatic.requestUpdateVolume(this, direction);
17898e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
17908e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
17918e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
17928e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        /**
17931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the maximum volume at which the playback associated with this route is performed
17941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setVolumeMax(int)
17951357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
17961357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getVolumeMax() {
17971357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
17981357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                int volMax = 0;
17991357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                try {
18001357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    volMax = sStatic.mAudioService.getStreamMaxVolume(mPlaybackStream);
18011357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                } catch (RemoteException e) {
18021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    Log.e(TAG, "Error getting local stream volume", e);
18031357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                }
18041357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return volMax;
18051357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            } else {
18061357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return mVolumeMax;
18071357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
18081357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
18091357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
18101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
18111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return how volume is handling on the route
18121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setVolumeHandling(int)
18131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
18141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getVolumeHandling() {
18151357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            return mVolumeHandling;
18161357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
18171357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
1818705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        /**
181992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * Gets the {@link Display} that should be used by the application to show
182092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * a {@link android.app.Presentation} on an external display when this route is selected.
182192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * Depending on the route, this may only be valid if the route is currently
182292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * selected.
182392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * <p>
182492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * The preferred presentation display may change independently of the route
182592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * being selected or unselected.  For example, the presentation display
182692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * of the default system route may change when an external HDMI display is connected
182792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * or disconnected even though the route itself has not changed.
182892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p><p>
182992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * This method may return null if there is no external display associated with
183092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * the route or if the display is not ready to show UI yet.
183192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p><p>
183292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * The application should listen for changes to the presentation display
183392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * using the {@link Callback#onRoutePresentationDisplayChanged} callback and
183492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * show or dismiss its {@link android.app.Presentation} accordingly when the display
183592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * becomes available or is removed.
183692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p><p>
183792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * This method only makes sense for {@link #ROUTE_TYPE_LIVE_VIDEO live video} routes.
183892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p>
183992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
184092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @return The preferred presentation display to use when this route is
184192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * selected or null if none.
184292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
184392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @see #ROUTE_TYPE_LIVE_VIDEO
184492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @see android.app.Presentation
184592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         */
184692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public Display getPresentationDisplay() {
184792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            return mPresentationDisplay;
184892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
184992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
18505830b0b33618940d65197cec99d697b21908fec8Chong Zhang        boolean updatePresentationDisplay() {
18515830b0b33618940d65197cec99d697b21908fec8Chong Zhang            Display display = choosePresentationDisplay();
18525830b0b33618940d65197cec99d697b21908fec8Chong Zhang            if (mPresentationDisplay != display) {
18535830b0b33618940d65197cec99d697b21908fec8Chong Zhang                mPresentationDisplay = display;
18545830b0b33618940d65197cec99d697b21908fec8Chong Zhang                return true;
18555830b0b33618940d65197cec99d697b21908fec8Chong Zhang            }
18565830b0b33618940d65197cec99d697b21908fec8Chong Zhang            return false;
18575830b0b33618940d65197cec99d697b21908fec8Chong Zhang        }
18585830b0b33618940d65197cec99d697b21908fec8Chong Zhang
18595830b0b33618940d65197cec99d697b21908fec8Chong Zhang        private Display choosePresentationDisplay() {
18605830b0b33618940d65197cec99d697b21908fec8Chong Zhang            if ((mSupportedTypes & ROUTE_TYPE_LIVE_VIDEO) != 0) {
18615830b0b33618940d65197cec99d697b21908fec8Chong Zhang                Display[] displays = sStatic.getAllPresentationDisplays();
18625830b0b33618940d65197cec99d697b21908fec8Chong Zhang
18635830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // Ensure that the specified display is valid for presentations.
18645830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // This check will normally disallow the default display unless it was
18655830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // configured as a presentation display for some reason.
18665830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (mPresentationDisplayId >= 0) {
18675830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    for (Display display : displays) {
18685830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        if (display.getDisplayId() == mPresentationDisplayId) {
18695830b0b33618940d65197cec99d697b21908fec8Chong Zhang                            return display;
18705830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        }
18715830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    }
18725830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    return null;
18735830b0b33618940d65197cec99d697b21908fec8Chong Zhang                }
18745830b0b33618940d65197cec99d697b21908fec8Chong Zhang
18755830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // Find the indicated Wifi display by its address.
18765830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (mDeviceAddress != null) {
18775830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    for (Display display : displays) {
18785830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        if (display.getType() == Display.TYPE_WIFI
18795830b0b33618940d65197cec99d697b21908fec8Chong Zhang                                && mDeviceAddress.equals(display.getAddress())) {
18805830b0b33618940d65197cec99d697b21908fec8Chong Zhang                            return display;
18815830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        }
18825830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    }
18835830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    return null;
18845830b0b33618940d65197cec99d697b21908fec8Chong Zhang                }
18855830b0b33618940d65197cec99d697b21908fec8Chong Zhang
18865830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // For the default route, choose the first presentation display from the list.
18875830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (this == sStatic.mDefaultAudioVideo && displays.length > 0) {
18885830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    return displays[0];
18895830b0b33618940d65197cec99d697b21908fec8Chong Zhang                }
18905830b0b33618940d65197cec99d697b21908fec8Chong Zhang            }
18915830b0b33618940d65197cec99d697b21908fec8Chong Zhang            return null;
18925830b0b33618940d65197cec99d697b21908fec8Chong Zhang        }
18935830b0b33618940d65197cec99d697b21908fec8Chong Zhang
189475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        /** @hide */
189575af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        public String getDeviceAddress() {
189675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            return mDeviceAddress;
189775af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        }
189875af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown
189992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        /**
1900a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * Returns true if this route is enabled and may be selected.
1901a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         *
1902a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * @return True if this route is enabled.
1903705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         */
1904705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        public boolean isEnabled() {
1905705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return mEnabled;
1906705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1907705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1908a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown        /**
1909a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * Returns true if the route is in the process of connecting and is not
1910a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * yet ready for use.
1911a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         *
1912a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * @return True if this route is in the process of connecting.
1913a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         */
1914a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown        public boolean isConnecting() {
191539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return mResolvedStatusCode == STATUS_CONNECTING;
19160abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
19170abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
19180abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
19190abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public boolean isSelected() {
19200abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            return this == sStatic.mSelectedRoute;
19210abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
19220abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
19230abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
19240abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public boolean isDefault() {
19250abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            return this == sStatic.mDefaultAudioVideo;
19260abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
19270abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
19280abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
19290abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public void select() {
19300abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            selectRouteStatic(mSupportedTypes, this, true);
1931a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown        }
1932a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown
19339a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void setStatusInt(CharSequence status) {
19349a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (!status.equals(mStatus)) {
19359a1de308cea2d160778fd977825f10a07b49d738Adam Powell                mStatus = status;
19369a1de308cea2d160778fd977825f10a07b49d738Adam Powell                if (mGroup != null) {
19379a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    mGroup.memberStatusChanged(this, status);
19389a1de308cea2d160778fd977825f10a07b49d738Adam Powell                }
19399a1de308cea2d160778fd977825f10a07b49d738Adam Powell                routeUpdated();
19409a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
19419a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
19429a1de308cea2d160778fd977825f10a07b49d738Adam Powell
19431357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        final IRemoteVolumeObserver.Stub mRemoteVolObserver = new IRemoteVolumeObserver.Stub() {
194469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            @Override
19451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            public void dispatchRemoteVolumeUpdate(final int direction, final int value) {
19461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                sStatic.mHandler.post(new Runnable() {
19471357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    @Override
19481357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    public void run() {
19491357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                        if (mVcb != null) {
19501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                            if (direction != 0) {
19511357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                                mVcb.vcb.onVolumeUpdateRequest(mVcb.route, direction);
19521357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                            } else {
19531357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                                mVcb.vcb.onVolumeSetRequest(mVcb.route, value);
19541357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                            }
19551357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                        }
19561357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    }
19571357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                });
19581357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
19591357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        };
19601357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
19619a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void routeUpdated() {
19629a1de308cea2d160778fd977825f10a07b49d738Adam Powell            updateRoute(this);
19639a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
19649a1de308cea2d160778fd977825f10a07b49d738Adam Powell
19659a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
19669a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public String toString() {
1967d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            String supportedTypes = typesToString(getSupportedTypes());
196892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            return getClass().getSimpleName() + "{ name=" + getName() +
196956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown                    ", description=" + getDescription() +
197092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                    ", status=" + getStatus() +
197192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                    ", category=" + getCategory() +
197292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                    ", supportedTypes=" + supportedTypes +
197369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    ", presentationDisplay=" + mPresentationDisplay + " }";
19749a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
19759a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
19769a1de308cea2d160778fd977825f10a07b49d738Adam Powell
19779a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
19789a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a route that the application may define and modify.
19798e37a85bf3dc39519942698dc90a3951306b934bAdam Powell     * A user route defaults to {@link RouteInfo#PLAYBACK_TYPE_REMOTE} and
19808e37a85bf3dc39519942698dc90a3951306b934bAdam Powell     * {@link RouteInfo#PLAYBACK_VOLUME_FIXED}.
19819a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
19829a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see MediaRouter.RouteInfo
19839a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
1984b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class UserRouteInfo extends RouteInfo {
1985ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        RemoteControlClient mRcc;
19865d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        SessionVolumeProvider mSvp;
19879a1de308cea2d160778fd977825f10a07b49d738Adam Powell
19889a1de308cea2d160778fd977825f10a07b49d738Adam Powell        UserRouteInfo(RouteCategory category) {
19899a1de308cea2d160778fd977825f10a07b49d738Adam Powell            super(category);
19909a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mSupportedTypes = ROUTE_TYPE_USER;
19918e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mPlaybackType = PLAYBACK_TYPE_REMOTE;
19928e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeHandling = PLAYBACK_VOLUME_FIXED;
19939a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
19949a1de308cea2d160778fd977825f10a07b49d738Adam Powell
19959a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
19969a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Set the user-visible name of this route.
19979a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param name Name to display to the user to describe this route
19989a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
19999a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void setName(CharSequence name) {
20009a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = name;
20019a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
20029a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
20035d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
20040d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        /**
20050d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * Set the user-visible name of this route.
200656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
200756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route name identifies the destination represented by the route.
200856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied name, an alias, or device serial number.
200956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
201056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
20110d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @param resId Resource ID of the name to display to the user to describe this route
20120d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         */
20130d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public void setName(int resId) {
20140d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mNameResId = resId;
20150d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mName = null;
20160d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            routeUpdated();
20170d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
20189a1de308cea2d160778fd977825f10a07b49d738Adam Powell
20199a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
202056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Set the user-visible description of this route.
202156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
202256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route description describes the kind of destination represented by the route.
202356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied string, a model number or brand of device.
202456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
202556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
202656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @param description The description of the route, or null if none.
202756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         */
202856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        public void setDescription(CharSequence description) {
202956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown            mDescription = description;
203056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown            routeUpdated();
203156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        }
203256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
203356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        /**
20349a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Set the current user-visible status for this route.
20359a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param status Status to display to the user to describe what the endpoint
20369a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * of this route is currently doing
20379a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
20389a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void setStatus(CharSequence status) {
20399a1de308cea2d160778fd977825f10a07b49d738Adam Powell            setStatusInt(status);
20409a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2041ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2042ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2043ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set the RemoteControlClient responsible for reporting playback info for this
2044ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * user route.
2045ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
2046ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * <p>If this route manages remote playback, the data exposed by this
2047ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * RemoteControlClient will be used to reflect and update information
2048ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * such as route volume info in related UIs.</p>
2049ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
20501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * <p>The RemoteControlClient must have been previously registered with
20511357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}.</p>
20521357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *
2053ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @param rcc RemoteControlClient associated with this route
2054ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
2055ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setRemoteControlClient(RemoteControlClient rcc) {
2056ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            mRcc = rcc;
20571357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            updatePlaybackInfoOnRcc();
2058ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2059ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2060ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
20614599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         * Retrieve the RemoteControlClient associated with this route, if one has been set.
20624599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         *
20634599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         * @return the RemoteControlClient associated with this route
20644599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         * @see #setRemoteControlClient(RemoteControlClient)
20654599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         */
20664599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        public RemoteControlClient getRemoteControlClient() {
20674599696591f745b3a546197d2ba7e5cfc5562484Adam Powell            return mRcc;
20684599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        }
20694599696591f745b3a546197d2ba7e5cfc5562484Adam Powell
20704599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        /**
2071ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this route.
2072ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2073ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
2074ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @param icon icon drawable to use to represent this route
2075ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
2076ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setIconDrawable(Drawable icon) {
2077ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            mIcon = icon;
2078ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2079ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2080ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2081ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this route.
2082ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2083ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
208471c69897ad0a55d590698bfa399bfe99c763b9dbAdam Powell         * @param resId Resource ID of an icon drawable to use to represent this route
2085ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
2086ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setIconResource(int resId) {
2087ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            setIconDrawable(sStatic.mResources.getDrawable(resId));
2088ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
20891357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
20901357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
20911357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Set a callback to be notified of volume update requests
20921357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param vcb
20931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
20941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolumeCallback(VolumeCallback vcb) {
20951357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            mVcb = new VolumeCallbackInfo(vcb, this);
20961357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
20971357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
20981357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
20991357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines whether playback associated with this route is "local"
21001357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *    ({@link RouteInfo#PLAYBACK_TYPE_LOCAL}) or "remote"
21011357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *    ({@link RouteInfo#PLAYBACK_TYPE_REMOTE}).
21021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param type
21031357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
21041357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setPlaybackType(int type) {
21051357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackType != type) {
21061357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mPlaybackType = type;
2107430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
21081357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
21091357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
21101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
21111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
21121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines whether volume for the playback associated with this route is fixed
21131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * ({@link RouteInfo#PLAYBACK_VOLUME_FIXED}) or can modified
21141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * ({@link RouteInfo#PLAYBACK_VOLUME_VARIABLE}).
21151357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volumeHandling
21161357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
21171357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolumeHandling(int volumeHandling) {
21181357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mVolumeHandling != volumeHandling) {
21191357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mVolumeHandling = volumeHandling;
2120430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
21211357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
21221357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
21231357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
21241357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
21251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines at what volume the playback associated with this route is performed (for user
21261357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * feedback purposes). This information is only used when the playback is not local.
21271357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volume
21281357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
21291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolume(int volume) {
21308e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            volume = Math.max(0, Math.min(volume, getVolumeMax()));
21311357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mVolume != volume) {
21321357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mVolume = volume;
2133430fc48865e5a371b08f180390946b96d73848feRoboErik                if (mSvp != null) {
21340d0f67f5ee5f939a1b611bc4583212707afd9beeRoboErik                    mSvp.setCurrentVolume(mVolume);
21355d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
21368e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                dispatchRouteVolumeChanged(this);
2137f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                if (mGroup != null) {
2138f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                    mGroup.memberVolumeChanged(this);
2139f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                }
21408e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
21418e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
21428e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
21438e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
21448e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestSetVolume(int volume) {
21458e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mVolumeHandling == PLAYBACK_VOLUME_VARIABLE) {
21468e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (mVcb == null) {
21478e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Cannot requestSetVolume on user route - no volume callback set");
21488e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    return;
21498e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
21508e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVcb.vcb.onVolumeSetRequest(this, volume);
21518e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
21528e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
21538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
21548e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
21558e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestUpdateVolume(int direction) {
21568e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mVolumeHandling == PLAYBACK_VOLUME_VARIABLE) {
21578e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (mVcb == null) {
21588e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Cannot requestChangeVolume on user route - no volumec callback set");
21598e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    return;
21608e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
21618e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVcb.vcb.onVolumeUpdateRequest(this, direction);
21621357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
21631357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
21641357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
21651357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
21661357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines the maximum volume at which the playback associated with this route is performed
21671357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * (for user feedback purposes). This information is only used when the playback is not
21681357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * local.
21691357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volumeMax
21701357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
21711357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolumeMax(int volumeMax) {
21721357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mVolumeMax != volumeMax) {
21731357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mVolumeMax = volumeMax;
2174430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
21751357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
21761357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
21771357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
21781357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
21791357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines over what stream type the media is presented.
21801357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param stream
21811357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
21821357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setPlaybackStream(int stream) {
21831357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackStream != stream) {
21841357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mPlaybackStream = stream;
2185430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
21861357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
21871357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
21881357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
21891357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        private void updatePlaybackInfoOnRcc() {
2190430fc48865e5a371b08f180390946b96d73848feRoboErik            configureSessionVolume();
21915d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        }
21925d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
21935d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        private void configureSessionVolume() {
21945d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            if (mRcc == null) {
21955d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                if (DEBUG) {
21965d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    Log.d(TAG, "No Rcc to configure volume for route " + mName);
21975d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
21985d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                return;
21995d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
22005d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            MediaSession session = mRcc.getMediaSession();
22015d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            if (session == null) {
22025d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                if (DEBUG) {
22035d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    Log.d(TAG, "Rcc has no session to configure volume");
22045d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
22055d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                return;
22065d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
22075d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            if (mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
2208ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik                int volumeControl = VolumeProvider.VOLUME_CONTROL_FIXED;
22095d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                switch (mVolumeHandling) {
22105d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    case RemoteControlClient.PLAYBACK_VOLUME_VARIABLE:
2211ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik                        volumeControl = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
22125d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        break;
22135d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    case RemoteControlClient.PLAYBACK_VOLUME_FIXED:
22145d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    default:
22155d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        break;
22161357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                }
22175d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                // Only register a new listener if necessary
22185d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                if (mSvp == null || mSvp.getVolumeControl() != volumeControl
22195d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        || mSvp.getMaxVolume() != mVolumeMax) {
22200d0f67f5ee5f939a1b611bc4583212707afd9beeRoboErik                    mSvp = new SessionVolumeProvider(volumeControl, mVolumeMax, mVolume);
22215d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    session.setPlaybackToRemote(mSvp);
22225d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
22235d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            } else {
22245d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                // We only know how to handle local and remote, fall back to local if not remote.
22259db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik                AudioAttributes.Builder bob = new AudioAttributes.Builder();
22269db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik                bob.setLegacyStreamType(mPlaybackStream);
22279db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik                session.setPlaybackToLocal(bob.build());
22285d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                mSvp = null;
22291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
22301357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
22311357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
2232ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        class SessionVolumeProvider extends VolumeProvider {
22335d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
22340d0f67f5ee5f939a1b611bc4583212707afd9beeRoboErik            public SessionVolumeProvider(int volumeControl, int maxVolume, int currentVolume) {
22350d0f67f5ee5f939a1b611bc4583212707afd9beeRoboErik                super(volumeControl, maxVolume, currentVolume);
22365d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
22375d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
22385d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            @Override
22395d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            public void onSetVolumeTo(final int volume) {
22405d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                sStatic.mHandler.post(new Runnable() {
22415d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    @Override
22425d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    public void run() {
22435d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        if (mVcb != null) {
22445d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                            mVcb.vcb.onVolumeSetRequest(mVcb.route, volume);
22455d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        }
22465d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    }
22475d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                });
22485d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
22495d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
22505d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            @Override
22511ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik            public void onAdjustVolume(final int direction) {
22525d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                sStatic.mHandler.post(new Runnable() {
22535d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    @Override
22545d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    public void run() {
22555d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        if (mVcb != null) {
22561ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik                            mVcb.vcb.onVolumeUpdateRequest(mVcb.route, direction);
22575d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        }
22585d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    }
22595d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                });
22605d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
22615d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        }
22629a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
22639a1de308cea2d160778fd977825f10a07b49d738Adam Powell
22649a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
22659a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a route that consists of multiple other routes in a group.
22669a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
2267b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class RouteGroup extends RouteInfo {
22689a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
22699a1de308cea2d160778fd977825f10a07b49d738Adam Powell        private boolean mUpdateName;
22709a1de308cea2d160778fd977825f10a07b49d738Adam Powell
22719a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteGroup(RouteCategory category) {
22729a1de308cea2d160778fd977825f10a07b49d738Adam Powell            super(category);
22739a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mGroup = this;
22748e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeHandling = PLAYBACK_VOLUME_FIXED;
22759a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
22769a1de308cea2d160778fd977825f10a07b49d738Adam Powell
227769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
22780d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        CharSequence getName(Resources res) {
22799a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (mUpdateName) updateName();
22800d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return super.getName(res);
22819a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
22829a1de308cea2d160778fd977825f10a07b49d738Adam Powell
22839a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
22849a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Add a route to this group. The route must not currently belong to another group.
22859a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
22869a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to add to this group
22879a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
22889a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void addRoute(RouteInfo route) {
22899a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != null) {
22909a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalStateException("Route " + route + " is already part of a group.");
22919a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
22929a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getCategory() != mCategory) {
22939a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException(
22949a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        "Route cannot be added to a group with a different category. " +
22959a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            "(Route category=" + route.getCategory() +
22969a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            " group category=" + mCategory + ")");
22979a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
2298d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            final int at = mRoutes.size();
22999a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.add(route);
2300d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = this;
23019a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2302f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
23039a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
2304f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell            dispatchRouteGrouped(route, this, at);
23059a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
23069a1de308cea2d160778fd977825f10a07b49d738Adam Powell
23079a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
23089a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Add a route to this group before the specified index.
23099a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
23109a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to add
23119a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param insertAt insert the new route before this index
23129a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
23139a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void addRoute(RouteInfo route, int insertAt) {
23149a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != null) {
23159a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalStateException("Route " + route + " is already part of a group.");
23169a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
23179a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getCategory() != mCategory) {
23189a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException(
23199a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        "Route cannot be added to a group with a different category. " +
23209a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            "(Route category=" + route.getCategory() +
23219a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            " group category=" + mCategory + ")");
23229a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
23239a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.add(insertAt, route);
2324d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = this;
23259a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2326f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
23279a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
2328f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell            dispatchRouteGrouped(route, this, insertAt);
23299a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
23309a1de308cea2d160778fd977825f10a07b49d738Adam Powell
23319a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
23329a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Remove a route from this group.
23339a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
23349a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to remove
23359a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
23369a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void removeRoute(RouteInfo route) {
23379a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != this) {
23389a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException("Route " + route +
23399a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        " is not a member of this group.");
23409a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
23419a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.remove(route);
2342d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = null;
23439a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2344f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
2345d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteUngrouped(route, this);
23469a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
23479a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
23489a1de308cea2d160778fd977825f10a07b49d738Adam Powell
23499a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
23509a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Remove the route at the specified index from this group.
23519a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
23529a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param index index of the route to remove
23539a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
23549a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void removeRoute(int index) {
2355d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            RouteInfo route = mRoutes.remove(index);
2356d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = null;
23579a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2358f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
2359d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteUngrouped(route, this);
23609a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
23619a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
23629a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2363d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2364d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @return The number of routes in this group
2365d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
2366d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public int getRouteCount() {
2367d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            return mRoutes.size();
2368d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        }
2369d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2370d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2371d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Return the route in this group at the specified index
2372d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2373d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param index Index to fetch
2374d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @return The route at index
2375d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
2376d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public RouteInfo getRouteAt(int index) {
2377d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            return mRoutes.get(index);
2378d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        }
2379d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2380ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2381ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this group.
2382ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2383ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
2384ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @param icon icon drawable to use to represent this group
2385ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
2386ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setIconDrawable(Drawable icon) {
2387ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            mIcon = icon;
2388ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2389ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2390ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2391ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this group.
2392ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2393ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
239471c69897ad0a55d590698bfa399bfe99c763b9dbAdam Powell         * @param resId Resource ID of an icon drawable to use to represent this group
2395ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
2396ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setIconResource(int resId) {
2397ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            setIconDrawable(sStatic.mResources.getDrawable(resId));
2398ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2399ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
24008e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
24018e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestSetVolume(int volume) {
24028e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int maxVol = getVolumeMax();
24038e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (maxVol == 0) {
24048e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                return;
24058e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
24068e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
24078e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final float scaledVolume = (float) volume / maxVol;
24088e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int routeCount = getRouteCount();
24098e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            for (int i = 0; i < routeCount; i++) {
24108e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final RouteInfo route = getRouteAt(i);
24118e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int routeVol = (int) (scaledVolume * route.getVolumeMax());
24128e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                route.requestSetVolume(routeVol);
24138e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
24148e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (volume != mVolume) {
24158e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVolume = volume;
24168e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                dispatchRouteVolumeChanged(this);
24178e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
24188e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
24198e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
24208e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
24218e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestUpdateVolume(int direction) {
24228e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int maxVol = getVolumeMax();
24238e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (maxVol == 0) {
24248e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                return;
24258e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
24268e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
24278e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int routeCount = getRouteCount();
2428f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            int volume = 0;
24298e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            for (int i = 0; i < routeCount; i++) {
24308e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final RouteInfo route = getRouteAt(i);
24318e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                route.requestUpdateVolume(direction);
2432f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                final int routeVol = route.getVolume();
2433f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                if (routeVol > volume) {
2434f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                    volume = routeVol;
2435f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                }
24368e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
24378e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (volume != mVolume) {
24388e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVolume = volume;
24398e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                dispatchRouteVolumeChanged(this);
24408e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
24418e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
24428e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
24439a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void memberNameChanged(RouteInfo info, CharSequence name) {
24449a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
24459a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
24469a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
24479a1de308cea2d160778fd977825f10a07b49d738Adam Powell
24489a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void memberStatusChanged(RouteInfo info, CharSequence status) {
24499a1de308cea2d160778fd977825f10a07b49d738Adam Powell            setStatusInt(status);
24509a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
24519a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2452f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        void memberVolumeChanged(RouteInfo info) {
2453f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
2454f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        }
2455f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell
2456f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        void updateVolume() {
2457f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            // A group always represents the highest component volume value.
2458f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            final int routeCount = getRouteCount();
2459f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            int volume = 0;
2460f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            for (int i = 0; i < routeCount; i++) {
2461f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                final int routeVol = getRouteAt(i).getVolume();
2462f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                if (routeVol > volume) {
2463f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                    volume = routeVol;
2464f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                }
2465f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            }
2466f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            if (volume != mVolume) {
2467f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                mVolume = volume;
2468f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                dispatchRouteVolumeChanged(this);
2469f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            }
2470f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        }
2471f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell
2472d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        @Override
2473d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        void routeUpdated() {
2474d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            int types = 0;
2475d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            final int count = mRoutes.size();
2476b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            if (count == 0) {
2477b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell                // Don't keep empty groups in the router.
247869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                MediaRouter.removeRouteStatic(this);
2479b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell                return;
2480b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            }
2481b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
24828e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            int maxVolume = 0;
24838e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            boolean isLocal = true;
24848e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            boolean isFixedVolume = true;
2485d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            for (int i = 0; i < count; i++) {
24868e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final RouteInfo route = mRoutes.get(i);
24878e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                types |= route.mSupportedTypes;
24888e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int routeMaxVolume = route.getVolumeMax();
24898e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (routeMaxVolume > maxVolume) {
24908e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    maxVolume = routeMaxVolume;
24918e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
24928e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                isLocal &= route.getPlaybackType() == PLAYBACK_TYPE_LOCAL;
24938e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                isFixedVolume &= route.getVolumeHandling() == PLAYBACK_VOLUME_FIXED;
2494d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            }
24958e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mPlaybackType = isLocal ? PLAYBACK_TYPE_LOCAL : PLAYBACK_TYPE_REMOTE;
24968e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeHandling = isFixedVolume ? PLAYBACK_VOLUME_FIXED : PLAYBACK_VOLUME_VARIABLE;
2497d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            mSupportedTypes = types;
24988e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeMax = maxVolume;
2499d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            mIcon = count == 1 ? mRoutes.get(0).getIconDrawable() : null;
2500d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            super.routeUpdated();
2501d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        }
2502d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
25039a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void updateName() {
25049a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final StringBuilder sb = new StringBuilder();
25059a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final int count = mRoutes.size();
25069a1de308cea2d160778fd977825f10a07b49d738Adam Powell            for (int i = 0; i < count; i++) {
25079a1de308cea2d160778fd977825f10a07b49d738Adam Powell                final RouteInfo info = mRoutes.get(i);
2508b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell                // TODO: There's probably a much more correct way to localize this.
25099a1de308cea2d160778fd977825f10a07b49d738Adam Powell                if (i > 0) sb.append(", ");
25109a1de308cea2d160778fd977825f10a07b49d738Adam Powell                sb.append(info.mName);
25119a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
25129a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = sb.toString();
25139a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = false;
25149a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2515d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
2516d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        @Override
2517d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        public String toString() {
2518d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            StringBuilder sb = new StringBuilder(super.toString());
2519d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            sb.append('[');
2520d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            final int count = mRoutes.size();
2521d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            for (int i = 0; i < count; i++) {
2522d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                if (i > 0) sb.append(", ");
2523d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                sb.append(mRoutes.get(i));
2524d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            }
2525d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            sb.append(']');
2526d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            return sb.toString();
2527d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        }
25289a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
25299a1de308cea2d160778fd977825f10a07b49d738Adam Powell
25309a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
25319a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Definition of a category of routes. All routes belong to a category.
25329a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
2533b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class RouteCategory {
25349a1de308cea2d160778fd977825f10a07b49d738Adam Powell        CharSequence mName;
25350d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        int mNameResId;
25369a1de308cea2d160778fd977825f10a07b49d738Adam Powell        int mTypes;
25379a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final boolean mGroupable;
2538705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        boolean mIsSystem;
25399a1de308cea2d160778fd977825f10a07b49d738Adam Powell
25409a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteCategory(CharSequence name, int types, boolean groupable) {
25419a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = name;
25429a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mTypes = types;
25439a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mGroupable = groupable;
25449a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
25459a1de308cea2d160778fd977825f10a07b49d738Adam Powell
25460d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        RouteCategory(int nameResId, int types, boolean groupable) {
25470d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mNameResId = nameResId;
25480d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mTypes = types;
25490d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mGroupable = groupable;
25500d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
25510d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell
25529a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
25539a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return the name of this route category
25549a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
25559a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getName() {
25560d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(sStatic.mResources);
25570d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
25585d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
25590d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        /**
25600d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * Return the properly localized/configuration dependent name of this RouteCategory.
25615d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik         *
25620d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @param context Context to resolve name resources
25630d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @return the name of this route category
25640d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         */
25650d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public CharSequence getName(Context context) {
25660d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(context.getResources());
25670d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
25685d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
25690d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        CharSequence getName(Resources res) {
25700d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            if (mNameResId != 0) {
25710d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                return res.getText(mNameResId);
25720d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            }
25739a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mName;
25749a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
25759a1de308cea2d160778fd977825f10a07b49d738Adam Powell
25769a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
2577d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Return the current list of routes in this category that have been added
2578d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * to the MediaRouter.
25799a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2580d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * <p>This list will not include routes that are nested within RouteGroups.
2581d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * A RouteGroup is treated as a single route within its category.</p>
2582d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2583d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param out a List to fill with the routes in this category. If this parameter is
2584d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *            non-null, it will be cleared, filled with the current routes with this
2585d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *            category, and returned. If this parameter is null, a new List will be
2586d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *            allocated to report the category's current routes.
2587d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @return A list with the routes in this category that have been added to the MediaRouter.
25889a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
2589d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public List<RouteInfo> getRoutes(List<RouteInfo> out) {
2590d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            if (out == null) {
2591d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                out = new ArrayList<RouteInfo>();
2592d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            } else {
2593d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                out.clear();
2594d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            }
2595d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2596b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final int count = getRouteCountStatic();
2597d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            for (int i = 0; i < count; i++) {
2598b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                final RouteInfo route = getRouteAtStatic(i);
2599d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                if (route.mCategory == this) {
2600d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                    out.add(route);
2601d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                }
2602d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            }
2603d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            return out;
26049a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
26059a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26069a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
26079a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return Flag set describing the route types supported by this category
26089a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
26099a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int getSupportedTypes() {
26109a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mTypes;
26119a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
26129a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26139a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
26149a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Return whether or not this category supports grouping.
26159a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
26169a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * <p>If this method returns true, all routes obtained from this category
2617d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * via calls to {@link #getRouteAt(int)} will be {@link MediaRouter.RouteGroup}s.</p>
26189a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
26199a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return true if this category supports
26209a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
26219a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public boolean isGroupable() {
26229a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mGroupable;
26239a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
26249a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2625705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        /**
2626705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * @return true if this is the category reserved for system routes.
2627705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * @hide
2628705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         */
2629705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        public boolean isSystem() {
2630705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return mIsSystem;
2631705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
2632705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
263369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
26349a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public String toString() {
26359a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return "RouteCategory{ name=" + mName + " types=" + typesToString(mTypes) +
2636d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                    " groupable=" + mGroupable + " }";
26379a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
26389a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
26399a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26409a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static class CallbackInfo {
26419a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int type;
264266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        public int flags;
2643b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        public final Callback cb;
2644b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        public final MediaRouter router;
26459a1de308cea2d160778fd977825f10a07b49d738Adam Powell
264666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        public CallbackInfo(Callback cb, int type, int flags, MediaRouter router) {
26479a1de308cea2d160778fd977825f10a07b49d738Adam Powell            this.cb = cb;
26489a1de308cea2d160778fd977825f10a07b49d738Adam Powell            this.type = type;
264966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            this.flags = flags;
2650b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            this.router = router;
26519a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
265266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
265366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        public boolean filterRouteEvent(RouteInfo route) {
2654af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            return filterRouteEvent(route.mSupportedTypes);
2655af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        }
2656af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
2657af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        public boolean filterRouteEvent(int supportedTypes) {
265866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            return (flags & CALLBACK_FLAG_UNFILTERED_EVENTS) != 0
2659af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    || (type & supportedTypes) != 0;
266066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
26619a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
26629a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26639a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
26649a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Interface for receiving events about media routing changes.
26659a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * All methods of this interface will be called from the application's main thread.
266666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
266766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * A Callback will only receive events relevant to routes that the callback
266866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * was registered for unless the {@link MediaRouter#CALLBACK_FLAG_UNFILTERED_EVENTS}
266966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * flag was specified in {@link MediaRouter#addCallback(int, Callback, int)}.
267066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
26719a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
267266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @see MediaRouter#addCallback(int, Callback, int)
26739a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see MediaRouter#removeCallback(Callback)
26749a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
26750d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    public static abstract class Callback {
26769a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
26779a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when the supplied route becomes selected as the active route
26789a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * for the given route type.
26799a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2680d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
26819a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param type Type flag set indicating the routes that have been selected
26829a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been selected for the given route types
26839a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
26840d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteSelected(MediaRouter router, int type, RouteInfo info);
26859a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26869a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
26879a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when the supplied route becomes unselected as the active route
26889a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * for the given route type.
26899a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2690d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
26919a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param type Type flag set indicating the routes that have been unselected
26929a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been unselected for the given route types
26939a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
26940d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteUnselected(MediaRouter router, int type, RouteInfo info);
26959a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26969a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
26979a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when a route for the specified type was added.
26989a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2699d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
27009a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has become available for use
27019a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
27020d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteAdded(MediaRouter router, RouteInfo info);
27039a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27049a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
27059a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when a route for the specified type was removed.
27069a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2707d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
27089a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been removed from availability
27099a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
27100d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteRemoved(MediaRouter router, RouteInfo info);
27119a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27129a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
27139a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when an aspect of the indicated route has changed.
27149a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
27159a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * <p>This will not indicate that the types supported by this route have
27169a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * changed, only that cosmetic info such as name or status have been updated.</p>
27179a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2718d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
27199a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info The route that was changed
27209a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
27210d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteChanged(MediaRouter router, RouteInfo info);
2722d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2723d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2724d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Called when a route is added to a group.
2725d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2726d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
2727d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param info The route that was added
2728d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param group The group the route was added to
2729d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param index The route index within group that info was added at
2730d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
27310d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
27320d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                int index);
2733d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2734d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2735d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Called when a route is removed from a group.
2736d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2737d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
2738d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param info The route that was removed
2739d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param group The group the route was removed from
2740d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
27410d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group);
27428e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
27438e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        /**
27448e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Called when a route's volume changes.
27458e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         *
27468e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param router the MediaRouter reporting the event
27478e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param info The route with altered volume
27488e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         */
27498e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public abstract void onRouteVolumeChanged(MediaRouter router, RouteInfo info);
275092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
275192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        /**
275292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * Called when a route's presentation display changes.
275392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * <p>
275492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * This method is called whenever the route's presentation display becomes
275592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * available, is removes or has changes to some of its properties (such as its size).
275692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p>
275792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
275892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @param router the MediaRouter reporting the event
275992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @param info The route whose presentation display changed
276092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
276192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @see RouteInfo#getPresentationDisplay()
276292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         */
276392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
276492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
27659a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
27669a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27679a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
27680d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * Stub implementation of {@link MediaRouter.Callback}.
27690d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * Each abstract method is defined as a no-op. Override just the ones
27709a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * you need.
27719a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
27720d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    public static class SimpleCallback extends Callback {
27739a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27749a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2775d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
27769a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27779a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27789a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2779d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
27809a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27819a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27829a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2783d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteAdded(MediaRouter router, RouteInfo info) {
27849a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27859a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27869a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2787d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteRemoved(MediaRouter router, RouteInfo info) {
27889a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27899a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27909a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2791d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteChanged(MediaRouter router, RouteInfo info) {
27929a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27939a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27949a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2795d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
2796d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                int index) {
27979a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27989a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27999a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2800d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
28019a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2802d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
28038e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
28048e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void onRouteVolumeChanged(MediaRouter router, RouteInfo info) {
28058e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
28069a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
28071357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
28081357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    static class VolumeCallbackInfo {
28091357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final VolumeCallback vcb;
28101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final RouteInfo route;
28111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
28121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public VolumeCallbackInfo(VolumeCallback vcb, RouteInfo route) {
28131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            this.vcb = vcb;
28141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            this.route = route;
28151357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
28161357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    }
28171357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
28181357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    /**
28191357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * Interface for receiving events about volume changes.
28201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * All methods of this interface will be called from the application's main thread.
28211357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     *
28221357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * <p>A VolumeCallback will only receive events relevant to routes that the callback
28231357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * was registered for.</p>
28241357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     *
28251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * @see UserRouteInfo#setVolumeCallback(VolumeCallback)
28261357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     */
28271357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    public static abstract class VolumeCallback {
28281357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
28291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Called when the volume for the route should be increased or decreased.
28301357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param info the route affected by this event
28311357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param direction an integer indicating whether the volume is to be increased
28321357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     (positive value) or decreased (negative value).
28331357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     For bundled changes, the absolute value indicates the number of changes
28341357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     in the same direction, e.g. +3 corresponds to three "volume up" changes.
28351357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
28361357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public abstract void onVolumeUpdateRequest(RouteInfo info, int direction);
28371357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
28381357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Called when the volume for the route should be set to the given value
28391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param info the route affected by this event
28401357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volume an integer indicating the new volume value that should be used, always
28411357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     between 0 and the value set by {@link UserRouteInfo#setVolumeMax(int)}.
28421357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
28431357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public abstract void onVolumeSetRequest(RouteInfo info, int volume);
28441357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    }
28451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
28468e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    static class VolumeChangeReceiver extends BroadcastReceiver {
28478e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
28488e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void onReceive(Context context, Intent intent) {
28498e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (intent.getAction().equals(AudioManager.VOLUME_CHANGED_ACTION)) {
28508e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
28518e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                        -1);
28528e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (streamType != AudioManager.STREAM_MUSIC) {
28538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    return;
28548e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
28558e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
28568e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int newVolume = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
28578e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int oldVolume = intent.getIntExtra(
28588e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                        AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0);
28598e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (newVolume != oldVolume) {
28608e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    systemVolumeChanged(newVolume);
28618e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
28628e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
28638e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
2864705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
28658e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
2866705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    static class WifiDisplayStatusChangedReceiver extends BroadcastReceiver {
2867705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        @Override
2868705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        public void onReceive(Context context, Intent intent) {
2869705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (intent.getAction().equals(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)) {
2870705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                updateWifiDisplayStatus((WifiDisplayStatus) intent.getParcelableExtra(
2871705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                        DisplayManager.EXTRA_WIFI_DISPLAY_STATUS));
2872705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
2873705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
28748e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    }
28759a1de308cea2d160778fd977825f10a07b49d738Adam Powell}
2876