MediaRouter.java revision 742e379863ff08a880cb6d9e5f274da7589f9a53
19a1de308cea2d160778fd977825f10a07b49d738Adam Powell/*
29a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Copyright (C) 2012 The Android Open Source Project
39a1de308cea2d160778fd977825f10a07b49d738Adam Powell *
49a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Licensed under the Apache License, Version 2.0 (the "License");
59a1de308cea2d160778fd977825f10a07b49d738Adam Powell * you may not use this file except in compliance with the License.
69a1de308cea2d160778fd977825f10a07b49d738Adam Powell * You may obtain a copy of the License at
79a1de308cea2d160778fd977825f10a07b49d738Adam Powell *
89a1de308cea2d160778fd977825f10a07b49d738Adam Powell *      http://www.apache.org/licenses/LICENSE-2.0
99a1de308cea2d160778fd977825f10a07b49d738Adam Powell *
109a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Unless required by applicable law or agreed to in writing, software
119a1de308cea2d160778fd977825f10a07b49d738Adam Powell * distributed under the License is distributed on an "AS IS" BASIS,
129a1de308cea2d160778fd977825f10a07b49d738Adam Powell * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139a1de308cea2d160778fd977825f10a07b49d738Adam Powell * See the License for the specific language governing permissions and
149a1de308cea2d160778fd977825f10a07b49d738Adam Powell * limitations under the License.
159a1de308cea2d160778fd977825f10a07b49d738Adam Powell */
169a1de308cea2d160778fd977825f10a07b49d738Adam Powell
179a1de308cea2d160778fd977825f10a07b49d738Adam Powellpackage android.media;
189a1de308cea2d160778fd977825f10a07b49d738Adam Powell
19af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brownimport android.Manifest;
207b9c912f536925ac6ec43935d6e97506851b33d6Tor Norbyeimport android.annotation.DrawableRes;
219dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kangimport android.annotation.IntDef;
229d93a378c5158d14f80d46af70433234330ec568P.Y. Laligandimport android.annotation.NonNull;
23ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackbornimport android.app.ActivityThread;
248e37a85bf3dc39519942698dc90a3951306b934bAdam Powellimport android.content.BroadcastReceiver;
259a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.content.Context;
268e37a85bf3dc39519942698dc90a3951306b934bAdam Powellimport android.content.Intent;
278e37a85bf3dc39519942698dc90a3951306b934bAdam Powellimport android.content.IntentFilter;
28af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brownimport android.content.pm.PackageManager;
29b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackbornimport android.content.res.Resources;
30ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powellimport android.graphics.drawable.Drawable;
31705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.hardware.display.DisplayManager;
32705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.hardware.display.WifiDisplay;
33705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.hardware.display.WifiDisplayStatus;
345d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErikimport android.media.session.MediaSession;
359a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.os.Handler;
36632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.os.IBinder;
37af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brownimport android.os.Process;
38632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.os.RemoteException;
39632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.os.ServiceManager;
4069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.os.UserHandle;
41632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.text.TextUtils;
429a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.util.Log;
43705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.view.Display;
449a1de308cea2d160778fd977825f10a07b49d738Adam Powell
459dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kangimport java.lang.annotation.Retention;
469dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kangimport java.lang.annotation.RetentionPolicy;
479a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport java.util.ArrayList;
489a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport java.util.HashMap;
49d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powellimport java.util.List;
50e6585b32ea586743258a5457e2184ffc087f2d2fKenny Rootimport java.util.Objects;
5139d5c6172503620ac3761148adac5fd7fa20d02dAdam Powellimport java.util.concurrent.CopyOnWriteArrayList;
529a1de308cea2d160778fd977825f10a07b49d738Adam Powell
539a1de308cea2d160778fd977825f10a07b49d738Adam Powell/**
549a1de308cea2d160778fd977825f10a07b49d738Adam Powell * MediaRouter allows applications to control the routing of media channels
559a1de308cea2d160778fd977825f10a07b49d738Adam Powell * and streams from the current device to external speakers and destination devices.
569a1de308cea2d160778fd977825f10a07b49d738Adam Powell *
57b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * <p>A MediaRouter is retrieved through {@link Context#getSystemService(String)
58b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * Context.getSystemService()} of a {@link Context#MEDIA_ROUTER_SERVICE
59b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * Context.MEDIA_ROUTER_SERVICE}.
60b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn *
61b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * <p>The media router API is not thread-safe; all interactions with it must be
62b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * done from the main thread of the process.</p>
639a1de308cea2d160778fd977825f10a07b49d738Adam Powell */
649a1de308cea2d160778fd977825f10a07b49d738Adam Powellpublic class MediaRouter {
659a1de308cea2d160778fd977825f10a07b49d738Adam Powell    private static final String TAG = "MediaRouter";
6669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
679a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown    static class Static implements DisplayManager.DisplayListener {
69eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon        final String mPackageName;
70b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final Resources mResources;
71632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn        final IAudioService mAudioService;
72705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final DisplayManager mDisplayService;
7369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final IMediaRouterService mMediaRouterService;
74b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final Handler mHandler;
7539d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        final CopyOnWriteArrayList<CallbackInfo> mCallbacks =
7639d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell                new CopyOnWriteArrayList<CallbackInfo>();
77b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
78b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
79b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final ArrayList<RouteCategory> mCategories = new ArrayList<RouteCategory>();
80b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
81b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final RouteCategory mSystemCategory;
82632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn
83705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
84b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
85705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        RouteInfo mDefaultAudioVideo;
86b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        RouteInfo mBluetoothA2dpRoute;
87b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
88b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        RouteInfo mSelectedRoute;
899a1de308cea2d160778fd977825f10a07b49d738Adam Powell
90af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        final boolean mCanConfigureWifiDisplays;
9166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        boolean mActivelyScanningWifiDisplays;
92ce468a35b388ca46578934706b38dbae94941643Jeff Brown        String mPreviousActiveWifiDisplayAddress;
93705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
9469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        int mDiscoveryRequestRouteTypes;
9569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        boolean mDiscoverRequestActiveScan;
9669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
9769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        int mCurrentUserId = -1;
9869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        IMediaRouterClient mClient;
9969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        MediaRouterClientState mClientState;
10069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
101705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() {
10266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            @Override
103632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
104632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                mHandler.post(new Runnable() {
105632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    @Override public void run() {
106705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                        updateAudioRoutes(newRoutes);
107632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    }
108632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                });
109632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
110632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn        };
111632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn
112b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        Static(Context appContext) {
113eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon            mPackageName = appContext.getPackageName();
114eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon            mResources = appContext.getResources();
115b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            mHandler = new Handler(appContext.getMainLooper());
1169a1de308cea2d160778fd977825f10a07b49d738Adam Powell
117632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
118632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            mAudioService = IAudioService.Stub.asInterface(b);
1199a1de308cea2d160778fd977825f10a07b49d738Adam Powell
120705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            mDisplayService = (DisplayManager) appContext.getSystemService(Context.DISPLAY_SERVICE);
121705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
12269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mMediaRouterService = IMediaRouterService.Stub.asInterface(
12369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
12469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
125dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell            mSystemCategory = new RouteCategory(
126dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell                    com.android.internal.R.string.default_audio_route_category_name,
127705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO, false);
128705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            mSystemCategory.mIsSystem = true;
129af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
130af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Only the system can configure wifi displays.  The display manager
131af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // enforces this with a permission check.  Set a flag here so that we
132af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // know whether this process is actually allowed to scan and connect.
133af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            mCanConfigureWifiDisplays = appContext.checkPermission(
134af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    Manifest.permission.CONFIGURE_WIFI_DISPLAY,
135af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    Process.myPid(), Process.myUid()) == PackageManager.PERMISSION_GRANTED;
136b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        }
137b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
138b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        // Called after sStatic is initialized
1398e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        void startMonitoringRoutes(Context appContext) {
140705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            mDefaultAudioVideo = new RouteInfo(mSystemCategory);
141705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            mDefaultAudioVideo.mNameResId = com.android.internal.R.string.default_audio_route_name;
142705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            mDefaultAudioVideo.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO;
1435830b0b33618940d65197cec99d697b21908fec8Chong Zhang            mDefaultAudioVideo.updatePresentationDisplay();
1442ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            addRouteStatic(mDefaultAudioVideo);
145632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn
1462ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            // This will select the active wifi display route if there is one.
1472ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            updateWifiDisplayStatus(mDisplayService.getWifiDisplayStatus());
1482ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell
1492ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            appContext.registerReceiver(new WifiDisplayStatusChangedReceiver(),
1502ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell                    new IntentFilter(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED));
1518e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            appContext.registerReceiver(new VolumeChangeReceiver(),
1528e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
1538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
15492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            mDisplayService.registerDisplayListener(this, mHandler);
15592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
156705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            AudioRoutesInfo newAudioRoutes = null;
157632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            try {
158705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
159632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            } catch (RemoteException e) {
160632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
161705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (newAudioRoutes != null) {
1622ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell                // This will select the active BT route if there is one and the current
1632ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell                // selected route is the default system route, or if there is no selected
1642ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell                // route yet.
165705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                updateAudioRoutes(newAudioRoutes);
166632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
167705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
16869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Bind to the media router service.
16969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            rebindAsUser(UserHandle.myUserId());
17069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
1712ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            // Select the default route if the above didn't sync us up
1722ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            // appropriately with relevant system state.
1732ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            if (mSelectedRoute == null) {
17469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                selectDefaultRouteStatic();
1752ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            }
176632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn        }
177632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn
178705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        void updateAudioRoutes(AudioRoutesInfo newRoutes) {
179742e379863ff08a880cb6d9e5f274da7589f9a53Jaewan Kim            boolean updated = false;
1806156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock            if (newRoutes.mainType != mCurAudioRoutesInfo.mainType) {
1816156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                mCurAudioRoutesInfo.mainType = newRoutes.mainType;
182632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                int name;
1836156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                if ((newRoutes.mainType&AudioRoutesInfo.MAIN_HEADPHONES) != 0
1846156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                        || (newRoutes.mainType&AudioRoutesInfo.MAIN_HEADSET) != 0) {
185632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    name = com.android.internal.R.string.default_audio_route_name_headphones;
1866156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                } else if ((newRoutes.mainType&AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
187632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
1886156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                } else if ((newRoutes.mainType&AudioRoutesInfo.MAIN_HDMI) != 0) {
1894131a37366d59b5e61f55c4e48d2b22ee0c4cad4Adam Powell                    name = com.android.internal.R.string.default_media_route_name_hdmi;
190632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                } else {
191632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    name = com.android.internal.R.string.default_audio_route_name;
192632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                }
193705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                sStatic.mDefaultAudioVideo.mNameResId = name;
194705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                dispatchRouteChanged(sStatic.mDefaultAudioVideo);
195742e379863ff08a880cb6d9e5f274da7589f9a53Jaewan Kim                updated = true;
196632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
197bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell
1986156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock            final int mainType = mCurAudioRoutesInfo.mainType;
1993f369684e13dfea0ba8ea134f3e95930b0dd7df0Adam Powell
2006156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock            if (!TextUtils.equals(newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) {
2016156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                mCurAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName;
2026156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                if (mCurAudioRoutesInfo.bluetoothName != null) {
203632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    if (sStatic.mBluetoothA2dpRoute == null) {
204632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        final RouteInfo info = new RouteInfo(sStatic.mSystemCategory);
2056156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                        info.mName = mCurAudioRoutesInfo.bluetoothName;
20656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown                        info.mDescription = sStatic.mResources.getText(
20756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown                                com.android.internal.R.string.bluetooth_a2dp_audio_route_name);
208632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        info.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO;
2099dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang                        info.mDeviceType = RouteInfo.DEVICE_TYPE_BLUETOOTH;
210632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        sStatic.mBluetoothA2dpRoute = info;
2112ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell                        addRouteStatic(sStatic.mBluetoothA2dpRoute);
212632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    } else {
2136156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                        sStatic.mBluetoothA2dpRoute.mName = mCurAudioRoutesInfo.bluetoothName;
214632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        dispatchRouteChanged(sStatic.mBluetoothA2dpRoute);
215632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    }
216632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                } else if (sStatic.mBluetoothA2dpRoute != null) {
21769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    removeRouteStatic(sStatic.mBluetoothA2dpRoute);
218632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    sStatic.mBluetoothA2dpRoute = null;
219632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                }
220742e379863ff08a880cb6d9e5f274da7589f9a53Jaewan Kim                updated = true;
221632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
222bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell
223bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell            if (mBluetoothA2dpRoute != null) {
224fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                final boolean a2dpEnabled = isBluetoothA2dpOn();
22571f3563c3960bd862dfa96844f646ea5b2f03de5Sungsoo                if (mSelectedRoute == mBluetoothA2dpRoute && !a2dpEnabled) {
226fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                    selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mDefaultAudioVideo, false);
227742e379863ff08a880cb6d9e5f274da7589f9a53Jaewan Kim                    updated = true;
228fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                } else if ((mSelectedRoute == mDefaultAudioVideo || mSelectedRoute == null) &&
229fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                        a2dpEnabled) {
23069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mBluetoothA2dpRoute, false);
231742e379863ff08a880cb6d9e5f274da7589f9a53Jaewan Kim                    updated = true;
232bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell                }
233bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell            }
234742e379863ff08a880cb6d9e5f274da7589f9a53Jaewan Kim            if (updated) {
235742e379863ff08a880cb6d9e5f274da7589f9a53Jaewan Kim                Log.v(TAG, "Audio routes updated: " + newRoutes + ", a2dp=" + isBluetoothA2dpOn());
236742e379863ff08a880cb6d9e5f274da7589f9a53Jaewan Kim            }
237b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        }
23892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
239fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo        boolean isBluetoothA2dpOn() {
240fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            try {
241fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                return mAudioService.isBluetoothA2dpOn();
242fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            } catch (RemoteException e) {
243fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                Log.e(TAG, "Error querying Bluetooth A2DP state", e);
244fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                return false;
245fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            }
246fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo        }
247fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo
24869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void updateDiscoveryRequest() {
24969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // What are we looking for today?
25069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int routeTypes = 0;
25169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int passiveRouteTypes = 0;
25269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean activeScan = false;
25369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean activeScanWifiDisplay = false;
25469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int count = mCallbacks.size();
25569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < count; i++) {
25669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                CallbackInfo cbi = mCallbacks.get(i);
25769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if ((cbi.flags & (CALLBACK_FLAG_PERFORM_ACTIVE_SCAN
25869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        | CALLBACK_FLAG_REQUEST_DISCOVERY)) != 0) {
25969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Discovery explicitly requested.
26069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    routeTypes |= cbi.type;
26169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else if ((cbi.flags & CALLBACK_FLAG_PASSIVE_DISCOVERY) != 0) {
26269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Discovery only passively requested.
26369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    passiveRouteTypes |= cbi.type;
26469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else {
26569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Legacy case since applications don't specify the discovery flag.
26669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Unfortunately we just have to assume they always need discovery
26769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // whenever they have a callback registered.
26869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    routeTypes |= cbi.type;
26969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
27069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if ((cbi.flags & CALLBACK_FLAG_PERFORM_ACTIVE_SCAN) != 0) {
27169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    activeScan = true;
272af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    if ((cbi.type & ROUTE_TYPE_REMOTE_DISPLAY) != 0) {
27369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        activeScanWifiDisplay = true;
27469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
27569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
27669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
27769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (routeTypes != 0 || activeScan) {
27869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // If someone else requests discovery then enable the passive listeners.
27969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // This is used by the MediaRouteButton and MediaRouteActionProvider since
28069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // they don't receive lifecycle callbacks from the Activity.
28169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                routeTypes |= passiveRouteTypes;
28269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
28369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
28469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Update wifi display scanning.
285ce468a35b388ca46578934706b38dbae94941643Jeff Brown            // TODO: All of this should be managed by the media router service.
286ce468a35b388ca46578934706b38dbae94941643Jeff Brown            if (mCanConfigureWifiDisplays) {
287ce468a35b388ca46578934706b38dbae94941643Jeff Brown                if (mSelectedRoute != null
288ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        && mSelectedRoute.matchesTypes(ROUTE_TYPE_REMOTE_DISPLAY)) {
289ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    // Don't scan while already connected to a remote display since
290ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    // it may interfere with the ongoing transmission.
291ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    activeScanWifiDisplay = false;
29266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown                }
293ce468a35b388ca46578934706b38dbae94941643Jeff Brown                if (activeScanWifiDisplay) {
294ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    if (!mActivelyScanningWifiDisplays) {
295ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mActivelyScanningWifiDisplays = true;
296ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mDisplayService.startWifiDisplayScan();
297ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    }
298ce468a35b388ca46578934706b38dbae94941643Jeff Brown                } else {
299ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    if (mActivelyScanningWifiDisplays) {
300ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mActivelyScanningWifiDisplays = false;
301ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mDisplayService.stopWifiDisplayScan();
302ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    }
30366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown                }
30466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            }
30566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
30669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Tell the media router service all about it.
30769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (routeTypes != mDiscoveryRequestRouteTypes
30869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    || activeScan != mDiscoverRequestActiveScan) {
30969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mDiscoveryRequestRouteTypes = routeTypes;
31069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mDiscoverRequestActiveScan = activeScan;
31169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                publishClientDiscoveryRequest();
31266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            }
31366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
31466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
31592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        @Override
31692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onDisplayAdded(int displayId) {
31792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            updatePresentationDisplays(displayId);
31892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
31992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
32092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        @Override
32192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onDisplayChanged(int displayId) {
32292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            updatePresentationDisplays(displayId);
32392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
32492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
32592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        @Override
32692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onDisplayRemoved(int displayId) {
32792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            updatePresentationDisplays(displayId);
32892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
32992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
33092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public Display[] getAllPresentationDisplays() {
33192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
33292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
33392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
33492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        private void updatePresentationDisplays(int changedDisplayId) {
33592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            final int count = mRoutes.size();
33692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            for (int i = 0; i < count; i++) {
3375830b0b33618940d65197cec99d697b21908fec8Chong Zhang                final RouteInfo route = mRoutes.get(i);
3385830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (route.updatePresentationDisplay() || (route.mPresentationDisplay != null
3395830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        && route.mPresentationDisplay.getDisplayId() == changedDisplayId)) {
3405830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    dispatchRoutePresentationDisplayChanged(route);
34192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                }
34292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            }
34392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
34469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
34569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void setSelectedRoute(RouteInfo info, boolean explicit) {
34669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Must be non-reentrant.
34769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mSelectedRoute = info;
34869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            publishClientSelectedRoute(explicit);
34969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
35069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
35169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void rebindAsUser(int userId) {
35269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mCurrentUserId != userId || userId < 0 || mClient == null) {
35369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (mClient != null) {
35469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    try {
35569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        mMediaRouterService.unregisterClient(mClient);
35669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    } catch (RemoteException ex) {
35769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        Log.e(TAG, "Unable to unregister media router client.", ex);
35869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
35969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClient = null;
36069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
36169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
36269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mCurrentUserId = userId;
36369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
36469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
36569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Client client = new Client();
366eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon                    mMediaRouterService.registerClientAsUser(client, mPackageName, userId);
36769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClient = client;
36869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
36969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to register media router client.", ex);
37069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
37169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
37269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                publishClientDiscoveryRequest();
37369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                publishClientSelectedRoute(false);
37469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                updateClientState();
37569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
37669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
37769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
37869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void publishClientDiscoveryRequest() {
37969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mClient != null) {
38069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
38169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.setDiscoveryRequest(mClient,
38269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mDiscoveryRequestRouteTypes, mDiscoverRequestActiveScan);
38369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
38469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to publish media router client discovery request.", ex);
38569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
38669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
38769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
38869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
38969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void publishClientSelectedRoute(boolean explicit) {
39069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mClient != null) {
39169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
39269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.setSelectedRoute(mClient,
39369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mSelectedRoute != null ? mSelectedRoute.mGlobalRouteId : null,
39469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            explicit);
39569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
39669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to publish media router client selected route.", ex);
39769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
39869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
39969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
40069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
40169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void updateClientState() {
40269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Update the client state.
40369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mClientState = null;
40469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mClient != null) {
40569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
40669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClientState = mMediaRouterService.getState(mClient);
40769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
40869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to retrieve media router client state.", ex);
40969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
41069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
41169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final ArrayList<MediaRouterClientState.RouteInfo> globalRoutes =
41269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClientState != null ? mClientState.routes : null;
41369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final String globallySelectedRouteId = mClientState != null ?
41469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClientState.globallySelectedRouteId : null;
41569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
41669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Add or update routes.
41769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int globalRouteCount = globalRoutes != null ? globalRoutes.size() : 0;
41869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < globalRouteCount; i++) {
41969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final MediaRouterClientState.RouteInfo globalRoute = globalRoutes.get(i);
42069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                RouteInfo route = findGlobalRoute(globalRoute.id);
42169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (route == null) {
42269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    route = makeGlobalRoute(globalRoute);
42369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    addRouteStatic(route);
42469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else {
42569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    updateGlobalRoute(route, globalRoute);
42669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
42769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
42869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
42969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Synchronize state with the globally selected route.
43069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (globallySelectedRouteId != null) {
43169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final RouteInfo route = findGlobalRoute(globallySelectedRouteId);
43269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (route == null) {
43369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.w(TAG, "Could not find new globally selected route: "
43469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            + globallySelectedRouteId);
43569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else if (route != mSelectedRoute) {
43669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    if (DEBUG) {
43769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        Log.d(TAG, "Selecting new globally selected route: " + route);
43869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
43969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    selectRouteStatic(route.mSupportedTypes, route, false);
44069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
44169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            } else if (mSelectedRoute != null && mSelectedRoute.mGlobalRouteId != null) {
44269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (DEBUG) {
44369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.d(TAG, "Unselecting previous globally selected route: " + mSelectedRoute);
44469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
44569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                selectDefaultRouteStatic();
44669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
44769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
44869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Remove defunct routes.
44969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            outer: for (int i = mRoutes.size(); i-- > 0; ) {
45069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final RouteInfo route = mRoutes.get(i);
45169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final String globalRouteId = route.mGlobalRouteId;
45269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (globalRouteId != null) {
45369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    for (int j = 0; j < globalRouteCount; j++) {
45469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        MediaRouterClientState.RouteInfo globalRoute = globalRoutes.get(j);
45569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (globalRouteId.equals(globalRoute.id)) {
45669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            continue outer; // found
45769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
45869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
45969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // not found
46069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    removeRouteStatic(route);
46169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
46269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
46369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
46469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
46569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void requestSetVolume(RouteInfo route, int volume) {
46669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mGlobalRouteId != null && mClient != null) {
46769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
46869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.requestSetVolume(mClient,
46969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            route.mGlobalRouteId, volume);
47069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
47169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.w(TAG, "Unable to request volume change.", ex);
47269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
47369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
47469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
47569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
47669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void requestUpdateVolume(RouteInfo route, int direction) {
47769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mGlobalRouteId != null && mClient != null) {
47869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
47969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.requestUpdateVolume(mClient,
48069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            route.mGlobalRouteId, direction);
48169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
48269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.w(TAG, "Unable to request volume change.", ex);
48369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
48469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
48569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
48669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
48769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        RouteInfo makeGlobalRoute(MediaRouterClientState.RouteInfo globalRoute) {
48869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            RouteInfo route = new RouteInfo(sStatic.mSystemCategory);
48969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mGlobalRouteId = globalRoute.id;
49069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mName = globalRoute.name;
49169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mDescription = globalRoute.description;
49269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mSupportedTypes = globalRoute.supportedTypes;
4939dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang            route.mDeviceType = globalRoute.deviceType;
49469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mEnabled = globalRoute.enabled;
49539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            route.setRealStatusCode(globalRoute.statusCode);
49669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mPlaybackType = globalRoute.playbackType;
49769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mPlaybackStream = globalRoute.playbackStream;
49869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mVolume = globalRoute.volume;
49969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mVolumeMax = globalRoute.volumeMax;
50069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mVolumeHandling = globalRoute.volumeHandling;
5015830b0b33618940d65197cec99d697b21908fec8Chong Zhang            route.mPresentationDisplayId = globalRoute.presentationDisplayId;
5025830b0b33618940d65197cec99d697b21908fec8Chong Zhang            route.updatePresentationDisplay();
50369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            return route;
50469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
50569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
50669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void updateGlobalRoute(RouteInfo route, MediaRouterClientState.RouteInfo globalRoute) {
50769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean changed = false;
50869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean volumeChanged = false;
50969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean presentationDisplayChanged = false;
51069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
511e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root            if (!Objects.equals(route.mName, globalRoute.name)) {
51269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mName = globalRoute.name;
51369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
51469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
515e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root            if (!Objects.equals(route.mDescription, globalRoute.description)) {
51669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mDescription = globalRoute.description;
51769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
51869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
519af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            final int oldSupportedTypes = route.mSupportedTypes;
520af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (oldSupportedTypes != globalRoute.supportedTypes) {
52169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mSupportedTypes = globalRoute.supportedTypes;
52269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
52369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
52469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mEnabled != globalRoute.enabled) {
52569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mEnabled = globalRoute.enabled;
52669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
52769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
52839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (route.mRealStatusCode != globalRoute.statusCode) {
52939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                route.setRealStatusCode(globalRoute.statusCode);
53069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
53169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
53269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mPlaybackType != globalRoute.playbackType) {
53369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mPlaybackType = globalRoute.playbackType;
53469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
53569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
53669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mPlaybackStream != globalRoute.playbackStream) {
53769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mPlaybackStream = globalRoute.playbackStream;
53869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
53969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
54069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mVolume != globalRoute.volume) {
54169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mVolume = globalRoute.volume;
54269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
54369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                volumeChanged = true;
54469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
54569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mVolumeMax != globalRoute.volumeMax) {
54669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mVolumeMax = globalRoute.volumeMax;
54769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
54869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                volumeChanged = true;
54969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
55069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mVolumeHandling != globalRoute.volumeHandling) {
55169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mVolumeHandling = globalRoute.volumeHandling;
55269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
55369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                volumeChanged = true;
55469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
5555830b0b33618940d65197cec99d697b21908fec8Chong Zhang            if (route.mPresentationDisplayId != globalRoute.presentationDisplayId) {
5565830b0b33618940d65197cec99d697b21908fec8Chong Zhang                route.mPresentationDisplayId = globalRoute.presentationDisplayId;
5575830b0b33618940d65197cec99d697b21908fec8Chong Zhang                route.updatePresentationDisplay();
55869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
55969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                presentationDisplayChanged = true;
56069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
56169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
56269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (changed) {
563af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                dispatchRouteChanged(route, oldSupportedTypes);
56469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
56569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (volumeChanged) {
56669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                dispatchRouteVolumeChanged(route);
56769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
56869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (presentationDisplayChanged) {
56969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                dispatchRoutePresentationDisplayChanged(route);
57069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
57169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
57269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
57369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        RouteInfo findGlobalRoute(String globalRouteId) {
57469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int count = mRoutes.size();
57569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < count; i++) {
57669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final RouteInfo route = mRoutes.get(i);
57769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (globalRouteId.equals(route.mGlobalRouteId)) {
57869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    return route;
57969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
58069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
58169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            return null;
58269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
58369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
58469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final class Client extends IMediaRouterClient.Stub {
58569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            @Override
58669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public void onStateChanged() {
58769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mHandler.post(new Runnable() {
58869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    @Override
58969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    public void run() {
59069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (Client.this == mClient) {
59169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            updateClientState();
59269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
59369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
59469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                });
59569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
59669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
597b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
5989a1de308cea2d160778fd977825f10a07b49d738Adam Powell
599b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static Static sStatic;
6009a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6019a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
6029a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Route type flag for live audio.
6039a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
6049a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>A device that supports live audio routing will allow the media audio stream
6059a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * to be routed to supported destinations. This can include internal speakers or
6069a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * audio jacks on the device itself, A2DP devices, and more.</p>
6079a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
6089a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>Once initiated this routing is transparent to the application. All audio
6099a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * played on the media stream will be routed to the selected destination.</p>
6109a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
61169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_LIVE_AUDIO = 1 << 0;
6129a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6139a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
614705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * Route type flag for live video.
615705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     *
616705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * <p>A device that supports live video routing will allow a mirrored version
617705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * of the device's primary display or a customized
618705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * {@link android.app.Presentation Presentation} to be routed to supported destinations.</p>
619705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     *
620705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * <p>Once initiated, display mirroring is transparent to the application.
621705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * While remote routing is active the application may use a
622705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * {@link android.app.Presentation Presentation} to replace the mirrored view
623705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * on the external display with different content.</p>
62492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown     *
62592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown     * @see RouteInfo#getPresentationDisplay()
62692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown     * @see android.app.Presentation
627705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     */
62869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_LIVE_VIDEO = 1 << 1;
62969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
63069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
63169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Temporary interop constant to identify remote displays.
63269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide To be removed when media router API is updated.
63369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
63469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_REMOTE_DISPLAY = 1 << 2;
635705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
636705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    /**
6379a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Route type flag for application-specific usage.
6389a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
6399a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>Unlike other media route types, user routes are managed by the application.
6409a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * The MediaRouter will manage and dispatch events for user routes, but the application
6419a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * is expected to interpret the meaning of these events and perform the requested
6429a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * routing tasks.</p>
6439a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
64469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_USER = 1 << 23;
64569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
64669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static final int ROUTE_TYPE_ANY = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO
64769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            | ROUTE_TYPE_REMOTE_DISPLAY | ROUTE_TYPE_USER;
6489a1de308cea2d160778fd977825f10a07b49d738Adam Powell
64966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    /**
65066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Flag for {@link #addCallback}: Actively scan for routes while this callback
65166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * is registered.
65266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
65366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * When this flag is specified, the media router will actively scan for new
65466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * routes.  Certain routes, such as wifi display routes, may not be discoverable
65566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * except when actively scanning.  This flag is typically used when the route picker
65666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * dialog has been opened by the user to ensure that the route information is
65766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * up to date.
65866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p><p>
65966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Active scanning may consume a significant amount of power and may have intrusive
66066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * effects on wireless connectivity.  Therefore it is important that active scanning
66166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * only be requested when it is actually needed to satisfy a user request to
66266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * discover and select a new route.
66366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
66466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     */
66514507e257af5d71577574e25cbd690c4b54c9272Jeff Brown    public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1 << 0;
66666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
66766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    /**
66866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Flag for {@link #addCallback}: Do not filter route events.
66966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
67066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * When this flag is specified, the callback will be invoked for event that affect any
67169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * route even if they do not match the callback's filter.
67266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
67366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     */
67466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 1 << 1;
67566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
67669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
67769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Explicitly requests discovery.
67869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     *
67969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide Future API ported from support library.  Revisit this later.
68069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
68169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 1 << 2;
68269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
68369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
68469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Requests that discovery be performed but only if there is some other active
68569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * callback already registered.
68669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     *
68769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide Compatibility workaround for the fact that applications do not currently
68869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * request discovery explicitly (except when using the support library API).
68969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
69069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int CALLBACK_FLAG_PASSIVE_DISCOVERY = 1 << 3;
69169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
6920abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    /**
6930abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * Flag for {@link #isRouteAvailable}: Ignore the default route.
6940abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * <p>
6950abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * This flag is used to determine whether a matching non-default route is available.
6960abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * This constraint may be used to decide whether to offer the route chooser dialog
6970abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * to the user.  There is no point offering the chooser if there are no
6980abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * non-default choices.
6990abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * </p>
7000abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     *
7010abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @hide Future API ported from support library.  Revisit this later.
7020abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     */
7030abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1 << 0;
7040abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
7059a1de308cea2d160778fd977825f10a07b49d738Adam Powell    // Maps application contexts
7069a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static final HashMap<Context, MediaRouter> sRouters = new HashMap<Context, MediaRouter>();
7079a1de308cea2d160778fd977825f10a07b49d738Adam Powell
7089a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static String typesToString(int types) {
7099a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final StringBuilder result = new StringBuilder();
7109a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if ((types & ROUTE_TYPE_LIVE_AUDIO) != 0) {
7119a1de308cea2d160778fd977825f10a07b49d738Adam Powell            result.append("ROUTE_TYPE_LIVE_AUDIO ");
7129a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
7132bb7c122ef6ce8539dfbaeb3292adcd942185f82Bryan Mawhinney        if ((types & ROUTE_TYPE_LIVE_VIDEO) != 0) {
7142bb7c122ef6ce8539dfbaeb3292adcd942185f82Bryan Mawhinney            result.append("ROUTE_TYPE_LIVE_VIDEO ");
7152bb7c122ef6ce8539dfbaeb3292adcd942185f82Bryan Mawhinney        }
71669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if ((types & ROUTE_TYPE_REMOTE_DISPLAY) != 0) {
71769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            result.append("ROUTE_TYPE_REMOTE_DISPLAY ");
71869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
7199a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if ((types & ROUTE_TYPE_USER) != 0) {
7209a1de308cea2d160778fd977825f10a07b49d738Adam Powell            result.append("ROUTE_TYPE_USER ");
7219a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
7229a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return result.toString();
7239a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
7249a1de308cea2d160778fd977825f10a07b49d738Adam Powell
725b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    /** @hide */
726b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public MediaRouter(Context context) {
727b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        synchronized (Static.class) {
728b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            if (sStatic == null) {
7298e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final Context appContext = context.getApplicationContext();
7308e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                sStatic = new Static(appContext);
7318e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                sStatic.startMonitoringRoutes(appContext);
732b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            }
7339a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
7349a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
7359a1de308cea2d160778fd977825f10a07b49d738Adam Powell
736690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    /**
7373afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * Gets the default route for playing media content on the system.
7383afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * <p>
7393afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * The system always provides a default route.
7403afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * </p>
7413afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     *
7423afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * @return The default route, which is guaranteed to never be null.
743690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     */
7443afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown    public RouteInfo getDefaultRoute() {
745705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return sStatic.mDefaultAudioVideo;
746690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
747690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
748690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    /**
7494599696591f745b3a546197d2ba7e5cfc5562484Adam Powell     * @hide for use by framework routing UI
7504599696591f745b3a546197d2ba7e5cfc5562484Adam Powell     */
7513afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown    public RouteCategory getSystemCategory() {
7524599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        return sStatic.mSystemCategory;
7534599696591f745b3a546197d2ba7e5cfc5562484Adam Powell    }
7544599696591f745b3a546197d2ba7e5cfc5562484Adam Powell
7550abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    /** @hide */
7560abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public RouteInfo getSelectedRoute() {
7570abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        return getSelectedRoute(ROUTE_TYPE_ANY);
7580abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    }
7590abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
7604599696591f745b3a546197d2ba7e5cfc5562484Adam Powell    /**
7611cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell     * Return the currently selected route for any of the given types
762690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     *
763690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * @param type route types
764690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * @return the selected route
765690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     */
766690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public RouteInfo getSelectedRoute(int type) {
7671cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        if (sStatic.mSelectedRoute != null &&
7681cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell                (sStatic.mSelectedRoute.mSupportedTypes & type) != 0) {
7691cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // If the selected route supports any of the types supplied, it's still considered
7701cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // 'selected' for that type.
7711cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            return sStatic.mSelectedRoute;
7721cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        } else if (type == ROUTE_TYPE_USER) {
7731cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // The caller specifically asked for a user route and the currently selected route
7741cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // doesn't qualify.
7751cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            return null;
7761cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        }
7771cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        // If the above didn't match and we're not specifically asking for a user route,
7781cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        // consider the default selected.
7791cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        return sStatic.mDefaultAudioVideo;
780690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
781690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
7829a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
7830abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * Returns true if there is a route that matches the specified types.
7840abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * <p>
7850abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * This method returns true if there are any available routes that match the types
7860abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * regardless of whether they are enabled or disabled.  If the
7870abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} flag is specified, then
7880abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * the method will only consider non-default routes.
7890abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * </p>
7900abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     *
7910abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @param types The types to match.
7920abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @param flags Flags to control the determination of whether a route may be available.
7930abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * May be zero or {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE}.
7940abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @return True if a matching route may be available.
7950abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     *
7960abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @hide Future API ported from support library.  Revisit this later.
7970abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     */
7980abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public boolean isRouteAvailable(int types, int flags) {
7990abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        final int count = sStatic.mRoutes.size();
8000abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        for (int i = 0; i < count; i++) {
8010abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            RouteInfo route = sStatic.mRoutes.get(i);
8020abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            if (route.matchesTypes(types)) {
8030abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                if ((flags & AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE) == 0
8040abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                        || route != sStatic.mDefaultAudioVideo) {
8050abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                    return true;
8060abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                }
8070abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            }
8080abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
8090abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
8100abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        // It doesn't look like we can find a matching route right now.
8110abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        return false;
8120abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    }
8130abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
8140abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    /**
8159a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Add a callback to listen to events about specific kinds of media routes.
8169a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * If the specified callback is already registered, its registration will be updated for any
8179a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * additional route types specified.
81866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
81966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * This is a convenience method that has the same effect as calling
82066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * {@link #addCallback(int, Callback, int)} without flags.
82166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
8229a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
8239a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param types Types of routes this callback is interested in
8249a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param cb Callback to add
8259a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
8269a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void addCallback(int types, Callback cb) {
82766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        addCallback(types, cb, 0);
82866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    }
82966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
83066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    /**
83166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Add a callback to listen to events about specific kinds of media routes.
83266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * If the specified callback is already registered, its registration will be updated for any
83366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * additional route types specified.
83466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
83566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * By default, the callback will only be invoked for events that affect routes
83666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * that match the specified selector.  The filtering may be disabled by specifying
83766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * the {@link #CALLBACK_FLAG_UNFILTERED_EVENTS} flag.
83866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
83966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     *
84066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @param types Types of routes this callback is interested in
84166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @param cb Callback to add
84266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @param flags Flags to control the behavior of the callback.
84314507e257af5d71577574e25cbd690c4b54c9272Jeff Brown     * May be zero or a combination of {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and
84466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * {@link #CALLBACK_FLAG_UNFILTERED_EVENTS}.
84566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     */
84666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    public void addCallback(int types, Callback cb, int flags) {
84766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        CallbackInfo info;
84866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        int index = findCallbackInfo(cb);
84966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        if (index >= 0) {
85066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info = sStatic.mCallbacks.get(index);
85166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info.type |= types;
85266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info.flags |= flags;
85366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        } else {
85466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info = new CallbackInfo(cb, types, flags, this);
85566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            sStatic.mCallbacks.add(info);
85666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
85769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        sStatic.updateDiscoveryRequest();
8589a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
8599a1de308cea2d160778fd977825f10a07b49d738Adam Powell
8609a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
8619a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Remove the specified callback. It will no longer receive events about media routing.
8629a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
8639a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param cb Callback to remove
8649a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
8659a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void removeCallback(Callback cb) {
86666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        int index = findCallbackInfo(cb);
86766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        if (index >= 0) {
86869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            sStatic.mCallbacks.remove(index);
86969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            sStatic.updateDiscoveryRequest();
87066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        } else {
87166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            Log.w(TAG, "removeCallback(" + cb + "): callback not registered");
87266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
87366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    }
87466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
87566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    private int findCallbackInfo(Callback cb) {
876b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        final int count = sStatic.mCallbacks.size();
8779a1de308cea2d160778fd977825f10a07b49d738Adam Powell        for (int i = 0; i < count; i++) {
87866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            final CallbackInfo info = sStatic.mCallbacks.get(i);
87966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (info.cb == cb) {
88066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown                return i;
8819a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
8829a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
88366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        return -1;
8849a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
8859a1de308cea2d160778fd977825f10a07b49d738Adam Powell
886d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell    /**
887d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     * Select the specified route to use for output of the given media types.
8883afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * <p class="note">
8893afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * As API version 18, this function may be used to select any route.
8903afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * In prior versions, this function could only be used to select user
8913afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * routes and would ignore any attempt to select a system route.
8923afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * </p>
893d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     *
894d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     * @param types type flags indicating which types this route should be used for.
895d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     *              The route must support at least a subset.
896d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     * @param route Route to select
8979d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand     * @throws IllegalArgumentException if the given route is {@code null}
898d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     */
8999d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand    public void selectRoute(int types, @NonNull RouteInfo route) {
9009d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand        if (route == null) {
9019d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand            throw new IllegalArgumentException("Route cannot be null.");
9029d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand        }
90369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        selectRouteStatic(types, route, true);
9040d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    }
90569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
9060d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    /**
9070d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @hide internal use
9080d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     */
90969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void selectRouteInt(int types, RouteInfo route, boolean explicit) {
91069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        selectRouteStatic(types, route, explicit);
911b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
912b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
9139d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand    static void selectRouteStatic(int types, @NonNull RouteInfo route, boolean explicit) {
91418687b7a275b3782e63d52cdbe8ee9df191046e7Jae Seo        Log.v(TAG, "Selecting route: " + route);
9159d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand        assert(route != null);
916705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final RouteInfo oldRoute = sStatic.mSelectedRoute;
917705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (oldRoute == route) return;
9180abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        if (!route.matchesTypes(types)) {
9190d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            Log.w(TAG, "selectRoute ignored; cannot select route with supported types " +
9200d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                    typesToString(route.getSupportedTypes()) + " into route types " +
9210d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                    typesToString(types));
9224ee1f55ce0f4909a7430ab44563a81852f335071Adam Powell            return;
9230d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
9249a1de308cea2d160778fd977825f10a07b49d738Adam Powell
925fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo        final RouteInfo btRoute = sStatic.mBluetoothA2dpRoute;
926fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo        if (btRoute != null && (types & ROUTE_TYPE_LIVE_AUDIO) != 0 &&
927fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                (route == btRoute || route == sStatic.mDefaultAudioVideo)) {
928fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            try {
929fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                sStatic.mAudioService.setBluetoothA2dpOn(route == btRoute);
930fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            } catch (RemoteException e) {
931fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                Log.e(TAG, "Error changing Bluetooth A2DP state", e);
932fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            }
933fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo        }
934fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo
935705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final WifiDisplay activeDisplay =
936705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                sStatic.mDisplayService.getWifiDisplayStatus().getActiveDisplay();
937705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final boolean oldRouteHasAddress = oldRoute != null && oldRoute.mDeviceAddress != null;
9389d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand        final boolean newRouteHasAddress = route.mDeviceAddress != null;
939705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (activeDisplay != null || oldRouteHasAddress || newRouteHasAddress) {
940705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (newRouteHasAddress && !matchesDeviceAddress(activeDisplay, route)) {
941af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (sStatic.mCanConfigureWifiDisplays) {
942af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    sStatic.mDisplayService.connectWifiDisplay(route.mDeviceAddress);
943af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                } else {
944af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    Log.e(TAG, "Cannot connect to wifi displays because this process "
945af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                            + "is not allowed to do so.");
946af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
947705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            } else if (activeDisplay != null && !newRouteHasAddress) {
948705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                sStatic.mDisplayService.disconnectWifiDisplay();
949705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
950705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
951705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
95269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        sStatic.setSelectedRoute(route, explicit);
95369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
954705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (oldRoute != null) {
955705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            dispatchRouteUnselected(types & oldRoute.getSupportedTypes(), oldRoute);
95639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (oldRoute.resolveStatusCode()) {
95739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                dispatchRouteChanged(oldRoute);
95839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
9599a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
9609a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if (route != null) {
96139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (route.resolveStatusCode()) {
96239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                dispatchRouteChanged(route);
96339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
9649a1de308cea2d160778fd977825f10a07b49d738Adam Powell            dispatchRouteSelected(types & route.getSupportedTypes(), route);
9659a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
966ce468a35b388ca46578934706b38dbae94941643Jeff Brown
967ce468a35b388ca46578934706b38dbae94941643Jeff Brown        // The behavior of active scans may depend on the currently selected route.
968ce468a35b388ca46578934706b38dbae94941643Jeff Brown        sStatic.updateDiscoveryRequest();
9699a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
9709a1de308cea2d160778fd977825f10a07b49d738Adam Powell
97169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static void selectDefaultRouteStatic() {
97269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // TODO: Be smarter about the route types here; this selects for all valid.
97369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (sStatic.mSelectedRoute != sStatic.mBluetoothA2dpRoute
974fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                && sStatic.mBluetoothA2dpRoute != null && sStatic.isBluetoothA2dpOn()) {
97569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mBluetoothA2dpRoute, false);
97669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        } else {
97769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mDefaultAudioVideo, false);
97869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
97969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
98069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
9819a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
982705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * Compare the device address of a display and a route.
983705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * Nulls/no device address will match another null/no address.
984705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     */
985705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    static boolean matchesDeviceAddress(WifiDisplay display, RouteInfo info) {
986705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final boolean routeHasAddress = info != null && info.mDeviceAddress != null;
987705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (display == null && !routeHasAddress) {
988705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return true;
989705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
990705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
991705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (display != null && routeHasAddress) {
992705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return display.getDeviceAddress().equals(info.mDeviceAddress);
993705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
994705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return false;
995705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
996705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
997705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    /**
9989a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Add an app-specified route for media to the MediaRouter.
9999a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * App-specified route definitions are created using {@link #createUserRoute(RouteCategory)}
10009a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
10019a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param info Definition of the route to add
10023afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * @see #createUserRoute(RouteCategory)
10039a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #removeUserRoute(UserRouteInfo)
10049a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
10059a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void addUserRoute(UserRouteInfo info) {
10062ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        addRouteStatic(info);
10079a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
10089a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1009d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    /**
1010d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     * @hide Framework use only
1011d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     */
1012d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    public void addRouteInt(RouteInfo info) {
10132ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        addRouteStatic(info);
1014d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    }
1015d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
10162ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell    static void addRouteStatic(RouteInfo info) {
101718687b7a275b3782e63d52cdbe8ee9df191046e7Jae Seo        Log.v(TAG, "Adding route: " + info);
10189a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final RouteCategory cat = info.getCategory();
1019b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        if (!sStatic.mCategories.contains(cat)) {
1020b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            sStatic.mCategories.add(cat);
10219a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
1022d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        if (cat.isGroupable() && !(info instanceof RouteGroup)) {
10239a1de308cea2d160778fd977825f10a07b49d738Adam Powell            // Enforce that any added route in a groupable category must be in a group.
10249a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final RouteGroup group = new RouteGroup(info.getCategory());
1025dbbfa702a09f6d2d36dee1b552442d04a4673f89Adam Powell            group.mSupportedTypes = info.mSupportedTypes;
1026b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            sStatic.mRoutes.add(group);
1027d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteAdded(group);
1028b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            group.addRoute(info);
1029d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
10309a1de308cea2d160778fd977825f10a07b49d738Adam Powell            info = group;
1031d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        } else {
1032b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            sStatic.mRoutes.add(info);
1033d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteAdded(info);
10349a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
10359a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
10369a1de308cea2d160778fd977825f10a07b49d738Adam Powell
10379a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
10389a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Remove an app-specified route for media from the MediaRouter.
10399a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
10409a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param info Definition of the route to remove
10419a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #addUserRoute(UserRouteInfo)
10429a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
10439a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void removeUserRoute(UserRouteInfo info) {
104469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        removeRouteStatic(info);
10459a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
10469a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1047690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    /**
1048690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * Remove all app-specified routes from the MediaRouter.
1049690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     *
1050690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * @see #removeUserRoute(UserRouteInfo)
1051690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     */
1052690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public void clearUserRoutes() {
1053b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        for (int i = 0; i < sStatic.mRoutes.size(); i++) {
1054b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final RouteInfo info = sStatic.mRoutes.get(i);
1055d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            // TODO Right now, RouteGroups only ever contain user routes.
1056d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            // The code below will need to change if this assumption does.
1057d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            if (info instanceof UserRouteInfo || info instanceof RouteGroup) {
105869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                removeRouteStatic(info);
1059690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell                i--;
1060690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            }
1061690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
1062690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
1063690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
1064d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    /**
1065d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     * @hide internal use only
1066d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     */
1067d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    public void removeRouteInt(RouteInfo info) {
106869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        removeRouteStatic(info);
1069d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    }
1070d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
107169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static void removeRouteStatic(RouteInfo info) {
107218687b7a275b3782e63d52cdbe8ee9df191046e7Jae Seo        Log.v(TAG, "Removing route: " + info);
1073b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        if (sStatic.mRoutes.remove(info)) {
10749a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final RouteCategory removingCat = info.getCategory();
1075b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final int count = sStatic.mRoutes.size();
10769a1de308cea2d160778fd977825f10a07b49d738Adam Powell            boolean found = false;
10779a1de308cea2d160778fd977825f10a07b49d738Adam Powell            for (int i = 0; i < count; i++) {
1078b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                final RouteCategory cat = sStatic.mRoutes.get(i).getCategory();
10799a1de308cea2d160778fd977825f10a07b49d738Adam Powell                if (removingCat == cat) {
10809a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    found = true;
10819a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    break;
10829a1de308cea2d160778fd977825f10a07b49d738Adam Powell                }
10839a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
10840abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            if (info.isSelected()) {
1085d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                // Removing the currently selected route? Select the default before we remove it.
108669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                selectDefaultRouteStatic();
1087d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            }
1088690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            if (!found) {
1089b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                sStatic.mCategories.remove(removingCat);
1090690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            }
1091690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            dispatchRouteRemoved(info);
1092690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
1093690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
1094690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
10959a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
10969a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the number of {@link MediaRouter.RouteCategory categories} currently
10979a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * represented by routes known to this MediaRouter.
10989a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
10999a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the number of unique categories represented by this MediaRouter's known routes
11009a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11019a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public int getCategoryCount() {
1102b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mCategories.size();
11039a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11049a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11059a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11069a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the {@link MediaRouter.RouteCategory category} at the given index.
11079a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Valid indices are in the range [0-getCategoryCount).
11089a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11099a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param index which category to return
11109a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the category at index
11119a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11129a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteCategory getCategoryAt(int index) {
1113b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mCategories.get(index);
11149a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11159a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11169a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11179a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the number of {@link MediaRouter.RouteInfo routes} currently known
11189a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * to this MediaRouter.
11199a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11209a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the number of routes tracked by this router
11219a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11229a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public int getRouteCount() {
1123b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.size();
11249a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11259a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11269a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11279a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the route at the specified index.
11289a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11299a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param index index of the route to return
11309a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the route at index
11319a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11329a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteInfo getRouteAt(int index) {
1133b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.get(index);
1134b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
1135b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
1136b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static int getRouteCountStatic() {
1137b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.size();
1138b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
1139b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
1140b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static RouteInfo getRouteAtStatic(int index) {
1141b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.get(index);
11429a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11439a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11449a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11459a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Create a new user route that may be modified and registered for use by the application.
11469a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11479a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param category The category the new route will belong to
11489a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return A new UserRouteInfo for use by the application
11499a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11509a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #addUserRoute(UserRouteInfo)
11519a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #removeUserRoute(UserRouteInfo)
115269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @see #createRouteCategory(CharSequence, boolean)
11539a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11549a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public UserRouteInfo createUserRoute(RouteCategory category) {
11559a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return new UserRouteInfo(category);
11569a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11579a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11589a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11599a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Create a new route category. Each route must belong to a category.
11609a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11619a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param name Name of the new category
11629a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param isGroupable true if routes in this category may be grouped with one another
11639a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the new RouteCategory
11649a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11659a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteCategory createRouteCategory(CharSequence name, boolean isGroupable) {
11669a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return new RouteCategory(name, ROUTE_TYPE_USER, isGroupable);
11679a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11685d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
11690d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    /**
11700d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * Create a new route category. Each route must belong to a category.
11710d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     *
11720d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @param nameResId Resource ID of the name of the new category
11730d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @param isGroupable true if routes in this category may be grouped with one another
11740d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @return the new RouteCategory
11750d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     */
11760d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    public RouteCategory createRouteCategory(int nameResId, boolean isGroupable) {
11770d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        return new RouteCategory(nameResId, ROUTE_TYPE_USER, isGroupable);
11780d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    }
11799a1de308cea2d160778fd977825f10a07b49d738Adam Powell
118069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
118169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Rebinds the media router to handle routes that belong to the specified user.
118269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Requires the interact across users permission to access the routes of another user.
118369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * <p>
118469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * This method is a complete hack to work around the singleton nature of the
118569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * media router when running inside of singleton processes like QuickSettings.
118669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * This mechanism should be burned to the ground when MediaRouter is redesigned.
118769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Ideally the current user would be pulled from the Context but we need to break
118869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * down MediaRouter.Static before we can get there.
118969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * </p>
119069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     *
119169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide
119269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
119369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void rebindAsUser(int userId) {
119469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        sStatic.rebindAsUser(userId);
119569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
119669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
1197b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void updateRoute(final RouteInfo info) {
11989a1de308cea2d160778fd977825f10a07b49d738Adam Powell        dispatchRouteChanged(info);
11999a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12009a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1201b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteSelected(int type, RouteInfo info) {
120239d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
120366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1204b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteSelected(cbi.router, type, info);
12059a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
12069a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12079a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12089a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1209b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteUnselected(int type, RouteInfo info) {
121039d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
121166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1212b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteUnselected(cbi.router, type, info);
12139a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
12149a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12159a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12169a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1217b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteChanged(RouteInfo info) {
1218af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        dispatchRouteChanged(info, info.mSupportedTypes);
1219af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown    }
1220af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
1221af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown    static void dispatchRouteChanged(RouteInfo info, int oldSupportedTypes) {
122218687b7a275b3782e63d52cdbe8ee9df191046e7Jae Seo        Log.v(TAG, "Dispatching route change: " + info);
1223af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        final int newSupportedTypes = info.mSupportedTypes;
122439d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
1225af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Reconstruct some of the history for callbacks that may not have observed
1226af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // all of the events needed to correctly interpret the current state.
1227af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // FIXME: This is a strong signal that we should deprecate route type filtering
1228af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // completely in the future because it can lead to inconsistencies in
1229af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // applications.
1230af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            final boolean oldVisibility = cbi.filterRouteEvent(oldSupportedTypes);
1231af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            final boolean newVisibility = cbi.filterRouteEvent(newSupportedTypes);
1232af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (!oldVisibility && newVisibility) {
1233af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                cbi.cb.onRouteAdded(cbi.router, info);
1234af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (info.isSelected()) {
1235af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    cbi.cb.onRouteSelected(cbi.router, newSupportedTypes, info);
1236af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
1237af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            }
1238af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (oldVisibility || newVisibility) {
1239b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteChanged(cbi.router, info);
12409a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
1241af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (oldVisibility && !newVisibility) {
1242af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (info.isSelected()) {
1243af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    cbi.cb.onRouteUnselected(cbi.router, oldSupportedTypes, info);
1244af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
1245af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                cbi.cb.onRouteRemoved(cbi.router, info);
1246af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            }
12479a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12489a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12499a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1250b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteAdded(RouteInfo info) {
125139d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
125266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1253b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteAdded(cbi.router, info);
12549a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
12559a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12569a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12579a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1258b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteRemoved(RouteInfo info) {
125939d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
126066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1261b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteRemoved(cbi.router, info);
12629a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
12639a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12649a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12659a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1266b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteGrouped(RouteInfo info, RouteGroup group, int index) {
126739d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
126866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(group)) {
1269b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteGrouped(cbi.router, info, group, index);
1270d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            }
1271d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        }
1272d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell    }
1273d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
1274b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteUngrouped(RouteInfo info, RouteGroup group) {
127539d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
127666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(group)) {
1277b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteUngrouped(cbi.router, info, group);
12789a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
12799a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12809a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12819a1de308cea2d160778fd977825f10a07b49d738Adam Powell
12828e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    static void dispatchRouteVolumeChanged(RouteInfo info) {
12838e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
128466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
12858e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                cbi.cb.onRouteVolumeChanged(cbi.router, info);
12868e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
12878e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
12888e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    }
12898e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
129092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown    static void dispatchRoutePresentationDisplayChanged(RouteInfo info) {
129192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        for (CallbackInfo cbi : sStatic.mCallbacks) {
129266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
129392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                cbi.cb.onRoutePresentationDisplayChanged(cbi.router, info);
129492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            }
129592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
129692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown    }
129792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
12988e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    static void systemVolumeChanged(int newValue) {
12998e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        final RouteInfo selectedRoute = sStatic.mSelectedRoute;
13008e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        if (selectedRoute == null) return;
13018e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
13028e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        if (selectedRoute == sStatic.mBluetoothA2dpRoute ||
1303705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                selectedRoute == sStatic.mDefaultAudioVideo) {
13048e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            dispatchRouteVolumeChanged(selectedRoute);
13058e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        } else if (sStatic.mBluetoothA2dpRoute != null) {
1306fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            try {
1307fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                dispatchRouteVolumeChanged(sStatic.mAudioService.isBluetoothA2dpOn() ?
1308fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                        sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo);
1309fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            } catch (RemoteException e) {
1310fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                Log.e(TAG, "Error checking Bluetooth A2DP state to report volume change", e);
1311fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            }
13128e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        } else {
1313705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            dispatchRouteVolumeChanged(sStatic.mDefaultAudioVideo);
1314705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1315705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1316705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
131775af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    static void updateWifiDisplayStatus(WifiDisplayStatus status) {
131875af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        WifiDisplay[] displays;
1319615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell        WifiDisplay activeDisplay;
132075af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        if (status.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) {
132175af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            displays = status.getDisplays();
132275af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            activeDisplay = status.getActiveDisplay();
1323af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
1324af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Only the system is able to connect to wifi display routes.
1325af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // The display manager will enforce this with a permission check but it
1326af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // still publishes information about all available displays.
1327af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Filter the list down to just the active display.
1328af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (!sStatic.mCanConfigureWifiDisplays) {
1329af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (activeDisplay != null) {
1330af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    displays = new WifiDisplay[] { activeDisplay };
1331af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                } else {
1332af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    displays = WifiDisplay.EMPTY_ARRAY;
1333af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
1334af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            }
1335615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell        } else {
133675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            displays = WifiDisplay.EMPTY_ARRAY;
1337615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell            activeDisplay = null;
1338615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell        }
1339ce468a35b388ca46578934706b38dbae94941643Jeff Brown        String activeDisplayAddress = activeDisplay != null ?
1340ce468a35b388ca46578934706b38dbae94941643Jeff Brown                activeDisplay.getDeviceAddress() : null;
1341705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
134275af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        // Add or update routes.
134375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        for (int i = 0; i < displays.length; i++) {
134475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            final WifiDisplay d = displays[i];
134575af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            if (shouldShowWifiDisplay(d, activeDisplay)) {
1346ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                RouteInfo route = findWifiDisplayRoute(d);
1347ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                if (route == null) {
134875af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                    route = makeWifiDisplayRoute(d, status);
1349ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                    addRouteStatic(route);
1350ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                } else {
1351ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    String address = d.getDeviceAddress();
1352ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    boolean disconnected = !address.equals(activeDisplayAddress)
1353ce468a35b388ca46578934706b38dbae94941643Jeff Brown                            && address.equals(sStatic.mPreviousActiveWifiDisplayAddress);
1354ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    updateWifiDisplayRoute(route, d, status, disconnected);
1355ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                }
1356ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                if (d.equals(activeDisplay)) {
135769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    selectRouteStatic(route.getSupportedTypes(), route, false);
1358ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                }
13592ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            }
1360705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
136175af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown
136275af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        // Remove stale routes.
136375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        for (int i = sStatic.mRoutes.size(); i-- > 0; ) {
136475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            RouteInfo route = sStatic.mRoutes.get(i);
136575af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            if (route.mDeviceAddress != null) {
136675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                WifiDisplay d = findWifiDisplay(displays, route.mDeviceAddress);
136775af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                if (d == null || !shouldShowWifiDisplay(d, activeDisplay)) {
136875af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                    removeRouteStatic(route);
1369ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                }
1370705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1371705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1372ce468a35b388ca46578934706b38dbae94941643Jeff Brown
1373ce468a35b388ca46578934706b38dbae94941643Jeff Brown        // Remember the current active wifi display address so that we can infer disconnections.
1374ce468a35b388ca46578934706b38dbae94941643Jeff Brown        // TODO: This hack will go away once all of this is moved into the media router service.
1375ce468a35b388ca46578934706b38dbae94941643Jeff Brown        sStatic.mPreviousActiveWifiDisplayAddress = activeDisplayAddress;
137675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    }
1377705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
137875af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    private static boolean shouldShowWifiDisplay(WifiDisplay d, WifiDisplay activeDisplay) {
137975af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        return d.isRemembered() || d.equals(activeDisplay);
1380705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1381705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1382ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    static int getWifiDisplayStatusCode(WifiDisplay d, WifiDisplayStatus wfdStatus) {
138369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        int newStatus;
1384ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        if (wfdStatus.getScanState() == WifiDisplayStatus.SCAN_STATE_SCANNING) {
1385ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang            newStatus = RouteInfo.STATUS_SCANNING;
1386ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        } else if (d.isAvailable()) {
1387ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang            newStatus = d.canConnect() ?
1388ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                    RouteInfo.STATUS_AVAILABLE: RouteInfo.STATUS_IN_USE;
1389705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        } else {
1390705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            newStatus = RouteInfo.STATUS_NOT_AVAILABLE;
1391705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1392705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1393ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        if (d.equals(wfdStatus.getActiveDisplay())) {
1394ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang            final int activeState = wfdStatus.getActiveDisplayState();
1395705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            switch (activeState) {
1396705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                case WifiDisplayStatus.DISPLAY_STATE_CONNECTED:
139769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    newStatus = RouteInfo.STATUS_CONNECTED;
1398705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    break;
1399705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                case WifiDisplayStatus.DISPLAY_STATE_CONNECTING:
1400705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    newStatus = RouteInfo.STATUS_CONNECTING;
1401705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    break;
1402705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                case WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED:
1403705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    Log.e(TAG, "Active display is not connected!");
1404705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    break;
1405705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1406705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1407705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1408ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        return newStatus;
1409ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    }
1410ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
1411ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    static boolean isWifiDisplayEnabled(WifiDisplay d, WifiDisplayStatus wfdStatus) {
1412ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        return d.isAvailable() && (d.canConnect() || d.equals(wfdStatus.getActiveDisplay()));
1413ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    }
1414ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
1415ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    static RouteInfo makeWifiDisplayRoute(WifiDisplay display, WifiDisplayStatus wfdStatus) {
1416ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        final RouteInfo newRoute = new RouteInfo(sStatic.mSystemCategory);
1417ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mDeviceAddress = display.getDeviceAddress();
141869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        newRoute.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO
141969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                | ROUTE_TYPE_REMOTE_DISPLAY;
1420ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mVolumeHandling = RouteInfo.PLAYBACK_VOLUME_FIXED;
1421ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mPlaybackType = RouteInfo.PLAYBACK_TYPE_REMOTE;
1422ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
142339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        newRoute.setRealStatusCode(getWifiDisplayStatusCode(display, wfdStatus));
1424ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mEnabled = isWifiDisplayEnabled(display, wfdStatus);
1425ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mName = display.getFriendlyDisplayName();
1426ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mDescription = sStatic.mResources.getText(
1427ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                com.android.internal.R.string.wireless_display_route_description);
14285830b0b33618940d65197cec99d697b21908fec8Chong Zhang        newRoute.updatePresentationDisplay();
14299dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        newRoute.mDeviceType = RouteInfo.DEVICE_TYPE_TV;
1430ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        return newRoute;
1431ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    }
1432ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
1433ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    private static void updateWifiDisplayRoute(
1434ce468a35b388ca46578934706b38dbae94941643Jeff Brown            RouteInfo route, WifiDisplay display, WifiDisplayStatus wfdStatus,
1435ce468a35b388ca46578934706b38dbae94941643Jeff Brown            boolean disconnected) {
1436ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        boolean changed = false;
14372444ae7e2b8658a4a90f996e678423558744b4a2Jeff Brown        final String newName = display.getFriendlyDisplayName();
14382444ae7e2b8658a4a90f996e678423558744b4a2Jeff Brown        if (!route.getName().equals(newName)) {
1439705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            route.mName = newName;
1440705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            changed = true;
1441705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1442705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1443ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        boolean enabled = isWifiDisplayEnabled(display, wfdStatus);
1444ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        changed |= route.mEnabled != enabled;
1445ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        route.mEnabled = enabled;
1446705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
144739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        changed |= route.setRealStatusCode(getWifiDisplayStatusCode(display, wfdStatus));
1448705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1449705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (changed) {
1450705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            dispatchRouteChanged(route);
1451705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1452705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1453ce468a35b388ca46578934706b38dbae94941643Jeff Brown        if ((!enabled || disconnected) && route.isSelected()) {
1454705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            // Oops, no longer available. Reselect the default.
145569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            selectDefaultRouteStatic();
1456705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1457705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1458705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
145975af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    private static WifiDisplay findWifiDisplay(WifiDisplay[] displays, String deviceAddress) {
1460705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        for (int i = 0; i < displays.length; i++) {
146175af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            final WifiDisplay d = displays[i];
146275af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            if (d.getDeviceAddress().equals(deviceAddress)) {
146375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                return d;
1464705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1465705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1466705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return null;
1467705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1468705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1469705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    private static RouteInfo findWifiDisplayRoute(WifiDisplay d) {
1470705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final int count = sStatic.mRoutes.size();
1471705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        for (int i = 0; i < count; i++) {
1472705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            final RouteInfo info = sStatic.mRoutes.get(i);
1473705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (d.getDeviceAddress().equals(info.mDeviceAddress)) {
1474705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                return info;
1475705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1476705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1477705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return null;
14788e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    }
14798e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
14809a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
14819a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a media route.
14829a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
1483b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class RouteInfo {
14849a1de308cea2d160778fd977825f10a07b49d738Adam Powell        CharSequence mName;
14850d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        int mNameResId;
148656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        CharSequence mDescription;
14879a1de308cea2d160778fd977825f10a07b49d738Adam Powell        private CharSequence mStatus;
14889a1de308cea2d160778fd977825f10a07b49d738Adam Powell        int mSupportedTypes;
14899dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        int mDeviceType;
14909a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteGroup mGroup;
14919a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final RouteCategory mCategory;
1492ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        Drawable mIcon;
14931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        // playback information
14941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mPlaybackType = PLAYBACK_TYPE_LOCAL;
14951357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
14961357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
14971357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
14981357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mPlaybackStream = AudioManager.STREAM_MUSIC;
14991357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        VolumeCallbackInfo mVcb;
150092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        Display mPresentationDisplay;
15015830b0b33618940d65197cec99d697b21908fec8Chong Zhang        int mPresentationDisplayId = -1;
15029a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1503705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        String mDeviceAddress;
1504705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        boolean mEnabled = true;
1505705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
150669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // An id by which the route is known to the media router service.
150769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // Null if this route only exists as an artifact within this process.
150869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        String mGlobalRouteId;
150969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
1510705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        // A predetermined connection status that can override mStatus
151139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private int mRealStatusCode;
151239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private int mResolvedStatusCode;
1513705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
15142ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_NONE = 0;
15152ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_SCANNING = 1;
15162ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_CONNECTING = 2;
15172ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_AVAILABLE = 3;
15182ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_NOT_AVAILABLE = 4;
1519ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        /** @hide */ public static final int STATUS_IN_USE = 5;
152069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        /** @hide */ public static final int STATUS_CONNECTED = 6;
1521705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
15229dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /** @hide */
15239dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        @IntDef({DEVICE_TYPE_UNKNOWN, DEVICE_TYPE_TV, DEVICE_TYPE_SPEAKER, DEVICE_TYPE_BLUETOOTH})
15249dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        @Retention(RetentionPolicy.SOURCE)
15259dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public @interface DeviceType {}
15269dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
15279dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /**
15289dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * The default receiver device type of the route indicating the type is unknown.
15299dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         *
15309dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * @see #getDeviceType
15319dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         */
15329dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public static final int DEVICE_TYPE_UNKNOWN = 0;
15339dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
15349dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /**
15359dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * A receiver device type of the route indicating the presentation of the media is happening
15369dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * on a TV.
15379dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         *
15389dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * @see #getDeviceType
15399dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         */
15409dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public static final int DEVICE_TYPE_TV = 1;
15419dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
15429dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /**
15439dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * A receiver device type of the route indicating the presentation of the media is happening
15449dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * on a speaker.
15459dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         *
15469dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * @see #getDeviceType
15479dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         */
15489dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public static final int DEVICE_TYPE_SPEAKER = 2;
15499dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
15509dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /**
15519dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * A receiver device type of the route indicating the presentation of the media is happening
15529dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * on a bluetooth device such as a bluetooth speaker.
15539dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         *
15549dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * @see #getDeviceType
15559dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         */
15569dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public static final int DEVICE_TYPE_BLUETOOTH = 3;
15579dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
1558b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        private Object mTag;
1559b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
15607c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        /** @hide */
15617c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        @IntDef({PLAYBACK_TYPE_LOCAL, PLAYBACK_TYPE_REMOTE})
15627c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        @Retention(RetentionPolicy.SOURCE)
15637c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        public @interface PlaybackType {}
15647c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang
15651357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
15661357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * The default playback type, "local", indicating the presentation of the media is happening
1567bc4cf00dc5bf6e0e3c01206d5c46e64306df260aJohn Spurlock         * on the same device (e&#46;g&#46; a phone, a tablet) as where it is controlled from.
156869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getPlaybackType()
15691357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
15701357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_TYPE_LOCAL = 0;
15717c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang
15721357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
15731357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * A playback type indicating the presentation of the media is happening on
1574bc4cf00dc5bf6e0e3c01206d5c46e64306df260aJohn Spurlock         * a different device (i&#46;e&#46; the remote device) than where it is controlled from.
157569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getPlaybackType()
15761357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
15771357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_TYPE_REMOTE = 1;
15787c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang
15797c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        /** @hide */
15807c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang         @IntDef({PLAYBACK_VOLUME_FIXED,PLAYBACK_VOLUME_VARIABLE})
15817c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang         @Retention(RetentionPolicy.SOURCE)
15827c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang         private @interface PlaybackVolume {}
15837c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang
15841357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
1585bc4cf00dc5bf6e0e3c01206d5c46e64306df260aJohn Spurlock         * Playback information indicating the playback volume is fixed, i&#46;e&#46; it cannot be
15861357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * controlled from this object. An example of fixed playback volume is a remote player,
15871357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
15881357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * than attenuate at the source.
158969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getVolumeHandling()
15901357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
15911357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_VOLUME_FIXED = 0;
15921357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
15931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Playback information indicating the playback volume is variable and can be controlled
15941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * from this object.
159569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getVolumeHandling()
15961357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
15971357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_VOLUME_VARIABLE = 1;
15981357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
15999a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteInfo(RouteCategory category) {
16009a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mCategory = category;
16019dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang            mDeviceType = DEVICE_TYPE_UNKNOWN;
16029a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
16039a1de308cea2d160778fd977825f10a07b49d738Adam Powell
16049a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
160556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Gets the user-visible name of the route.
160656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
160756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route name identifies the destination represented by the route.
160856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied name, an alias, or device serial number.
160956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
161056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
161156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The user-visible name of a media route.  This is the string presented
16129a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * to users who may select this as the active route.
16139a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
16149a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getName() {
16150d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(sStatic.mResources);
16160d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
161756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
16180d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        /**
161956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Return the properly localized/resource user-visible name of this route.
162056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
162156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route name identifies the destination represented by the route.
162256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied name, an alias, or device serial number.
162356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
162456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
16250d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @param context Context used to resolve the correct configuration to load
162656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The user-visible name of a media route.  This is the string presented
16270d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * to users who may select this as the active route.
16280d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         */
16290d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public CharSequence getName(Context context) {
16300d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(context.getResources());
16310d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
163256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
16330d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        CharSequence getName(Resources res) {
16340d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            if (mNameResId != 0) {
1635eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon                return res.getText(mNameResId);
16360d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            }
16379a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mName;
16389a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
16399a1de308cea2d160778fd977825f10a07b49d738Adam Powell
16409a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
164156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Gets the user-visible description of the route.
164256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
164356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route description describes the kind of destination represented by the route.
164456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied string, a model number or brand of device.
164556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
164656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
164756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The description of the route, or null if none.
164856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         */
164956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        public CharSequence getDescription() {
165056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown            return mDescription;
165156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        }
165256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
165356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        /**
165456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The user-visible status for a media route. This may include a description
16559a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * of the currently playing media, if available.
16569a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
16579a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getStatus() {
16589a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mStatus;
16599a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
16609a1de308cea2d160778fd977825f10a07b49d738Adam Powell
16619a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
1662705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * Set this route's status by predetermined status code. If the caller
1663705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * should dispatch a route changed event this call will return true;
1664705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         */
166539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        boolean setRealStatusCode(int statusCode) {
166639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (mRealStatusCode != statusCode) {
166739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                mRealStatusCode = statusCode;
166839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                return resolveStatusCode();
166939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
167039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return false;
167139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        }
167239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown
167339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        /**
167439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown         * Resolves the status code whenever the real status code or selection state
167539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown         * changes.
167639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown         */
167739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        boolean resolveStatusCode() {
167839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            int statusCode = mRealStatusCode;
167939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (isSelected()) {
1680705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                switch (statusCode) {
168139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // If the route is selected and its status appears to be between states
168239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // then report it as connecting even though it has not yet had a chance
168339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // to officially move into the CONNECTING state.  Note that routes in
168439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // the NONE state are assumed to not require an explicit connection
168539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // lifecycle whereas those that are AVAILABLE are assumed to have
168639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // to eventually proceed to CONNECTED.
1687705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    case STATUS_AVAILABLE:
168839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    case STATUS_SCANNING:
168939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                        statusCode = STATUS_CONNECTING;
169069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        break;
1691705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                }
1692705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
169339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (mResolvedStatusCode == statusCode) {
169439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                return false;
169539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
169639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown
169739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            mResolvedStatusCode = statusCode;
169839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            int resId;
169939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            switch (statusCode) {
170039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_SCANNING:
170139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_scanning;
170239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
170339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_CONNECTING:
170439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_connecting;
170539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
170639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_AVAILABLE:
170739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_available;
170839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
170939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_NOT_AVAILABLE:
171039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_not_available;
171139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
171239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_IN_USE:
171339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_in_use;
171439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
171539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_CONNECTED:
171639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_NONE:
171739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                default:
171839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = 0;
171939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
172039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
172139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            mStatus = resId != 0 ? sStatic.mResources.getText(resId) : null;
172239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return true;
1723705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1724705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1725705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        /**
17262ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell         * @hide
17272ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell         */
17282ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        public int getStatusCode() {
172939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return mResolvedStatusCode;
17302ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        }
17312ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell
17322ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /**
17339a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return A media type flag set describing which types this route supports.
17349a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
17359a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int getSupportedTypes() {
17369a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mSupportedTypes;
17379a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
17389a1de308cea2d160778fd977825f10a07b49d738Adam Powell
17399dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /**
17409dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * Gets the type of the receiver device associated with this route.
17419dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         *
17429dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * @return The type of the receiver device associated with this route:
17439dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * {@link #DEVICE_TYPE_BLUETOOTH}, {@link #DEVICE_TYPE_TV}, {@link #DEVICE_TYPE_SPEAKER},
17449dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * or {@link #DEVICE_TYPE_UNKNOWN}.
17459dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         */
17469dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        @DeviceType
17479dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public int getDeviceType() {
17489dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang            return mDeviceType;
17499dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        }
17509dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
17510abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
17520abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public boolean matchesTypes(int types) {
17530abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            return (mSupportedTypes & types) != 0;
17540abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
17550abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
17569a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
17579a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return The group that this route belongs to.
17589a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
17599a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public RouteGroup getGroup() {
17609a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mGroup;
17619a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
17629a1de308cea2d160778fd977825f10a07b49d738Adam Powell
17639a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
17649a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return the category this route belongs to.
17659a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
17669a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public RouteCategory getCategory() {
17679a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mCategory;
17689a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
17699a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1770ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
1771ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Get the icon representing this route.
1772ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * This icon will be used in picker UIs if available.
1773ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
1774ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @return the icon representing this route or null if no icon is available
1775ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
1776ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public Drawable getIconDrawable() {
1777ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            return mIcon;
1778ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
1779ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
1780b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        /**
1781b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * Set an application-specific tag object for this route.
1782b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * The application may use this to store arbitrary data associated with the
1783b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * route for internal tracking.
1784b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         *
1785b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * <p>Note that the lifespan of a route may be well past the lifespan of
1786b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * an Activity or other Context; take care that objects you store here
1787b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * will not keep more data in memory alive than you intend.</p>
1788b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         *
1789b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * @param tag Arbitrary, app-specific data for this route to hold for later use
1790b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         */
1791b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        public void setTag(Object tag) {
1792b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            mTag = tag;
1793130b4572d1f3df702e5b296a655d15a41f6d4c66Adam Powell            routeUpdated();
1794b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        }
1795b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
1796b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        /**
1797b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * @return The tag object previously set by the application
1798b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * @see #setTag(Object)
1799b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         */
1800b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        public Object getTag() {
1801b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            return mTag;
1802b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        }
1803b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
18041357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
18051357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the type of playback associated with this route
18061357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setPlaybackType(int)
18071357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
18087c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        @PlaybackType
18091357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getPlaybackType() {
18101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            return mPlaybackType;
18111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
18121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
18131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
18141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the stream over which the playback associated with this route is performed
18151357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setPlaybackStream(int)
18161357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
18171357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getPlaybackStream() {
18181357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            return mPlaybackStream;
18191357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
18201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
18211357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
18228e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Return the current volume for this route. Depending on the route, this may only
18238e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * be valid if the route is currently selected.
18248e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         *
18251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the volume at which the playback associated with this route is performed
18261357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setVolume(int)
18271357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
18281357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getVolume() {
18291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
18301357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                int vol = 0;
18311357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                try {
18321357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    vol = sStatic.mAudioService.getStreamVolume(mPlaybackStream);
18331357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                } catch (RemoteException e) {
18341357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    Log.e(TAG, "Error getting local stream volume", e);
18351357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                }
18361357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return vol;
18371357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            } else {
18381357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return mVolume;
18391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
18401357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
18411357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
18421357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
18438e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Request a volume change for this route.
18448e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param volume value between 0 and getVolumeMax
18458e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         */
18468e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestSetVolume(int volume) {
18478e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
18488e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                try {
1849ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                    sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0,
1850ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                            ActivityThread.currentPackageName());
18518e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                } catch (RemoteException e) {
18528e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Error setting local stream volume", e);
18538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
18548e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            } else {
185569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                sStatic.requestSetVolume(this, volume);
18568e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
18578e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
18588e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
18598e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        /**
18608e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Request an incremental volume update for this route.
18618e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param direction Delta to apply to the current volume
18628e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         */
18638e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestUpdateVolume(int direction) {
18648e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
18658e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                try {
18668e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    final int volume =
18678e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                            Math.max(0, Math.min(getVolume() + direction, getVolumeMax()));
1868ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                    sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0,
1869ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                            ActivityThread.currentPackageName());
18708e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                } catch (RemoteException e) {
18718e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Error setting local stream volume", e);
18728e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
18738e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            } else {
187469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                sStatic.requestUpdateVolume(this, direction);
18758e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
18768e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
18778e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
18788e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        /**
18791357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the maximum volume at which the playback associated with this route is performed
18801357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setVolumeMax(int)
18811357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
18821357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getVolumeMax() {
18831357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
18841357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                int volMax = 0;
18851357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                try {
18861357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    volMax = sStatic.mAudioService.getStreamMaxVolume(mPlaybackStream);
18871357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                } catch (RemoteException e) {
18881357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    Log.e(TAG, "Error getting local stream volume", e);
18891357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                }
18901357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return volMax;
18911357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            } else {
18921357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return mVolumeMax;
18931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
18941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
18951357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
18961357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
18971357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return how volume is handling on the route
18981357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setVolumeHandling(int)
18991357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
19007c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        @PlaybackVolume
19011357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getVolumeHandling() {
19021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            return mVolumeHandling;
19031357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
19041357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
1905705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        /**
190692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * Gets the {@link Display} that should be used by the application to show
190792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * a {@link android.app.Presentation} on an external display when this route is selected.
190892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * Depending on the route, this may only be valid if the route is currently
190992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * selected.
191092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * <p>
191192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * The preferred presentation display may change independently of the route
191292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * being selected or unselected.  For example, the presentation display
191392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * of the default system route may change when an external HDMI display is connected
191492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * or disconnected even though the route itself has not changed.
191592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p><p>
191692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * This method may return null if there is no external display associated with
191792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * the route or if the display is not ready to show UI yet.
191892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p><p>
191992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * The application should listen for changes to the presentation display
192092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * using the {@link Callback#onRoutePresentationDisplayChanged} callback and
192192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * show or dismiss its {@link android.app.Presentation} accordingly when the display
192292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * becomes available or is removed.
192392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p><p>
192492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * This method only makes sense for {@link #ROUTE_TYPE_LIVE_VIDEO live video} routes.
192592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p>
192692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
192792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @return The preferred presentation display to use when this route is
192892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * selected or null if none.
192992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
193092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @see #ROUTE_TYPE_LIVE_VIDEO
193192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @see android.app.Presentation
193292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         */
193392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public Display getPresentationDisplay() {
193492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            return mPresentationDisplay;
193592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
193692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
19375830b0b33618940d65197cec99d697b21908fec8Chong Zhang        boolean updatePresentationDisplay() {
19385830b0b33618940d65197cec99d697b21908fec8Chong Zhang            Display display = choosePresentationDisplay();
19395830b0b33618940d65197cec99d697b21908fec8Chong Zhang            if (mPresentationDisplay != display) {
19405830b0b33618940d65197cec99d697b21908fec8Chong Zhang                mPresentationDisplay = display;
19415830b0b33618940d65197cec99d697b21908fec8Chong Zhang                return true;
19425830b0b33618940d65197cec99d697b21908fec8Chong Zhang            }
19435830b0b33618940d65197cec99d697b21908fec8Chong Zhang            return false;
19445830b0b33618940d65197cec99d697b21908fec8Chong Zhang        }
19455830b0b33618940d65197cec99d697b21908fec8Chong Zhang
19465830b0b33618940d65197cec99d697b21908fec8Chong Zhang        private Display choosePresentationDisplay() {
19475830b0b33618940d65197cec99d697b21908fec8Chong Zhang            if ((mSupportedTypes & ROUTE_TYPE_LIVE_VIDEO) != 0) {
19485830b0b33618940d65197cec99d697b21908fec8Chong Zhang                Display[] displays = sStatic.getAllPresentationDisplays();
19495830b0b33618940d65197cec99d697b21908fec8Chong Zhang
19505830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // Ensure that the specified display is valid for presentations.
19515830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // This check will normally disallow the default display unless it was
19525830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // configured as a presentation display for some reason.
19535830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (mPresentationDisplayId >= 0) {
19545830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    for (Display display : displays) {
19555830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        if (display.getDisplayId() == mPresentationDisplayId) {
19565830b0b33618940d65197cec99d697b21908fec8Chong Zhang                            return display;
19575830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        }
19585830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    }
19595830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    return null;
19605830b0b33618940d65197cec99d697b21908fec8Chong Zhang                }
19615830b0b33618940d65197cec99d697b21908fec8Chong Zhang
19625830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // Find the indicated Wifi display by its address.
19635830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (mDeviceAddress != null) {
19645830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    for (Display display : displays) {
19655830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        if (display.getType() == Display.TYPE_WIFI
19665830b0b33618940d65197cec99d697b21908fec8Chong Zhang                                && mDeviceAddress.equals(display.getAddress())) {
19675830b0b33618940d65197cec99d697b21908fec8Chong Zhang                            return display;
19685830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        }
19695830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    }
19705830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    return null;
19715830b0b33618940d65197cec99d697b21908fec8Chong Zhang                }
19725830b0b33618940d65197cec99d697b21908fec8Chong Zhang
19735830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // For the default route, choose the first presentation display from the list.
19745830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (this == sStatic.mDefaultAudioVideo && displays.length > 0) {
19755830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    return displays[0];
19765830b0b33618940d65197cec99d697b21908fec8Chong Zhang                }
19775830b0b33618940d65197cec99d697b21908fec8Chong Zhang            }
19785830b0b33618940d65197cec99d697b21908fec8Chong Zhang            return null;
19795830b0b33618940d65197cec99d697b21908fec8Chong Zhang        }
19805830b0b33618940d65197cec99d697b21908fec8Chong Zhang
198175af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        /** @hide */
198275af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        public String getDeviceAddress() {
198375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            return mDeviceAddress;
198475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        }
198575af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown
198692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        /**
1987a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * Returns true if this route is enabled and may be selected.
1988a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         *
1989a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * @return True if this route is enabled.
1990705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         */
1991705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        public boolean isEnabled() {
1992705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return mEnabled;
1993705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1994705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1995a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown        /**
1996a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * Returns true if the route is in the process of connecting and is not
1997a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * yet ready for use.
1998a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         *
1999a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * @return True if this route is in the process of connecting.
2000a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         */
2001a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown        public boolean isConnecting() {
200239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return mResolvedStatusCode == STATUS_CONNECTING;
20030abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
20040abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
20050abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
20060abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public boolean isSelected() {
20070abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            return this == sStatic.mSelectedRoute;
20080abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
20090abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
20100abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
20110abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public boolean isDefault() {
20120abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            return this == sStatic.mDefaultAudioVideo;
20130abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
20140abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
20150abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
20160abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public void select() {
20170abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            selectRouteStatic(mSupportedTypes, this, true);
2018a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown        }
2019a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown
20209a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void setStatusInt(CharSequence status) {
20219a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (!status.equals(mStatus)) {
20229a1de308cea2d160778fd977825f10a07b49d738Adam Powell                mStatus = status;
20239a1de308cea2d160778fd977825f10a07b49d738Adam Powell                if (mGroup != null) {
20249a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    mGroup.memberStatusChanged(this, status);
20259a1de308cea2d160778fd977825f10a07b49d738Adam Powell                }
20269a1de308cea2d160778fd977825f10a07b49d738Adam Powell                routeUpdated();
20279a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
20289a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
20299a1de308cea2d160778fd977825f10a07b49d738Adam Powell
20301357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        final IRemoteVolumeObserver.Stub mRemoteVolObserver = new IRemoteVolumeObserver.Stub() {
203169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            @Override
20321357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            public void dispatchRemoteVolumeUpdate(final int direction, final int value) {
20331357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                sStatic.mHandler.post(new Runnable() {
20341357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    @Override
20351357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    public void run() {
20361357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                        if (mVcb != null) {
20371357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                            if (direction != 0) {
20381357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                                mVcb.vcb.onVolumeUpdateRequest(mVcb.route, direction);
20391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                            } else {
20401357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                                mVcb.vcb.onVolumeSetRequest(mVcb.route, value);
20411357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                            }
20421357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                        }
20431357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    }
20441357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                });
20451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
20461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        };
20471357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
20489a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void routeUpdated() {
20499a1de308cea2d160778fd977825f10a07b49d738Adam Powell            updateRoute(this);
20509a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
20519a1de308cea2d160778fd977825f10a07b49d738Adam Powell
20529a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
20539a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public String toString() {
2054d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            String supportedTypes = typesToString(getSupportedTypes());
205592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            return getClass().getSimpleName() + "{ name=" + getName() +
205656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown                    ", description=" + getDescription() +
205792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                    ", status=" + getStatus() +
205892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                    ", category=" + getCategory() +
205992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                    ", supportedTypes=" + supportedTypes +
206069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    ", presentationDisplay=" + mPresentationDisplay + " }";
20619a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
20629a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
20639a1de308cea2d160778fd977825f10a07b49d738Adam Powell
20649a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
20659a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a route that the application may define and modify.
20668e37a85bf3dc39519942698dc90a3951306b934bAdam Powell     * A user route defaults to {@link RouteInfo#PLAYBACK_TYPE_REMOTE} and
20678e37a85bf3dc39519942698dc90a3951306b934bAdam Powell     * {@link RouteInfo#PLAYBACK_VOLUME_FIXED}.
20689a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
20699a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see MediaRouter.RouteInfo
20709a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
2071b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class UserRouteInfo extends RouteInfo {
2072ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        RemoteControlClient mRcc;
20735d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        SessionVolumeProvider mSvp;
20749a1de308cea2d160778fd977825f10a07b49d738Adam Powell
20759a1de308cea2d160778fd977825f10a07b49d738Adam Powell        UserRouteInfo(RouteCategory category) {
20769a1de308cea2d160778fd977825f10a07b49d738Adam Powell            super(category);
20779a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mSupportedTypes = ROUTE_TYPE_USER;
20788e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mPlaybackType = PLAYBACK_TYPE_REMOTE;
20798e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeHandling = PLAYBACK_VOLUME_FIXED;
20809a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
20819a1de308cea2d160778fd977825f10a07b49d738Adam Powell
20829a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
20839a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Set the user-visible name of this route.
20849a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param name Name to display to the user to describe this route
20859a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
20869a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void setName(CharSequence name) {
2087eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon            mNameResId = 0;
20889a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = name;
20899a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
20909a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
20915d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
20920d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        /**
20930d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * Set the user-visible name of this route.
209456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
209556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route name identifies the destination represented by the route.
209656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied name, an alias, or device serial number.
209756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
209856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
20990d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @param resId Resource ID of the name to display to the user to describe this route
21000d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         */
21010d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public void setName(int resId) {
21020d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mNameResId = resId;
21030d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mName = null;
21040d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            routeUpdated();
21050d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
21069a1de308cea2d160778fd977825f10a07b49d738Adam Powell
21079a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
210856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Set the user-visible description of this route.
210956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
211056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route description describes the kind of destination represented by the route.
211156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied string, a model number or brand of device.
211256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
211356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
211456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @param description The description of the route, or null if none.
211556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         */
211656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        public void setDescription(CharSequence description) {
211756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown            mDescription = description;
211856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown            routeUpdated();
211956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        }
212056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
212156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        /**
21229a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Set the current user-visible status for this route.
21239a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param status Status to display to the user to describe what the endpoint
21249a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * of this route is currently doing
21259a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
21269a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void setStatus(CharSequence status) {
21279a1de308cea2d160778fd977825f10a07b49d738Adam Powell            setStatusInt(status);
21289a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2129ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2130ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2131ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set the RemoteControlClient responsible for reporting playback info for this
2132ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * user route.
2133ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
2134ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * <p>If this route manages remote playback, the data exposed by this
2135ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * RemoteControlClient will be used to reflect and update information
2136ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * such as route volume info in related UIs.</p>
2137ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
21381357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * <p>The RemoteControlClient must have been previously registered with
21391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}.</p>
21401357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *
2141ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @param rcc RemoteControlClient associated with this route
2142ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
2143ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setRemoteControlClient(RemoteControlClient rcc) {
2144ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            mRcc = rcc;
21451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            updatePlaybackInfoOnRcc();
2146ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2147ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2148ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
21494599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         * Retrieve the RemoteControlClient associated with this route, if one has been set.
21504599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         *
21514599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         * @return the RemoteControlClient associated with this route
21524599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         * @see #setRemoteControlClient(RemoteControlClient)
21534599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         */
21544599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        public RemoteControlClient getRemoteControlClient() {
21554599696591f745b3a546197d2ba7e5cfc5562484Adam Powell            return mRcc;
21564599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        }
21574599696591f745b3a546197d2ba7e5cfc5562484Adam Powell
21584599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        /**
2159ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this route.
2160ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2161ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
2162ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @param icon icon drawable to use to represent this route
2163ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
2164ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setIconDrawable(Drawable icon) {
2165ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            mIcon = icon;
2166ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2167ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2168ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2169ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this route.
2170ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2171ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
217271c69897ad0a55d590698bfa399bfe99c763b9dbAdam Powell         * @param resId Resource ID of an icon drawable to use to represent this route
2173ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
21747b9c912f536925ac6ec43935d6e97506851b33d6Tor Norbye        public void setIconResource(@DrawableRes int resId) {
2175ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            setIconDrawable(sStatic.mResources.getDrawable(resId));
2176ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
21771357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
21781357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
21791357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Set a callback to be notified of volume update requests
21801357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param vcb
21811357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
21821357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolumeCallback(VolumeCallback vcb) {
21831357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            mVcb = new VolumeCallbackInfo(vcb, this);
21841357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
21851357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
21861357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
21871357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines whether playback associated with this route is "local"
21881357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *    ({@link RouteInfo#PLAYBACK_TYPE_LOCAL}) or "remote"
21891357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *    ({@link RouteInfo#PLAYBACK_TYPE_REMOTE}).
21901357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param type
21911357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
21927c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        public void setPlaybackType(@RouteInfo.PlaybackType int type) {
21931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackType != type) {
21941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mPlaybackType = type;
2195430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
21961357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
21971357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
21981357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
21991357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
22001357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines whether volume for the playback associated with this route is fixed
22011357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * ({@link RouteInfo#PLAYBACK_VOLUME_FIXED}) or can modified
22021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * ({@link RouteInfo#PLAYBACK_VOLUME_VARIABLE}).
22031357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volumeHandling
22041357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
22057c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        public void setVolumeHandling(@RouteInfo.PlaybackVolume int volumeHandling) {
22061357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mVolumeHandling != volumeHandling) {
22071357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mVolumeHandling = volumeHandling;
2208430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
22091357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
22101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
22111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
22121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
22131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines at what volume the playback associated with this route is performed (for user
22141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * feedback purposes). This information is only used when the playback is not local.
22151357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volume
22161357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
22171357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolume(int volume) {
22188e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            volume = Math.max(0, Math.min(volume, getVolumeMax()));
22191357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mVolume != volume) {
22201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mVolume = volume;
2221430fc48865e5a371b08f180390946b96d73848feRoboErik                if (mSvp != null) {
22220d0f67f5ee5f939a1b611bc4583212707afd9beeRoboErik                    mSvp.setCurrentVolume(mVolume);
22235d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
22248e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                dispatchRouteVolumeChanged(this);
2225f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                if (mGroup != null) {
2226f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                    mGroup.memberVolumeChanged(this);
2227f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                }
22288e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
22298e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
22308e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
22318e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
22328e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestSetVolume(int volume) {
22338e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mVolumeHandling == PLAYBACK_VOLUME_VARIABLE) {
22348e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (mVcb == null) {
22358e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Cannot requestSetVolume on user route - no volume callback set");
22368e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    return;
22378e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
22388e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVcb.vcb.onVolumeSetRequest(this, volume);
22398e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
22408e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
22418e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
22428e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
22438e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestUpdateVolume(int direction) {
22448e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mVolumeHandling == PLAYBACK_VOLUME_VARIABLE) {
22458e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (mVcb == null) {
22468e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Cannot requestChangeVolume on user route - no volumec callback set");
22478e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    return;
22488e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
22498e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVcb.vcb.onVolumeUpdateRequest(this, direction);
22501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
22511357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
22521357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
22531357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
22541357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines the maximum volume at which the playback associated with this route is performed
22551357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * (for user feedback purposes). This information is only used when the playback is not
22561357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * local.
22571357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volumeMax
22581357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
22591357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolumeMax(int volumeMax) {
22601357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mVolumeMax != volumeMax) {
22611357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mVolumeMax = volumeMax;
2262430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
22631357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
22641357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
22651357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
22661357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
22671357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines over what stream type the media is presented.
22681357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param stream
22691357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
22701357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setPlaybackStream(int stream) {
22711357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackStream != stream) {
22721357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mPlaybackStream = stream;
2273430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
22741357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
22751357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
22761357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
22771357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        private void updatePlaybackInfoOnRcc() {
2278430fc48865e5a371b08f180390946b96d73848feRoboErik            configureSessionVolume();
22795d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        }
22805d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
22815d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        private void configureSessionVolume() {
22825d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            if (mRcc == null) {
22835d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                if (DEBUG) {
2284eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon                    Log.d(TAG, "No Rcc to configure volume for route " + getName());
22855d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
22865d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                return;
22875d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
22885d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            MediaSession session = mRcc.getMediaSession();
22895d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            if (session == null) {
22905d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                if (DEBUG) {
22915d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    Log.d(TAG, "Rcc has no session to configure volume");
22925d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
22935d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                return;
22945d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
22955d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            if (mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
22967c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang                @VolumeProvider.ControlType int volumeControl =
22977c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang                        VolumeProvider.VOLUME_CONTROL_FIXED;
22985d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                switch (mVolumeHandling) {
22995d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    case RemoteControlClient.PLAYBACK_VOLUME_VARIABLE:
2300ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik                        volumeControl = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
23015d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        break;
23025d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    case RemoteControlClient.PLAYBACK_VOLUME_FIXED:
23035d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    default:
23045d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        break;
23051357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                }
23065d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                // Only register a new listener if necessary
23075d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                if (mSvp == null || mSvp.getVolumeControl() != volumeControl
23085d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        || mSvp.getMaxVolume() != mVolumeMax) {
23090d0f67f5ee5f939a1b611bc4583212707afd9beeRoboErik                    mSvp = new SessionVolumeProvider(volumeControl, mVolumeMax, mVolume);
23105d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    session.setPlaybackToRemote(mSvp);
23115d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
23125d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            } else {
23135d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                // We only know how to handle local and remote, fall back to local if not remote.
23149db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik                AudioAttributes.Builder bob = new AudioAttributes.Builder();
23159db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik                bob.setLegacyStreamType(mPlaybackStream);
23169db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik                session.setPlaybackToLocal(bob.build());
23175d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                mSvp = null;
23181357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
23191357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
23201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
2321ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        class SessionVolumeProvider extends VolumeProvider {
23225d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
23237c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang            public SessionVolumeProvider(@VolumeProvider.ControlType int volumeControl,
23247c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang                    int maxVolume, int currentVolume) {
23250d0f67f5ee5f939a1b611bc4583212707afd9beeRoboErik                super(volumeControl, maxVolume, currentVolume);
23265d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
23275d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
23285d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            @Override
23295d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            public void onSetVolumeTo(final int volume) {
23305d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                sStatic.mHandler.post(new Runnable() {
23315d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    @Override
23325d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    public void run() {
23335d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        if (mVcb != null) {
23345d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                            mVcb.vcb.onVolumeSetRequest(mVcb.route, volume);
23355d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        }
23365d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    }
23375d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                });
23385d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
23395d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
23405d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            @Override
23411ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik            public void onAdjustVolume(final int direction) {
23425d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                sStatic.mHandler.post(new Runnable() {
23435d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    @Override
23445d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    public void run() {
23455d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        if (mVcb != null) {
23461ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik                            mVcb.vcb.onVolumeUpdateRequest(mVcb.route, direction);
23475d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        }
23485d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    }
23495d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                });
23505d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
23515d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        }
23529a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
23539a1de308cea2d160778fd977825f10a07b49d738Adam Powell
23549a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
23559a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a route that consists of multiple other routes in a group.
23569a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
2357b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class RouteGroup extends RouteInfo {
23589a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
23599a1de308cea2d160778fd977825f10a07b49d738Adam Powell        private boolean mUpdateName;
23609a1de308cea2d160778fd977825f10a07b49d738Adam Powell
23619a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteGroup(RouteCategory category) {
23629a1de308cea2d160778fd977825f10a07b49d738Adam Powell            super(category);
23639a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mGroup = this;
23648e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeHandling = PLAYBACK_VOLUME_FIXED;
23659a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
23669a1de308cea2d160778fd977825f10a07b49d738Adam Powell
236769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
23680d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        CharSequence getName(Resources res) {
23699a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (mUpdateName) updateName();
23700d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return super.getName(res);
23719a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
23729a1de308cea2d160778fd977825f10a07b49d738Adam Powell
23739a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
23749a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Add a route to this group. The route must not currently belong to another group.
23759a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
23769a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to add to this group
23779a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
23789a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void addRoute(RouteInfo route) {
23799a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != null) {
23809a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalStateException("Route " + route + " is already part of a group.");
23819a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
23829a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getCategory() != mCategory) {
23839a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException(
23849a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        "Route cannot be added to a group with a different category. " +
23859a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            "(Route category=" + route.getCategory() +
23869a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            " group category=" + mCategory + ")");
23879a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
2388d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            final int at = mRoutes.size();
23899a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.add(route);
2390d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = this;
23919a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2392f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
23939a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
2394f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell            dispatchRouteGrouped(route, this, at);
23959a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
23969a1de308cea2d160778fd977825f10a07b49d738Adam Powell
23979a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
23989a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Add a route to this group before the specified index.
23999a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
24009a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to add
24019a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param insertAt insert the new route before this index
24029a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
24039a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void addRoute(RouteInfo route, int insertAt) {
24049a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != null) {
24059a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalStateException("Route " + route + " is already part of a group.");
24069a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
24079a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getCategory() != mCategory) {
24089a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException(
24099a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        "Route cannot be added to a group with a different category. " +
24109a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            "(Route category=" + route.getCategory() +
24119a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            " group category=" + mCategory + ")");
24129a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
24139a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.add(insertAt, route);
2414d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = this;
24159a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2416f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
24179a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
2418f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell            dispatchRouteGrouped(route, this, insertAt);
24199a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
24209a1de308cea2d160778fd977825f10a07b49d738Adam Powell
24219a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
24229a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Remove a route from this group.
24239a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
24249a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to remove
24259a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
24269a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void removeRoute(RouteInfo route) {
24279a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != this) {
24289a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException("Route " + route +
24299a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        " is not a member of this group.");
24309a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
24319a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.remove(route);
2432d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = null;
24339a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2434f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
2435d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteUngrouped(route, this);
24369a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
24379a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
24389a1de308cea2d160778fd977825f10a07b49d738Adam Powell
24399a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
24409a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Remove the route at the specified index from this group.
24419a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
24429a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param index index of the route to remove
24439a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
24449a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void removeRoute(int index) {
2445d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            RouteInfo route = mRoutes.remove(index);
2446d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = null;
24479a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2448f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
2449d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteUngrouped(route, this);
24509a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
24519a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
24529a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2453d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2454d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @return The number of routes in this group
2455d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
2456d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public int getRouteCount() {
2457d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            return mRoutes.size();
2458d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        }
2459d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2460d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2461d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Return the route in this group at the specified index
2462d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2463d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param index Index to fetch
2464d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @return The route at index
2465d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
2466d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public RouteInfo getRouteAt(int index) {
2467d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            return mRoutes.get(index);
2468d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        }
2469d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2470ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2471ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this group.
2472ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2473ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
2474ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @param icon icon drawable to use to represent this group
2475ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
2476ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setIconDrawable(Drawable icon) {
2477ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            mIcon = icon;
2478ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2479ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2480ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2481ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this group.
2482ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2483ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
248471c69897ad0a55d590698bfa399bfe99c763b9dbAdam Powell         * @param resId Resource ID of an icon drawable to use to represent this group
2485ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
24867b9c912f536925ac6ec43935d6e97506851b33d6Tor Norbye        public void setIconResource(@DrawableRes int resId) {
2487ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            setIconDrawable(sStatic.mResources.getDrawable(resId));
2488ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2489ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
24908e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
24918e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestSetVolume(int volume) {
24928e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int maxVol = getVolumeMax();
24938e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (maxVol == 0) {
24948e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                return;
24958e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
24968e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
24978e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final float scaledVolume = (float) volume / maxVol;
24988e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int routeCount = getRouteCount();
24998e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            for (int i = 0; i < routeCount; i++) {
25008e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final RouteInfo route = getRouteAt(i);
25018e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int routeVol = (int) (scaledVolume * route.getVolumeMax());
25028e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                route.requestSetVolume(routeVol);
25038e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
25048e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (volume != mVolume) {
25058e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVolume = volume;
25068e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                dispatchRouteVolumeChanged(this);
25078e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
25088e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
25098e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
25108e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
25118e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestUpdateVolume(int direction) {
25128e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int maxVol = getVolumeMax();
25138e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (maxVol == 0) {
25148e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                return;
25158e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
25168e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
25178e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int routeCount = getRouteCount();
2518f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            int volume = 0;
25198e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            for (int i = 0; i < routeCount; i++) {
25208e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final RouteInfo route = getRouteAt(i);
25218e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                route.requestUpdateVolume(direction);
2522f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                final int routeVol = route.getVolume();
2523f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                if (routeVol > volume) {
2524f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                    volume = routeVol;
2525f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                }
25268e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
25278e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (volume != mVolume) {
25288e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVolume = volume;
25298e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                dispatchRouteVolumeChanged(this);
25308e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
25318e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
25328e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
25339a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void memberNameChanged(RouteInfo info, CharSequence name) {
25349a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
25359a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
25369a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
25379a1de308cea2d160778fd977825f10a07b49d738Adam Powell
25389a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void memberStatusChanged(RouteInfo info, CharSequence status) {
25399a1de308cea2d160778fd977825f10a07b49d738Adam Powell            setStatusInt(status);
25409a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
25419a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2542f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        void memberVolumeChanged(RouteInfo info) {
2543f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
2544f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        }
2545f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell
2546f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        void updateVolume() {
2547f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            // A group always represents the highest component volume value.
2548f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            final int routeCount = getRouteCount();
2549f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            int volume = 0;
2550f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            for (int i = 0; i < routeCount; i++) {
2551f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                final int routeVol = getRouteAt(i).getVolume();
2552f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                if (routeVol > volume) {
2553f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                    volume = routeVol;
2554f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                }
2555f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            }
2556f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            if (volume != mVolume) {
2557f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                mVolume = volume;
2558f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                dispatchRouteVolumeChanged(this);
2559f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            }
2560f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        }
2561f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell
2562d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        @Override
2563d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        void routeUpdated() {
2564d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            int types = 0;
2565d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            final int count = mRoutes.size();
2566b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            if (count == 0) {
2567b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell                // Don't keep empty groups in the router.
256869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                MediaRouter.removeRouteStatic(this);
2569b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell                return;
2570b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            }
2571b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
25728e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            int maxVolume = 0;
25738e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            boolean isLocal = true;
25748e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            boolean isFixedVolume = true;
2575d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            for (int i = 0; i < count; i++) {
25768e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final RouteInfo route = mRoutes.get(i);
25778e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                types |= route.mSupportedTypes;
25788e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int routeMaxVolume = route.getVolumeMax();
25798e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (routeMaxVolume > maxVolume) {
25808e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    maxVolume = routeMaxVolume;
25818e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
25828e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                isLocal &= route.getPlaybackType() == PLAYBACK_TYPE_LOCAL;
25838e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                isFixedVolume &= route.getVolumeHandling() == PLAYBACK_VOLUME_FIXED;
2584d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            }
25858e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mPlaybackType = isLocal ? PLAYBACK_TYPE_LOCAL : PLAYBACK_TYPE_REMOTE;
25868e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeHandling = isFixedVolume ? PLAYBACK_VOLUME_FIXED : PLAYBACK_VOLUME_VARIABLE;
2587d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            mSupportedTypes = types;
25888e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeMax = maxVolume;
2589d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            mIcon = count == 1 ? mRoutes.get(0).getIconDrawable() : null;
2590d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            super.routeUpdated();
2591d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        }
2592d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
25939a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void updateName() {
25949a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final StringBuilder sb = new StringBuilder();
25959a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final int count = mRoutes.size();
25969a1de308cea2d160778fd977825f10a07b49d738Adam Powell            for (int i = 0; i < count; i++) {
25979a1de308cea2d160778fd977825f10a07b49d738Adam Powell                final RouteInfo info = mRoutes.get(i);
2598b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell                // TODO: There's probably a much more correct way to localize this.
2599eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon                if (i > 0) {
2600eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon                    sb.append(", ");
2601eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon                }
2602eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon                sb.append(info.getName());
26039a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
26049a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = sb.toString();
26059a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = false;
26069a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2607d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
2608d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        @Override
2609d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        public String toString() {
2610d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            StringBuilder sb = new StringBuilder(super.toString());
2611d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            sb.append('[');
2612d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            final int count = mRoutes.size();
2613d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            for (int i = 0; i < count; i++) {
2614d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                if (i > 0) sb.append(", ");
2615d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                sb.append(mRoutes.get(i));
2616d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            }
2617d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            sb.append(']');
2618d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            return sb.toString();
2619d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        }
26209a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
26219a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26229a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
26239a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Definition of a category of routes. All routes belong to a category.
26249a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
2625b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class RouteCategory {
26269a1de308cea2d160778fd977825f10a07b49d738Adam Powell        CharSequence mName;
26270d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        int mNameResId;
26289a1de308cea2d160778fd977825f10a07b49d738Adam Powell        int mTypes;
26299a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final boolean mGroupable;
2630705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        boolean mIsSystem;
26319a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26329a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteCategory(CharSequence name, int types, boolean groupable) {
26339a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = name;
26349a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mTypes = types;
26359a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mGroupable = groupable;
26369a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
26379a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26380d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        RouteCategory(int nameResId, int types, boolean groupable) {
26390d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mNameResId = nameResId;
26400d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mTypes = types;
26410d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mGroupable = groupable;
26420d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
26430d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell
26449a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
26459a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return the name of this route category
26469a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
26479a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getName() {
26480d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(sStatic.mResources);
26490d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
26505d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
26510d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        /**
26520d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * Return the properly localized/configuration dependent name of this RouteCategory.
26535d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik         *
26540d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @param context Context to resolve name resources
26550d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @return the name of this route category
26560d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         */
26570d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public CharSequence getName(Context context) {
26580d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(context.getResources());
26590d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
26605d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
26610d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        CharSequence getName(Resources res) {
26620d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            if (mNameResId != 0) {
26630d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                return res.getText(mNameResId);
26640d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            }
26659a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mName;
26669a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
26679a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26689a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
2669d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Return the current list of routes in this category that have been added
2670d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * to the MediaRouter.
26719a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2672d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * <p>This list will not include routes that are nested within RouteGroups.
2673d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * A RouteGroup is treated as a single route within its category.</p>
2674d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2675d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param out a List to fill with the routes in this category. If this parameter is
2676d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *            non-null, it will be cleared, filled with the current routes with this
2677d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *            category, and returned. If this parameter is null, a new List will be
2678d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *            allocated to report the category's current routes.
2679d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @return A list with the routes in this category that have been added to the MediaRouter.
26809a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
2681d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public List<RouteInfo> getRoutes(List<RouteInfo> out) {
2682d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            if (out == null) {
2683d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                out = new ArrayList<RouteInfo>();
2684d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            } else {
2685d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                out.clear();
2686d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            }
2687d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2688b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final int count = getRouteCountStatic();
2689d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            for (int i = 0; i < count; i++) {
2690b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                final RouteInfo route = getRouteAtStatic(i);
2691d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                if (route.mCategory == this) {
2692d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                    out.add(route);
2693d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                }
2694d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            }
2695d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            return out;
26969a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
26979a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26989a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
26999a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return Flag set describing the route types supported by this category
27009a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
27019a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int getSupportedTypes() {
27029a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mTypes;
27039a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27049a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27059a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
27069a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Return whether or not this category supports grouping.
27079a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
27089a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * <p>If this method returns true, all routes obtained from this category
2709d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * via calls to {@link #getRouteAt(int)} will be {@link MediaRouter.RouteGroup}s.</p>
27109a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
27119a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return true if this category supports
27129a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
27139a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public boolean isGroupable() {
27149a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mGroupable;
27159a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27169a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2717705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        /**
2718705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * @return true if this is the category reserved for system routes.
2719705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * @hide
2720705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         */
2721705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        public boolean isSystem() {
2722705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return mIsSystem;
2723705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
2724705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
272569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
27269a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public String toString() {
2727eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon            return "RouteCategory{ name=" + getName() + " types=" + typesToString(mTypes) +
2728d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                    " groupable=" + mGroupable + " }";
27299a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27309a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
27319a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27329a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static class CallbackInfo {
27339a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int type;
273466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        public int flags;
2735b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        public final Callback cb;
2736b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        public final MediaRouter router;
27379a1de308cea2d160778fd977825f10a07b49d738Adam Powell
273866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        public CallbackInfo(Callback cb, int type, int flags, MediaRouter router) {
27399a1de308cea2d160778fd977825f10a07b49d738Adam Powell            this.cb = cb;
27409a1de308cea2d160778fd977825f10a07b49d738Adam Powell            this.type = type;
274166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            this.flags = flags;
2742b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            this.router = router;
27439a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
274466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
274566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        public boolean filterRouteEvent(RouteInfo route) {
2746af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            return filterRouteEvent(route.mSupportedTypes);
2747af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        }
2748af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
2749af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        public boolean filterRouteEvent(int supportedTypes) {
275066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            return (flags & CALLBACK_FLAG_UNFILTERED_EVENTS) != 0
2751af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    || (type & supportedTypes) != 0;
275266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
27539a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
27549a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27559a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
27569a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Interface for receiving events about media routing changes.
27579a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * All methods of this interface will be called from the application's main thread.
275866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
275966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * A Callback will only receive events relevant to routes that the callback
276066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * was registered for unless the {@link MediaRouter#CALLBACK_FLAG_UNFILTERED_EVENTS}
276166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * flag was specified in {@link MediaRouter#addCallback(int, Callback, int)}.
276266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
27639a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
276466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @see MediaRouter#addCallback(int, Callback, int)
27659a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see MediaRouter#removeCallback(Callback)
27669a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
27670d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    public static abstract class Callback {
27689a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
27699a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when the supplied route becomes selected as the active route
27709a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * for the given route type.
27719a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2772d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
27739a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param type Type flag set indicating the routes that have been selected
27749a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been selected for the given route types
27759a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
27760d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteSelected(MediaRouter router, int type, RouteInfo info);
27779a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27789a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
27799a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when the supplied route becomes unselected as the active route
27809a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * for the given route type.
27819a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2782d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
27839a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param type Type flag set indicating the routes that have been unselected
27849a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been unselected for the given route types
27859a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
27860d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteUnselected(MediaRouter router, int type, RouteInfo info);
27879a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27889a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
27899a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when a route for the specified type was added.
27909a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2791d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
27929a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has become available for use
27939a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
27940d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteAdded(MediaRouter router, RouteInfo info);
27959a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27969a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
27979a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when a route for the specified type was removed.
27989a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2799d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
28009a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been removed from availability
28019a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
28020d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteRemoved(MediaRouter router, RouteInfo info);
28039a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28049a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
28059a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when an aspect of the indicated route has changed.
28069a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
28079a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * <p>This will not indicate that the types supported by this route have
28089a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * changed, only that cosmetic info such as name or status have been updated.</p>
28099a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2810d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
28119a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info The route that was changed
28129a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
28130d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteChanged(MediaRouter router, RouteInfo info);
2814d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2815d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2816d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Called when a route is added to a group.
2817d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2818d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
2819d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param info The route that was added
2820d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param group The group the route was added to
2821d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param index The route index within group that info was added at
2822d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
28230d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
28240d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                int index);
2825d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2826d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2827d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Called when a route is removed from a group.
2828d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2829d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
2830d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param info The route that was removed
2831d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param group The group the route was removed from
2832d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
28330d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group);
28348e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
28358e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        /**
28368e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Called when a route's volume changes.
28378e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         *
28388e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param router the MediaRouter reporting the event
28398e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param info The route with altered volume
28408e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         */
28418e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public abstract void onRouteVolumeChanged(MediaRouter router, RouteInfo info);
284292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
284392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        /**
284492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * Called when a route's presentation display changes.
284592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * <p>
284692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * This method is called whenever the route's presentation display becomes
284792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * available, is removes or has changes to some of its properties (such as its size).
284892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p>
284992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
285092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @param router the MediaRouter reporting the event
285192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @param info The route whose presentation display changed
285292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
285392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @see RouteInfo#getPresentationDisplay()
285492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         */
285592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
285692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
28579a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
28589a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28599a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
28600d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * Stub implementation of {@link MediaRouter.Callback}.
28610d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * Each abstract method is defined as a no-op. Override just the ones
28629a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * you need.
28639a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
28640d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    public static class SimpleCallback extends Callback {
28659a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28669a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2867d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
28689a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
28699a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28709a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2871d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
28729a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
28739a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28749a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2875d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteAdded(MediaRouter router, RouteInfo info) {
28769a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
28779a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28789a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2879d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteRemoved(MediaRouter router, RouteInfo info) {
28809a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
28819a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28829a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2883d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteChanged(MediaRouter router, RouteInfo info) {
28849a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
28859a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28869a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2887d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
2888d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                int index) {
28899a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
28909a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28919a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2892d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
28939a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2894d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
28958e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
28968e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void onRouteVolumeChanged(MediaRouter router, RouteInfo info) {
28978e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
28989a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
28991357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
29001357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    static class VolumeCallbackInfo {
29011357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final VolumeCallback vcb;
29021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final RouteInfo route;
29031357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
29041357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public VolumeCallbackInfo(VolumeCallback vcb, RouteInfo route) {
29051357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            this.vcb = vcb;
29061357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            this.route = route;
29071357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
29081357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    }
29091357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
29101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    /**
29111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * Interface for receiving events about volume changes.
29121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * All methods of this interface will be called from the application's main thread.
29131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     *
29141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * <p>A VolumeCallback will only receive events relevant to routes that the callback
29151357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * was registered for.</p>
29161357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     *
29171357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * @see UserRouteInfo#setVolumeCallback(VolumeCallback)
29181357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     */
29191357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    public static abstract class VolumeCallback {
29201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
29211357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Called when the volume for the route should be increased or decreased.
29221357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param info the route affected by this event
29231357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param direction an integer indicating whether the volume is to be increased
29241357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     (positive value) or decreased (negative value).
29251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     For bundled changes, the absolute value indicates the number of changes
29261357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     in the same direction, e.g. +3 corresponds to three "volume up" changes.
29271357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
29281357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public abstract void onVolumeUpdateRequest(RouteInfo info, int direction);
29291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
29301357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Called when the volume for the route should be set to the given value
29311357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param info the route affected by this event
29321357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volume an integer indicating the new volume value that should be used, always
29331357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     between 0 and the value set by {@link UserRouteInfo#setVolumeMax(int)}.
29341357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
29351357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public abstract void onVolumeSetRequest(RouteInfo info, int volume);
29361357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    }
29371357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
29388e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    static class VolumeChangeReceiver extends BroadcastReceiver {
29398e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
29408e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void onReceive(Context context, Intent intent) {
29418e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (intent.getAction().equals(AudioManager.VOLUME_CHANGED_ACTION)) {
29428e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
29438e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                        -1);
29448e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (streamType != AudioManager.STREAM_MUSIC) {
29458e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    return;
29468e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
29478e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
29488e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int newVolume = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
29498e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int oldVolume = intent.getIntExtra(
29508e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                        AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0);
29518e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (newVolume != oldVolume) {
29528e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    systemVolumeChanged(newVolume);
29538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
29548e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
29558e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
2956705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
29578e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
2958705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    static class WifiDisplayStatusChangedReceiver extends BroadcastReceiver {
2959705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        @Override
2960705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        public void onReceive(Context context, Intent intent) {
2961705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (intent.getAction().equals(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)) {
2962705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                updateWifiDisplayStatus((WifiDisplayStatus) intent.getParcelableExtra(
2963705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                        DisplayManager.EXTRA_WIFI_DISPLAY_STATUS));
2964705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
2965705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
29668e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    }
29679a1de308cea2d160778fd977825f10a07b49d738Adam Powell}
2968