MediaRouter.java revision 4bad663518908c20ed4f93a27f46ffb09448497f
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;
23d86b8fea43ebb6e5c31691b44d8ceb0d8d3c9072Jeff Sharkeyimport android.annotation.SystemService;
24ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackbornimport android.app.ActivityThread;
258e37a85bf3dc39519942698dc90a3951306b934bAdam Powellimport android.content.BroadcastReceiver;
269a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.content.Context;
278e37a85bf3dc39519942698dc90a3951306b934bAdam Powellimport android.content.Intent;
288e37a85bf3dc39519942698dc90a3951306b934bAdam Powellimport android.content.IntentFilter;
29af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brownimport android.content.pm.PackageManager;
30b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackbornimport android.content.res.Resources;
31ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powellimport android.graphics.drawable.Drawable;
32705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.hardware.display.DisplayManager;
33705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.hardware.display.WifiDisplay;
34705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.hardware.display.WifiDisplayStatus;
355d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErikimport android.media.session.MediaSession;
369a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.os.Handler;
37632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.os.IBinder;
38af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brownimport android.os.Process;
39632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.os.RemoteException;
40632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.os.ServiceManager;
4169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.os.UserHandle;
42632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.text.TextUtils;
439a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.util.Log;
44705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.view.Display;
459a1de308cea2d160778fd977825f10a07b49d738Adam Powell
469dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kangimport java.lang.annotation.Retention;
479dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kangimport java.lang.annotation.RetentionPolicy;
489a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport java.util.ArrayList;
499a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport java.util.HashMap;
50d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powellimport java.util.List;
51e6585b32ea586743258a5457e2184ffc087f2d2fKenny Rootimport java.util.Objects;
5239d5c6172503620ac3761148adac5fd7fa20d02dAdam Powellimport java.util.concurrent.CopyOnWriteArrayList;
539a1de308cea2d160778fd977825f10a07b49d738Adam Powell
549a1de308cea2d160778fd977825f10a07b49d738Adam Powell/**
559a1de308cea2d160778fd977825f10a07b49d738Adam Powell * MediaRouter allows applications to control the routing of media channels
569a1de308cea2d160778fd977825f10a07b49d738Adam Powell * and streams from the current device to external speakers and destination devices.
579a1de308cea2d160778fd977825f10a07b49d738Adam Powell *
58b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * <p>A MediaRouter is retrieved through {@link Context#getSystemService(String)
59b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * Context.getSystemService()} of a {@link Context#MEDIA_ROUTER_SERVICE
60b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * Context.MEDIA_ROUTER_SERVICE}.
61b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn *
62b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * <p>The media router API is not thread-safe; all interactions with it must be
63b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * done from the main thread of the process.</p>
649a1de308cea2d160778fd977825f10a07b49d738Adam Powell */
65d86b8fea43ebb6e5c31691b44d8ceb0d8d3c9072Jeff Sharkey@SystemService(Context.MEDIA_ROUTER_SERVICE)
669a1de308cea2d160778fd977825f10a07b49d738Adam Powellpublic class MediaRouter {
679a1de308cea2d160778fd977825f10a07b49d738Adam Powell    private static final String TAG = "MediaRouter";
6869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
699a1de308cea2d160778fd977825f10a07b49d738Adam Powell
7092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown    static class Static implements DisplayManager.DisplayListener {
71eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon        final String mPackageName;
72b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final Resources mResources;
73632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn        final IAudioService mAudioService;
74705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final DisplayManager mDisplayService;
7569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final IMediaRouterService mMediaRouterService;
76b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final Handler mHandler;
7739d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        final CopyOnWriteArrayList<CallbackInfo> mCallbacks =
7839d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell                new CopyOnWriteArrayList<CallbackInfo>();
79b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
80b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
81b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final ArrayList<RouteCategory> mCategories = new ArrayList<RouteCategory>();
82b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
83b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final RouteCategory mSystemCategory;
84632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn
85705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
86b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
87705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        RouteInfo mDefaultAudioVideo;
88b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        RouteInfo mBluetoothA2dpRoute;
89b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
90b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        RouteInfo mSelectedRoute;
919a1de308cea2d160778fd977825f10a07b49d738Adam Powell
92af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        final boolean mCanConfigureWifiDisplays;
9366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        boolean mActivelyScanningWifiDisplays;
94ce468a35b388ca46578934706b38dbae94941643Jeff Brown        String mPreviousActiveWifiDisplayAddress;
95705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
9669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        int mDiscoveryRequestRouteTypes;
9769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        boolean mDiscoverRequestActiveScan;
9869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
9969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        int mCurrentUserId = -1;
10069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        IMediaRouterClient mClient;
10169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        MediaRouterClientState mClientState;
10269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
103705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() {
10466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            @Override
105632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
106632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                mHandler.post(new Runnable() {
107632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    @Override public void run() {
108705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                        updateAudioRoutes(newRoutes);
109632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    }
110632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                });
111632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
112632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn        };
113632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn
114b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        Static(Context appContext) {
115eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon            mPackageName = appContext.getPackageName();
116eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon            mResources = appContext.getResources();
117b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            mHandler = new Handler(appContext.getMainLooper());
1189a1de308cea2d160778fd977825f10a07b49d738Adam Powell
119632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
120632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            mAudioService = IAudioService.Stub.asInterface(b);
1219a1de308cea2d160778fd977825f10a07b49d738Adam Powell
122705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            mDisplayService = (DisplayManager) appContext.getSystemService(Context.DISPLAY_SERVICE);
123705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
12469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mMediaRouterService = IMediaRouterService.Stub.asInterface(
12569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
12669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
127dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell            mSystemCategory = new RouteCategory(
128dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell                    com.android.internal.R.string.default_audio_route_category_name,
129705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO, false);
130705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            mSystemCategory.mIsSystem = true;
131af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
132af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Only the system can configure wifi displays.  The display manager
133af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // enforces this with a permission check.  Set a flag here so that we
134af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // know whether this process is actually allowed to scan and connect.
135af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            mCanConfigureWifiDisplays = appContext.checkPermission(
136af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    Manifest.permission.CONFIGURE_WIFI_DISPLAY,
137af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    Process.myPid(), Process.myUid()) == PackageManager.PERMISSION_GRANTED;
138b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        }
139b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
140b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        // Called after sStatic is initialized
1418e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        void startMonitoringRoutes(Context appContext) {
142705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            mDefaultAudioVideo = new RouteInfo(mSystemCategory);
143705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            mDefaultAudioVideo.mNameResId = com.android.internal.R.string.default_audio_route_name;
144705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            mDefaultAudioVideo.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO;
1455830b0b33618940d65197cec99d697b21908fec8Chong Zhang            mDefaultAudioVideo.updatePresentationDisplay();
146bd6118ffb453c7597c8679bc6bfefb4887d7662fSungsoo            if (((AudioManager) appContext.getSystemService(Context.AUDIO_SERVICE))
147bd6118ffb453c7597c8679bc6bfefb4887d7662fSungsoo                    .isVolumeFixed()) {
148bd6118ffb453c7597c8679bc6bfefb4887d7662fSungsoo                mDefaultAudioVideo.mVolumeHandling = RouteInfo.PLAYBACK_VOLUME_FIXED;
149bd6118ffb453c7597c8679bc6bfefb4887d7662fSungsoo            }
150bd6118ffb453c7597c8679bc6bfefb4887d7662fSungsoo
1512ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            addRouteStatic(mDefaultAudioVideo);
152632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn
1532ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            // This will select the active wifi display route if there is one.
1542ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            updateWifiDisplayStatus(mDisplayService.getWifiDisplayStatus());
1552ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell
1562ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            appContext.registerReceiver(new WifiDisplayStatusChangedReceiver(),
1572ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell                    new IntentFilter(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED));
1588e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            appContext.registerReceiver(new VolumeChangeReceiver(),
1598e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
1608e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
16192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            mDisplayService.registerDisplayListener(this, mHandler);
16292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
163705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            AudioRoutesInfo newAudioRoutes = null;
164632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            try {
165705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
166632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            } catch (RemoteException e) {
167632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
168705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (newAudioRoutes != null) {
1692ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell                // This will select the active BT route if there is one and the current
1702ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell                // selected route is the default system route, or if there is no selected
1712ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell                // route yet.
172705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                updateAudioRoutes(newAudioRoutes);
173632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
174705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
17569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Bind to the media router service.
17669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            rebindAsUser(UserHandle.myUserId());
17769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
1782ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            // Select the default route if the above didn't sync us up
1792ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            // appropriately with relevant system state.
1802ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            if (mSelectedRoute == null) {
18169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                selectDefaultRouteStatic();
1822ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            }
183632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn        }
184632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn
185705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        void updateAudioRoutes(AudioRoutesInfo newRoutes) {
186742e379863ff08a880cb6d9e5f274da7589f9a53Jaewan Kim            boolean updated = false;
1876156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock            if (newRoutes.mainType != mCurAudioRoutesInfo.mainType) {
1886156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                mCurAudioRoutesInfo.mainType = newRoutes.mainType;
189632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                int name;
1906156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                if ((newRoutes.mainType&AudioRoutesInfo.MAIN_HEADPHONES) != 0
1916156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                        || (newRoutes.mainType&AudioRoutesInfo.MAIN_HEADSET) != 0) {
192632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    name = com.android.internal.R.string.default_audio_route_name_headphones;
1936156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                } else if ((newRoutes.mainType&AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
194632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
1956156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                } else if ((newRoutes.mainType&AudioRoutesInfo.MAIN_HDMI) != 0) {
1964131a37366d59b5e61f55c4e48d2b22ee0c4cad4Adam Powell                    name = com.android.internal.R.string.default_media_route_name_hdmi;
197632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                } else {
198632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    name = com.android.internal.R.string.default_audio_route_name;
199632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                }
200705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                sStatic.mDefaultAudioVideo.mNameResId = name;
201705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                dispatchRouteChanged(sStatic.mDefaultAudioVideo);
202742e379863ff08a880cb6d9e5f274da7589f9a53Jaewan Kim                updated = true;
203632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
204bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell
2056156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock            final int mainType = mCurAudioRoutesInfo.mainType;
2063f369684e13dfea0ba8ea134f3e95930b0dd7df0Adam Powell
2076156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock            if (!TextUtils.equals(newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) {
2086156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                mCurAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName;
2096156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                if (mCurAudioRoutesInfo.bluetoothName != null) {
210632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    if (sStatic.mBluetoothA2dpRoute == null) {
211632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        final RouteInfo info = new RouteInfo(sStatic.mSystemCategory);
2126156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                        info.mName = mCurAudioRoutesInfo.bluetoothName;
21356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown                        info.mDescription = sStatic.mResources.getText(
21456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown                                com.android.internal.R.string.bluetooth_a2dp_audio_route_name);
215632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        info.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO;
2169dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang                        info.mDeviceType = RouteInfo.DEVICE_TYPE_BLUETOOTH;
217632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        sStatic.mBluetoothA2dpRoute = info;
2182ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell                        addRouteStatic(sStatic.mBluetoothA2dpRoute);
219632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    } else {
2206156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                        sStatic.mBluetoothA2dpRoute.mName = mCurAudioRoutesInfo.bluetoothName;
221632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        dispatchRouteChanged(sStatic.mBluetoothA2dpRoute);
222632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    }
223632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                } else if (sStatic.mBluetoothA2dpRoute != null) {
22469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    removeRouteStatic(sStatic.mBluetoothA2dpRoute);
225632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    sStatic.mBluetoothA2dpRoute = null;
226632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                }
227742e379863ff08a880cb6d9e5f274da7589f9a53Jaewan Kim                updated = true;
228632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
229bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell
230bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell            if (mBluetoothA2dpRoute != null) {
231fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                final boolean a2dpEnabled = isBluetoothA2dpOn();
23271f3563c3960bd862dfa96844f646ea5b2f03de5Sungsoo                if (mSelectedRoute == mBluetoothA2dpRoute && !a2dpEnabled) {
233fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                    selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mDefaultAudioVideo, false);
234742e379863ff08a880cb6d9e5f274da7589f9a53Jaewan Kim                    updated = true;
235fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                } else if ((mSelectedRoute == mDefaultAudioVideo || mSelectedRoute == null) &&
236fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                        a2dpEnabled) {
23769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mBluetoothA2dpRoute, false);
238742e379863ff08a880cb6d9e5f274da7589f9a53Jaewan Kim                    updated = true;
239bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell                }
240bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell            }
241742e379863ff08a880cb6d9e5f274da7589f9a53Jaewan Kim            if (updated) {
242742e379863ff08a880cb6d9e5f274da7589f9a53Jaewan Kim                Log.v(TAG, "Audio routes updated: " + newRoutes + ", a2dp=" + isBluetoothA2dpOn());
243742e379863ff08a880cb6d9e5f274da7589f9a53Jaewan Kim            }
244b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        }
24592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
246fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo        boolean isBluetoothA2dpOn() {
247fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            try {
248fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                return mAudioService.isBluetoothA2dpOn();
249fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            } catch (RemoteException e) {
250fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                Log.e(TAG, "Error querying Bluetooth A2DP state", e);
251fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                return false;
252fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            }
253fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo        }
254fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo
25569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void updateDiscoveryRequest() {
25669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // What are we looking for today?
25769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int routeTypes = 0;
25869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int passiveRouteTypes = 0;
25969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean activeScan = false;
26069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean activeScanWifiDisplay = false;
26169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int count = mCallbacks.size();
26269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < count; i++) {
26369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                CallbackInfo cbi = mCallbacks.get(i);
26469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if ((cbi.flags & (CALLBACK_FLAG_PERFORM_ACTIVE_SCAN
26569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        | CALLBACK_FLAG_REQUEST_DISCOVERY)) != 0) {
26669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Discovery explicitly requested.
26769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    routeTypes |= cbi.type;
26869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else if ((cbi.flags & CALLBACK_FLAG_PASSIVE_DISCOVERY) != 0) {
26969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Discovery only passively requested.
27069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    passiveRouteTypes |= cbi.type;
27169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else {
27269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Legacy case since applications don't specify the discovery flag.
27369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Unfortunately we just have to assume they always need discovery
27469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // whenever they have a callback registered.
27569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    routeTypes |= cbi.type;
27669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
27769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if ((cbi.flags & CALLBACK_FLAG_PERFORM_ACTIVE_SCAN) != 0) {
27869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    activeScan = true;
279af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    if ((cbi.type & ROUTE_TYPE_REMOTE_DISPLAY) != 0) {
28069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        activeScanWifiDisplay = true;
28169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
28269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
28369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
28469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (routeTypes != 0 || activeScan) {
28569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // If someone else requests discovery then enable the passive listeners.
28669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // This is used by the MediaRouteButton and MediaRouteActionProvider since
28769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // they don't receive lifecycle callbacks from the Activity.
28869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                routeTypes |= passiveRouteTypes;
28969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
29069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
29169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Update wifi display scanning.
292ce468a35b388ca46578934706b38dbae94941643Jeff Brown            // TODO: All of this should be managed by the media router service.
293ce468a35b388ca46578934706b38dbae94941643Jeff Brown            if (mCanConfigureWifiDisplays) {
294ce468a35b388ca46578934706b38dbae94941643Jeff Brown                if (mSelectedRoute != null
295ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        && mSelectedRoute.matchesTypes(ROUTE_TYPE_REMOTE_DISPLAY)) {
296ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    // Don't scan while already connected to a remote display since
297ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    // it may interfere with the ongoing transmission.
298ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    activeScanWifiDisplay = false;
29966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown                }
300ce468a35b388ca46578934706b38dbae94941643Jeff Brown                if (activeScanWifiDisplay) {
301ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    if (!mActivelyScanningWifiDisplays) {
302ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mActivelyScanningWifiDisplays = true;
303ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mDisplayService.startWifiDisplayScan();
304ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    }
305ce468a35b388ca46578934706b38dbae94941643Jeff Brown                } else {
306ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    if (mActivelyScanningWifiDisplays) {
307ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mActivelyScanningWifiDisplays = false;
308ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mDisplayService.stopWifiDisplayScan();
309ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    }
31066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown                }
31166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            }
31266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
31369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Tell the media router service all about it.
31469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (routeTypes != mDiscoveryRequestRouteTypes
31569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    || activeScan != mDiscoverRequestActiveScan) {
31669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mDiscoveryRequestRouteTypes = routeTypes;
31769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mDiscoverRequestActiveScan = activeScan;
31869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                publishClientDiscoveryRequest();
31966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            }
32066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
32166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
32292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        @Override
32392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onDisplayAdded(int displayId) {
32492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            updatePresentationDisplays(displayId);
32592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
32692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
32792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        @Override
32892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onDisplayChanged(int displayId) {
32992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            updatePresentationDisplays(displayId);
33092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
33192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
33292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        @Override
33392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onDisplayRemoved(int displayId) {
33492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            updatePresentationDisplays(displayId);
33592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
33692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
33792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public Display[] getAllPresentationDisplays() {
33892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
33992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
34092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
34192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        private void updatePresentationDisplays(int changedDisplayId) {
34292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            final int count = mRoutes.size();
34392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            for (int i = 0; i < count; i++) {
3445830b0b33618940d65197cec99d697b21908fec8Chong Zhang                final RouteInfo route = mRoutes.get(i);
3455830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (route.updatePresentationDisplay() || (route.mPresentationDisplay != null
3465830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        && route.mPresentationDisplay.getDisplayId() == changedDisplayId)) {
3475830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    dispatchRoutePresentationDisplayChanged(route);
34892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                }
34992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            }
35092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
35169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
35269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void setSelectedRoute(RouteInfo info, boolean explicit) {
35369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Must be non-reentrant.
35469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mSelectedRoute = info;
35569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            publishClientSelectedRoute(explicit);
35669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
35769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
35869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void rebindAsUser(int userId) {
35969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mCurrentUserId != userId || userId < 0 || mClient == null) {
36069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (mClient != null) {
36169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    try {
36269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        mMediaRouterService.unregisterClient(mClient);
36369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    } catch (RemoteException ex) {
36469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        Log.e(TAG, "Unable to unregister media router client.", ex);
36569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
36669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClient = null;
36769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
36869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
36969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mCurrentUserId = userId;
37069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
37169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
37269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Client client = new Client();
373eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon                    mMediaRouterService.registerClientAsUser(client, mPackageName, userId);
37469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClient = client;
37569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
37669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to register media router client.", ex);
37769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
37869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
37969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                publishClientDiscoveryRequest();
38069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                publishClientSelectedRoute(false);
38169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                updateClientState();
38269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
38369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
38469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
38569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void publishClientDiscoveryRequest() {
38669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mClient != null) {
38769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
38869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.setDiscoveryRequest(mClient,
38969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mDiscoveryRequestRouteTypes, mDiscoverRequestActiveScan);
39069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
39169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to publish media router client discovery request.", ex);
39269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
39369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
39469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
39569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
39669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void publishClientSelectedRoute(boolean explicit) {
39769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mClient != null) {
39869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
39969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.setSelectedRoute(mClient,
40069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mSelectedRoute != null ? mSelectedRoute.mGlobalRouteId : null,
40169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            explicit);
40269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
40369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to publish media router client selected route.", ex);
40469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
40569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
40669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
40769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
40869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void updateClientState() {
40969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Update the client state.
41069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mClientState = null;
41169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mClient != null) {
41269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
41369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClientState = mMediaRouterService.getState(mClient);
41469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
41569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to retrieve media router client state.", ex);
41669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
41769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
41869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final ArrayList<MediaRouterClientState.RouteInfo> globalRoutes =
41969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClientState != null ? mClientState.routes : null;
42069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final String globallySelectedRouteId = mClientState != null ?
42169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClientState.globallySelectedRouteId : null;
42269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
42369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Add or update routes.
42469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int globalRouteCount = globalRoutes != null ? globalRoutes.size() : 0;
42569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < globalRouteCount; i++) {
42669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final MediaRouterClientState.RouteInfo globalRoute = globalRoutes.get(i);
42769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                RouteInfo route = findGlobalRoute(globalRoute.id);
42869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (route == null) {
42969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    route = makeGlobalRoute(globalRoute);
43069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    addRouteStatic(route);
43169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else {
43269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    updateGlobalRoute(route, globalRoute);
43369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
43469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
43569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
43669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Synchronize state with the globally selected route.
43769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (globallySelectedRouteId != null) {
43869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final RouteInfo route = findGlobalRoute(globallySelectedRouteId);
43969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (route == null) {
44069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.w(TAG, "Could not find new globally selected route: "
44169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            + globallySelectedRouteId);
44269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else if (route != mSelectedRoute) {
44369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    if (DEBUG) {
44469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        Log.d(TAG, "Selecting new globally selected route: " + route);
44569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
44669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    selectRouteStatic(route.mSupportedTypes, route, false);
44769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
44869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            } else if (mSelectedRoute != null && mSelectedRoute.mGlobalRouteId != null) {
44969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (DEBUG) {
45069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.d(TAG, "Unselecting previous globally selected route: " + mSelectedRoute);
45169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
45269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                selectDefaultRouteStatic();
45369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
45469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
45569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Remove defunct routes.
45669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            outer: for (int i = mRoutes.size(); i-- > 0; ) {
45769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final RouteInfo route = mRoutes.get(i);
45869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final String globalRouteId = route.mGlobalRouteId;
45969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (globalRouteId != null) {
46069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    for (int j = 0; j < globalRouteCount; j++) {
46169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        MediaRouterClientState.RouteInfo globalRoute = globalRoutes.get(j);
46269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (globalRouteId.equals(globalRoute.id)) {
46369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            continue outer; // found
46469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
46569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
46669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // not found
46769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    removeRouteStatic(route);
46869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
46969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
47069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
47169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
47269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void requestSetVolume(RouteInfo route, int volume) {
47369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mGlobalRouteId != null && mClient != null) {
47469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
47569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.requestSetVolume(mClient,
47669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            route.mGlobalRouteId, volume);
47769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
47869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.w(TAG, "Unable to request volume change.", ex);
47969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
48069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
48169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
48269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
48369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void requestUpdateVolume(RouteInfo route, int direction) {
48469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mGlobalRouteId != null && mClient != null) {
48569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
48669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.requestUpdateVolume(mClient,
48769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            route.mGlobalRouteId, direction);
48869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
48969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.w(TAG, "Unable to request volume change.", ex);
49069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
49169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
49269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
49369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
49469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        RouteInfo makeGlobalRoute(MediaRouterClientState.RouteInfo globalRoute) {
49569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            RouteInfo route = new RouteInfo(sStatic.mSystemCategory);
49669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mGlobalRouteId = globalRoute.id;
49769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mName = globalRoute.name;
49869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mDescription = globalRoute.description;
49969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mSupportedTypes = globalRoute.supportedTypes;
5009dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang            route.mDeviceType = globalRoute.deviceType;
50169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mEnabled = globalRoute.enabled;
50239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            route.setRealStatusCode(globalRoute.statusCode);
50369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mPlaybackType = globalRoute.playbackType;
50469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mPlaybackStream = globalRoute.playbackStream;
50569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mVolume = globalRoute.volume;
50669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mVolumeMax = globalRoute.volumeMax;
50769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mVolumeHandling = globalRoute.volumeHandling;
5085830b0b33618940d65197cec99d697b21908fec8Chong Zhang            route.mPresentationDisplayId = globalRoute.presentationDisplayId;
5095830b0b33618940d65197cec99d697b21908fec8Chong Zhang            route.updatePresentationDisplay();
51069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            return route;
51169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
51269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
51369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void updateGlobalRoute(RouteInfo route, MediaRouterClientState.RouteInfo globalRoute) {
51469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean changed = false;
51569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean volumeChanged = false;
51669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean presentationDisplayChanged = false;
51769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
518e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root            if (!Objects.equals(route.mName, globalRoute.name)) {
51969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mName = globalRoute.name;
52069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
52169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
522e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root            if (!Objects.equals(route.mDescription, globalRoute.description)) {
52369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mDescription = globalRoute.description;
52469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
52569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
526af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            final int oldSupportedTypes = route.mSupportedTypes;
527af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (oldSupportedTypes != globalRoute.supportedTypes) {
52869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mSupportedTypes = globalRoute.supportedTypes;
52969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
53069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
53169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mEnabled != globalRoute.enabled) {
53269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mEnabled = globalRoute.enabled;
53369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
53469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
53539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (route.mRealStatusCode != globalRoute.statusCode) {
53639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                route.setRealStatusCode(globalRoute.statusCode);
53769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
53869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
53969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mPlaybackType != globalRoute.playbackType) {
54069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mPlaybackType = globalRoute.playbackType;
54169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
54269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
54369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mPlaybackStream != globalRoute.playbackStream) {
54469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mPlaybackStream = globalRoute.playbackStream;
54569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
54669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
54769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mVolume != globalRoute.volume) {
54869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mVolume = globalRoute.volume;
54969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
55069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                volumeChanged = true;
55169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
55269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mVolumeMax != globalRoute.volumeMax) {
55369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mVolumeMax = globalRoute.volumeMax;
55469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
55569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                volumeChanged = true;
55669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
55769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mVolumeHandling != globalRoute.volumeHandling) {
55869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mVolumeHandling = globalRoute.volumeHandling;
55969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
56069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                volumeChanged = true;
56169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
5625830b0b33618940d65197cec99d697b21908fec8Chong Zhang            if (route.mPresentationDisplayId != globalRoute.presentationDisplayId) {
5635830b0b33618940d65197cec99d697b21908fec8Chong Zhang                route.mPresentationDisplayId = globalRoute.presentationDisplayId;
5645830b0b33618940d65197cec99d697b21908fec8Chong Zhang                route.updatePresentationDisplay();
56569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
56669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                presentationDisplayChanged = true;
56769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
56869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
56969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (changed) {
570af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                dispatchRouteChanged(route, oldSupportedTypes);
57169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
57269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (volumeChanged) {
57369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                dispatchRouteVolumeChanged(route);
57469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
57569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (presentationDisplayChanged) {
57669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                dispatchRoutePresentationDisplayChanged(route);
57769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
57869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
57969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
58069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        RouteInfo findGlobalRoute(String globalRouteId) {
58169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int count = mRoutes.size();
58269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < count; i++) {
58369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final RouteInfo route = mRoutes.get(i);
58469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (globalRouteId.equals(route.mGlobalRouteId)) {
58569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    return route;
58669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
58769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
58869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            return null;
58969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
59069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
59169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final class Client extends IMediaRouterClient.Stub {
59269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            @Override
59369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public void onStateChanged() {
59469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mHandler.post(new Runnable() {
59569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    @Override
59669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    public void run() {
59769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (Client.this == mClient) {
59869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            updateClientState();
59969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
60069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
60169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                });
60269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
60369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
604b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
6059a1de308cea2d160778fd977825f10a07b49d738Adam Powell
606b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static Static sStatic;
6079a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6089a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
6099a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Route type flag for live audio.
6109a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
6119a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>A device that supports live audio routing will allow the media audio stream
6129a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * to be routed to supported destinations. This can include internal speakers or
6139a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * audio jacks on the device itself, A2DP devices, and more.</p>
6149a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
6159a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>Once initiated this routing is transparent to the application. All audio
6169a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * played on the media stream will be routed to the selected destination.</p>
6179a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
61869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_LIVE_AUDIO = 1 << 0;
6199a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6209a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
621705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * Route type flag for live video.
622705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     *
623705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * <p>A device that supports live video routing will allow a mirrored version
624705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * of the device's primary display or a customized
625705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * {@link android.app.Presentation Presentation} to be routed to supported destinations.</p>
626705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     *
627705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * <p>Once initiated, display mirroring is transparent to the application.
628705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * While remote routing is active the application may use a
629705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * {@link android.app.Presentation Presentation} to replace the mirrored view
630705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * on the external display with different content.</p>
63192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown     *
63292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown     * @see RouteInfo#getPresentationDisplay()
63392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown     * @see android.app.Presentation
634705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     */
63569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_LIVE_VIDEO = 1 << 1;
63669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
63769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
63869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Temporary interop constant to identify remote displays.
63969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide To be removed when media router API is updated.
64069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
64169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_REMOTE_DISPLAY = 1 << 2;
642705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
643705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    /**
6449a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Route type flag for application-specific usage.
6459a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
6469a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>Unlike other media route types, user routes are managed by the application.
6479a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * The MediaRouter will manage and dispatch events for user routes, but the application
6489a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * is expected to interpret the meaning of these events and perform the requested
6499a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * routing tasks.</p>
6509a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
65169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_USER = 1 << 23;
65269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
65369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static final int ROUTE_TYPE_ANY = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO
65469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            | ROUTE_TYPE_REMOTE_DISPLAY | ROUTE_TYPE_USER;
6559a1de308cea2d160778fd977825f10a07b49d738Adam Powell
65666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    /**
65766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Flag for {@link #addCallback}: Actively scan for routes while this callback
65866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * is registered.
65966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
66066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * When this flag is specified, the media router will actively scan for new
66166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * routes.  Certain routes, such as wifi display routes, may not be discoverable
66266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * except when actively scanning.  This flag is typically used when the route picker
66366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * dialog has been opened by the user to ensure that the route information is
66466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * up to date.
66566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p><p>
66666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Active scanning may consume a significant amount of power and may have intrusive
66766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * effects on wireless connectivity.  Therefore it is important that active scanning
66866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * only be requested when it is actually needed to satisfy a user request to
66966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * discover and select a new route.
67066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
67166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     */
67214507e257af5d71577574e25cbd690c4b54c9272Jeff Brown    public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1 << 0;
67366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
67466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    /**
67566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Flag for {@link #addCallback}: Do not filter route events.
67666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
67766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * When this flag is specified, the callback will be invoked for event that affect any
67869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * route even if they do not match the callback's filter.
67966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
68066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     */
68166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 1 << 1;
68266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
68369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
68469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Explicitly requests discovery.
68569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     *
68669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide Future API ported from support library.  Revisit this later.
68769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
68869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 1 << 2;
68969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
69069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
69169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Requests that discovery be performed but only if there is some other active
69269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * callback already registered.
69369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     *
69469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide Compatibility workaround for the fact that applications do not currently
69569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * request discovery explicitly (except when using the support library API).
69669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
69769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int CALLBACK_FLAG_PASSIVE_DISCOVERY = 1 << 3;
69869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
6990abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    /**
7000abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * Flag for {@link #isRouteAvailable}: Ignore the default route.
7010abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * <p>
7020abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * This flag is used to determine whether a matching non-default route is available.
7030abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * This constraint may be used to decide whether to offer the route chooser dialog
7040abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * to the user.  There is no point offering the chooser if there are no
7050abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * non-default choices.
7060abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * </p>
7070abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     *
7080abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @hide Future API ported from support library.  Revisit this later.
7090abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     */
7100abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1 << 0;
7110abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
7129a1de308cea2d160778fd977825f10a07b49d738Adam Powell    // Maps application contexts
7139a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static final HashMap<Context, MediaRouter> sRouters = new HashMap<Context, MediaRouter>();
7149a1de308cea2d160778fd977825f10a07b49d738Adam Powell
7159a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static String typesToString(int types) {
7169a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final StringBuilder result = new StringBuilder();
7179a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if ((types & ROUTE_TYPE_LIVE_AUDIO) != 0) {
7189a1de308cea2d160778fd977825f10a07b49d738Adam Powell            result.append("ROUTE_TYPE_LIVE_AUDIO ");
7199a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
7202bb7c122ef6ce8539dfbaeb3292adcd942185f82Bryan Mawhinney        if ((types & ROUTE_TYPE_LIVE_VIDEO) != 0) {
7212bb7c122ef6ce8539dfbaeb3292adcd942185f82Bryan Mawhinney            result.append("ROUTE_TYPE_LIVE_VIDEO ");
7222bb7c122ef6ce8539dfbaeb3292adcd942185f82Bryan Mawhinney        }
72369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if ((types & ROUTE_TYPE_REMOTE_DISPLAY) != 0) {
72469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            result.append("ROUTE_TYPE_REMOTE_DISPLAY ");
72569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
7269a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if ((types & ROUTE_TYPE_USER) != 0) {
7279a1de308cea2d160778fd977825f10a07b49d738Adam Powell            result.append("ROUTE_TYPE_USER ");
7289a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
7299a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return result.toString();
7309a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
7319a1de308cea2d160778fd977825f10a07b49d738Adam Powell
732b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    /** @hide */
733b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public MediaRouter(Context context) {
734b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        synchronized (Static.class) {
735b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            if (sStatic == null) {
7368e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final Context appContext = context.getApplicationContext();
7378e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                sStatic = new Static(appContext);
7388e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                sStatic.startMonitoringRoutes(appContext);
739b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            }
7409a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
7419a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
7429a1de308cea2d160778fd977825f10a07b49d738Adam Powell
743690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    /**
7443afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * Gets the default route for playing media content on the system.
7453afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * <p>
7463afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * The system always provides a default route.
7473afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * </p>
7483afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     *
7493afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * @return The default route, which is guaranteed to never be null.
750690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     */
7513afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown    public RouteInfo getDefaultRoute() {
752705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return sStatic.mDefaultAudioVideo;
753690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
754690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
755690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    /**
7564599696591f745b3a546197d2ba7e5cfc5562484Adam Powell     * @hide for use by framework routing UI
7574599696591f745b3a546197d2ba7e5cfc5562484Adam Powell     */
7583afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown    public RouteCategory getSystemCategory() {
7594599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        return sStatic.mSystemCategory;
7604599696591f745b3a546197d2ba7e5cfc5562484Adam Powell    }
7614599696591f745b3a546197d2ba7e5cfc5562484Adam Powell
7620abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    /** @hide */
7630abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public RouteInfo getSelectedRoute() {
7640abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        return getSelectedRoute(ROUTE_TYPE_ANY);
7650abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    }
7660abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
7674599696591f745b3a546197d2ba7e5cfc5562484Adam Powell    /**
7681cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell     * Return the currently selected route for any of the given types
769690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     *
770690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * @param type route types
771690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * @return the selected route
772690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     */
773690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public RouteInfo getSelectedRoute(int type) {
7741cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        if (sStatic.mSelectedRoute != null &&
7751cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell                (sStatic.mSelectedRoute.mSupportedTypes & type) != 0) {
7761cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // If the selected route supports any of the types supplied, it's still considered
7771cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // 'selected' for that type.
7781cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            return sStatic.mSelectedRoute;
7791cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        } else if (type == ROUTE_TYPE_USER) {
7801cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // The caller specifically asked for a user route and the currently selected route
7811cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // doesn't qualify.
7821cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            return null;
7831cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        }
7841cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        // If the above didn't match and we're not specifically asking for a user route,
7851cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        // consider the default selected.
7861cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        return sStatic.mDefaultAudioVideo;
787690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
788690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
7899a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
7900abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * Returns true if there is a route that matches the specified types.
7910abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * <p>
7920abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * This method returns true if there are any available routes that match the types
7930abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * regardless of whether they are enabled or disabled.  If the
7940abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} flag is specified, then
7950abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * the method will only consider non-default routes.
7960abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * </p>
7970abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     *
7980abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @param types The types to match.
7990abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @param flags Flags to control the determination of whether a route may be available.
8000abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * May be zero or {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE}.
8010abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @return True if a matching route may be available.
8020abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     *
8030abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @hide Future API ported from support library.  Revisit this later.
8040abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     */
8050abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public boolean isRouteAvailable(int types, int flags) {
8060abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        final int count = sStatic.mRoutes.size();
8070abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        for (int i = 0; i < count; i++) {
8080abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            RouteInfo route = sStatic.mRoutes.get(i);
8090abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            if (route.matchesTypes(types)) {
8100abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                if ((flags & AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE) == 0
8110abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                        || route != sStatic.mDefaultAudioVideo) {
8120abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                    return true;
8130abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                }
8140abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            }
8150abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
8160abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
8170abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        // It doesn't look like we can find a matching route right now.
8180abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        return false;
8190abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    }
8200abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
8210abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    /**
8229a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Add a callback to listen to events about specific kinds of media routes.
8239a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * If the specified callback is already registered, its registration will be updated for any
8249a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * additional route types specified.
82566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
82666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * This is a convenience method that has the same effect as calling
82766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * {@link #addCallback(int, Callback, int)} without flags.
82866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
8299a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
8309a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param types Types of routes this callback is interested in
8319a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param cb Callback to add
8329a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
8339a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void addCallback(int types, Callback cb) {
83466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        addCallback(types, cb, 0);
83566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    }
83666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
83766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    /**
83866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Add a callback to listen to events about specific kinds of media routes.
83966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * If the specified callback is already registered, its registration will be updated for any
84066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * additional route types specified.
84166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
84266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * By default, the callback will only be invoked for events that affect routes
84366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * that match the specified selector.  The filtering may be disabled by specifying
84466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * the {@link #CALLBACK_FLAG_UNFILTERED_EVENTS} flag.
84566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
84666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     *
84766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @param types Types of routes this callback is interested in
84866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @param cb Callback to add
84966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @param flags Flags to control the behavior of the callback.
85014507e257af5d71577574e25cbd690c4b54c9272Jeff Brown     * May be zero or a combination of {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and
85166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * {@link #CALLBACK_FLAG_UNFILTERED_EVENTS}.
85266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     */
85366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    public void addCallback(int types, Callback cb, int flags) {
85466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        CallbackInfo info;
85566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        int index = findCallbackInfo(cb);
85666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        if (index >= 0) {
85766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info = sStatic.mCallbacks.get(index);
85866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info.type |= types;
85966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info.flags |= flags;
86066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        } else {
86166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info = new CallbackInfo(cb, types, flags, this);
86266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            sStatic.mCallbacks.add(info);
86366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
86469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        sStatic.updateDiscoveryRequest();
8659a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
8669a1de308cea2d160778fd977825f10a07b49d738Adam Powell
8679a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
8689a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Remove the specified callback. It will no longer receive events about media routing.
8699a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
8709a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param cb Callback to remove
8719a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
8729a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void removeCallback(Callback cb) {
87366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        int index = findCallbackInfo(cb);
87466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        if (index >= 0) {
87569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            sStatic.mCallbacks.remove(index);
87669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            sStatic.updateDiscoveryRequest();
87766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        } else {
87866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            Log.w(TAG, "removeCallback(" + cb + "): callback not registered");
87966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
88066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    }
88166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
88266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    private int findCallbackInfo(Callback cb) {
883b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        final int count = sStatic.mCallbacks.size();
8849a1de308cea2d160778fd977825f10a07b49d738Adam Powell        for (int i = 0; i < count; i++) {
88566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            final CallbackInfo info = sStatic.mCallbacks.get(i);
88666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (info.cb == cb) {
88766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown                return i;
8889a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
8899a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
89066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        return -1;
8919a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
8929a1de308cea2d160778fd977825f10a07b49d738Adam Powell
893d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell    /**
894d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     * Select the specified route to use for output of the given media types.
8953afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * <p class="note">
8963afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * As API version 18, this function may be used to select any route.
8973afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * In prior versions, this function could only be used to select user
8983afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * routes and would ignore any attempt to select a system route.
8993afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * </p>
900d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     *
901d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     * @param types type flags indicating which types this route should be used for.
902d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     *              The route must support at least a subset.
903d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     * @param route Route to select
9049d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand     * @throws IllegalArgumentException if the given route is {@code null}
905d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     */
9069d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand    public void selectRoute(int types, @NonNull RouteInfo route) {
9079d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand        if (route == null) {
9089d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand            throw new IllegalArgumentException("Route cannot be null.");
9099d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand        }
91069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        selectRouteStatic(types, route, true);
9110d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    }
91269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
9130d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    /**
9140d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @hide internal use
9150d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     */
91669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void selectRouteInt(int types, RouteInfo route, boolean explicit) {
91769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        selectRouteStatic(types, route, explicit);
918b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
919b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
9209d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand    static void selectRouteStatic(int types, @NonNull RouteInfo route, boolean explicit) {
92118687b7a275b3782e63d52cdbe8ee9df191046e7Jae Seo        Log.v(TAG, "Selecting route: " + route);
9229d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand        assert(route != null);
923705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final RouteInfo oldRoute = sStatic.mSelectedRoute;
924705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (oldRoute == route) return;
9250abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        if (!route.matchesTypes(types)) {
9260d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            Log.w(TAG, "selectRoute ignored; cannot select route with supported types " +
9270d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                    typesToString(route.getSupportedTypes()) + " into route types " +
9280d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                    typesToString(types));
9294ee1f55ce0f4909a7430ab44563a81852f335071Adam Powell            return;
9300d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
9319a1de308cea2d160778fd977825f10a07b49d738Adam Powell
932fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo        final RouteInfo btRoute = sStatic.mBluetoothA2dpRoute;
933fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo        if (btRoute != null && (types & ROUTE_TYPE_LIVE_AUDIO) != 0 &&
934fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                (route == btRoute || route == sStatic.mDefaultAudioVideo)) {
935fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            try {
936fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                sStatic.mAudioService.setBluetoothA2dpOn(route == btRoute);
937fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            } catch (RemoteException e) {
938fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                Log.e(TAG, "Error changing Bluetooth A2DP state", e);
939fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            }
940fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo        }
941fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo
942705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final WifiDisplay activeDisplay =
943705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                sStatic.mDisplayService.getWifiDisplayStatus().getActiveDisplay();
944705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final boolean oldRouteHasAddress = oldRoute != null && oldRoute.mDeviceAddress != null;
9459d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand        final boolean newRouteHasAddress = route.mDeviceAddress != null;
946705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (activeDisplay != null || oldRouteHasAddress || newRouteHasAddress) {
947705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (newRouteHasAddress && !matchesDeviceAddress(activeDisplay, route)) {
948af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (sStatic.mCanConfigureWifiDisplays) {
949af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    sStatic.mDisplayService.connectWifiDisplay(route.mDeviceAddress);
950af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                } else {
951af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    Log.e(TAG, "Cannot connect to wifi displays because this process "
952af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                            + "is not allowed to do so.");
953af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
954705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            } else if (activeDisplay != null && !newRouteHasAddress) {
955705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                sStatic.mDisplayService.disconnectWifiDisplay();
956705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
957705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
958705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
95969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        sStatic.setSelectedRoute(route, explicit);
96069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
961705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (oldRoute != null) {
962705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            dispatchRouteUnselected(types & oldRoute.getSupportedTypes(), oldRoute);
96339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (oldRoute.resolveStatusCode()) {
96439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                dispatchRouteChanged(oldRoute);
96539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
9669a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
9679a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if (route != null) {
96839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (route.resolveStatusCode()) {
96939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                dispatchRouteChanged(route);
97039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
9719a1de308cea2d160778fd977825f10a07b49d738Adam Powell            dispatchRouteSelected(types & route.getSupportedTypes(), route);
9729a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
973ce468a35b388ca46578934706b38dbae94941643Jeff Brown
974ce468a35b388ca46578934706b38dbae94941643Jeff Brown        // The behavior of active scans may depend on the currently selected route.
975ce468a35b388ca46578934706b38dbae94941643Jeff Brown        sStatic.updateDiscoveryRequest();
9769a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
9779a1de308cea2d160778fd977825f10a07b49d738Adam Powell
97869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static void selectDefaultRouteStatic() {
97969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // TODO: Be smarter about the route types here; this selects for all valid.
98069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (sStatic.mSelectedRoute != sStatic.mBluetoothA2dpRoute
981fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                && sStatic.mBluetoothA2dpRoute != null && sStatic.isBluetoothA2dpOn()) {
98269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mBluetoothA2dpRoute, false);
98369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        } else {
98469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mDefaultAudioVideo, false);
98569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
98669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
98769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
9889a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
989705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * Compare the device address of a display and a route.
990705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * Nulls/no device address will match another null/no address.
991705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     */
992705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    static boolean matchesDeviceAddress(WifiDisplay display, RouteInfo info) {
993705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final boolean routeHasAddress = info != null && info.mDeviceAddress != null;
994705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (display == null && !routeHasAddress) {
995705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return true;
996705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
997705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
998705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (display != null && routeHasAddress) {
999705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return display.getDeviceAddress().equals(info.mDeviceAddress);
1000705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1001705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return false;
1002705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1003705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1004705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    /**
10059a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Add an app-specified route for media to the MediaRouter.
10069a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * App-specified route definitions are created using {@link #createUserRoute(RouteCategory)}
10079a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
10089a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param info Definition of the route to add
10093afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * @see #createUserRoute(RouteCategory)
10109a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #removeUserRoute(UserRouteInfo)
10119a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
10129a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void addUserRoute(UserRouteInfo info) {
10132ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        addRouteStatic(info);
10149a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
10159a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1016d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    /**
1017d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     * @hide Framework use only
1018d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     */
1019d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    public void addRouteInt(RouteInfo info) {
10202ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        addRouteStatic(info);
1021d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    }
1022d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
10232ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell    static void addRouteStatic(RouteInfo info) {
102418687b7a275b3782e63d52cdbe8ee9df191046e7Jae Seo        Log.v(TAG, "Adding route: " + info);
10259a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final RouteCategory cat = info.getCategory();
1026b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        if (!sStatic.mCategories.contains(cat)) {
1027b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            sStatic.mCategories.add(cat);
10289a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
1029d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        if (cat.isGroupable() && !(info instanceof RouteGroup)) {
10309a1de308cea2d160778fd977825f10a07b49d738Adam Powell            // Enforce that any added route in a groupable category must be in a group.
10319a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final RouteGroup group = new RouteGroup(info.getCategory());
1032dbbfa702a09f6d2d36dee1b552442d04a4673f89Adam Powell            group.mSupportedTypes = info.mSupportedTypes;
1033b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            sStatic.mRoutes.add(group);
1034d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteAdded(group);
1035b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            group.addRoute(info);
1036d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
10379a1de308cea2d160778fd977825f10a07b49d738Adam Powell            info = group;
1038d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        } else {
1039b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            sStatic.mRoutes.add(info);
1040d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteAdded(info);
10419a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
10429a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
10439a1de308cea2d160778fd977825f10a07b49d738Adam Powell
10449a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
10459a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Remove an app-specified route for media from the MediaRouter.
10469a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
10479a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param info Definition of the route to remove
10489a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #addUserRoute(UserRouteInfo)
10499a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
10509a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void removeUserRoute(UserRouteInfo info) {
105169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        removeRouteStatic(info);
10529a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
10539a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1054690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    /**
1055690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * Remove all app-specified routes from the MediaRouter.
1056690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     *
1057690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * @see #removeUserRoute(UserRouteInfo)
1058690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     */
1059690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public void clearUserRoutes() {
1060b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        for (int i = 0; i < sStatic.mRoutes.size(); i++) {
1061b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final RouteInfo info = sStatic.mRoutes.get(i);
1062d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            // TODO Right now, RouteGroups only ever contain user routes.
1063d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            // The code below will need to change if this assumption does.
1064d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            if (info instanceof UserRouteInfo || info instanceof RouteGroup) {
106569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                removeRouteStatic(info);
1066690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell                i--;
1067690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            }
1068690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
1069690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
1070690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
1071d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    /**
1072d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     * @hide internal use only
1073d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     */
1074d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    public void removeRouteInt(RouteInfo info) {
107569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        removeRouteStatic(info);
1076d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    }
1077d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
107869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static void removeRouteStatic(RouteInfo info) {
107918687b7a275b3782e63d52cdbe8ee9df191046e7Jae Seo        Log.v(TAG, "Removing route: " + info);
1080b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        if (sStatic.mRoutes.remove(info)) {
10819a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final RouteCategory removingCat = info.getCategory();
1082b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final int count = sStatic.mRoutes.size();
10839a1de308cea2d160778fd977825f10a07b49d738Adam Powell            boolean found = false;
10849a1de308cea2d160778fd977825f10a07b49d738Adam Powell            for (int i = 0; i < count; i++) {
1085b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                final RouteCategory cat = sStatic.mRoutes.get(i).getCategory();
10869a1de308cea2d160778fd977825f10a07b49d738Adam Powell                if (removingCat == cat) {
10879a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    found = true;
10889a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    break;
10899a1de308cea2d160778fd977825f10a07b49d738Adam Powell                }
10909a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
10910abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            if (info.isSelected()) {
1092d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                // Removing the currently selected route? Select the default before we remove it.
109369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                selectDefaultRouteStatic();
1094d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            }
1095690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            if (!found) {
1096b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                sStatic.mCategories.remove(removingCat);
1097690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            }
1098690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            dispatchRouteRemoved(info);
1099690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
1100690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
1101690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
11029a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11039a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the number of {@link MediaRouter.RouteCategory categories} currently
11049a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * represented by routes known to this MediaRouter.
11059a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11069a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the number of unique categories represented by this MediaRouter's known routes
11079a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11089a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public int getCategoryCount() {
1109b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mCategories.size();
11109a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11119a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11129a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11139a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the {@link MediaRouter.RouteCategory category} at the given index.
11149a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Valid indices are in the range [0-getCategoryCount).
11159a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11169a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param index which category to return
11179a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the category at index
11189a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11199a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteCategory getCategoryAt(int index) {
1120b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mCategories.get(index);
11219a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11229a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11239a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11249a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the number of {@link MediaRouter.RouteInfo routes} currently known
11259a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * to this MediaRouter.
11269a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11279a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the number of routes tracked by this router
11289a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11299a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public int getRouteCount() {
1130b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.size();
11319a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11329a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11339a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11349a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the route at the specified index.
11359a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11369a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param index index of the route to return
11379a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the route at index
11389a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11399a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteInfo getRouteAt(int index) {
1140b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.get(index);
1141b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
1142b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
1143b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static int getRouteCountStatic() {
1144b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.size();
1145b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
1146b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
1147b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static RouteInfo getRouteAtStatic(int index) {
1148b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.get(index);
11499a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11509a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11519a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11529a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Create a new user route that may be modified and registered for use by the application.
11539a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11549a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param category The category the new route will belong to
11559a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return A new UserRouteInfo for use by the application
11569a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11579a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #addUserRoute(UserRouteInfo)
11589a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #removeUserRoute(UserRouteInfo)
115969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @see #createRouteCategory(CharSequence, boolean)
11609a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11619a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public UserRouteInfo createUserRoute(RouteCategory category) {
11629a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return new UserRouteInfo(category);
11639a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11649a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11659a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11669a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Create a new route category. Each route must belong to a category.
11679a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11689a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param name Name of the new category
11699a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param isGroupable true if routes in this category may be grouped with one another
11709a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the new RouteCategory
11719a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11729a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteCategory createRouteCategory(CharSequence name, boolean isGroupable) {
11739a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return new RouteCategory(name, ROUTE_TYPE_USER, isGroupable);
11749a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11755d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
11760d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    /**
11770d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * Create a new route category. Each route must belong to a category.
11780d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     *
11790d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @param nameResId Resource ID of the name of the new category
11800d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @param isGroupable true if routes in this category may be grouped with one another
11810d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @return the new RouteCategory
11820d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     */
11830d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    public RouteCategory createRouteCategory(int nameResId, boolean isGroupable) {
11840d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        return new RouteCategory(nameResId, ROUTE_TYPE_USER, isGroupable);
11850d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    }
11869a1de308cea2d160778fd977825f10a07b49d738Adam Powell
118769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
118869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Rebinds the media router to handle routes that belong to the specified user.
118969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Requires the interact across users permission to access the routes of another user.
119069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * <p>
119169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * This method is a complete hack to work around the singleton nature of the
119269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * media router when running inside of singleton processes like QuickSettings.
119369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * This mechanism should be burned to the ground when MediaRouter is redesigned.
119469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Ideally the current user would be pulled from the Context but we need to break
119569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * down MediaRouter.Static before we can get there.
119669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * </p>
119769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     *
119869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide
119969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
120069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void rebindAsUser(int userId) {
120169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        sStatic.rebindAsUser(userId);
120269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
120369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
1204b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void updateRoute(final RouteInfo info) {
12059a1de308cea2d160778fd977825f10a07b49d738Adam Powell        dispatchRouteChanged(info);
12069a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12079a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1208b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteSelected(int type, RouteInfo info) {
120939d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
121066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1211b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteSelected(cbi.router, type, info);
12129a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
12139a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12149a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12159a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1216b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteUnselected(int type, RouteInfo info) {
121739d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
121866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1219b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteUnselected(cbi.router, type, info);
12209a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
12219a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12229a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12239a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1224b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteChanged(RouteInfo info) {
1225af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        dispatchRouteChanged(info, info.mSupportedTypes);
1226af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown    }
1227af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
1228af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown    static void dispatchRouteChanged(RouteInfo info, int oldSupportedTypes) {
12294bad663518908c20ed4f93a27f46ffb09448497fSungsoo Lim        if (DEBUG) {
12304bad663518908c20ed4f93a27f46ffb09448497fSungsoo Lim            Log.d(TAG, "Dispatching route change: " + info);
12314bad663518908c20ed4f93a27f46ffb09448497fSungsoo Lim        }
1232af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        final int newSupportedTypes = info.mSupportedTypes;
123339d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
1234af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Reconstruct some of the history for callbacks that may not have observed
1235af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // all of the events needed to correctly interpret the current state.
1236af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // FIXME: This is a strong signal that we should deprecate route type filtering
1237af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // completely in the future because it can lead to inconsistencies in
1238af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // applications.
1239af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            final boolean oldVisibility = cbi.filterRouteEvent(oldSupportedTypes);
1240af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            final boolean newVisibility = cbi.filterRouteEvent(newSupportedTypes);
1241af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (!oldVisibility && newVisibility) {
1242af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                cbi.cb.onRouteAdded(cbi.router, info);
1243af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (info.isSelected()) {
1244af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    cbi.cb.onRouteSelected(cbi.router, newSupportedTypes, info);
1245af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
1246af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            }
1247af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (oldVisibility || newVisibility) {
1248b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteChanged(cbi.router, info);
12499a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
1250af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (oldVisibility && !newVisibility) {
1251af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (info.isSelected()) {
1252af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    cbi.cb.onRouteUnselected(cbi.router, oldSupportedTypes, info);
1253af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
1254af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                cbi.cb.onRouteRemoved(cbi.router, info);
1255af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            }
12569a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12579a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12589a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1259b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteAdded(RouteInfo info) {
126039d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
126166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1262b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteAdded(cbi.router, info);
12639a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
12649a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12659a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12669a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1267b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteRemoved(RouteInfo info) {
126839d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
126966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1270b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteRemoved(cbi.router, info);
12719a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
12729a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12739a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12749a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1275b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteGrouped(RouteInfo info, RouteGroup group, int index) {
127639d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
127766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(group)) {
1278b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteGrouped(cbi.router, info, group, index);
1279d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            }
1280d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        }
1281d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell    }
1282d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
1283b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteUngrouped(RouteInfo info, RouteGroup group) {
128439d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
128566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(group)) {
1286b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteUngrouped(cbi.router, info, group);
12879a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
12889a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12899a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12909a1de308cea2d160778fd977825f10a07b49d738Adam Powell
12918e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    static void dispatchRouteVolumeChanged(RouteInfo info) {
12928e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
129366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
12948e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                cbi.cb.onRouteVolumeChanged(cbi.router, info);
12958e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
12968e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
12978e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    }
12988e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
129992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown    static void dispatchRoutePresentationDisplayChanged(RouteInfo info) {
130092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        for (CallbackInfo cbi : sStatic.mCallbacks) {
130166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
130292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                cbi.cb.onRoutePresentationDisplayChanged(cbi.router, info);
130392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            }
130492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
130592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown    }
130692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
13078e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    static void systemVolumeChanged(int newValue) {
13088e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        final RouteInfo selectedRoute = sStatic.mSelectedRoute;
13098e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        if (selectedRoute == null) return;
13108e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
13118e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        if (selectedRoute == sStatic.mBluetoothA2dpRoute ||
1312705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                selectedRoute == sStatic.mDefaultAudioVideo) {
13138e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            dispatchRouteVolumeChanged(selectedRoute);
13148e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        } else if (sStatic.mBluetoothA2dpRoute != null) {
1315fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            try {
1316fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                dispatchRouteVolumeChanged(sStatic.mAudioService.isBluetoothA2dpOn() ?
1317fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                        sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo);
1318fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            } catch (RemoteException e) {
1319fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                Log.e(TAG, "Error checking Bluetooth A2DP state to report volume change", e);
1320fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            }
13218e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        } else {
1322705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            dispatchRouteVolumeChanged(sStatic.mDefaultAudioVideo);
1323705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1324705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1325705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
132675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    static void updateWifiDisplayStatus(WifiDisplayStatus status) {
132775af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        WifiDisplay[] displays;
1328615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell        WifiDisplay activeDisplay;
132975af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        if (status.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) {
133075af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            displays = status.getDisplays();
133175af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            activeDisplay = status.getActiveDisplay();
1332af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
1333af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Only the system is able to connect to wifi display routes.
1334af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // The display manager will enforce this with a permission check but it
1335af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // still publishes information about all available displays.
1336af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Filter the list down to just the active display.
1337af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (!sStatic.mCanConfigureWifiDisplays) {
1338af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (activeDisplay != null) {
1339af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    displays = new WifiDisplay[] { activeDisplay };
1340af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                } else {
1341af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    displays = WifiDisplay.EMPTY_ARRAY;
1342af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
1343af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            }
1344615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell        } else {
134575af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            displays = WifiDisplay.EMPTY_ARRAY;
1346615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell            activeDisplay = null;
1347615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell        }
1348ce468a35b388ca46578934706b38dbae94941643Jeff Brown        String activeDisplayAddress = activeDisplay != null ?
1349ce468a35b388ca46578934706b38dbae94941643Jeff Brown                activeDisplay.getDeviceAddress() : null;
1350705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
135175af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        // Add or update routes.
135275af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        for (int i = 0; i < displays.length; i++) {
135375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            final WifiDisplay d = displays[i];
135475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            if (shouldShowWifiDisplay(d, activeDisplay)) {
1355ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                RouteInfo route = findWifiDisplayRoute(d);
1356ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                if (route == null) {
135775af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                    route = makeWifiDisplayRoute(d, status);
1358ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                    addRouteStatic(route);
1359ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                } else {
1360ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    String address = d.getDeviceAddress();
1361ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    boolean disconnected = !address.equals(activeDisplayAddress)
1362ce468a35b388ca46578934706b38dbae94941643Jeff Brown                            && address.equals(sStatic.mPreviousActiveWifiDisplayAddress);
1363ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    updateWifiDisplayRoute(route, d, status, disconnected);
1364ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                }
1365ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                if (d.equals(activeDisplay)) {
136669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    selectRouteStatic(route.getSupportedTypes(), route, false);
1367ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                }
13682ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            }
1369705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
137075af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown
137175af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        // Remove stale routes.
137275af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        for (int i = sStatic.mRoutes.size(); i-- > 0; ) {
137375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            RouteInfo route = sStatic.mRoutes.get(i);
137475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            if (route.mDeviceAddress != null) {
137575af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                WifiDisplay d = findWifiDisplay(displays, route.mDeviceAddress);
137675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                if (d == null || !shouldShowWifiDisplay(d, activeDisplay)) {
137775af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                    removeRouteStatic(route);
1378ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                }
1379705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1380705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1381ce468a35b388ca46578934706b38dbae94941643Jeff Brown
1382ce468a35b388ca46578934706b38dbae94941643Jeff Brown        // Remember the current active wifi display address so that we can infer disconnections.
1383ce468a35b388ca46578934706b38dbae94941643Jeff Brown        // TODO: This hack will go away once all of this is moved into the media router service.
1384ce468a35b388ca46578934706b38dbae94941643Jeff Brown        sStatic.mPreviousActiveWifiDisplayAddress = activeDisplayAddress;
138575af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    }
1386705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
138775af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    private static boolean shouldShowWifiDisplay(WifiDisplay d, WifiDisplay activeDisplay) {
138875af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        return d.isRemembered() || d.equals(activeDisplay);
1389705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1390705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1391ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    static int getWifiDisplayStatusCode(WifiDisplay d, WifiDisplayStatus wfdStatus) {
139269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        int newStatus;
1393ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        if (wfdStatus.getScanState() == WifiDisplayStatus.SCAN_STATE_SCANNING) {
1394ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang            newStatus = RouteInfo.STATUS_SCANNING;
1395ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        } else if (d.isAvailable()) {
1396ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang            newStatus = d.canConnect() ?
1397ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                    RouteInfo.STATUS_AVAILABLE: RouteInfo.STATUS_IN_USE;
1398705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        } else {
1399705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            newStatus = RouteInfo.STATUS_NOT_AVAILABLE;
1400705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1401705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1402ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        if (d.equals(wfdStatus.getActiveDisplay())) {
1403ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang            final int activeState = wfdStatus.getActiveDisplayState();
1404705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            switch (activeState) {
1405705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                case WifiDisplayStatus.DISPLAY_STATE_CONNECTED:
140669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    newStatus = RouteInfo.STATUS_CONNECTED;
1407705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    break;
1408705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                case WifiDisplayStatus.DISPLAY_STATE_CONNECTING:
1409705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    newStatus = RouteInfo.STATUS_CONNECTING;
1410705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    break;
1411705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                case WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED:
1412705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    Log.e(TAG, "Active display is not connected!");
1413705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    break;
1414705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1415705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1416705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1417ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        return newStatus;
1418ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    }
1419ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
1420ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    static boolean isWifiDisplayEnabled(WifiDisplay d, WifiDisplayStatus wfdStatus) {
1421ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        return d.isAvailable() && (d.canConnect() || d.equals(wfdStatus.getActiveDisplay()));
1422ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    }
1423ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
1424ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    static RouteInfo makeWifiDisplayRoute(WifiDisplay display, WifiDisplayStatus wfdStatus) {
1425ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        final RouteInfo newRoute = new RouteInfo(sStatic.mSystemCategory);
1426ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mDeviceAddress = display.getDeviceAddress();
142769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        newRoute.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO
142869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                | ROUTE_TYPE_REMOTE_DISPLAY;
1429ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mVolumeHandling = RouteInfo.PLAYBACK_VOLUME_FIXED;
1430ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mPlaybackType = RouteInfo.PLAYBACK_TYPE_REMOTE;
1431ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
143239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        newRoute.setRealStatusCode(getWifiDisplayStatusCode(display, wfdStatus));
1433ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mEnabled = isWifiDisplayEnabled(display, wfdStatus);
1434ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mName = display.getFriendlyDisplayName();
1435ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mDescription = sStatic.mResources.getText(
1436ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                com.android.internal.R.string.wireless_display_route_description);
14375830b0b33618940d65197cec99d697b21908fec8Chong Zhang        newRoute.updatePresentationDisplay();
14389dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        newRoute.mDeviceType = RouteInfo.DEVICE_TYPE_TV;
1439ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        return newRoute;
1440ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    }
1441ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
1442ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    private static void updateWifiDisplayRoute(
1443ce468a35b388ca46578934706b38dbae94941643Jeff Brown            RouteInfo route, WifiDisplay display, WifiDisplayStatus wfdStatus,
1444ce468a35b388ca46578934706b38dbae94941643Jeff Brown            boolean disconnected) {
1445ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        boolean changed = false;
14462444ae7e2b8658a4a90f996e678423558744b4a2Jeff Brown        final String newName = display.getFriendlyDisplayName();
14472444ae7e2b8658a4a90f996e678423558744b4a2Jeff Brown        if (!route.getName().equals(newName)) {
1448705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            route.mName = newName;
1449705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            changed = true;
1450705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1451705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1452ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        boolean enabled = isWifiDisplayEnabled(display, wfdStatus);
1453ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        changed |= route.mEnabled != enabled;
1454ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        route.mEnabled = enabled;
1455705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
145639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        changed |= route.setRealStatusCode(getWifiDisplayStatusCode(display, wfdStatus));
1457705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1458705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (changed) {
1459705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            dispatchRouteChanged(route);
1460705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1461705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1462ce468a35b388ca46578934706b38dbae94941643Jeff Brown        if ((!enabled || disconnected) && route.isSelected()) {
1463705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            // Oops, no longer available. Reselect the default.
146469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            selectDefaultRouteStatic();
1465705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1466705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1467705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
146875af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    private static WifiDisplay findWifiDisplay(WifiDisplay[] displays, String deviceAddress) {
1469705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        for (int i = 0; i < displays.length; i++) {
147075af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            final WifiDisplay d = displays[i];
147175af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            if (d.getDeviceAddress().equals(deviceAddress)) {
147275af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                return d;
1473705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1474705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1475705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return null;
1476705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1477705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1478705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    private static RouteInfo findWifiDisplayRoute(WifiDisplay d) {
1479705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final int count = sStatic.mRoutes.size();
1480705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        for (int i = 0; i < count; i++) {
1481705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            final RouteInfo info = sStatic.mRoutes.get(i);
1482705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (d.getDeviceAddress().equals(info.mDeviceAddress)) {
1483705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                return info;
1484705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1485705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1486705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return null;
14878e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    }
14888e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
14899a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
14909a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a media route.
14919a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
1492b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class RouteInfo {
14939a1de308cea2d160778fd977825f10a07b49d738Adam Powell        CharSequence mName;
14940d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        int mNameResId;
149556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        CharSequence mDescription;
14969a1de308cea2d160778fd977825f10a07b49d738Adam Powell        private CharSequence mStatus;
14979a1de308cea2d160778fd977825f10a07b49d738Adam Powell        int mSupportedTypes;
14989dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        int mDeviceType;
14999a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteGroup mGroup;
15009a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final RouteCategory mCategory;
1501ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        Drawable mIcon;
15021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        // playback information
15031357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mPlaybackType = PLAYBACK_TYPE_LOCAL;
15041357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
15051357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
15061357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
15071357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mPlaybackStream = AudioManager.STREAM_MUSIC;
15081357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        VolumeCallbackInfo mVcb;
150992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        Display mPresentationDisplay;
15105830b0b33618940d65197cec99d697b21908fec8Chong Zhang        int mPresentationDisplayId = -1;
15119a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1512705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        String mDeviceAddress;
1513705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        boolean mEnabled = true;
1514705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
151569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // An id by which the route is known to the media router service.
151669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // Null if this route only exists as an artifact within this process.
151769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        String mGlobalRouteId;
151869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
1519705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        // A predetermined connection status that can override mStatus
152039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private int mRealStatusCode;
152139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private int mResolvedStatusCode;
1522705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
15232ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_NONE = 0;
15242ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_SCANNING = 1;
15252ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_CONNECTING = 2;
15262ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_AVAILABLE = 3;
15272ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_NOT_AVAILABLE = 4;
1528ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        /** @hide */ public static final int STATUS_IN_USE = 5;
152969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        /** @hide */ public static final int STATUS_CONNECTED = 6;
1530705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
15319dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /** @hide */
15329dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        @IntDef({DEVICE_TYPE_UNKNOWN, DEVICE_TYPE_TV, DEVICE_TYPE_SPEAKER, DEVICE_TYPE_BLUETOOTH})
15339dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        @Retention(RetentionPolicy.SOURCE)
15349dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public @interface DeviceType {}
15359dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
15369dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /**
15379dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * The default receiver device type of the route indicating the type is unknown.
15389dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         *
15399dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * @see #getDeviceType
15409dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         */
15419dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public static final int DEVICE_TYPE_UNKNOWN = 0;
15429dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
15439dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /**
15449dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * A receiver device type of the route indicating the presentation of the media is happening
15459dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * on a TV.
15469dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         *
15479dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * @see #getDeviceType
15489dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         */
15499dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public static final int DEVICE_TYPE_TV = 1;
15509dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
15519dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /**
15529dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * A receiver device type of the route indicating the presentation of the media is happening
15539dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * on a speaker.
15549dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         *
15559dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * @see #getDeviceType
15569dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         */
15579dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public static final int DEVICE_TYPE_SPEAKER = 2;
15589dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
15599dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /**
15609dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * A receiver device type of the route indicating the presentation of the media is happening
15619dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * on a bluetooth device such as a bluetooth speaker.
15629dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         *
15639dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * @see #getDeviceType
15649dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         */
15659dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public static final int DEVICE_TYPE_BLUETOOTH = 3;
15669dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
1567b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        private Object mTag;
1568b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
15697c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        /** @hide */
15707c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        @IntDef({PLAYBACK_TYPE_LOCAL, PLAYBACK_TYPE_REMOTE})
15717c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        @Retention(RetentionPolicy.SOURCE)
15727c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        public @interface PlaybackType {}
15737c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang
15741357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
15751357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * The default playback type, "local", indicating the presentation of the media is happening
1576bc4cf00dc5bf6e0e3c01206d5c46e64306df260aJohn Spurlock         * on the same device (e&#46;g&#46; a phone, a tablet) as where it is controlled from.
157769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getPlaybackType()
15781357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
15791357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_TYPE_LOCAL = 0;
15807c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang
15811357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
15821357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * A playback type indicating the presentation of the media is happening on
1583bc4cf00dc5bf6e0e3c01206d5c46e64306df260aJohn Spurlock         * a different device (i&#46;e&#46; the remote device) than where it is controlled from.
158469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getPlaybackType()
15851357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
15861357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_TYPE_REMOTE = 1;
15877c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang
15887c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        /** @hide */
15897c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang         @IntDef({PLAYBACK_VOLUME_FIXED,PLAYBACK_VOLUME_VARIABLE})
15907c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang         @Retention(RetentionPolicy.SOURCE)
15917c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang         private @interface PlaybackVolume {}
15927c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang
15931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
1594bc4cf00dc5bf6e0e3c01206d5c46e64306df260aJohn Spurlock         * Playback information indicating the playback volume is fixed, i&#46;e&#46; it cannot be
15951357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * controlled from this object. An example of fixed playback volume is a remote player,
15961357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
15971357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * than attenuate at the source.
159869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getVolumeHandling()
15991357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
16001357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_VOLUME_FIXED = 0;
16011357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
16021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Playback information indicating the playback volume is variable and can be controlled
16031357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * from this object.
160469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getVolumeHandling()
16051357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
16061357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_VOLUME_VARIABLE = 1;
16071357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
16089a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteInfo(RouteCategory category) {
16099a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mCategory = category;
16109dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang            mDeviceType = DEVICE_TYPE_UNKNOWN;
16119a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
16129a1de308cea2d160778fd977825f10a07b49d738Adam Powell
16139a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
161456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Gets the user-visible name of the route.
161556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
161656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route name identifies the destination represented by the route.
161756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied name, an alias, or device serial number.
161856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
161956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
162056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The user-visible name of a media route.  This is the string presented
16219a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * to users who may select this as the active route.
16229a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
16239a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getName() {
16240d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(sStatic.mResources);
16250d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
162656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
16270d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        /**
162856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Return the properly localized/resource user-visible name of this route.
162956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
163056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route name identifies the destination represented by the route.
163156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied name, an alias, or device serial number.
163256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
163356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
16340d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @param context Context used to resolve the correct configuration to load
163556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The user-visible name of a media route.  This is the string presented
16360d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * to users who may select this as the active route.
16370d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         */
16380d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public CharSequence getName(Context context) {
16390d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(context.getResources());
16400d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
164156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
16420d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        CharSequence getName(Resources res) {
16430d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            if (mNameResId != 0) {
1644eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon                return res.getText(mNameResId);
16450d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            }
16469a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mName;
16479a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
16489a1de308cea2d160778fd977825f10a07b49d738Adam Powell
16499a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
165056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Gets the user-visible description of the route.
165156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
165256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route description describes the kind of destination represented by the route.
165356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied string, a model number or brand of device.
165456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
165556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
165656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The description of the route, or null if none.
165756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         */
165856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        public CharSequence getDescription() {
165956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown            return mDescription;
166056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        }
166156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
166256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        /**
166356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The user-visible status for a media route. This may include a description
16649a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * of the currently playing media, if available.
16659a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
16669a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getStatus() {
16679a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mStatus;
16689a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
16699a1de308cea2d160778fd977825f10a07b49d738Adam Powell
16709a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
1671705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * Set this route's status by predetermined status code. If the caller
1672705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * should dispatch a route changed event this call will return true;
1673705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         */
167439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        boolean setRealStatusCode(int statusCode) {
167539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (mRealStatusCode != statusCode) {
167639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                mRealStatusCode = statusCode;
167739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                return resolveStatusCode();
167839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
167939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return false;
168039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        }
168139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown
168239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        /**
168339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown         * Resolves the status code whenever the real status code or selection state
168439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown         * changes.
168539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown         */
168639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        boolean resolveStatusCode() {
168739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            int statusCode = mRealStatusCode;
168839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (isSelected()) {
1689705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                switch (statusCode) {
169039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // If the route is selected and its status appears to be between states
169139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // then report it as connecting even though it has not yet had a chance
169239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // to officially move into the CONNECTING state.  Note that routes in
169339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // the NONE state are assumed to not require an explicit connection
169439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // lifecycle whereas those that are AVAILABLE are assumed to have
169539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // to eventually proceed to CONNECTED.
1696705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    case STATUS_AVAILABLE:
169739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    case STATUS_SCANNING:
169839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                        statusCode = STATUS_CONNECTING;
169969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        break;
1700705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                }
1701705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
170239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (mResolvedStatusCode == statusCode) {
170339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                return false;
170439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
170539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown
170639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            mResolvedStatusCode = statusCode;
170739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            int resId;
170839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            switch (statusCode) {
170939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_SCANNING:
171039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_scanning;
171139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
171239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_CONNECTING:
171339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_connecting;
171439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
171539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_AVAILABLE:
171639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_available;
171739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
171839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_NOT_AVAILABLE:
171939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_not_available;
172039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
172139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_IN_USE:
172239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_in_use;
172339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
172439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_CONNECTED:
172539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_NONE:
172639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                default:
172739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = 0;
172839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
172939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
173039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            mStatus = resId != 0 ? sStatic.mResources.getText(resId) : null;
173139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return true;
1732705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1733705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1734705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        /**
17352ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell         * @hide
17362ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell         */
17372ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        public int getStatusCode() {
173839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return mResolvedStatusCode;
17392ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        }
17402ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell
17412ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /**
17429a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return A media type flag set describing which types this route supports.
17439a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
17449a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int getSupportedTypes() {
17459a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mSupportedTypes;
17469a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
17479a1de308cea2d160778fd977825f10a07b49d738Adam Powell
17489dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /**
17499dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * Gets the type of the receiver device associated with this route.
17509dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         *
17519dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * @return The type of the receiver device associated with this route:
17529dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * {@link #DEVICE_TYPE_BLUETOOTH}, {@link #DEVICE_TYPE_TV}, {@link #DEVICE_TYPE_SPEAKER},
17539dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * or {@link #DEVICE_TYPE_UNKNOWN}.
17549dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         */
17559dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        @DeviceType
17569dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public int getDeviceType() {
17579dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang            return mDeviceType;
17589dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        }
17599dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
17600abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
17610abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public boolean matchesTypes(int types) {
17620abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            return (mSupportedTypes & types) != 0;
17630abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
17640abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
17659a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
17669a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return The group that this route belongs to.
17679a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
17689a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public RouteGroup getGroup() {
17699a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mGroup;
17709a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
17719a1de308cea2d160778fd977825f10a07b49d738Adam Powell
17729a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
17739a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return the category this route belongs to.
17749a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
17759a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public RouteCategory getCategory() {
17769a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mCategory;
17779a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
17789a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1779ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
1780ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Get the icon representing this route.
1781ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * This icon will be used in picker UIs if available.
1782ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
1783ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @return the icon representing this route or null if no icon is available
1784ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
1785ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public Drawable getIconDrawable() {
1786ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            return mIcon;
1787ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
1788ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
1789b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        /**
1790b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * Set an application-specific tag object for this route.
1791b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * The application may use this to store arbitrary data associated with the
1792b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * route for internal tracking.
1793b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         *
1794b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * <p>Note that the lifespan of a route may be well past the lifespan of
1795b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * an Activity or other Context; take care that objects you store here
1796b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * will not keep more data in memory alive than you intend.</p>
1797b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         *
1798b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * @param tag Arbitrary, app-specific data for this route to hold for later use
1799b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         */
1800b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        public void setTag(Object tag) {
1801b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            mTag = tag;
1802130b4572d1f3df702e5b296a655d15a41f6d4c66Adam Powell            routeUpdated();
1803b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        }
1804b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
1805b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        /**
1806b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * @return The tag object previously set by the application
1807b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * @see #setTag(Object)
1808b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         */
1809b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        public Object getTag() {
1810b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            return mTag;
1811b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        }
1812b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
18131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
18141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the type of playback associated with this route
18151357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setPlaybackType(int)
18161357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
18177c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        @PlaybackType
18181357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getPlaybackType() {
18191357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            return mPlaybackType;
18201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
18211357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
18221357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
18231357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the stream over which the playback associated with this route is performed
18241357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setPlaybackStream(int)
18251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
18261357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getPlaybackStream() {
18271357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            return mPlaybackStream;
18281357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
18291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
18301357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
18318e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Return the current volume for this route. Depending on the route, this may only
18328e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * be valid if the route is currently selected.
18338e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         *
18341357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the volume at which the playback associated with this route is performed
18351357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setVolume(int)
18361357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
18371357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getVolume() {
18381357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
18391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                int vol = 0;
18401357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                try {
18411357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    vol = sStatic.mAudioService.getStreamVolume(mPlaybackStream);
18421357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                } catch (RemoteException e) {
18431357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    Log.e(TAG, "Error getting local stream volume", e);
18441357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                }
18451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return vol;
18461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            } else {
18471357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return mVolume;
18481357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
18491357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
18501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
18511357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
18528e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Request a volume change for this route.
18538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param volume value between 0 and getVolumeMax
18548e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         */
18558e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestSetVolume(int volume) {
18568e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
18578e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                try {
1858ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                    sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0,
1859ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                            ActivityThread.currentPackageName());
18608e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                } catch (RemoteException e) {
18618e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Error setting local stream volume", e);
18628e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
18638e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            } else {
186469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                sStatic.requestSetVolume(this, volume);
18658e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
18668e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
18678e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
18688e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        /**
18698e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Request an incremental volume update for this route.
18708e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param direction Delta to apply to the current volume
18718e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         */
18728e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestUpdateVolume(int direction) {
18738e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
18748e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                try {
18758e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    final int volume =
18768e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                            Math.max(0, Math.min(getVolume() + direction, getVolumeMax()));
1877ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                    sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0,
1878ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                            ActivityThread.currentPackageName());
18798e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                } catch (RemoteException e) {
18808e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Error setting local stream volume", e);
18818e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
18828e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            } else {
188369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                sStatic.requestUpdateVolume(this, direction);
18848e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
18858e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
18868e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
18878e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        /**
18881357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the maximum volume at which the playback associated with this route is performed
18891357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setVolumeMax(int)
18901357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
18911357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getVolumeMax() {
18921357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
18931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                int volMax = 0;
18941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                try {
18951357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    volMax = sStatic.mAudioService.getStreamMaxVolume(mPlaybackStream);
18961357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                } catch (RemoteException e) {
18971357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    Log.e(TAG, "Error getting local stream volume", e);
18981357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                }
18991357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return volMax;
19001357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            } else {
19011357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return mVolumeMax;
19021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
19031357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
19041357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
19051357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
19061357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return how volume is handling on the route
19071357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setVolumeHandling(int)
19081357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
19097c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        @PlaybackVolume
19101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getVolumeHandling() {
19111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            return mVolumeHandling;
19121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
19131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
1914705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        /**
191592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * Gets the {@link Display} that should be used by the application to show
191692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * a {@link android.app.Presentation} on an external display when this route is selected.
191792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * Depending on the route, this may only be valid if the route is currently
191892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * selected.
191992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * <p>
192092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * The preferred presentation display may change independently of the route
192192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * being selected or unselected.  For example, the presentation display
192292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * of the default system route may change when an external HDMI display is connected
192392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * or disconnected even though the route itself has not changed.
192492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p><p>
192592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * This method may return null if there is no external display associated with
192692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * the route or if the display is not ready to show UI yet.
192792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p><p>
192892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * The application should listen for changes to the presentation display
192992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * using the {@link Callback#onRoutePresentationDisplayChanged} callback and
193092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * show or dismiss its {@link android.app.Presentation} accordingly when the display
193192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * becomes available or is removed.
193292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p><p>
193392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * This method only makes sense for {@link #ROUTE_TYPE_LIVE_VIDEO live video} routes.
193492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p>
193592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
193692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @return The preferred presentation display to use when this route is
193792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * selected or null if none.
193892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
193992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @see #ROUTE_TYPE_LIVE_VIDEO
194092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @see android.app.Presentation
194192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         */
194292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public Display getPresentationDisplay() {
194392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            return mPresentationDisplay;
194492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
194592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
19465830b0b33618940d65197cec99d697b21908fec8Chong Zhang        boolean updatePresentationDisplay() {
19475830b0b33618940d65197cec99d697b21908fec8Chong Zhang            Display display = choosePresentationDisplay();
19485830b0b33618940d65197cec99d697b21908fec8Chong Zhang            if (mPresentationDisplay != display) {
19495830b0b33618940d65197cec99d697b21908fec8Chong Zhang                mPresentationDisplay = display;
19505830b0b33618940d65197cec99d697b21908fec8Chong Zhang                return true;
19515830b0b33618940d65197cec99d697b21908fec8Chong Zhang            }
19525830b0b33618940d65197cec99d697b21908fec8Chong Zhang            return false;
19535830b0b33618940d65197cec99d697b21908fec8Chong Zhang        }
19545830b0b33618940d65197cec99d697b21908fec8Chong Zhang
19555830b0b33618940d65197cec99d697b21908fec8Chong Zhang        private Display choosePresentationDisplay() {
19565830b0b33618940d65197cec99d697b21908fec8Chong Zhang            if ((mSupportedTypes & ROUTE_TYPE_LIVE_VIDEO) != 0) {
19575830b0b33618940d65197cec99d697b21908fec8Chong Zhang                Display[] displays = sStatic.getAllPresentationDisplays();
19585830b0b33618940d65197cec99d697b21908fec8Chong Zhang
19595830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // Ensure that the specified display is valid for presentations.
19605830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // This check will normally disallow the default display unless it was
19615830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // configured as a presentation display for some reason.
19625830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (mPresentationDisplayId >= 0) {
19635830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    for (Display display : displays) {
19645830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        if (display.getDisplayId() == mPresentationDisplayId) {
19655830b0b33618940d65197cec99d697b21908fec8Chong Zhang                            return display;
19665830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        }
19675830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    }
19685830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    return null;
19695830b0b33618940d65197cec99d697b21908fec8Chong Zhang                }
19705830b0b33618940d65197cec99d697b21908fec8Chong Zhang
19715830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // Find the indicated Wifi display by its address.
19725830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (mDeviceAddress != null) {
19735830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    for (Display display : displays) {
19745830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        if (display.getType() == Display.TYPE_WIFI
19755830b0b33618940d65197cec99d697b21908fec8Chong Zhang                                && mDeviceAddress.equals(display.getAddress())) {
19765830b0b33618940d65197cec99d697b21908fec8Chong Zhang                            return display;
19775830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        }
19785830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    }
19795830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    return null;
19805830b0b33618940d65197cec99d697b21908fec8Chong Zhang                }
19815830b0b33618940d65197cec99d697b21908fec8Chong Zhang
19825830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // For the default route, choose the first presentation display from the list.
19835830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (this == sStatic.mDefaultAudioVideo && displays.length > 0) {
19845830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    return displays[0];
19855830b0b33618940d65197cec99d697b21908fec8Chong Zhang                }
19865830b0b33618940d65197cec99d697b21908fec8Chong Zhang            }
19875830b0b33618940d65197cec99d697b21908fec8Chong Zhang            return null;
19885830b0b33618940d65197cec99d697b21908fec8Chong Zhang        }
19895830b0b33618940d65197cec99d697b21908fec8Chong Zhang
199075af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        /** @hide */
199175af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        public String getDeviceAddress() {
199275af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            return mDeviceAddress;
199375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        }
199475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown
199592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        /**
1996a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * Returns true if this route is enabled and may be selected.
1997a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         *
1998a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * @return True if this route is enabled.
1999705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         */
2000705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        public boolean isEnabled() {
2001705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return mEnabled;
2002705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
2003705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
2004a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown        /**
2005a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * Returns true if the route is in the process of connecting and is not
2006a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * yet ready for use.
2007a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         *
2008a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * @return True if this route is in the process of connecting.
2009a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         */
2010a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown        public boolean isConnecting() {
201139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return mResolvedStatusCode == STATUS_CONNECTING;
20120abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
20130abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
20140abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
20150abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public boolean isSelected() {
20160abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            return this == sStatic.mSelectedRoute;
20170abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
20180abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
20190abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
20200abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public boolean isDefault() {
20210abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            return this == sStatic.mDefaultAudioVideo;
20220abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
20230abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
20240abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
20250abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public void select() {
20260abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            selectRouteStatic(mSupportedTypes, this, true);
2027a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown        }
2028a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown
20299a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void setStatusInt(CharSequence status) {
20309a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (!status.equals(mStatus)) {
20319a1de308cea2d160778fd977825f10a07b49d738Adam Powell                mStatus = status;
20329a1de308cea2d160778fd977825f10a07b49d738Adam Powell                if (mGroup != null) {
20339a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    mGroup.memberStatusChanged(this, status);
20349a1de308cea2d160778fd977825f10a07b49d738Adam Powell                }
20359a1de308cea2d160778fd977825f10a07b49d738Adam Powell                routeUpdated();
20369a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
20379a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
20389a1de308cea2d160778fd977825f10a07b49d738Adam Powell
20391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        final IRemoteVolumeObserver.Stub mRemoteVolObserver = new IRemoteVolumeObserver.Stub() {
204069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            @Override
20411357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            public void dispatchRemoteVolumeUpdate(final int direction, final int value) {
20421357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                sStatic.mHandler.post(new Runnable() {
20431357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    @Override
20441357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    public void run() {
20451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                        if (mVcb != null) {
20461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                            if (direction != 0) {
20471357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                                mVcb.vcb.onVolumeUpdateRequest(mVcb.route, direction);
20481357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                            } else {
20491357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                                mVcb.vcb.onVolumeSetRequest(mVcb.route, value);
20501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                            }
20511357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                        }
20521357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    }
20531357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                });
20541357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
20551357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        };
20561357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
20579a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void routeUpdated() {
20589a1de308cea2d160778fd977825f10a07b49d738Adam Powell            updateRoute(this);
20599a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
20609a1de308cea2d160778fd977825f10a07b49d738Adam Powell
20619a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
20629a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public String toString() {
2063d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            String supportedTypes = typesToString(getSupportedTypes());
206492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            return getClass().getSimpleName() + "{ name=" + getName() +
206556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown                    ", description=" + getDescription() +
206692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                    ", status=" + getStatus() +
206792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                    ", category=" + getCategory() +
206892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                    ", supportedTypes=" + supportedTypes +
206969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    ", presentationDisplay=" + mPresentationDisplay + " }";
20709a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
20719a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
20729a1de308cea2d160778fd977825f10a07b49d738Adam Powell
20739a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
20749a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a route that the application may define and modify.
20758e37a85bf3dc39519942698dc90a3951306b934bAdam Powell     * A user route defaults to {@link RouteInfo#PLAYBACK_TYPE_REMOTE} and
20768e37a85bf3dc39519942698dc90a3951306b934bAdam Powell     * {@link RouteInfo#PLAYBACK_VOLUME_FIXED}.
20779a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
20789a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see MediaRouter.RouteInfo
20799a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
2080b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class UserRouteInfo extends RouteInfo {
2081ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        RemoteControlClient mRcc;
20825d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        SessionVolumeProvider mSvp;
20839a1de308cea2d160778fd977825f10a07b49d738Adam Powell
20849a1de308cea2d160778fd977825f10a07b49d738Adam Powell        UserRouteInfo(RouteCategory category) {
20859a1de308cea2d160778fd977825f10a07b49d738Adam Powell            super(category);
20869a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mSupportedTypes = ROUTE_TYPE_USER;
20878e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mPlaybackType = PLAYBACK_TYPE_REMOTE;
20888e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeHandling = PLAYBACK_VOLUME_FIXED;
20899a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
20909a1de308cea2d160778fd977825f10a07b49d738Adam Powell
20919a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
20929a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Set the user-visible name of this route.
20939a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param name Name to display to the user to describe this route
20949a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
20959a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void setName(CharSequence name) {
2096eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon            mNameResId = 0;
20979a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = name;
20989a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
20999a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
21005d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
21010d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        /**
21020d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * Set the user-visible name of this route.
210356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
210456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route name identifies the destination represented by the route.
210556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied name, an alias, or device serial number.
210656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
210756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
21080d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @param resId Resource ID of the name to display to the user to describe this route
21090d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         */
21100d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public void setName(int resId) {
21110d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mNameResId = resId;
21120d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mName = null;
21130d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            routeUpdated();
21140d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
21159a1de308cea2d160778fd977825f10a07b49d738Adam Powell
21169a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
211756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Set the user-visible description of this route.
211856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
211956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route description describes the kind of destination represented by the route.
212056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied string, a model number or brand of device.
212156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
212256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
212356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @param description The description of the route, or null if none.
212456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         */
212556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        public void setDescription(CharSequence description) {
212656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown            mDescription = description;
212756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown            routeUpdated();
212856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        }
212956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
213056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        /**
21319a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Set the current user-visible status for this route.
21329a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param status Status to display to the user to describe what the endpoint
21339a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * of this route is currently doing
21349a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
21359a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void setStatus(CharSequence status) {
21369a1de308cea2d160778fd977825f10a07b49d738Adam Powell            setStatusInt(status);
21379a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2138ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2139ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2140ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set the RemoteControlClient responsible for reporting playback info for this
2141ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * user route.
2142ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
2143ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * <p>If this route manages remote playback, the data exposed by this
2144ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * RemoteControlClient will be used to reflect and update information
2145ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * such as route volume info in related UIs.</p>
2146ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
21471357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * <p>The RemoteControlClient must have been previously registered with
21481357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}.</p>
21491357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *
2150ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @param rcc RemoteControlClient associated with this route
2151ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
2152ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setRemoteControlClient(RemoteControlClient rcc) {
2153ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            mRcc = rcc;
21541357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            updatePlaybackInfoOnRcc();
2155ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2156ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2157ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
21584599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         * Retrieve the RemoteControlClient associated with this route, if one has been set.
21594599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         *
21604599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         * @return the RemoteControlClient associated with this route
21614599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         * @see #setRemoteControlClient(RemoteControlClient)
21624599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         */
21634599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        public RemoteControlClient getRemoteControlClient() {
21644599696591f745b3a546197d2ba7e5cfc5562484Adam Powell            return mRcc;
21654599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        }
21664599696591f745b3a546197d2ba7e5cfc5562484Adam Powell
21674599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        /**
2168ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this route.
2169ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2170ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
2171ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @param icon icon drawable to use to represent this route
2172ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
2173ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setIconDrawable(Drawable icon) {
2174ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            mIcon = icon;
2175ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2176ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2177ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2178ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this route.
2179ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2180ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
218171c69897ad0a55d590698bfa399bfe99c763b9dbAdam Powell         * @param resId Resource ID of an icon drawable to use to represent this route
2182ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
21837b9c912f536925ac6ec43935d6e97506851b33d6Tor Norbye        public void setIconResource(@DrawableRes int resId) {
2184ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            setIconDrawable(sStatic.mResources.getDrawable(resId));
2185ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
21861357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
21871357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
21881357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Set a callback to be notified of volume update requests
21891357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param vcb
21901357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
21911357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolumeCallback(VolumeCallback vcb) {
21921357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            mVcb = new VolumeCallbackInfo(vcb, this);
21931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
21941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
21951357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
21961357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines whether playback associated with this route is "local"
21971357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *    ({@link RouteInfo#PLAYBACK_TYPE_LOCAL}) or "remote"
21981357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *    ({@link RouteInfo#PLAYBACK_TYPE_REMOTE}).
21991357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param type
22001357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
22017c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        public void setPlaybackType(@RouteInfo.PlaybackType int type) {
22021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackType != type) {
22031357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mPlaybackType = type;
2204430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
22051357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
22061357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
22071357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
22081357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
22091357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines whether volume for the playback associated with this route is fixed
22101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * ({@link RouteInfo#PLAYBACK_VOLUME_FIXED}) or can modified
22111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * ({@link RouteInfo#PLAYBACK_VOLUME_VARIABLE}).
22121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volumeHandling
22131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
22147c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        public void setVolumeHandling(@RouteInfo.PlaybackVolume int volumeHandling) {
22151357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mVolumeHandling != volumeHandling) {
22161357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mVolumeHandling = volumeHandling;
2217430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
22181357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
22191357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
22201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
22211357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
22221357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines at what volume the playback associated with this route is performed (for user
22231357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * feedback purposes). This information is only used when the playback is not local.
22241357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volume
22251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
22261357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolume(int volume) {
22278e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            volume = Math.max(0, Math.min(volume, getVolumeMax()));
22281357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mVolume != volume) {
22291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mVolume = volume;
2230430fc48865e5a371b08f180390946b96d73848feRoboErik                if (mSvp != null) {
22310d0f67f5ee5f939a1b611bc4583212707afd9beeRoboErik                    mSvp.setCurrentVolume(mVolume);
22325d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
22338e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                dispatchRouteVolumeChanged(this);
2234f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                if (mGroup != null) {
2235f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                    mGroup.memberVolumeChanged(this);
2236f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                }
22378e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
22388e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
22398e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
22408e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
22418e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestSetVolume(int volume) {
22428e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mVolumeHandling == PLAYBACK_VOLUME_VARIABLE) {
22438e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (mVcb == null) {
22448e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Cannot requestSetVolume on user route - no volume callback set");
22458e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    return;
22468e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
22478e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVcb.vcb.onVolumeSetRequest(this, volume);
22488e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
22498e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
22508e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
22518e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
22528e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestUpdateVolume(int direction) {
22538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mVolumeHandling == PLAYBACK_VOLUME_VARIABLE) {
22548e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (mVcb == null) {
22558e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Cannot requestChangeVolume on user route - no volumec callback set");
22568e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    return;
22578e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
22588e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVcb.vcb.onVolumeUpdateRequest(this, direction);
22591357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
22601357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
22611357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
22621357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
22631357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines the maximum volume at which the playback associated with this route is performed
22641357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * (for user feedback purposes). This information is only used when the playback is not
22651357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * local.
22661357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volumeMax
22671357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
22681357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolumeMax(int volumeMax) {
22691357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mVolumeMax != volumeMax) {
22701357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mVolumeMax = volumeMax;
2271430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
22721357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
22731357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
22741357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
22751357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
22761357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines over what stream type the media is presented.
22771357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param stream
22781357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
22791357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setPlaybackStream(int stream) {
22801357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackStream != stream) {
22811357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mPlaybackStream = stream;
2282430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
22831357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
22841357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
22851357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
22861357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        private void updatePlaybackInfoOnRcc() {
2287430fc48865e5a371b08f180390946b96d73848feRoboErik            configureSessionVolume();
22885d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        }
22895d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
22905d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        private void configureSessionVolume() {
22915d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            if (mRcc == null) {
22925d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                if (DEBUG) {
2293eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon                    Log.d(TAG, "No Rcc to configure volume for route " + getName());
22945d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
22955d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                return;
22965d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
22975d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            MediaSession session = mRcc.getMediaSession();
22985d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            if (session == null) {
22995d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                if (DEBUG) {
23005d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    Log.d(TAG, "Rcc has no session to configure volume");
23015d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
23025d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                return;
23035d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
23045d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            if (mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
23057c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang                @VolumeProvider.ControlType int volumeControl =
23067c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang                        VolumeProvider.VOLUME_CONTROL_FIXED;
23075d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                switch (mVolumeHandling) {
23085d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    case RemoteControlClient.PLAYBACK_VOLUME_VARIABLE:
2309ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik                        volumeControl = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
23105d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        break;
23115d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    case RemoteControlClient.PLAYBACK_VOLUME_FIXED:
23125d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    default:
23135d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        break;
23141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                }
23155d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                // Only register a new listener if necessary
23165d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                if (mSvp == null || mSvp.getVolumeControl() != volumeControl
23175d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        || mSvp.getMaxVolume() != mVolumeMax) {
23180d0f67f5ee5f939a1b611bc4583212707afd9beeRoboErik                    mSvp = new SessionVolumeProvider(volumeControl, mVolumeMax, mVolume);
23195d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    session.setPlaybackToRemote(mSvp);
23205d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
23215d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            } else {
23225d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                // We only know how to handle local and remote, fall back to local if not remote.
23239db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik                AudioAttributes.Builder bob = new AudioAttributes.Builder();
23249db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik                bob.setLegacyStreamType(mPlaybackStream);
23259db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik                session.setPlaybackToLocal(bob.build());
23265d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                mSvp = null;
23271357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
23281357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
23291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
2330ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        class SessionVolumeProvider extends VolumeProvider {
23315d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
23327c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang            public SessionVolumeProvider(@VolumeProvider.ControlType int volumeControl,
23337c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang                    int maxVolume, int currentVolume) {
23340d0f67f5ee5f939a1b611bc4583212707afd9beeRoboErik                super(volumeControl, maxVolume, currentVolume);
23355d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
23365d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
23375d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            @Override
23385d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            public void onSetVolumeTo(final int volume) {
23395d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                sStatic.mHandler.post(new Runnable() {
23405d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    @Override
23415d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    public void run() {
23425d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        if (mVcb != null) {
23435d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                            mVcb.vcb.onVolumeSetRequest(mVcb.route, volume);
23445d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        }
23455d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    }
23465d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                });
23475d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
23485d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
23495d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            @Override
23501ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik            public void onAdjustVolume(final int direction) {
23515d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                sStatic.mHandler.post(new Runnable() {
23525d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    @Override
23535d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    public void run() {
23545d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        if (mVcb != null) {
23551ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik                            mVcb.vcb.onVolumeUpdateRequest(mVcb.route, direction);
23565d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        }
23575d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    }
23585d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                });
23595d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
23605d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        }
23619a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
23629a1de308cea2d160778fd977825f10a07b49d738Adam Powell
23639a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
23649a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a route that consists of multiple other routes in a group.
23659a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
2366b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class RouteGroup extends RouteInfo {
23679a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
23689a1de308cea2d160778fd977825f10a07b49d738Adam Powell        private boolean mUpdateName;
23699a1de308cea2d160778fd977825f10a07b49d738Adam Powell
23709a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteGroup(RouteCategory category) {
23719a1de308cea2d160778fd977825f10a07b49d738Adam Powell            super(category);
23729a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mGroup = this;
23738e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeHandling = PLAYBACK_VOLUME_FIXED;
23749a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
23759a1de308cea2d160778fd977825f10a07b49d738Adam Powell
237669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
23770d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        CharSequence getName(Resources res) {
23789a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (mUpdateName) updateName();
23790d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return super.getName(res);
23809a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
23819a1de308cea2d160778fd977825f10a07b49d738Adam Powell
23829a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
23839a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Add a route to this group. The route must not currently belong to another group.
23849a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
23859a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to add to this group
23869a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
23879a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void addRoute(RouteInfo route) {
23889a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != null) {
23899a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalStateException("Route " + route + " is already part of a group.");
23909a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
23919a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getCategory() != mCategory) {
23929a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException(
23939a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        "Route cannot be added to a group with a different category. " +
23949a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            "(Route category=" + route.getCategory() +
23959a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            " group category=" + mCategory + ")");
23969a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
2397d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            final int at = mRoutes.size();
23989a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.add(route);
2399d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = this;
24009a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2401f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
24029a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
2403f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell            dispatchRouteGrouped(route, this, at);
24049a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
24059a1de308cea2d160778fd977825f10a07b49d738Adam Powell
24069a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
24079a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Add a route to this group before the specified index.
24089a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
24099a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to add
24109a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param insertAt insert the new route before this index
24119a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
24129a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void addRoute(RouteInfo route, int insertAt) {
24139a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != null) {
24149a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalStateException("Route " + route + " is already part of a group.");
24159a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
24169a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getCategory() != mCategory) {
24179a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException(
24189a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        "Route cannot be added to a group with a different category. " +
24199a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            "(Route category=" + route.getCategory() +
24209a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            " group category=" + mCategory + ")");
24219a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
24229a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.add(insertAt, route);
2423d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = this;
24249a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2425f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
24269a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
2427f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell            dispatchRouteGrouped(route, this, insertAt);
24289a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
24299a1de308cea2d160778fd977825f10a07b49d738Adam Powell
24309a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
24319a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Remove a route from this group.
24329a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
24339a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to remove
24349a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
24359a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void removeRoute(RouteInfo route) {
24369a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != this) {
24379a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException("Route " + route +
24389a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        " is not a member of this group.");
24399a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
24409a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.remove(route);
2441d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = null;
24429a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2443f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
2444d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteUngrouped(route, this);
24459a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
24469a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
24479a1de308cea2d160778fd977825f10a07b49d738Adam Powell
24489a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
24499a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Remove the route at the specified index from this group.
24509a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
24519a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param index index of the route to remove
24529a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
24539a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void removeRoute(int index) {
2454d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            RouteInfo route = mRoutes.remove(index);
2455d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = null;
24569a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2457f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
2458d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteUngrouped(route, this);
24599a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
24609a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
24619a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2462d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2463d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @return The number of routes in this group
2464d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
2465d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public int getRouteCount() {
2466d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            return mRoutes.size();
2467d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        }
2468d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2469d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2470d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Return the route in this group at the specified index
2471d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2472d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param index Index to fetch
2473d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @return The route at index
2474d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
2475d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public RouteInfo getRouteAt(int index) {
2476d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            return mRoutes.get(index);
2477d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        }
2478d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2479ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2480ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this group.
2481ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2482ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
2483ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @param icon icon drawable to use to represent this group
2484ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
2485ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setIconDrawable(Drawable icon) {
2486ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            mIcon = icon;
2487ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2488ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2489ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2490ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this group.
2491ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2492ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
249371c69897ad0a55d590698bfa399bfe99c763b9dbAdam Powell         * @param resId Resource ID of an icon drawable to use to represent this group
2494ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
24957b9c912f536925ac6ec43935d6e97506851b33d6Tor Norbye        public void setIconResource(@DrawableRes int resId) {
2496ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            setIconDrawable(sStatic.mResources.getDrawable(resId));
2497ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2498ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
24998e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
25008e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestSetVolume(int volume) {
25018e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int maxVol = getVolumeMax();
25028e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (maxVol == 0) {
25038e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                return;
25048e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
25058e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
25068e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final float scaledVolume = (float) volume / maxVol;
25078e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int routeCount = getRouteCount();
25088e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            for (int i = 0; i < routeCount; i++) {
25098e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final RouteInfo route = getRouteAt(i);
25108e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int routeVol = (int) (scaledVolume * route.getVolumeMax());
25118e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                route.requestSetVolume(routeVol);
25128e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
25138e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (volume != mVolume) {
25148e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVolume = volume;
25158e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                dispatchRouteVolumeChanged(this);
25168e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
25178e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
25188e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
25198e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
25208e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestUpdateVolume(int direction) {
25218e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int maxVol = getVolumeMax();
25228e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (maxVol == 0) {
25238e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                return;
25248e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
25258e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
25268e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int routeCount = getRouteCount();
2527f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            int volume = 0;
25288e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            for (int i = 0; i < routeCount; i++) {
25298e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final RouteInfo route = getRouteAt(i);
25308e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                route.requestUpdateVolume(direction);
2531f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                final int routeVol = route.getVolume();
2532f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                if (routeVol > volume) {
2533f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                    volume = routeVol;
2534f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                }
25358e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
25368e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (volume != mVolume) {
25378e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVolume = volume;
25388e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                dispatchRouteVolumeChanged(this);
25398e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
25408e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
25418e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
25429a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void memberNameChanged(RouteInfo info, CharSequence name) {
25439a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
25449a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
25459a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
25469a1de308cea2d160778fd977825f10a07b49d738Adam Powell
25479a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void memberStatusChanged(RouteInfo info, CharSequence status) {
25489a1de308cea2d160778fd977825f10a07b49d738Adam Powell            setStatusInt(status);
25499a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
25509a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2551f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        void memberVolumeChanged(RouteInfo info) {
2552f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
2553f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        }
2554f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell
2555f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        void updateVolume() {
2556f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            // A group always represents the highest component volume value.
2557f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            final int routeCount = getRouteCount();
2558f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            int volume = 0;
2559f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            for (int i = 0; i < routeCount; i++) {
2560f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                final int routeVol = getRouteAt(i).getVolume();
2561f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                if (routeVol > volume) {
2562f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                    volume = routeVol;
2563f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                }
2564f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            }
2565f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            if (volume != mVolume) {
2566f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                mVolume = volume;
2567f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                dispatchRouteVolumeChanged(this);
2568f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            }
2569f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        }
2570f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell
2571d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        @Override
2572d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        void routeUpdated() {
2573d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            int types = 0;
2574d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            final int count = mRoutes.size();
2575b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            if (count == 0) {
2576b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell                // Don't keep empty groups in the router.
257769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                MediaRouter.removeRouteStatic(this);
2578b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell                return;
2579b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            }
2580b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
25818e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            int maxVolume = 0;
25828e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            boolean isLocal = true;
25838e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            boolean isFixedVolume = true;
2584d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            for (int i = 0; i < count; i++) {
25858e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final RouteInfo route = mRoutes.get(i);
25868e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                types |= route.mSupportedTypes;
25878e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int routeMaxVolume = route.getVolumeMax();
25888e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (routeMaxVolume > maxVolume) {
25898e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    maxVolume = routeMaxVolume;
25908e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
25918e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                isLocal &= route.getPlaybackType() == PLAYBACK_TYPE_LOCAL;
25928e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                isFixedVolume &= route.getVolumeHandling() == PLAYBACK_VOLUME_FIXED;
2593d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            }
25948e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mPlaybackType = isLocal ? PLAYBACK_TYPE_LOCAL : PLAYBACK_TYPE_REMOTE;
25958e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeHandling = isFixedVolume ? PLAYBACK_VOLUME_FIXED : PLAYBACK_VOLUME_VARIABLE;
2596d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            mSupportedTypes = types;
25978e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeMax = maxVolume;
2598d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            mIcon = count == 1 ? mRoutes.get(0).getIconDrawable() : null;
2599d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            super.routeUpdated();
2600d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        }
2601d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
26029a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void updateName() {
26039a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final StringBuilder sb = new StringBuilder();
26049a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final int count = mRoutes.size();
26059a1de308cea2d160778fd977825f10a07b49d738Adam Powell            for (int i = 0; i < count; i++) {
26069a1de308cea2d160778fd977825f10a07b49d738Adam Powell                final RouteInfo info = mRoutes.get(i);
2607b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell                // TODO: There's probably a much more correct way to localize this.
2608eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon                if (i > 0) {
2609eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon                    sb.append(", ");
2610eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon                }
2611eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon                sb.append(info.getName());
26129a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
26139a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = sb.toString();
26149a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = false;
26159a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2616d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
2617d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        @Override
2618d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        public String toString() {
2619d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            StringBuilder sb = new StringBuilder(super.toString());
2620d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            sb.append('[');
2621d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            final int count = mRoutes.size();
2622d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            for (int i = 0; i < count; i++) {
2623d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                if (i > 0) sb.append(", ");
2624d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                sb.append(mRoutes.get(i));
2625d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            }
2626d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            sb.append(']');
2627d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            return sb.toString();
2628d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        }
26299a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
26309a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26319a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
26329a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Definition of a category of routes. All routes belong to a category.
26339a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
2634b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class RouteCategory {
26359a1de308cea2d160778fd977825f10a07b49d738Adam Powell        CharSequence mName;
26360d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        int mNameResId;
26379a1de308cea2d160778fd977825f10a07b49d738Adam Powell        int mTypes;
26389a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final boolean mGroupable;
2639705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        boolean mIsSystem;
26409a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26419a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteCategory(CharSequence name, int types, boolean groupable) {
26429a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = name;
26439a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mTypes = types;
26449a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mGroupable = groupable;
26459a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
26469a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26470d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        RouteCategory(int nameResId, int types, boolean groupable) {
26480d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mNameResId = nameResId;
26490d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mTypes = types;
26500d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mGroupable = groupable;
26510d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
26520d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell
26539a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
26549a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return the name of this route category
26559a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
26569a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getName() {
26570d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(sStatic.mResources);
26580d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
26595d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
26600d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        /**
26610d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * Return the properly localized/configuration dependent name of this RouteCategory.
26625d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik         *
26630d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @param context Context to resolve name resources
26640d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @return the name of this route category
26650d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         */
26660d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public CharSequence getName(Context context) {
26670d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(context.getResources());
26680d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
26695d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
26700d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        CharSequence getName(Resources res) {
26710d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            if (mNameResId != 0) {
26720d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                return res.getText(mNameResId);
26730d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            }
26749a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mName;
26759a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
26769a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26779a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
2678d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Return the current list of routes in this category that have been added
2679d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * to the MediaRouter.
26809a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2681d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * <p>This list will not include routes that are nested within RouteGroups.
2682d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * A RouteGroup is treated as a single route within its category.</p>
2683d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2684d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param out a List to fill with the routes in this category. If this parameter is
2685d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *            non-null, it will be cleared, filled with the current routes with this
2686d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *            category, and returned. If this parameter is null, a new List will be
2687d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *            allocated to report the category's current routes.
2688d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @return A list with the routes in this category that have been added to the MediaRouter.
26899a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
2690d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public List<RouteInfo> getRoutes(List<RouteInfo> out) {
2691d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            if (out == null) {
2692d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                out = new ArrayList<RouteInfo>();
2693d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            } else {
2694d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                out.clear();
2695d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            }
2696d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2697b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final int count = getRouteCountStatic();
2698d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            for (int i = 0; i < count; i++) {
2699b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                final RouteInfo route = getRouteAtStatic(i);
2700d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                if (route.mCategory == this) {
2701d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                    out.add(route);
2702d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                }
2703d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            }
2704d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            return out;
27059a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27069a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27079a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
27089a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return Flag set describing the route types supported by this category
27099a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
27109a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int getSupportedTypes() {
27119a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mTypes;
27129a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27139a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27149a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
27159a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Return whether or not this category supports grouping.
27169a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
27179a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * <p>If this method returns true, all routes obtained from this category
2718d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * via calls to {@link #getRouteAt(int)} will be {@link MediaRouter.RouteGroup}s.</p>
27199a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
27209a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return true if this category supports
27219a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
27229a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public boolean isGroupable() {
27239a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mGroupable;
27249a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27259a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2726705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        /**
2727705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * @return true if this is the category reserved for system routes.
2728705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * @hide
2729705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         */
2730705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        public boolean isSystem() {
2731705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return mIsSystem;
2732705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
2733705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
273469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
27359a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public String toString() {
2736eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon            return "RouteCategory{ name=" + getName() + " types=" + typesToString(mTypes) +
2737d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                    " groupable=" + mGroupable + " }";
27389a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27399a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
27409a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27419a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static class CallbackInfo {
27429a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int type;
274366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        public int flags;
2744b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        public final Callback cb;
2745b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        public final MediaRouter router;
27469a1de308cea2d160778fd977825f10a07b49d738Adam Powell
274766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        public CallbackInfo(Callback cb, int type, int flags, MediaRouter router) {
27489a1de308cea2d160778fd977825f10a07b49d738Adam Powell            this.cb = cb;
27499a1de308cea2d160778fd977825f10a07b49d738Adam Powell            this.type = type;
275066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            this.flags = flags;
2751b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            this.router = router;
27529a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
275366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
275466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        public boolean filterRouteEvent(RouteInfo route) {
2755af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            return filterRouteEvent(route.mSupportedTypes);
2756af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        }
2757af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
2758af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        public boolean filterRouteEvent(int supportedTypes) {
275966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            return (flags & CALLBACK_FLAG_UNFILTERED_EVENTS) != 0
2760af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    || (type & supportedTypes) != 0;
276166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
27629a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
27639a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27649a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
27659a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Interface for receiving events about media routing changes.
27669a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * All methods of this interface will be called from the application's main thread.
276766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
276866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * A Callback will only receive events relevant to routes that the callback
276966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * was registered for unless the {@link MediaRouter#CALLBACK_FLAG_UNFILTERED_EVENTS}
277066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * flag was specified in {@link MediaRouter#addCallback(int, Callback, int)}.
277166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
27729a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
277366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @see MediaRouter#addCallback(int, Callback, int)
27749a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see MediaRouter#removeCallback(Callback)
27759a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
27760d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    public static abstract class Callback {
27779a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
27789a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when the supplied route becomes selected as the active route
27799a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * for the given route type.
27809a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2781d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
27829a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param type Type flag set indicating the routes that have been selected
27839a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been selected for the given route types
27849a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
27850d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteSelected(MediaRouter router, int type, RouteInfo info);
27869a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27879a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
27889a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when the supplied route becomes unselected as the active route
27899a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * for the given route type.
27909a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2791d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
27929a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param type Type flag set indicating the routes that have been unselected
27939a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been unselected for the given route types
27949a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
27950d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteUnselected(MediaRouter router, int type, RouteInfo info);
27969a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27979a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
27989a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when a route for the specified type was added.
27999a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2800d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
28019a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has become available for use
28029a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
28030d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteAdded(MediaRouter router, RouteInfo info);
28049a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28059a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
28069a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when a route for the specified type was removed.
28079a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2808d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
28099a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been removed from availability
28109a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
28110d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteRemoved(MediaRouter router, RouteInfo info);
28129a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28139a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
28149a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when an aspect of the indicated route has changed.
28159a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
28169a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * <p>This will not indicate that the types supported by this route have
28179a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * changed, only that cosmetic info such as name or status have been updated.</p>
28189a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2819d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
28209a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info The route that was changed
28219a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
28220d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteChanged(MediaRouter router, RouteInfo info);
2823d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2824d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2825d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Called when a route is added to a group.
2826d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2827d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
2828d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param info The route that was added
2829d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param group The group the route was added to
2830d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param index The route index within group that info was added at
2831d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
28320d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
28330d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                int index);
2834d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2835d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2836d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Called when a route is removed from a group.
2837d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2838d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
2839d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param info The route that was removed
2840d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param group The group the route was removed from
2841d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
28420d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group);
28438e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
28448e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        /**
28458e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Called when a route's volume changes.
28468e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         *
28478e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param router the MediaRouter reporting the event
28488e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param info The route with altered volume
28498e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         */
28508e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public abstract void onRouteVolumeChanged(MediaRouter router, RouteInfo info);
285192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
285292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        /**
285392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * Called when a route's presentation display changes.
285492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * <p>
285592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * This method is called whenever the route's presentation display becomes
285692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * available, is removes or has changes to some of its properties (such as its size).
285792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p>
285892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
285992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @param router the MediaRouter reporting the event
286092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @param info The route whose presentation display changed
286192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
286292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @see RouteInfo#getPresentationDisplay()
286392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         */
286492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
286592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
28669a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
28679a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28689a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
28690d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * Stub implementation of {@link MediaRouter.Callback}.
28700d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * Each abstract method is defined as a no-op. Override just the ones
28719a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * you need.
28729a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
28730d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    public static class SimpleCallback extends Callback {
28749a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28759a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2876d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
28779a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
28789a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28799a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2880d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
28819a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
28829a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28839a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2884d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteAdded(MediaRouter router, RouteInfo info) {
28859a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
28869a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28879a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2888d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteRemoved(MediaRouter router, RouteInfo info) {
28899a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
28909a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28919a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2892d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteChanged(MediaRouter router, RouteInfo info) {
28939a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
28949a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28959a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2896d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
2897d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                int index) {
28989a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
28999a1de308cea2d160778fd977825f10a07b49d738Adam Powell
29009a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2901d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
29029a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2903d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
29048e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
29058e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void onRouteVolumeChanged(MediaRouter router, RouteInfo info) {
29068e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
29079a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
29081357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
29091357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    static class VolumeCallbackInfo {
29101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final VolumeCallback vcb;
29111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final RouteInfo route;
29121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
29131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public VolumeCallbackInfo(VolumeCallback vcb, RouteInfo route) {
29141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            this.vcb = vcb;
29151357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            this.route = route;
29161357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
29171357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    }
29181357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
29191357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    /**
29201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * Interface for receiving events about volume changes.
29211357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * All methods of this interface will be called from the application's main thread.
29221357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     *
29231357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * <p>A VolumeCallback will only receive events relevant to routes that the callback
29241357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * was registered for.</p>
29251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     *
29261357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * @see UserRouteInfo#setVolumeCallback(VolumeCallback)
29271357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     */
29281357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    public static abstract class VolumeCallback {
29291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
29301357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Called when the volume for the route should be increased or decreased.
29311357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param info the route affected by this event
29321357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param direction an integer indicating whether the volume is to be increased
29331357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     (positive value) or decreased (negative value).
29341357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     For bundled changes, the absolute value indicates the number of changes
29351357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     in the same direction, e.g. +3 corresponds to three "volume up" changes.
29361357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
29371357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public abstract void onVolumeUpdateRequest(RouteInfo info, int direction);
29381357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
29391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Called when the volume for the route should be set to the given value
29401357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param info the route affected by this event
29411357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volume an integer indicating the new volume value that should be used, always
29421357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     between 0 and the value set by {@link UserRouteInfo#setVolumeMax(int)}.
29431357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
29441357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public abstract void onVolumeSetRequest(RouteInfo info, int volume);
29451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    }
29461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
29478e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    static class VolumeChangeReceiver extends BroadcastReceiver {
29488e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
29498e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void onReceive(Context context, Intent intent) {
29508e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (intent.getAction().equals(AudioManager.VOLUME_CHANGED_ACTION)) {
29518e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
29528e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                        -1);
29538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (streamType != AudioManager.STREAM_MUSIC) {
29548e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    return;
29558e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
29568e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
29578e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int newVolume = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
29588e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int oldVolume = intent.getIntExtra(
29598e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                        AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0);
29608e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (newVolume != oldVolume) {
29618e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    systemVolumeChanged(newVolume);
29628e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
29638e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
29648e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
2965705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
29668e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
2967705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    static class WifiDisplayStatusChangedReceiver extends BroadcastReceiver {
2968705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        @Override
2969705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        public void onReceive(Context context, Intent intent) {
2970705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (intent.getAction().equals(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)) {
2971705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                updateWifiDisplayStatus((WifiDisplayStatus) intent.getParcelableExtra(
2972705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                        DisplayManager.EXTRA_WIFI_DISPLAY_STATUS));
2973705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
2974705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
29758e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    }
29769a1de308cea2d160778fd977825f10a07b49d738Adam Powell}
2977