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) {
18676512a3d80dff2d32b68f3b3b6a14d4ed81ae90aSungsoo Lim            boolean audioRoutesChanged = false;
187546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim            boolean forceUseDefaultRoute = false;
188546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim
1896156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock            if (newRoutes.mainType != mCurAudioRoutesInfo.mainType) {
1906156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                mCurAudioRoutesInfo.mainType = newRoutes.mainType;
191632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                int name;
192546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim                if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0
193546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim                        || (newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) {
194632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    name = com.android.internal.R.string.default_audio_route_name_headphones;
195546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim                } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
196632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
1976156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                } else if ((newRoutes.mainType&AudioRoutesInfo.MAIN_HDMI) != 0) {
19860aa8b99a0f16b76feaf4a994bc1b235ef709830Sungsoo Lim                    name = com.android.internal.R.string.default_audio_route_name_hdmi;
19960aa8b99a0f16b76feaf4a994bc1b235ef709830Sungsoo Lim                } else if ((newRoutes.mainType&AudioRoutesInfo.MAIN_USB) != 0) {
20060aa8b99a0f16b76feaf4a994bc1b235ef709830Sungsoo Lim                    name = com.android.internal.R.string.default_audio_route_name_usb;
201632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                } else {
202632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    name = com.android.internal.R.string.default_audio_route_name;
203632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                }
204b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo                mDefaultAudioVideo.mNameResId = name;
205b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo                dispatchRouteChanged(mDefaultAudioVideo);
206546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim
207546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim                if ((newRoutes.mainType & (AudioRoutesInfo.MAIN_HEADSET
208546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim                        | AudioRoutesInfo.MAIN_HEADPHONES | AudioRoutesInfo.MAIN_USB)) != 0) {
209546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim                    forceUseDefaultRoute = true;
210546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim                }
21176512a3d80dff2d32b68f3b3b6a14d4ed81ae90aSungsoo Lim                audioRoutesChanged = true;
212632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
213bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell
2146156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock            if (!TextUtils.equals(newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) {
215546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim                forceUseDefaultRoute = false;
2166156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                mCurAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName;
2176156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                if (mCurAudioRoutesInfo.bluetoothName != null) {
218b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo                    if (mBluetoothA2dpRoute == null) {
219b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo                        // BT connected
220b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo                        final RouteInfo info = new RouteInfo(mSystemCategory);
2216156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock                        info.mName = mCurAudioRoutesInfo.bluetoothName;
222b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo                        info.mDescription = mResources.getText(
22356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown                                com.android.internal.R.string.bluetooth_a2dp_audio_route_name);
224632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        info.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO;
2259dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang                        info.mDeviceType = RouteInfo.DEVICE_TYPE_BLUETOOTH;
226b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo                        mBluetoothA2dpRoute = info;
227b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo                        addRouteStatic(mBluetoothA2dpRoute);
228632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    } else {
229b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo                        mBluetoothA2dpRoute.mName = mCurAudioRoutesInfo.bluetoothName;
230b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo                        dispatchRouteChanged(mBluetoothA2dpRoute);
231632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    }
232b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo                } else if (mBluetoothA2dpRoute != null) {
233b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo                    // BT disconnected
234b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo                    removeRouteStatic(mBluetoothA2dpRoute);
235b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo                    mBluetoothA2dpRoute = null;
236bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell                }
23776512a3d80dff2d32b68f3b3b6a14d4ed81ae90aSungsoo Lim                audioRoutesChanged = true;
238bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell            }
23976512a3d80dff2d32b68f3b3b6a14d4ed81ae90aSungsoo Lim
24076512a3d80dff2d32b68f3b3b6a14d4ed81ae90aSungsoo Lim            if (audioRoutesChanged) {
241742e379863ff08a880cb6d9e5f274da7589f9a53Jaewan Kim                Log.v(TAG, "Audio routes updated: " + newRoutes + ", a2dp=" + isBluetoothA2dpOn());
242f8e38e2dbbf3410179c9e18e05b7d4022720cfb6Sungsoo Lim                if (mSelectedRoute == null || mSelectedRoute == mDefaultAudioVideo
243f8e38e2dbbf3410179c9e18e05b7d4022720cfb6Sungsoo Lim                        || mSelectedRoute == mBluetoothA2dpRoute) {
244546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim                    if (forceUseDefaultRoute || mBluetoothA2dpRoute == null) {
245546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim                        selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mDefaultAudioVideo, false);
246546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim                    } else {
247546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim                        selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mBluetoothA2dpRoute, false);
248546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim                    }
249f8e38e2dbbf3410179c9e18e05b7d4022720cfb6Sungsoo Lim                }
250742e379863ff08a880cb6d9e5f274da7589f9a53Jaewan Kim            }
251b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        }
25292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
253fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo        boolean isBluetoothA2dpOn() {
254fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            try {
255546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim                return mBluetoothA2dpRoute != null && mAudioService.isBluetoothA2dpOn();
256fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            } catch (RemoteException e) {
257fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                Log.e(TAG, "Error querying Bluetooth A2DP state", e);
258fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                return false;
259fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            }
260fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo        }
261fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo
26269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void updateDiscoveryRequest() {
26369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // What are we looking for today?
26469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int routeTypes = 0;
26569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int passiveRouteTypes = 0;
26669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean activeScan = false;
26769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean activeScanWifiDisplay = false;
26869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int count = mCallbacks.size();
26969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < count; i++) {
27069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                CallbackInfo cbi = mCallbacks.get(i);
27169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if ((cbi.flags & (CALLBACK_FLAG_PERFORM_ACTIVE_SCAN
27269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        | CALLBACK_FLAG_REQUEST_DISCOVERY)) != 0) {
27369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Discovery explicitly requested.
27469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    routeTypes |= cbi.type;
27569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else if ((cbi.flags & CALLBACK_FLAG_PASSIVE_DISCOVERY) != 0) {
27669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Discovery only passively requested.
27769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    passiveRouteTypes |= cbi.type;
27869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else {
27969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Legacy case since applications don't specify the discovery flag.
28069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Unfortunately we just have to assume they always need discovery
28169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // whenever they have a callback registered.
28269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    routeTypes |= cbi.type;
28369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
28469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if ((cbi.flags & CALLBACK_FLAG_PERFORM_ACTIVE_SCAN) != 0) {
28569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    activeScan = true;
286af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    if ((cbi.type & ROUTE_TYPE_REMOTE_DISPLAY) != 0) {
28769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        activeScanWifiDisplay = true;
28869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
28969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
29069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
29169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (routeTypes != 0 || activeScan) {
29269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // If someone else requests discovery then enable the passive listeners.
29369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // This is used by the MediaRouteButton and MediaRouteActionProvider since
29469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // they don't receive lifecycle callbacks from the Activity.
29569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                routeTypes |= passiveRouteTypes;
29669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
29769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
29869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Update wifi display scanning.
299ce468a35b388ca46578934706b38dbae94941643Jeff Brown            // TODO: All of this should be managed by the media router service.
300ce468a35b388ca46578934706b38dbae94941643Jeff Brown            if (mCanConfigureWifiDisplays) {
301ce468a35b388ca46578934706b38dbae94941643Jeff Brown                if (mSelectedRoute != null
302ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        && mSelectedRoute.matchesTypes(ROUTE_TYPE_REMOTE_DISPLAY)) {
303ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    // Don't scan while already connected to a remote display since
304ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    // it may interfere with the ongoing transmission.
305ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    activeScanWifiDisplay = false;
30666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown                }
307ce468a35b388ca46578934706b38dbae94941643Jeff Brown                if (activeScanWifiDisplay) {
308ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    if (!mActivelyScanningWifiDisplays) {
309ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mActivelyScanningWifiDisplays = true;
310ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mDisplayService.startWifiDisplayScan();
311ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    }
312ce468a35b388ca46578934706b38dbae94941643Jeff Brown                } else {
313ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    if (mActivelyScanningWifiDisplays) {
314ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mActivelyScanningWifiDisplays = false;
315ce468a35b388ca46578934706b38dbae94941643Jeff Brown                        mDisplayService.stopWifiDisplayScan();
316ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    }
31766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown                }
31866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            }
31966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
32069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Tell the media router service all about it.
32169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (routeTypes != mDiscoveryRequestRouteTypes
32269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    || activeScan != mDiscoverRequestActiveScan) {
32369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mDiscoveryRequestRouteTypes = routeTypes;
32469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mDiscoverRequestActiveScan = activeScan;
32569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                publishClientDiscoveryRequest();
32666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            }
32766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
32866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
32992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        @Override
33092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onDisplayAdded(int displayId) {
33192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            updatePresentationDisplays(displayId);
33292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
33392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
33492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        @Override
33592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onDisplayChanged(int displayId) {
33692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            updatePresentationDisplays(displayId);
33792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
33892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
33992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        @Override
34092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onDisplayRemoved(int displayId) {
34192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            updatePresentationDisplays(displayId);
34292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
34392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
34492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public Display[] getAllPresentationDisplays() {
34592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
34692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
34792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
34892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        private void updatePresentationDisplays(int changedDisplayId) {
34992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            final int count = mRoutes.size();
35092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            for (int i = 0; i < count; i++) {
3515830b0b33618940d65197cec99d697b21908fec8Chong Zhang                final RouteInfo route = mRoutes.get(i);
3525830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (route.updatePresentationDisplay() || (route.mPresentationDisplay != null
3535830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        && route.mPresentationDisplay.getDisplayId() == changedDisplayId)) {
3545830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    dispatchRoutePresentationDisplayChanged(route);
35592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                }
35692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            }
35792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
35869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
35969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void setSelectedRoute(RouteInfo info, boolean explicit) {
36069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Must be non-reentrant.
36169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mSelectedRoute = info;
36269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            publishClientSelectedRoute(explicit);
36369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
36469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
36569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void rebindAsUser(int userId) {
36669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mCurrentUserId != userId || userId < 0 || mClient == null) {
36769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (mClient != null) {
36869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    try {
36969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        mMediaRouterService.unregisterClient(mClient);
37069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    } catch (RemoteException ex) {
37169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        Log.e(TAG, "Unable to unregister media router client.", ex);
37269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
37369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClient = null;
37469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
37569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
37669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mCurrentUserId = userId;
37769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
37869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
37969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Client client = new Client();
380eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon                    mMediaRouterService.registerClientAsUser(client, mPackageName, userId);
38169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClient = client;
38269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
38369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to register media router client.", ex);
38469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
38569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
38669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                publishClientDiscoveryRequest();
38769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                publishClientSelectedRoute(false);
38869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                updateClientState();
38969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
39069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
39169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
39269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void publishClientDiscoveryRequest() {
39369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mClient != null) {
39469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
39569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.setDiscoveryRequest(mClient,
39669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mDiscoveryRequestRouteTypes, mDiscoverRequestActiveScan);
39769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
39869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to publish media router client discovery request.", ex);
39969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
40069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
40169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
40269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
40369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void publishClientSelectedRoute(boolean explicit) {
40469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mClient != null) {
40569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
40669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.setSelectedRoute(mClient,
40769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mSelectedRoute != null ? mSelectedRoute.mGlobalRouteId : null,
40869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            explicit);
40969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
41069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to publish media router client selected route.", ex);
41169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
41269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
41369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
41469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
41569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void updateClientState() {
41669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Update the client state.
41769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mClientState = null;
41869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mClient != null) {
41969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
42069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClientState = mMediaRouterService.getState(mClient);
42169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
42269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.e(TAG, "Unable to retrieve media router client state.", ex);
42369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
42469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
42569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final ArrayList<MediaRouterClientState.RouteInfo> globalRoutes =
42669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClientState != null ? mClientState.routes : null;
42769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
42869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Add or update routes.
42969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int globalRouteCount = globalRoutes != null ? globalRoutes.size() : 0;
43069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < globalRouteCount; i++) {
43169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final MediaRouterClientState.RouteInfo globalRoute = globalRoutes.get(i);
43269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                RouteInfo route = findGlobalRoute(globalRoute.id);
43369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (route == null) {
43469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    route = makeGlobalRoute(globalRoute);
43569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    addRouteStatic(route);
43669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else {
43769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    updateGlobalRoute(route, globalRoute);
43869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
43969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
44069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
44169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Remove defunct routes.
44269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            outer: for (int i = mRoutes.size(); i-- > 0; ) {
44369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final RouteInfo route = mRoutes.get(i);
44469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final String globalRouteId = route.mGlobalRouteId;
44569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (globalRouteId != null) {
44669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    for (int j = 0; j < globalRouteCount; j++) {
44769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        MediaRouterClientState.RouteInfo globalRoute = globalRoutes.get(j);
44869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (globalRouteId.equals(globalRoute.id)) {
44969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            continue outer; // found
45069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
45169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
45269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // not found
45369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    removeRouteStatic(route);
45469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
45569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
45669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
45769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
45869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void requestSetVolume(RouteInfo route, int volume) {
45969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mGlobalRouteId != null && mClient != null) {
46069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
46169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.requestSetVolume(mClient,
46269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            route.mGlobalRouteId, volume);
46369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
46469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.w(TAG, "Unable to request volume change.", ex);
46569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
46669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
46769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
46869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
46969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void requestUpdateVolume(RouteInfo route, int direction) {
47069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mGlobalRouteId != null && mClient != null) {
47169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                try {
47269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mMediaRouterService.requestUpdateVolume(mClient,
47369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            route.mGlobalRouteId, direction);
47469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } catch (RemoteException ex) {
47569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Log.w(TAG, "Unable to request volume change.", ex);
47669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
47769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
47869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
47969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
48069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        RouteInfo makeGlobalRoute(MediaRouterClientState.RouteInfo globalRoute) {
481b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo            RouteInfo route = new RouteInfo(mSystemCategory);
48269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mGlobalRouteId = globalRoute.id;
48369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mName = globalRoute.name;
48469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mDescription = globalRoute.description;
48569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mSupportedTypes = globalRoute.supportedTypes;
4869dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang            route.mDeviceType = globalRoute.deviceType;
48769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mEnabled = globalRoute.enabled;
48839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            route.setRealStatusCode(globalRoute.statusCode);
48969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mPlaybackType = globalRoute.playbackType;
49069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mPlaybackStream = globalRoute.playbackStream;
49169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mVolume = globalRoute.volume;
49269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mVolumeMax = globalRoute.volumeMax;
49369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            route.mVolumeHandling = globalRoute.volumeHandling;
4945830b0b33618940d65197cec99d697b21908fec8Chong Zhang            route.mPresentationDisplayId = globalRoute.presentationDisplayId;
4955830b0b33618940d65197cec99d697b21908fec8Chong Zhang            route.updatePresentationDisplay();
49669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            return route;
49769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
49869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
49969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        void updateGlobalRoute(RouteInfo route, MediaRouterClientState.RouteInfo globalRoute) {
50069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean changed = false;
50169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean volumeChanged = false;
50269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean presentationDisplayChanged = false;
50369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
504e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root            if (!Objects.equals(route.mName, globalRoute.name)) {
50569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mName = globalRoute.name;
50669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
50769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
508e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root            if (!Objects.equals(route.mDescription, globalRoute.description)) {
50969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mDescription = globalRoute.description;
51069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
51169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
512af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            final int oldSupportedTypes = route.mSupportedTypes;
513af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (oldSupportedTypes != globalRoute.supportedTypes) {
51469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mSupportedTypes = globalRoute.supportedTypes;
51569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
51669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
51769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mEnabled != globalRoute.enabled) {
51869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mEnabled = globalRoute.enabled;
51969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
52069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
52139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (route.mRealStatusCode != globalRoute.statusCode) {
52239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                route.setRealStatusCode(globalRoute.statusCode);
52369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
52469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
52569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mPlaybackType != globalRoute.playbackType) {
52669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mPlaybackType = globalRoute.playbackType;
52769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
52869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
52969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mPlaybackStream != globalRoute.playbackStream) {
53069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mPlaybackStream = globalRoute.playbackStream;
53169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
53269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
53369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mVolume != globalRoute.volume) {
53469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mVolume = globalRoute.volume;
53569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
53669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                volumeChanged = true;
53769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
53869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mVolumeMax != globalRoute.volumeMax) {
53969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mVolumeMax = globalRoute.volumeMax;
54069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
54169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                volumeChanged = true;
54269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
54369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (route.mVolumeHandling != globalRoute.volumeHandling) {
54469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                route.mVolumeHandling = globalRoute.volumeHandling;
54569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
54669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                volumeChanged = true;
54769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
5485830b0b33618940d65197cec99d697b21908fec8Chong Zhang            if (route.mPresentationDisplayId != globalRoute.presentationDisplayId) {
5495830b0b33618940d65197cec99d697b21908fec8Chong Zhang                route.mPresentationDisplayId = globalRoute.presentationDisplayId;
5505830b0b33618940d65197cec99d697b21908fec8Chong Zhang                route.updatePresentationDisplay();
55169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                changed = true;
55269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                presentationDisplayChanged = true;
55369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
55469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
55569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (changed) {
556af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                dispatchRouteChanged(route, oldSupportedTypes);
55769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
55869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (volumeChanged) {
55969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                dispatchRouteVolumeChanged(route);
56069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
56169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (presentationDisplayChanged) {
56269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                dispatchRoutePresentationDisplayChanged(route);
56369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
56469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
56569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
56669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        RouteInfo findGlobalRoute(String globalRouteId) {
56769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int count = mRoutes.size();
56869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < count; i++) {
56969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final RouteInfo route = mRoutes.get(i);
57069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (globalRouteId.equals(route.mGlobalRouteId)) {
57169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    return route;
57269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
57369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
57469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            return null;
57569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
57669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
577b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo        boolean isPlaybackActive() {
578b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo            if (mClient != null) {
579b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo                try {
580b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo                    return mMediaRouterService.isPlaybackActive(mClient);
581b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo                } catch (RemoteException ex) {
582b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo                    Log.e(TAG, "Unable to retrieve playback active state.", ex);
583b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo                }
584b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo            }
585b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo            return false;
586b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo        }
587b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo
58869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final class Client extends IMediaRouterClient.Stub {
58969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            @Override
59069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public void onStateChanged() {
59169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mHandler.post(new Runnable() {
59269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    @Override
59369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    public void run() {
59469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (Client.this == mClient) {
59569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            updateClientState();
59669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
59769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
59869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                });
59969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
600b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo
601b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo            @Override
602b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo            public void onRestoreRoute() {
603133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim                mHandler.post(new Runnable() {
604133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim                    @Override
605133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim                    public void run() {
606133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim                        // Skip restoring route if the selected route is not a system audio route,
607133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim                        // MediaRouter is initializing, or mClient was changed.
608133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim                        if (Client.this != mClient || mSelectedRoute == null
609133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim                                || (mSelectedRoute != mDefaultAudioVideo
610133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim                                        && mSelectedRoute != mBluetoothA2dpRoute)) {
611133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim                            return;
612133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim                        }
613133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim                        Log.v(TAG, "onRestoreRoute() : route=" + mSelectedRoute);
614133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim                        mSelectedRoute.select();
615133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim                    }
616133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim                });
617b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo            }
61869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
619b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
6209a1de308cea2d160778fd977825f10a07b49d738Adam Powell
621b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static Static sStatic;
6229a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6239a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
6249a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Route type flag for live audio.
6259a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
6269a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>A device that supports live audio routing will allow the media audio stream
6279a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * to be routed to supported destinations. This can include internal speakers or
6289a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * audio jacks on the device itself, A2DP devices, and more.</p>
6299a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
6309a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>Once initiated this routing is transparent to the application. All audio
6319a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * played on the media stream will be routed to the selected destination.</p>
6329a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
63369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_LIVE_AUDIO = 1 << 0;
6349a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6359a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
636705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * Route type flag for live video.
637705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     *
638705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * <p>A device that supports live video routing will allow a mirrored version
639705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * of the device's primary display or a customized
640705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * {@link android.app.Presentation Presentation} to be routed to supported destinations.</p>
641705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     *
642705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * <p>Once initiated, display mirroring is transparent to the application.
643705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * While remote routing is active the application may use a
644705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * {@link android.app.Presentation Presentation} to replace the mirrored view
645705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * on the external display with different content.</p>
64692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown     *
64792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown     * @see RouteInfo#getPresentationDisplay()
64892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown     * @see android.app.Presentation
649705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     */
65069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_LIVE_VIDEO = 1 << 1;
65169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
65269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
65369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Temporary interop constant to identify remote displays.
65469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide To be removed when media router API is updated.
65569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
65669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_REMOTE_DISPLAY = 1 << 2;
657705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
658705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    /**
6599a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Route type flag for application-specific usage.
6609a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
6619a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>Unlike other media route types, user routes are managed by the application.
6629a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * The MediaRouter will manage and dispatch events for user routes, but the application
6639a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * is expected to interpret the meaning of these events and perform the requested
6649a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * routing tasks.</p>
6659a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
66669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int ROUTE_TYPE_USER = 1 << 23;
66769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
66869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static final int ROUTE_TYPE_ANY = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO
66969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            | ROUTE_TYPE_REMOTE_DISPLAY | ROUTE_TYPE_USER;
6709a1de308cea2d160778fd977825f10a07b49d738Adam Powell
67166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    /**
67266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Flag for {@link #addCallback}: Actively scan for routes while this callback
67366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * is registered.
67466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
67566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * When this flag is specified, the media router will actively scan for new
67666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * routes.  Certain routes, such as wifi display routes, may not be discoverable
67766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * except when actively scanning.  This flag is typically used when the route picker
67866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * dialog has been opened by the user to ensure that the route information is
67966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * up to date.
68066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p><p>
68166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Active scanning may consume a significant amount of power and may have intrusive
68266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * effects on wireless connectivity.  Therefore it is important that active scanning
68366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * only be requested when it is actually needed to satisfy a user request to
68466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * discover and select a new route.
68566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
68666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     */
68714507e257af5d71577574e25cbd690c4b54c9272Jeff Brown    public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1 << 0;
68866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
68966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    /**
69066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Flag for {@link #addCallback}: Do not filter route events.
69166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
69266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * When this flag is specified, the callback will be invoked for event that affect any
69369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * route even if they do not match the callback's filter.
69466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
69566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     */
69666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 1 << 1;
69766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
69869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
69969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Explicitly requests discovery.
70069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     *
70169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide Future API ported from support library.  Revisit this later.
70269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
70369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 1 << 2;
70469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
70569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
70669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Requests that discovery be performed but only if there is some other active
70769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * callback already registered.
70869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     *
70969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide Compatibility workaround for the fact that applications do not currently
71069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * request discovery explicitly (except when using the support library API).
71169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
71269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public static final int CALLBACK_FLAG_PASSIVE_DISCOVERY = 1 << 3;
71369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
7140abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    /**
7150abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * Flag for {@link #isRouteAvailable}: Ignore the default route.
7160abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * <p>
7170abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * This flag is used to determine whether a matching non-default route is available.
7180abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * This constraint may be used to decide whether to offer the route chooser dialog
7190abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * to the user.  There is no point offering the chooser if there are no
7200abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * non-default choices.
7210abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * </p>
7220abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     *
7230abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @hide Future API ported from support library.  Revisit this later.
7240abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     */
7250abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1 << 0;
7260abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
7279a1de308cea2d160778fd977825f10a07b49d738Adam Powell    // Maps application contexts
7289a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static final HashMap<Context, MediaRouter> sRouters = new HashMap<Context, MediaRouter>();
7299a1de308cea2d160778fd977825f10a07b49d738Adam Powell
7309a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static String typesToString(int types) {
7319a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final StringBuilder result = new StringBuilder();
7329a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if ((types & ROUTE_TYPE_LIVE_AUDIO) != 0) {
7339a1de308cea2d160778fd977825f10a07b49d738Adam Powell            result.append("ROUTE_TYPE_LIVE_AUDIO ");
7349a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
7352bb7c122ef6ce8539dfbaeb3292adcd942185f82Bryan Mawhinney        if ((types & ROUTE_TYPE_LIVE_VIDEO) != 0) {
7362bb7c122ef6ce8539dfbaeb3292adcd942185f82Bryan Mawhinney            result.append("ROUTE_TYPE_LIVE_VIDEO ");
7372bb7c122ef6ce8539dfbaeb3292adcd942185f82Bryan Mawhinney        }
73869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if ((types & ROUTE_TYPE_REMOTE_DISPLAY) != 0) {
73969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            result.append("ROUTE_TYPE_REMOTE_DISPLAY ");
74069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
7419a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if ((types & ROUTE_TYPE_USER) != 0) {
7429a1de308cea2d160778fd977825f10a07b49d738Adam Powell            result.append("ROUTE_TYPE_USER ");
7439a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
7449a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return result.toString();
7459a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
7469a1de308cea2d160778fd977825f10a07b49d738Adam Powell
747b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    /** @hide */
748b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public MediaRouter(Context context) {
749b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        synchronized (Static.class) {
750b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            if (sStatic == null) {
7518e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final Context appContext = context.getApplicationContext();
7528e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                sStatic = new Static(appContext);
7538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                sStatic.startMonitoringRoutes(appContext);
754b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            }
7559a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
7569a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
7579a1de308cea2d160778fd977825f10a07b49d738Adam Powell
758690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    /**
7593afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * Gets the default route for playing media content on the system.
7603afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * <p>
7613afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * The system always provides a default route.
7623afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * </p>
7633afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     *
7643afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * @return The default route, which is guaranteed to never be null.
765690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     */
7663afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown    public RouteInfo getDefaultRoute() {
767705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return sStatic.mDefaultAudioVideo;
768690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
769690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
770690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    /**
77159579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim     * Returns a Bluetooth route if available, otherwise the default route.
77259579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim     * @hide
77359579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim     */
77459579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim    public RouteInfo getFallbackRoute() {
77559579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim        return (sStatic.mBluetoothA2dpRoute != null)
77659579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim                ? sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo;
77759579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim    }
77859579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim
77959579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim    /**
7804599696591f745b3a546197d2ba7e5cfc5562484Adam Powell     * @hide for use by framework routing UI
7814599696591f745b3a546197d2ba7e5cfc5562484Adam Powell     */
7823afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown    public RouteCategory getSystemCategory() {
7834599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        return sStatic.mSystemCategory;
7844599696591f745b3a546197d2ba7e5cfc5562484Adam Powell    }
7854599696591f745b3a546197d2ba7e5cfc5562484Adam Powell
7860abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    /** @hide */
7870abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public RouteInfo getSelectedRoute() {
7880abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        return getSelectedRoute(ROUTE_TYPE_ANY);
7890abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    }
7900abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
7914599696591f745b3a546197d2ba7e5cfc5562484Adam Powell    /**
7921cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell     * Return the currently selected route for any of the given types
793690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     *
794690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * @param type route types
795690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * @return the selected route
796690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     */
797690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public RouteInfo getSelectedRoute(int type) {
7981cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        if (sStatic.mSelectedRoute != null &&
7991cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell                (sStatic.mSelectedRoute.mSupportedTypes & type) != 0) {
8001cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // If the selected route supports any of the types supplied, it's still considered
8011cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // 'selected' for that type.
8021cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            return sStatic.mSelectedRoute;
8031cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        } else if (type == ROUTE_TYPE_USER) {
8041cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // The caller specifically asked for a user route and the currently selected route
8051cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            // doesn't qualify.
8061cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell            return null;
8071cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        }
8081cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        // If the above didn't match and we're not specifically asking for a user route,
8091cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        // consider the default selected.
8101cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell        return sStatic.mDefaultAudioVideo;
811690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
812690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
8139a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
8140abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * Returns true if there is a route that matches the specified types.
8150abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * <p>
8160abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * This method returns true if there are any available routes that match the types
8170abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * regardless of whether they are enabled or disabled.  If the
8180abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} flag is specified, then
8190abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * the method will only consider non-default routes.
8200abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * </p>
8210abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     *
8220abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @param types The types to match.
8230abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @param flags Flags to control the determination of whether a route may be available.
8240abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * May be zero or {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE}.
8250abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @return True if a matching route may be available.
8260abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     *
8270abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @hide Future API ported from support library.  Revisit this later.
8280abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     */
8290abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public boolean isRouteAvailable(int types, int flags) {
8300abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        final int count = sStatic.mRoutes.size();
8310abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        for (int i = 0; i < count; i++) {
8320abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            RouteInfo route = sStatic.mRoutes.get(i);
8330abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            if (route.matchesTypes(types)) {
8340abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                if ((flags & AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE) == 0
8350abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                        || route != sStatic.mDefaultAudioVideo) {
8360abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                    return true;
8370abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                }
8380abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            }
8390abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
8400abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
8410abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        // It doesn't look like we can find a matching route right now.
8420abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        return false;
8430abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    }
8440abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
8450abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    /**
8469a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Add a callback to listen to events about specific kinds of media routes.
8479a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * If the specified callback is already registered, its registration will be updated for any
8489a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * additional route types specified.
84966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
85066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * This is a convenience method that has the same effect as calling
85166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * {@link #addCallback(int, Callback, int)} without flags.
85266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
8539a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
8549a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param types Types of routes this callback is interested in
8559a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param cb Callback to add
8569a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
8579a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void addCallback(int types, Callback cb) {
85866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        addCallback(types, cb, 0);
85966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    }
86066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
86166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    /**
86266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * Add a callback to listen to events about specific kinds of media routes.
86366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * If the specified callback is already registered, its registration will be updated for any
86466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * additional route types specified.
86566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
86666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * By default, the callback will only be invoked for events that affect routes
86766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * that match the specified selector.  The filtering may be disabled by specifying
86866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * the {@link #CALLBACK_FLAG_UNFILTERED_EVENTS} flag.
86966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
87066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     *
87166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @param types Types of routes this callback is interested in
87266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @param cb Callback to add
87366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @param flags Flags to control the behavior of the callback.
87414507e257af5d71577574e25cbd690c4b54c9272Jeff Brown     * May be zero or a combination of {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and
87566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * {@link #CALLBACK_FLAG_UNFILTERED_EVENTS}.
87666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     */
87766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    public void addCallback(int types, Callback cb, int flags) {
87866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        CallbackInfo info;
87966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        int index = findCallbackInfo(cb);
88066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        if (index >= 0) {
88166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info = sStatic.mCallbacks.get(index);
88266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info.type |= types;
88366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info.flags |= flags;
88466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        } else {
88566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            info = new CallbackInfo(cb, types, flags, this);
88666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            sStatic.mCallbacks.add(info);
88766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
88869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        sStatic.updateDiscoveryRequest();
8899a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
8909a1de308cea2d160778fd977825f10a07b49d738Adam Powell
8919a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
8929a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Remove the specified callback. It will no longer receive events about media routing.
8939a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
8949a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param cb Callback to remove
8959a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
8969a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void removeCallback(Callback cb) {
89766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        int index = findCallbackInfo(cb);
89866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        if (index >= 0) {
89969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            sStatic.mCallbacks.remove(index);
90069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            sStatic.updateDiscoveryRequest();
90166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        } else {
90266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            Log.w(TAG, "removeCallback(" + cb + "): callback not registered");
90366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
90466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    }
90566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
90666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown    private int findCallbackInfo(Callback cb) {
907b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        final int count = sStatic.mCallbacks.size();
9089a1de308cea2d160778fd977825f10a07b49d738Adam Powell        for (int i = 0; i < count; i++) {
90966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            final CallbackInfo info = sStatic.mCallbacks.get(i);
91066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (info.cb == cb) {
91166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown                return i;
9129a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
9139a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
91466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        return -1;
9159a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
9169a1de308cea2d160778fd977825f10a07b49d738Adam Powell
917d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell    /**
918d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     * Select the specified route to use for output of the given media types.
9193afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * <p class="note">
9203afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * As API version 18, this function may be used to select any route.
9213afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * In prior versions, this function could only be used to select user
9223afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * routes and would ignore any attempt to select a system route.
9233afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * </p>
924d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     *
925d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     * @param types type flags indicating which types this route should be used for.
926d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     *              The route must support at least a subset.
927d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     * @param route Route to select
9289d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand     * @throws IllegalArgumentException if the given route is {@code null}
929d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     */
9309d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand    public void selectRoute(int types, @NonNull RouteInfo route) {
9319d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand        if (route == null) {
9329d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand            throw new IllegalArgumentException("Route cannot be null.");
9339d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand        }
93469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        selectRouteStatic(types, route, true);
9350d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    }
93669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
9370d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    /**
9380d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @hide internal use
9390d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     */
94069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void selectRouteInt(int types, RouteInfo route, boolean explicit) {
94169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        selectRouteStatic(types, route, explicit);
942b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
943b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
9449d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand    static void selectRouteStatic(int types, @NonNull RouteInfo route, boolean explicit) {
94518687b7a275b3782e63d52cdbe8ee9df191046e7Jae Seo        Log.v(TAG, "Selecting route: " + route);
9469d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand        assert(route != null);
947705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final RouteInfo oldRoute = sStatic.mSelectedRoute;
948546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim        final RouteInfo currentSystemRoute = sStatic.isBluetoothA2dpOn()
949546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim                ? sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo;
950b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo        boolean wasDefaultOrBluetoothRoute = (oldRoute == sStatic.mDefaultAudioVideo
951b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo                || oldRoute == sStatic.mBluetoothA2dpRoute);
952b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo        if (oldRoute == route
953546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim                && (!wasDefaultOrBluetoothRoute || route == currentSystemRoute)) {
954b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo            return;
955b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo        }
9560abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        if (!route.matchesTypes(types)) {
9570d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            Log.w(TAG, "selectRoute ignored; cannot select route with supported types " +
9580d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                    typesToString(route.getSupportedTypes()) + " into route types " +
9590d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                    typesToString(types));
9604ee1f55ce0f4909a7430ab44563a81852f335071Adam Powell            return;
9610d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
9629a1de308cea2d160778fd977825f10a07b49d738Adam Powell
963fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo        final RouteInfo btRoute = sStatic.mBluetoothA2dpRoute;
964b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo        if (sStatic.isPlaybackActive() && btRoute != null && (types & ROUTE_TYPE_LIVE_AUDIO) != 0
965b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo                && (route == btRoute || route == sStatic.mDefaultAudioVideo)) {
966fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            try {
967fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                sStatic.mAudioService.setBluetoothA2dpOn(route == btRoute);
96859579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim                // TODO: Remove the following logging when no longer needed.
96959579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim                if (route != btRoute) {
97059579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim                    StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
97159579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim                    StringBuffer sb = new StringBuffer();
97259579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim                    // callStack[3] is the caller of this method.
97359579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim                    for (int i = 3; i < callStack.length; i++) {
97459579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim                        StackTraceElement caller = callStack[i];
97559579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim                        sb.append(caller.getClassName() + "." + caller.getMethodName()
97659579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim                                + ":" + caller.getLineNumber()).append("  ");
97759579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim                    }
97859579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim                    Log.w(TAG, "Default route is selected while a BT route is available: pkgName="
97959579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim                            + sStatic.mPackageName + ", callers=" + sb.toString());
98059579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim                }
981fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            } catch (RemoteException e) {
982fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                Log.e(TAG, "Error changing Bluetooth A2DP state", e);
983fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            }
984fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo        }
985fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo
986705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final WifiDisplay activeDisplay =
987705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                sStatic.mDisplayService.getWifiDisplayStatus().getActiveDisplay();
988705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final boolean oldRouteHasAddress = oldRoute != null && oldRoute.mDeviceAddress != null;
9899d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand        final boolean newRouteHasAddress = route.mDeviceAddress != null;
990705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (activeDisplay != null || oldRouteHasAddress || newRouteHasAddress) {
991705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (newRouteHasAddress && !matchesDeviceAddress(activeDisplay, route)) {
992af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (sStatic.mCanConfigureWifiDisplays) {
993af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    sStatic.mDisplayService.connectWifiDisplay(route.mDeviceAddress);
994af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                } else {
995af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    Log.e(TAG, "Cannot connect to wifi displays because this process "
996af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                            + "is not allowed to do so.");
997af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
998705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            } else if (activeDisplay != null && !newRouteHasAddress) {
999705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                sStatic.mDisplayService.disconnectWifiDisplay();
1000705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1001705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1002705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
100369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        sStatic.setSelectedRoute(route, explicit);
100469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
1005705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (oldRoute != null) {
1006705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            dispatchRouteUnselected(types & oldRoute.getSupportedTypes(), oldRoute);
100739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (oldRoute.resolveStatusCode()) {
100839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                dispatchRouteChanged(oldRoute);
100939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
10109a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
10119a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if (route != null) {
101239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (route.resolveStatusCode()) {
101339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                dispatchRouteChanged(route);
101439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
10159a1de308cea2d160778fd977825f10a07b49d738Adam Powell            dispatchRouteSelected(types & route.getSupportedTypes(), route);
10169a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
1017ce468a35b388ca46578934706b38dbae94941643Jeff Brown
1018ce468a35b388ca46578934706b38dbae94941643Jeff Brown        // The behavior of active scans may depend on the currently selected route.
1019ce468a35b388ca46578934706b38dbae94941643Jeff Brown        sStatic.updateDiscoveryRequest();
10209a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
10219a1de308cea2d160778fd977825f10a07b49d738Adam Powell
102269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static void selectDefaultRouteStatic() {
102369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // TODO: Be smarter about the route types here; this selects for all valid.
1024546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim        if (sStatic.mSelectedRoute != sStatic.mBluetoothA2dpRoute && sStatic.isBluetoothA2dpOn()) {
102569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mBluetoothA2dpRoute, false);
102669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        } else {
102769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mDefaultAudioVideo, false);
102869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
102969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
103069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
10319a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
1032705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * Compare the device address of a display and a route.
1033705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     * Nulls/no device address will match another null/no address.
1034705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell     */
1035705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    static boolean matchesDeviceAddress(WifiDisplay display, RouteInfo info) {
1036705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final boolean routeHasAddress = info != null && info.mDeviceAddress != null;
1037705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (display == null && !routeHasAddress) {
1038705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return true;
1039705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1040705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1041705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (display != null && routeHasAddress) {
1042705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return display.getDeviceAddress().equals(info.mDeviceAddress);
1043705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1044705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return false;
1045705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1046705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1047705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    /**
10489a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Add an app-specified route for media to the MediaRouter.
10499a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * App-specified route definitions are created using {@link #createUserRoute(RouteCategory)}
10509a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
10519a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param info Definition of the route to add
10523afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown     * @see #createUserRoute(RouteCategory)
10539a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #removeUserRoute(UserRouteInfo)
10549a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
10559a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void addUserRoute(UserRouteInfo info) {
10562ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        addRouteStatic(info);
10579a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
10589a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1059d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    /**
1060d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     * @hide Framework use only
1061d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     */
1062d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    public void addRouteInt(RouteInfo info) {
10632ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        addRouteStatic(info);
1064d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    }
1065d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
10662ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell    static void addRouteStatic(RouteInfo info) {
106718687b7a275b3782e63d52cdbe8ee9df191046e7Jae Seo        Log.v(TAG, "Adding route: " + info);
10689a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final RouteCategory cat = info.getCategory();
1069b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        if (!sStatic.mCategories.contains(cat)) {
1070b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            sStatic.mCategories.add(cat);
10719a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
1072d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        if (cat.isGroupable() && !(info instanceof RouteGroup)) {
10739a1de308cea2d160778fd977825f10a07b49d738Adam Powell            // Enforce that any added route in a groupable category must be in a group.
10749a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final RouteGroup group = new RouteGroup(info.getCategory());
1075dbbfa702a09f6d2d36dee1b552442d04a4673f89Adam Powell            group.mSupportedTypes = info.mSupportedTypes;
1076b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            sStatic.mRoutes.add(group);
1077d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteAdded(group);
1078b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            group.addRoute(info);
1079d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
10809a1de308cea2d160778fd977825f10a07b49d738Adam Powell            info = group;
1081d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        } else {
1082b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            sStatic.mRoutes.add(info);
1083d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteAdded(info);
10849a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
10859a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
10869a1de308cea2d160778fd977825f10a07b49d738Adam Powell
10879a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
10889a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Remove an app-specified route for media from the MediaRouter.
10899a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
10909a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param info Definition of the route to remove
10919a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #addUserRoute(UserRouteInfo)
10929a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
10939a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void removeUserRoute(UserRouteInfo info) {
109469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        removeRouteStatic(info);
10959a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
10969a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1097690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    /**
1098690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * Remove all app-specified routes from the MediaRouter.
1099690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     *
1100690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * @see #removeUserRoute(UserRouteInfo)
1101690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     */
1102690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public void clearUserRoutes() {
1103b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        for (int i = 0; i < sStatic.mRoutes.size(); i++) {
1104b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final RouteInfo info = sStatic.mRoutes.get(i);
1105d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            // TODO Right now, RouteGroups only ever contain user routes.
1106d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            // The code below will need to change if this assumption does.
1107d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            if (info instanceof UserRouteInfo || info instanceof RouteGroup) {
110869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                removeRouteStatic(info);
1109690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell                i--;
1110690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            }
1111690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
1112690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
1113690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
1114d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    /**
1115d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     * @hide internal use only
1116d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     */
1117d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    public void removeRouteInt(RouteInfo info) {
111869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        removeRouteStatic(info);
1119d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    }
1120d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
112169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static void removeRouteStatic(RouteInfo info) {
112218687b7a275b3782e63d52cdbe8ee9df191046e7Jae Seo        Log.v(TAG, "Removing route: " + info);
1123b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        if (sStatic.mRoutes.remove(info)) {
11249a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final RouteCategory removingCat = info.getCategory();
1125b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final int count = sStatic.mRoutes.size();
11269a1de308cea2d160778fd977825f10a07b49d738Adam Powell            boolean found = false;
11279a1de308cea2d160778fd977825f10a07b49d738Adam Powell            for (int i = 0; i < count; i++) {
1128b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                final RouteCategory cat = sStatic.mRoutes.get(i).getCategory();
11299a1de308cea2d160778fd977825f10a07b49d738Adam Powell                if (removingCat == cat) {
11309a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    found = true;
11319a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    break;
11329a1de308cea2d160778fd977825f10a07b49d738Adam Powell                }
11339a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
11340abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            if (info.isSelected()) {
1135d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                // Removing the currently selected route? Select the default before we remove it.
113669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                selectDefaultRouteStatic();
1137d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            }
1138690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            if (!found) {
1139b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                sStatic.mCategories.remove(removingCat);
1140690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            }
1141690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            dispatchRouteRemoved(info);
1142690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
1143690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
1144690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
11459a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11469a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the number of {@link MediaRouter.RouteCategory categories} currently
11479a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * represented by routes known to this MediaRouter.
11489a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11499a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the number of unique categories represented by this MediaRouter's known routes
11509a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11519a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public int getCategoryCount() {
1152b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mCategories.size();
11539a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11549a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11559a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11569a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the {@link MediaRouter.RouteCategory category} at the given index.
11579a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Valid indices are in the range [0-getCategoryCount).
11589a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11599a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param index which category to return
11609a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the category at index
11619a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11629a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteCategory getCategoryAt(int index) {
1163b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mCategories.get(index);
11649a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11659a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11669a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11679a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the number of {@link MediaRouter.RouteInfo routes} currently known
11689a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * to this MediaRouter.
11699a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11709a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the number of routes tracked by this router
11719a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11729a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public int getRouteCount() {
1173b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.size();
11749a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11759a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11769a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11779a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the route at the specified index.
11789a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11799a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param index index of the route to return
11809a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the route at index
11819a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
11829a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteInfo getRouteAt(int index) {
1183b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.get(index);
1184b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
1185b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
1186b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static int getRouteCountStatic() {
1187b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.size();
1188b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
1189b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
1190b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static RouteInfo getRouteAtStatic(int index) {
1191b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.get(index);
11929a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
11939a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11949a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
11959a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Create a new user route that may be modified and registered for use by the application.
11969a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
11979a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param category The category the new route will belong to
11989a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return A new UserRouteInfo for use by the application
11999a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
12009a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #addUserRoute(UserRouteInfo)
12019a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #removeUserRoute(UserRouteInfo)
120269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @see #createRouteCategory(CharSequence, boolean)
12039a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
12049a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public UserRouteInfo createUserRoute(RouteCategory category) {
12059a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return new UserRouteInfo(category);
12069a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12079a1de308cea2d160778fd977825f10a07b49d738Adam Powell
12089a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
12099a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Create a new route category. Each route must belong to a category.
12109a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
12119a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param name Name of the new category
12129a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param isGroupable true if routes in this category may be grouped with one another
12139a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the new RouteCategory
12149a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
12159a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteCategory createRouteCategory(CharSequence name, boolean isGroupable) {
12169a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return new RouteCategory(name, ROUTE_TYPE_USER, isGroupable);
12179a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12185d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
12190d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    /**
12200d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * Create a new route category. Each route must belong to a category.
12210d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     *
12220d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @param nameResId Resource ID of the name of the new category
12230d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @param isGroupable true if routes in this category may be grouped with one another
12240d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @return the new RouteCategory
12250d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     */
12260d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    public RouteCategory createRouteCategory(int nameResId, boolean isGroupable) {
12270d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        return new RouteCategory(nameResId, ROUTE_TYPE_USER, isGroupable);
12280d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    }
12299a1de308cea2d160778fd977825f10a07b49d738Adam Powell
123069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
123169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Rebinds the media router to handle routes that belong to the specified user.
123269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Requires the interact across users permission to access the routes of another user.
123369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * <p>
123469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * This method is a complete hack to work around the singleton nature of the
123569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * media router when running inside of singleton processes like QuickSettings.
123669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * This mechanism should be burned to the ground when MediaRouter is redesigned.
123769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Ideally the current user would be pulled from the Context but we need to break
123869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * down MediaRouter.Static before we can get there.
123969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * </p>
124069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     *
124169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * @hide
124269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
124369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void rebindAsUser(int userId) {
124469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        sStatic.rebindAsUser(userId);
124569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
124669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
1247b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void updateRoute(final RouteInfo info) {
12489a1de308cea2d160778fd977825f10a07b49d738Adam Powell        dispatchRouteChanged(info);
12499a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12509a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1251b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteSelected(int type, RouteInfo info) {
125239d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
125366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1254b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteSelected(cbi.router, type, info);
12559a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
12569a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12579a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12589a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1259b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteUnselected(int type, RouteInfo info) {
126039d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
126166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1262b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteUnselected(cbi.router, type, info);
12639a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
12649a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12659a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12669a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1267b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteChanged(RouteInfo info) {
1268af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        dispatchRouteChanged(info, info.mSupportedTypes);
1269af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown    }
1270af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
1271af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown    static void dispatchRouteChanged(RouteInfo info, int oldSupportedTypes) {
12724bad663518908c20ed4f93a27f46ffb09448497fSungsoo Lim        if (DEBUG) {
12734bad663518908c20ed4f93a27f46ffb09448497fSungsoo Lim            Log.d(TAG, "Dispatching route change: " + info);
12744bad663518908c20ed4f93a27f46ffb09448497fSungsoo Lim        }
1275af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        final int newSupportedTypes = info.mSupportedTypes;
127639d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
1277af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Reconstruct some of the history for callbacks that may not have observed
1278af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // all of the events needed to correctly interpret the current state.
1279af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // FIXME: This is a strong signal that we should deprecate route type filtering
1280af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // completely in the future because it can lead to inconsistencies in
1281af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // applications.
1282af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            final boolean oldVisibility = cbi.filterRouteEvent(oldSupportedTypes);
1283af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            final boolean newVisibility = cbi.filterRouteEvent(newSupportedTypes);
1284af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (!oldVisibility && newVisibility) {
1285af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                cbi.cb.onRouteAdded(cbi.router, info);
1286af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (info.isSelected()) {
1287af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    cbi.cb.onRouteSelected(cbi.router, newSupportedTypes, info);
1288af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
1289af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            }
1290af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (oldVisibility || newVisibility) {
1291b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteChanged(cbi.router, info);
12929a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
1293af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (oldVisibility && !newVisibility) {
1294af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (info.isSelected()) {
1295af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    cbi.cb.onRouteUnselected(cbi.router, oldSupportedTypes, info);
1296af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
1297af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                cbi.cb.onRouteRemoved(cbi.router, info);
1298af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            }
12999a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
13009a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
13019a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1302b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteAdded(RouteInfo info) {
130339d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
130466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1305b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteAdded(cbi.router, info);
13069a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
13079a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
13089a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
13099a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1310b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteRemoved(RouteInfo info) {
131139d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
131266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
1313b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteRemoved(cbi.router, info);
13149a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
13159a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
13169a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
13179a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1318b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteGrouped(RouteInfo info, RouteGroup group, int index) {
131939d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
132066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(group)) {
1321b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteGrouped(cbi.router, info, group, index);
1322d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            }
1323d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        }
1324d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell    }
1325d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
1326b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteUngrouped(RouteInfo info, RouteGroup group) {
132739d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
132866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(group)) {
1329b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteUngrouped(cbi.router, info, group);
13309a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
13319a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
13329a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
13339a1de308cea2d160778fd977825f10a07b49d738Adam Powell
13348e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    static void dispatchRouteVolumeChanged(RouteInfo info) {
13358e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
133666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
13378e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                cbi.cb.onRouteVolumeChanged(cbi.router, info);
13388e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
13398e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
13408e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    }
13418e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
134292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown    static void dispatchRoutePresentationDisplayChanged(RouteInfo info) {
134392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        for (CallbackInfo cbi : sStatic.mCallbacks) {
134466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            if (cbi.filterRouteEvent(info)) {
134592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                cbi.cb.onRoutePresentationDisplayChanged(cbi.router, info);
134692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            }
134792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
134892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown    }
134992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
13508e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    static void systemVolumeChanged(int newValue) {
13518e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        final RouteInfo selectedRoute = sStatic.mSelectedRoute;
13528e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        if (selectedRoute == null) return;
13538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
13548e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        if (selectedRoute == sStatic.mBluetoothA2dpRoute ||
1355705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                selectedRoute == sStatic.mDefaultAudioVideo) {
13568e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            dispatchRouteVolumeChanged(selectedRoute);
13578e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        } else if (sStatic.mBluetoothA2dpRoute != null) {
1358fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            try {
1359fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                dispatchRouteVolumeChanged(sStatic.mAudioService.isBluetoothA2dpOn() ?
1360fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                        sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo);
1361fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            } catch (RemoteException e) {
1362fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo                Log.e(TAG, "Error checking Bluetooth A2DP state to report volume change", e);
1363fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo            }
13648e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        } else {
1365705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            dispatchRouteVolumeChanged(sStatic.mDefaultAudioVideo);
1366705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1367705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1368705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
136975af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    static void updateWifiDisplayStatus(WifiDisplayStatus status) {
137075af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        WifiDisplay[] displays;
1371615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell        WifiDisplay activeDisplay;
137275af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        if (status.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) {
137375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            displays = status.getDisplays();
137475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            activeDisplay = status.getActiveDisplay();
1375af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
1376af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Only the system is able to connect to wifi display routes.
1377af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // The display manager will enforce this with a permission check but it
1378af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // still publishes information about all available displays.
1379af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Filter the list down to just the active display.
1380af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (!sStatic.mCanConfigureWifiDisplays) {
1381af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                if (activeDisplay != null) {
1382af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    displays = new WifiDisplay[] { activeDisplay };
1383af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                } else {
1384af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    displays = WifiDisplay.EMPTY_ARRAY;
1385af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                }
1386af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            }
1387615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell        } else {
138875af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            displays = WifiDisplay.EMPTY_ARRAY;
1389615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell            activeDisplay = null;
1390615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell        }
1391ce468a35b388ca46578934706b38dbae94941643Jeff Brown        String activeDisplayAddress = activeDisplay != null ?
1392ce468a35b388ca46578934706b38dbae94941643Jeff Brown                activeDisplay.getDeviceAddress() : null;
1393705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
139475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        // Add or update routes.
139575af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        for (int i = 0; i < displays.length; i++) {
139675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            final WifiDisplay d = displays[i];
139775af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            if (shouldShowWifiDisplay(d, activeDisplay)) {
1398ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                RouteInfo route = findWifiDisplayRoute(d);
1399ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                if (route == null) {
140075af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                    route = makeWifiDisplayRoute(d, status);
1401ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                    addRouteStatic(route);
1402ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                } else {
1403ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    String address = d.getDeviceAddress();
1404ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    boolean disconnected = !address.equals(activeDisplayAddress)
1405ce468a35b388ca46578934706b38dbae94941643Jeff Brown                            && address.equals(sStatic.mPreviousActiveWifiDisplayAddress);
1406ce468a35b388ca46578934706b38dbae94941643Jeff Brown                    updateWifiDisplayRoute(route, d, status, disconnected);
1407ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                }
1408ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                if (d.equals(activeDisplay)) {
140969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    selectRouteStatic(route.getSupportedTypes(), route, false);
1410ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                }
14112ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            }
1412705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
141375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown
141475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        // Remove stale routes.
141575af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        for (int i = sStatic.mRoutes.size(); i-- > 0; ) {
141675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            RouteInfo route = sStatic.mRoutes.get(i);
141775af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            if (route.mDeviceAddress != null) {
141875af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                WifiDisplay d = findWifiDisplay(displays, route.mDeviceAddress);
141975af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                if (d == null || !shouldShowWifiDisplay(d, activeDisplay)) {
142075af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                    removeRouteStatic(route);
1421ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                }
1422705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1423705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1424ce468a35b388ca46578934706b38dbae94941643Jeff Brown
1425ce468a35b388ca46578934706b38dbae94941643Jeff Brown        // Remember the current active wifi display address so that we can infer disconnections.
1426ce468a35b388ca46578934706b38dbae94941643Jeff Brown        // TODO: This hack will go away once all of this is moved into the media router service.
1427ce468a35b388ca46578934706b38dbae94941643Jeff Brown        sStatic.mPreviousActiveWifiDisplayAddress = activeDisplayAddress;
142875af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    }
1429705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
143075af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    private static boolean shouldShowWifiDisplay(WifiDisplay d, WifiDisplay activeDisplay) {
143175af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        return d.isRemembered() || d.equals(activeDisplay);
1432705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1433705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1434ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    static int getWifiDisplayStatusCode(WifiDisplay d, WifiDisplayStatus wfdStatus) {
143569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        int newStatus;
1436ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        if (wfdStatus.getScanState() == WifiDisplayStatus.SCAN_STATE_SCANNING) {
1437ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang            newStatus = RouteInfo.STATUS_SCANNING;
1438ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        } else if (d.isAvailable()) {
1439ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang            newStatus = d.canConnect() ?
1440ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                    RouteInfo.STATUS_AVAILABLE: RouteInfo.STATUS_IN_USE;
1441705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        } else {
1442705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            newStatus = RouteInfo.STATUS_NOT_AVAILABLE;
1443705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1444705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1445ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        if (d.equals(wfdStatus.getActiveDisplay())) {
1446ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang            final int activeState = wfdStatus.getActiveDisplayState();
1447705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            switch (activeState) {
1448705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                case WifiDisplayStatus.DISPLAY_STATE_CONNECTED:
144969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    newStatus = RouteInfo.STATUS_CONNECTED;
1450705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    break;
1451705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                case WifiDisplayStatus.DISPLAY_STATE_CONNECTING:
1452705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    newStatus = RouteInfo.STATUS_CONNECTING;
1453705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    break;
1454705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                case WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED:
1455705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    Log.e(TAG, "Active display is not connected!");
1456705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    break;
1457705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1458705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1459705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1460ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        return newStatus;
1461ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    }
1462ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
1463ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    static boolean isWifiDisplayEnabled(WifiDisplay d, WifiDisplayStatus wfdStatus) {
1464ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        return d.isAvailable() && (d.canConnect() || d.equals(wfdStatus.getActiveDisplay()));
1465ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    }
1466ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
1467ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    static RouteInfo makeWifiDisplayRoute(WifiDisplay display, WifiDisplayStatus wfdStatus) {
1468ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        final RouteInfo newRoute = new RouteInfo(sStatic.mSystemCategory);
1469ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mDeviceAddress = display.getDeviceAddress();
147069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        newRoute.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO
147169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                | ROUTE_TYPE_REMOTE_DISPLAY;
1472ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mVolumeHandling = RouteInfo.PLAYBACK_VOLUME_FIXED;
1473ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mPlaybackType = RouteInfo.PLAYBACK_TYPE_REMOTE;
1474ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
147539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        newRoute.setRealStatusCode(getWifiDisplayStatusCode(display, wfdStatus));
1476ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mEnabled = isWifiDisplayEnabled(display, wfdStatus);
1477ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mName = display.getFriendlyDisplayName();
1478ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        newRoute.mDescription = sStatic.mResources.getText(
1479ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang                com.android.internal.R.string.wireless_display_route_description);
14805830b0b33618940d65197cec99d697b21908fec8Chong Zhang        newRoute.updatePresentationDisplay();
14819dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        newRoute.mDeviceType = RouteInfo.DEVICE_TYPE_TV;
1482ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        return newRoute;
1483ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    }
1484ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang
1485ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang    private static void updateWifiDisplayRoute(
1486ce468a35b388ca46578934706b38dbae94941643Jeff Brown            RouteInfo route, WifiDisplay display, WifiDisplayStatus wfdStatus,
1487ce468a35b388ca46578934706b38dbae94941643Jeff Brown            boolean disconnected) {
1488ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        boolean changed = false;
14892444ae7e2b8658a4a90f996e678423558744b4a2Jeff Brown        final String newName = display.getFriendlyDisplayName();
14902444ae7e2b8658a4a90f996e678423558744b4a2Jeff Brown        if (!route.getName().equals(newName)) {
1491705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            route.mName = newName;
1492705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            changed = true;
1493705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1494705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1495ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        boolean enabled = isWifiDisplayEnabled(display, wfdStatus);
1496ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        changed |= route.mEnabled != enabled;
1497ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        route.mEnabled = enabled;
1498705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
149939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        changed |= route.setRealStatusCode(getWifiDisplayStatusCode(display, wfdStatus));
1500705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1501705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        if (changed) {
1502705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            dispatchRouteChanged(route);
1503705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1504705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1505ce468a35b388ca46578934706b38dbae94941643Jeff Brown        if ((!enabled || disconnected) && route.isSelected()) {
1506705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            // Oops, no longer available. Reselect the default.
150769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            selectDefaultRouteStatic();
1508705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1509705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1510705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
151175af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown    private static WifiDisplay findWifiDisplay(WifiDisplay[] displays, String deviceAddress) {
1512705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        for (int i = 0; i < displays.length; i++) {
151375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            final WifiDisplay d = displays[i];
151475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            if (d.getDeviceAddress().equals(deviceAddress)) {
151575af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown                return d;
1516705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1517705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1518705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return null;
1519705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
1520705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1521705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    private static RouteInfo findWifiDisplayRoute(WifiDisplay d) {
1522705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        final int count = sStatic.mRoutes.size();
1523705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        for (int i = 0; i < count; i++) {
1524705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            final RouteInfo info = sStatic.mRoutes.get(i);
1525705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (d.getDeviceAddress().equals(info.mDeviceAddress)) {
1526705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                return info;
1527705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
1528705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1529705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        return null;
15308e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    }
15318e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
15329a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
15339a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a media route.
15349a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
1535b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class RouteInfo {
15369a1de308cea2d160778fd977825f10a07b49d738Adam Powell        CharSequence mName;
15370d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        int mNameResId;
153856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        CharSequence mDescription;
15399a1de308cea2d160778fd977825f10a07b49d738Adam Powell        private CharSequence mStatus;
15409a1de308cea2d160778fd977825f10a07b49d738Adam Powell        int mSupportedTypes;
15419dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        int mDeviceType;
15429a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteGroup mGroup;
15439a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final RouteCategory mCategory;
1544ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        Drawable mIcon;
15451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        // playback information
15461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mPlaybackType = PLAYBACK_TYPE_LOCAL;
15471357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
15481357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
15491357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
15501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mPlaybackStream = AudioManager.STREAM_MUSIC;
15511357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        VolumeCallbackInfo mVcb;
155292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        Display mPresentationDisplay;
15535830b0b33618940d65197cec99d697b21908fec8Chong Zhang        int mPresentationDisplayId = -1;
15549a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1555705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        String mDeviceAddress;
1556705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        boolean mEnabled = true;
1557705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
155869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // An id by which the route is known to the media router service.
155969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // Null if this route only exists as an artifact within this process.
156069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        String mGlobalRouteId;
156169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
1562705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        // A predetermined connection status that can override mStatus
156339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private int mRealStatusCode;
156439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private int mResolvedStatusCode;
1565705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
15662ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_NONE = 0;
15672ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_SCANNING = 1;
15682ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_CONNECTING = 2;
15692ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_AVAILABLE = 3;
15702ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /** @hide */ public static final int STATUS_NOT_AVAILABLE = 4;
1571ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang        /** @hide */ public static final int STATUS_IN_USE = 5;
157269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        /** @hide */ public static final int STATUS_CONNECTED = 6;
1573705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
15749dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /** @hide */
15759dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        @IntDef({DEVICE_TYPE_UNKNOWN, DEVICE_TYPE_TV, DEVICE_TYPE_SPEAKER, DEVICE_TYPE_BLUETOOTH})
15769dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        @Retention(RetentionPolicy.SOURCE)
15779dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public @interface DeviceType {}
15789dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
15799dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /**
15809dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * The default receiver device type of the route indicating the type is unknown.
15819dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         *
15829dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * @see #getDeviceType
15839dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         */
15849dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public static final int DEVICE_TYPE_UNKNOWN = 0;
15859dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
15869dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /**
15879dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * A receiver device type of the route indicating the presentation of the media is happening
15889dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * on a TV.
15899dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         *
15909dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * @see #getDeviceType
15919dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         */
15929dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public static final int DEVICE_TYPE_TV = 1;
15939dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
15949dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /**
15959dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * A receiver device type of the route indicating the presentation of the media is happening
15969dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * on a speaker.
15979dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         *
15989dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * @see #getDeviceType
15999dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         */
16009dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public static final int DEVICE_TYPE_SPEAKER = 2;
16019dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
16029dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /**
16039dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * A receiver device type of the route indicating the presentation of the media is happening
16049dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * on a bluetooth device such as a bluetooth speaker.
16059dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         *
16069dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * @see #getDeviceType
16079dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         */
16089dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public static final int DEVICE_TYPE_BLUETOOTH = 3;
16099dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
1610b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        private Object mTag;
1611b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
16127c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        /** @hide */
16137c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        @IntDef({PLAYBACK_TYPE_LOCAL, PLAYBACK_TYPE_REMOTE})
16147c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        @Retention(RetentionPolicy.SOURCE)
16157c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        public @interface PlaybackType {}
16167c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang
16171357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
16181357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * The default playback type, "local", indicating the presentation of the media is happening
1619bc4cf00dc5bf6e0e3c01206d5c46e64306df260aJohn Spurlock         * on the same device (e&#46;g&#46; a phone, a tablet) as where it is controlled from.
162069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getPlaybackType()
16211357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
16221357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_TYPE_LOCAL = 0;
16237c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang
16241357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
16251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * A playback type indicating the presentation of the media is happening on
1626bc4cf00dc5bf6e0e3c01206d5c46e64306df260aJohn Spurlock         * a different device (i&#46;e&#46; the remote device) than where it is controlled from.
162769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getPlaybackType()
16281357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
16291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_TYPE_REMOTE = 1;
16307c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang
16317c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        /** @hide */
16327c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang         @IntDef({PLAYBACK_VOLUME_FIXED,PLAYBACK_VOLUME_VARIABLE})
16337c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang         @Retention(RetentionPolicy.SOURCE)
16347c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang         private @interface PlaybackVolume {}
16357c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang
16361357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
1637bc4cf00dc5bf6e0e3c01206d5c46e64306df260aJohn Spurlock         * Playback information indicating the playback volume is fixed, i&#46;e&#46; it cannot be
16381357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * controlled from this object. An example of fixed playback volume is a remote player,
16391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
16401357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * than attenuate at the source.
164169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getVolumeHandling()
16421357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
16431357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_VOLUME_FIXED = 0;
16441357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
16451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Playback information indicating the playback volume is variable and can be controlled
16461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * from this object.
164769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * @see #getVolumeHandling()
16481357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
16491357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_VOLUME_VARIABLE = 1;
16501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
16519a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteInfo(RouteCategory category) {
16529a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mCategory = category;
16539dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang            mDeviceType = DEVICE_TYPE_UNKNOWN;
16549a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
16559a1de308cea2d160778fd977825f10a07b49d738Adam Powell
16569a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
165756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Gets the user-visible name of the route.
165856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
165956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route name identifies the destination represented by the route.
166056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied name, an alias, or device serial number.
166156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
166256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
166356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The user-visible name of a media route.  This is the string presented
16649a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * to users who may select this as the active route.
16659a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
16669a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getName() {
16670d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(sStatic.mResources);
16680d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
166956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
16700d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        /**
167156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Return the properly localized/resource user-visible name of this route.
167256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
167356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route name identifies the destination represented by the route.
167456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied name, an alias, or device serial number.
167556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
167656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
16770d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @param context Context used to resolve the correct configuration to load
167856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The user-visible name of a media route.  This is the string presented
16790d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * to users who may select this as the active route.
16800d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         */
16810d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public CharSequence getName(Context context) {
16820d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(context.getResources());
16830d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
168456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
16850d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        CharSequence getName(Resources res) {
16860d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            if (mNameResId != 0) {
1687eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon                return res.getText(mNameResId);
16880d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            }
16899a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mName;
16909a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
16919a1de308cea2d160778fd977825f10a07b49d738Adam Powell
16929a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
169356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Gets the user-visible description of the route.
169456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
169556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route description describes the kind of destination represented by the route.
169656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied string, a model number or brand of device.
169756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
169856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
169956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The description of the route, or null if none.
170056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         */
170156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        public CharSequence getDescription() {
170256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown            return mDescription;
170356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        }
170456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
170556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        /**
170656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @return The user-visible status for a media route. This may include a description
17079a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * of the currently playing media, if available.
17089a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
17099a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getStatus() {
17109a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mStatus;
17119a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
17129a1de308cea2d160778fd977825f10a07b49d738Adam Powell
17139a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
1714705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * Set this route's status by predetermined status code. If the caller
1715705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * should dispatch a route changed event this call will return true;
1716705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         */
171739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        boolean setRealStatusCode(int statusCode) {
171839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (mRealStatusCode != statusCode) {
171939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                mRealStatusCode = statusCode;
172039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                return resolveStatusCode();
172139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
172239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return false;
172339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        }
172439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown
172539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        /**
172639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown         * Resolves the status code whenever the real status code or selection state
172739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown         * changes.
172839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown         */
172939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        boolean resolveStatusCode() {
173039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            int statusCode = mRealStatusCode;
173139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (isSelected()) {
1732705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                switch (statusCode) {
173339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // If the route is selected and its status appears to be between states
173439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // then report it as connecting even though it has not yet had a chance
173539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // to officially move into the CONNECTING state.  Note that routes in
173639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // the NONE state are assumed to not require an explicit connection
173739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // lifecycle whereas those that are AVAILABLE are assumed to have
173839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    // to eventually proceed to CONNECTED.
1739705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                    case STATUS_AVAILABLE:
174039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    case STATUS_SCANNING:
174139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                        statusCode = STATUS_CONNECTING;
174269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        break;
1743705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                }
1744705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
174539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (mResolvedStatusCode == statusCode) {
174639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                return false;
174739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
174839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown
174939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            mResolvedStatusCode = statusCode;
175039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            int resId;
175139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            switch (statusCode) {
175239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_SCANNING:
175339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_scanning;
175439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
175539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_CONNECTING:
175639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_connecting;
175739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
175839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_AVAILABLE:
175939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_available;
176039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
176139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_NOT_AVAILABLE:
176239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_not_available;
176339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
176439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_IN_USE:
176539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = com.android.internal.R.string.media_route_status_in_use;
176639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
176739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_CONNECTED:
176839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case STATUS_NONE:
176939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                default:
177039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    resId = 0;
177139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
177239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
177339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            mStatus = resId != 0 ? sStatic.mResources.getText(resId) : null;
177439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return true;
1775705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
1776705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
1777705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        /**
17782ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell         * @hide
17792ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell         */
17802ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        public int getStatusCode() {
178139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return mResolvedStatusCode;
17822ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        }
17832ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell
17842ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        /**
17859a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return A media type flag set describing which types this route supports.
17869a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
17879a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int getSupportedTypes() {
17889a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mSupportedTypes;
17899a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
17909a1de308cea2d160778fd977825f10a07b49d738Adam Powell
17919dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        /**
17929dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * Gets the type of the receiver device associated with this route.
17939dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         *
17949dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * @return The type of the receiver device associated with this route:
17959dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * {@link #DEVICE_TYPE_BLUETOOTH}, {@link #DEVICE_TYPE_TV}, {@link #DEVICE_TYPE_SPEAKER},
17969dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         * or {@link #DEVICE_TYPE_UNKNOWN}.
17979dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang         */
17989dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        @DeviceType
17999dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        public int getDeviceType() {
18009dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang            return mDeviceType;
18019dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang        }
18029dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang
18030abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
18040abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public boolean matchesTypes(int types) {
18050abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            return (mSupportedTypes & types) != 0;
18060abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
18070abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
18089a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
18099a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return The group that this route belongs to.
18109a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
18119a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public RouteGroup getGroup() {
18129a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mGroup;
18139a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
18149a1de308cea2d160778fd977825f10a07b49d738Adam Powell
18159a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
18169a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return the category this route belongs to.
18179a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
18189a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public RouteCategory getCategory() {
18199a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mCategory;
18209a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
18219a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1822ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
1823ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Get the icon representing this route.
1824ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * This icon will be used in picker UIs if available.
1825ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
1826ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @return the icon representing this route or null if no icon is available
1827ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
1828ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public Drawable getIconDrawable() {
1829ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            return mIcon;
1830ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
1831ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
1832b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        /**
1833b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * Set an application-specific tag object for this route.
1834b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * The application may use this to store arbitrary data associated with the
1835b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * route for internal tracking.
1836b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         *
1837b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * <p>Note that the lifespan of a route may be well past the lifespan of
1838b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * an Activity or other Context; take care that objects you store here
1839b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * will not keep more data in memory alive than you intend.</p>
1840b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         *
1841b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * @param tag Arbitrary, app-specific data for this route to hold for later use
1842b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         */
1843b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        public void setTag(Object tag) {
1844b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            mTag = tag;
1845130b4572d1f3df702e5b296a655d15a41f6d4c66Adam Powell            routeUpdated();
1846b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        }
1847b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
1848b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        /**
1849b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * @return The tag object previously set by the application
1850b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * @see #setTag(Object)
1851b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         */
1852b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        public Object getTag() {
1853b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            return mTag;
1854b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        }
1855b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
18561357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
18571357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the type of playback associated with this route
18581357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setPlaybackType(int)
18591357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
18607c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        @PlaybackType
18611357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getPlaybackType() {
18621357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            return mPlaybackType;
18631357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
18641357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
18651357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
18661357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the stream over which the playback associated with this route is performed
18671357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setPlaybackStream(int)
18681357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
18691357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getPlaybackStream() {
18701357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            return mPlaybackStream;
18711357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
18721357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
18731357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
18748e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Return the current volume for this route. Depending on the route, this may only
18758e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * be valid if the route is currently selected.
18768e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         *
18771357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the volume at which the playback associated with this route is performed
18781357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setVolume(int)
18791357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
18801357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getVolume() {
18811357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
18821357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                int vol = 0;
18831357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                try {
18841357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    vol = sStatic.mAudioService.getStreamVolume(mPlaybackStream);
18851357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                } catch (RemoteException e) {
18861357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    Log.e(TAG, "Error getting local stream volume", e);
18871357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                }
18881357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return vol;
18891357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            } else {
18901357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return mVolume;
18911357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
18921357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
18931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
18941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
18958e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Request a volume change for this route.
18968e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param volume value between 0 and getVolumeMax
18978e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         */
18988e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestSetVolume(int volume) {
18998e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
19008e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                try {
1901ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                    sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0,
1902ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                            ActivityThread.currentPackageName());
19038e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                } catch (RemoteException e) {
19048e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Error setting local stream volume", e);
19058e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
19068e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            } else {
190769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                sStatic.requestSetVolume(this, volume);
19088e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
19098e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
19108e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
19118e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        /**
19128e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Request an incremental volume update for this route.
19138e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param direction Delta to apply to the current volume
19148e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         */
19158e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestUpdateVolume(int direction) {
19168e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
19178e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                try {
19188e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    final int volume =
19198e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                            Math.max(0, Math.min(getVolume() + direction, getVolumeMax()));
1920ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                    sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0,
1921ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn                            ActivityThread.currentPackageName());
19228e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                } catch (RemoteException e) {
19238e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Error setting local stream volume", e);
19248e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
19258e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            } else {
192669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                sStatic.requestUpdateVolume(this, direction);
19278e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
19288e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
19298e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
19308e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        /**
19311357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the maximum volume at which the playback associated with this route is performed
19321357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setVolumeMax(int)
19331357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
19341357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getVolumeMax() {
19351357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
19361357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                int volMax = 0;
19371357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                try {
19381357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    volMax = sStatic.mAudioService.getStreamMaxVolume(mPlaybackStream);
19391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                } catch (RemoteException e) {
19401357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    Log.e(TAG, "Error getting local stream volume", e);
19411357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                }
19421357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return volMax;
19431357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            } else {
19441357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return mVolumeMax;
19451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
19461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
19471357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
19481357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
19491357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return how volume is handling on the route
19501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setVolumeHandling(int)
19511357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
19527c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        @PlaybackVolume
19531357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getVolumeHandling() {
19541357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            return mVolumeHandling;
19551357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
19561357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
1957705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        /**
195892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * Gets the {@link Display} that should be used by the application to show
195992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * a {@link android.app.Presentation} on an external display when this route is selected.
196092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * Depending on the route, this may only be valid if the route is currently
196192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * selected.
196292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * <p>
196392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * The preferred presentation display may change independently of the route
196492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * being selected or unselected.  For example, the presentation display
196592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * of the default system route may change when an external HDMI display is connected
196692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * or disconnected even though the route itself has not changed.
196792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p><p>
196892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * This method may return null if there is no external display associated with
196992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * the route or if the display is not ready to show UI yet.
197092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p><p>
197192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * The application should listen for changes to the presentation display
197292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * using the {@link Callback#onRoutePresentationDisplayChanged} callback and
197392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * show or dismiss its {@link android.app.Presentation} accordingly when the display
197492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * becomes available or is removed.
197592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p><p>
197692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * This method only makes sense for {@link #ROUTE_TYPE_LIVE_VIDEO live video} routes.
197792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p>
197892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
197992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @return The preferred presentation display to use when this route is
198092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * selected or null if none.
198192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
198292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @see #ROUTE_TYPE_LIVE_VIDEO
198392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @see android.app.Presentation
198492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         */
198592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public Display getPresentationDisplay() {
198692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            return mPresentationDisplay;
198792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
198892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
19895830b0b33618940d65197cec99d697b21908fec8Chong Zhang        boolean updatePresentationDisplay() {
19905830b0b33618940d65197cec99d697b21908fec8Chong Zhang            Display display = choosePresentationDisplay();
19915830b0b33618940d65197cec99d697b21908fec8Chong Zhang            if (mPresentationDisplay != display) {
19925830b0b33618940d65197cec99d697b21908fec8Chong Zhang                mPresentationDisplay = display;
19935830b0b33618940d65197cec99d697b21908fec8Chong Zhang                return true;
19945830b0b33618940d65197cec99d697b21908fec8Chong Zhang            }
19955830b0b33618940d65197cec99d697b21908fec8Chong Zhang            return false;
19965830b0b33618940d65197cec99d697b21908fec8Chong Zhang        }
19975830b0b33618940d65197cec99d697b21908fec8Chong Zhang
19985830b0b33618940d65197cec99d697b21908fec8Chong Zhang        private Display choosePresentationDisplay() {
19995830b0b33618940d65197cec99d697b21908fec8Chong Zhang            if ((mSupportedTypes & ROUTE_TYPE_LIVE_VIDEO) != 0) {
20005830b0b33618940d65197cec99d697b21908fec8Chong Zhang                Display[] displays = sStatic.getAllPresentationDisplays();
20015830b0b33618940d65197cec99d697b21908fec8Chong Zhang
20025830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // Ensure that the specified display is valid for presentations.
20035830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // This check will normally disallow the default display unless it was
20045830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // configured as a presentation display for some reason.
20055830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (mPresentationDisplayId >= 0) {
20065830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    for (Display display : displays) {
20075830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        if (display.getDisplayId() == mPresentationDisplayId) {
20085830b0b33618940d65197cec99d697b21908fec8Chong Zhang                            return display;
20095830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        }
20105830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    }
20115830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    return null;
20125830b0b33618940d65197cec99d697b21908fec8Chong Zhang                }
20135830b0b33618940d65197cec99d697b21908fec8Chong Zhang
20145830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // Find the indicated Wifi display by its address.
20155830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (mDeviceAddress != null) {
20165830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    for (Display display : displays) {
20175830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        if (display.getType() == Display.TYPE_WIFI
20185830b0b33618940d65197cec99d697b21908fec8Chong Zhang                                && mDeviceAddress.equals(display.getAddress())) {
20195830b0b33618940d65197cec99d697b21908fec8Chong Zhang                            return display;
20205830b0b33618940d65197cec99d697b21908fec8Chong Zhang                        }
20215830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    }
20225830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    return null;
20235830b0b33618940d65197cec99d697b21908fec8Chong Zhang                }
20245830b0b33618940d65197cec99d697b21908fec8Chong Zhang
20255830b0b33618940d65197cec99d697b21908fec8Chong Zhang                // For the default route, choose the first presentation display from the list.
20265830b0b33618940d65197cec99d697b21908fec8Chong Zhang                if (this == sStatic.mDefaultAudioVideo && displays.length > 0) {
20275830b0b33618940d65197cec99d697b21908fec8Chong Zhang                    return displays[0];
20285830b0b33618940d65197cec99d697b21908fec8Chong Zhang                }
20295830b0b33618940d65197cec99d697b21908fec8Chong Zhang            }
20305830b0b33618940d65197cec99d697b21908fec8Chong Zhang            return null;
20315830b0b33618940d65197cec99d697b21908fec8Chong Zhang        }
20325830b0b33618940d65197cec99d697b21908fec8Chong Zhang
203375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        /** @hide */
203475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        public String getDeviceAddress() {
203575af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown            return mDeviceAddress;
203675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown        }
203775af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown
203892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        /**
2039a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * Returns true if this route is enabled and may be selected.
2040a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         *
2041a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * @return True if this route is enabled.
2042705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         */
2043705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        public boolean isEnabled() {
2044705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return mEnabled;
2045705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
2046705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
2047a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown        /**
2048a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * Returns true if the route is in the process of connecting and is not
2049a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * yet ready for use.
2050a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         *
2051a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         * @return True if this route is in the process of connecting.
2052a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown         */
2053a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown        public boolean isConnecting() {
205439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            return mResolvedStatusCode == STATUS_CONNECTING;
20550abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
20560abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
20570abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
20580abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public boolean isSelected() {
20590abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            return this == sStatic.mSelectedRoute;
20600abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
20610abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
20620abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
20630abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public boolean isDefault() {
20640abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            return this == sStatic.mDefaultAudioVideo;
20650abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
20660abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
20670abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        /** @hide */
206859579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim        public boolean isBluetooth() {
206959579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim            return this == sStatic.mBluetoothA2dpRoute;
207059579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim        }
207159579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim
207259579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim        /** @hide */
20730abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public void select() {
20740abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            selectRouteStatic(mSupportedTypes, this, true);
2075a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown        }
2076a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown
20779a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void setStatusInt(CharSequence status) {
20789a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (!status.equals(mStatus)) {
20799a1de308cea2d160778fd977825f10a07b49d738Adam Powell                mStatus = status;
20809a1de308cea2d160778fd977825f10a07b49d738Adam Powell                if (mGroup != null) {
20819a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    mGroup.memberStatusChanged(this, status);
20829a1de308cea2d160778fd977825f10a07b49d738Adam Powell                }
20839a1de308cea2d160778fd977825f10a07b49d738Adam Powell                routeUpdated();
20849a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
20859a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
20869a1de308cea2d160778fd977825f10a07b49d738Adam Powell
20871357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        final IRemoteVolumeObserver.Stub mRemoteVolObserver = new IRemoteVolumeObserver.Stub() {
208869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            @Override
20891357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            public void dispatchRemoteVolumeUpdate(final int direction, final int value) {
20901357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                sStatic.mHandler.post(new Runnable() {
20911357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    @Override
20921357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    public void run() {
20931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                        if (mVcb != null) {
20941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                            if (direction != 0) {
20951357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                                mVcb.vcb.onVolumeUpdateRequest(mVcb.route, direction);
20961357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                            } else {
20971357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                                mVcb.vcb.onVolumeSetRequest(mVcb.route, value);
20981357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                            }
20991357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                        }
21001357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    }
21011357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                });
21021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
21031357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        };
21041357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
21059a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void routeUpdated() {
21069a1de308cea2d160778fd977825f10a07b49d738Adam Powell            updateRoute(this);
21079a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
21089a1de308cea2d160778fd977825f10a07b49d738Adam Powell
21099a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
21109a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public String toString() {
2111d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            String supportedTypes = typesToString(getSupportedTypes());
211292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown            return getClass().getSimpleName() + "{ name=" + getName() +
211356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown                    ", description=" + getDescription() +
211492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                    ", status=" + getStatus() +
211592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                    ", category=" + getCategory() +
211692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown                    ", supportedTypes=" + supportedTypes +
211769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    ", presentationDisplay=" + mPresentationDisplay + " }";
21189a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
21199a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
21209a1de308cea2d160778fd977825f10a07b49d738Adam Powell
21219a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
21229a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a route that the application may define and modify.
21238e37a85bf3dc39519942698dc90a3951306b934bAdam Powell     * A user route defaults to {@link RouteInfo#PLAYBACK_TYPE_REMOTE} and
21248e37a85bf3dc39519942698dc90a3951306b934bAdam Powell     * {@link RouteInfo#PLAYBACK_VOLUME_FIXED}.
21259a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
21269a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see MediaRouter.RouteInfo
21279a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
2128b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class UserRouteInfo extends RouteInfo {
2129ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        RemoteControlClient mRcc;
21305d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        SessionVolumeProvider mSvp;
21319a1de308cea2d160778fd977825f10a07b49d738Adam Powell
21329a1de308cea2d160778fd977825f10a07b49d738Adam Powell        UserRouteInfo(RouteCategory category) {
21339a1de308cea2d160778fd977825f10a07b49d738Adam Powell            super(category);
21349a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mSupportedTypes = ROUTE_TYPE_USER;
21358e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mPlaybackType = PLAYBACK_TYPE_REMOTE;
21368e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeHandling = PLAYBACK_VOLUME_FIXED;
21379a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
21389a1de308cea2d160778fd977825f10a07b49d738Adam Powell
21399a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
21409a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Set the user-visible name of this route.
21419a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param name Name to display to the user to describe this route
21429a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
21439a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void setName(CharSequence name) {
2144eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon            mNameResId = 0;
21459a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = name;
21469a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
21479a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
21485d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
21490d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        /**
21500d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * Set the user-visible name of this route.
215156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
215256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route name identifies the destination represented by the route.
215356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied name, an alias, or device serial number.
215456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
215556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
21560d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @param resId Resource ID of the name to display to the user to describe this route
21570d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         */
21580d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public void setName(int resId) {
21590d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mNameResId = resId;
21600d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mName = null;
21610d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            routeUpdated();
21620d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
21639a1de308cea2d160778fd977825f10a07b49d738Adam Powell
21649a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
216556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * Set the user-visible description of this route.
216656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * <p>
216756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * The route description describes the kind of destination represented by the route.
216856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * It may be a user-supplied string, a model number or brand of device.
216956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * </p>
217056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         *
217156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         * @param description The description of the route, or null if none.
217256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown         */
217356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        public void setDescription(CharSequence description) {
217456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown            mDescription = description;
217556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown            routeUpdated();
217656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        }
217756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown
217856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown        /**
21799a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Set the current user-visible status for this route.
21809a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param status Status to display to the user to describe what the endpoint
21819a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * of this route is currently doing
21829a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
21839a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void setStatus(CharSequence status) {
21849a1de308cea2d160778fd977825f10a07b49d738Adam Powell            setStatusInt(status);
21859a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2186ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2187ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2188ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set the RemoteControlClient responsible for reporting playback info for this
2189ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * user route.
2190ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
2191ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * <p>If this route manages remote playback, the data exposed by this
2192ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * RemoteControlClient will be used to reflect and update information
2193ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * such as route volume info in related UIs.</p>
2194ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
21951357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * <p>The RemoteControlClient must have been previously registered with
21961357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}.</p>
21971357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *
2198ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @param rcc RemoteControlClient associated with this route
2199ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
2200ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setRemoteControlClient(RemoteControlClient rcc) {
2201ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            mRcc = rcc;
22021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            updatePlaybackInfoOnRcc();
2203ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2204ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2205ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
22064599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         * Retrieve the RemoteControlClient associated with this route, if one has been set.
22074599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         *
22084599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         * @return the RemoteControlClient associated with this route
22094599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         * @see #setRemoteControlClient(RemoteControlClient)
22104599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         */
22114599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        public RemoteControlClient getRemoteControlClient() {
22124599696591f745b3a546197d2ba7e5cfc5562484Adam Powell            return mRcc;
22134599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        }
22144599696591f745b3a546197d2ba7e5cfc5562484Adam Powell
22154599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        /**
2216ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this route.
2217ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2218ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
2219ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @param icon icon drawable to use to represent this route
2220ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
2221ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setIconDrawable(Drawable icon) {
2222ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            mIcon = icon;
2223ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2224ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2225ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2226ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this route.
2227ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2228ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
222971c69897ad0a55d590698bfa399bfe99c763b9dbAdam Powell         * @param resId Resource ID of an icon drawable to use to represent this route
2230ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
22317b9c912f536925ac6ec43935d6e97506851b33d6Tor Norbye        public void setIconResource(@DrawableRes int resId) {
2232ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            setIconDrawable(sStatic.mResources.getDrawable(resId));
2233ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
22341357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
22351357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
22361357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Set a callback to be notified of volume update requests
22371357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param vcb
22381357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
22391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolumeCallback(VolumeCallback vcb) {
22401357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            mVcb = new VolumeCallbackInfo(vcb, this);
22411357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
22421357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
22431357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
22441357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines whether playback associated with this route is "local"
22451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *    ({@link RouteInfo#PLAYBACK_TYPE_LOCAL}) or "remote"
22461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *    ({@link RouteInfo#PLAYBACK_TYPE_REMOTE}).
22471357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param type
22481357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
22497c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        public void setPlaybackType(@RouteInfo.PlaybackType int type) {
22501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackType != type) {
22511357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mPlaybackType = type;
2252430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
22531357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
22541357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
22551357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
22561357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
22571357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines whether volume for the playback associated with this route is fixed
22581357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * ({@link RouteInfo#PLAYBACK_VOLUME_FIXED}) or can modified
22591357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * ({@link RouteInfo#PLAYBACK_VOLUME_VARIABLE}).
22601357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volumeHandling
22611357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
22627c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang        public void setVolumeHandling(@RouteInfo.PlaybackVolume int volumeHandling) {
22631357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mVolumeHandling != volumeHandling) {
22641357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mVolumeHandling = volumeHandling;
2265430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
22661357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
22671357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
22681357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
22691357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
22701357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines at what volume the playback associated with this route is performed (for user
22711357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * feedback purposes). This information is only used when the playback is not local.
22721357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volume
22731357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
22741357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolume(int volume) {
22758e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            volume = Math.max(0, Math.min(volume, getVolumeMax()));
22761357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mVolume != volume) {
22771357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mVolume = volume;
2278430fc48865e5a371b08f180390946b96d73848feRoboErik                if (mSvp != null) {
22790d0f67f5ee5f939a1b611bc4583212707afd9beeRoboErik                    mSvp.setCurrentVolume(mVolume);
22805d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
22818e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                dispatchRouteVolumeChanged(this);
2282f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                if (mGroup != null) {
2283f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                    mGroup.memberVolumeChanged(this);
2284f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                }
22858e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
22868e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
22878e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
22888e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
22898e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestSetVolume(int volume) {
22908e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mVolumeHandling == PLAYBACK_VOLUME_VARIABLE) {
22918e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (mVcb == null) {
22928e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Cannot requestSetVolume on user route - no volume callback set");
22938e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    return;
22948e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
22958e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVcb.vcb.onVolumeSetRequest(this, volume);
22968e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
22978e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
22988e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
22998e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
23008e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestUpdateVolume(int direction) {
23018e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (mVolumeHandling == PLAYBACK_VOLUME_VARIABLE) {
23028e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (mVcb == null) {
23038e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    Log.e(TAG, "Cannot requestChangeVolume on user route - no volumec callback set");
23048e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    return;
23058e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
23068e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVcb.vcb.onVolumeUpdateRequest(this, direction);
23071357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
23081357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
23091357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
23101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
23111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines the maximum volume at which the playback associated with this route is performed
23121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * (for user feedback purposes). This information is only used when the playback is not
23131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * local.
23141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volumeMax
23151357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
23161357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolumeMax(int volumeMax) {
23171357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mVolumeMax != volumeMax) {
23181357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mVolumeMax = volumeMax;
2319430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
23201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
23211357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
23221357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
23231357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
23241357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines over what stream type the media is presented.
23251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param stream
23261357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
23271357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setPlaybackStream(int stream) {
23281357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackStream != stream) {
23291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mPlaybackStream = stream;
2330430fc48865e5a371b08f180390946b96d73848feRoboErik                configureSessionVolume();
23311357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
23321357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
23331357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
23341357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        private void updatePlaybackInfoOnRcc() {
2335430fc48865e5a371b08f180390946b96d73848feRoboErik            configureSessionVolume();
23365d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        }
23375d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
23385d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        private void configureSessionVolume() {
23395d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            if (mRcc == null) {
23405d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                if (DEBUG) {
2341eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon                    Log.d(TAG, "No Rcc to configure volume for route " + getName());
23425d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
23435d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                return;
23445d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
23455d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            MediaSession session = mRcc.getMediaSession();
23465d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            if (session == null) {
23475d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                if (DEBUG) {
23485d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    Log.d(TAG, "Rcc has no session to configure volume");
23495d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
23505d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                return;
23515d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
23525d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            if (mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
23537c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang                @VolumeProvider.ControlType int volumeControl =
23547c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang                        VolumeProvider.VOLUME_CONTROL_FIXED;
23555d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                switch (mVolumeHandling) {
23565d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    case RemoteControlClient.PLAYBACK_VOLUME_VARIABLE:
2357ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik                        volumeControl = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
23585d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        break;
23595d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    case RemoteControlClient.PLAYBACK_VOLUME_FIXED:
23605d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    default:
23615d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        break;
23621357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                }
23635d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                // Only register a new listener if necessary
23645d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                if (mSvp == null || mSvp.getVolumeControl() != volumeControl
23655d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        || mSvp.getMaxVolume() != mVolumeMax) {
23660d0f67f5ee5f939a1b611bc4583212707afd9beeRoboErik                    mSvp = new SessionVolumeProvider(volumeControl, mVolumeMax, mVolume);
23675d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    session.setPlaybackToRemote(mSvp);
23685d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                }
23695d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            } else {
23705d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                // We only know how to handle local and remote, fall back to local if not remote.
23719db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik                AudioAttributes.Builder bob = new AudioAttributes.Builder();
23729db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik                bob.setLegacyStreamType(mPlaybackStream);
23739db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik                session.setPlaybackToLocal(bob.build());
23745d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                mSvp = null;
23751357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
23761357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
23771357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
2378ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        class SessionVolumeProvider extends VolumeProvider {
23795d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
23807c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang            public SessionVolumeProvider(@VolumeProvider.ControlType int volumeControl,
23817c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang                    int maxVolume, int currentVolume) {
23820d0f67f5ee5f939a1b611bc4583212707afd9beeRoboErik                super(volumeControl, maxVolume, currentVolume);
23835d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
23845d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
23855d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            @Override
23865d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            public void onSetVolumeTo(final int volume) {
23875d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                sStatic.mHandler.post(new Runnable() {
23885d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    @Override
23895d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    public void run() {
23905d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        if (mVcb != null) {
23915d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                            mVcb.vcb.onVolumeSetRequest(mVcb.route, volume);
23925d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        }
23935d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    }
23945d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                });
23955d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
23965d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
23975d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            @Override
23981ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik            public void onAdjustVolume(final int direction) {
23995d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                sStatic.mHandler.post(new Runnable() {
24005d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    @Override
24015d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    public void run() {
24025d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        if (mVcb != null) {
24031ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik                            mVcb.vcb.onVolumeUpdateRequest(mVcb.route, direction);
24045d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                        }
24055d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                    }
24065d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik                });
24075d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik            }
24085d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik        }
24099a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
24109a1de308cea2d160778fd977825f10a07b49d738Adam Powell
24119a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
24129a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a route that consists of multiple other routes in a group.
24139a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
2414b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class RouteGroup extends RouteInfo {
24159a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
24169a1de308cea2d160778fd977825f10a07b49d738Adam Powell        private boolean mUpdateName;
24179a1de308cea2d160778fd977825f10a07b49d738Adam Powell
24189a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteGroup(RouteCategory category) {
24199a1de308cea2d160778fd977825f10a07b49d738Adam Powell            super(category);
24209a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mGroup = this;
24218e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeHandling = PLAYBACK_VOLUME_FIXED;
24229a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
24239a1de308cea2d160778fd977825f10a07b49d738Adam Powell
242469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
24250d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        CharSequence getName(Resources res) {
24269a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (mUpdateName) updateName();
24270d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return super.getName(res);
24289a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
24299a1de308cea2d160778fd977825f10a07b49d738Adam Powell
24309a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
24319a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Add a route to this group. The route must not currently belong to another group.
24329a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
24339a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to add to this group
24349a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
24359a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void addRoute(RouteInfo route) {
24369a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != null) {
24379a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalStateException("Route " + route + " is already part of a group.");
24389a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
24399a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getCategory() != mCategory) {
24409a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException(
24419a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        "Route cannot be added to a group with a different category. " +
24429a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            "(Route category=" + route.getCategory() +
24439a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            " group category=" + mCategory + ")");
24449a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
2445d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            final int at = mRoutes.size();
24469a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.add(route);
2447d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = this;
24489a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2449f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
24509a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
2451f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell            dispatchRouteGrouped(route, this, at);
24529a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
24539a1de308cea2d160778fd977825f10a07b49d738Adam Powell
24549a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
24559a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Add a route to this group before the specified index.
24569a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
24579a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to add
24589a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param insertAt insert the new route before this index
24599a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
24609a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void addRoute(RouteInfo route, int insertAt) {
24619a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != null) {
24629a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalStateException("Route " + route + " is already part of a group.");
24639a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
24649a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getCategory() != mCategory) {
24659a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException(
24669a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        "Route cannot be added to a group with a different category. " +
24679a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            "(Route category=" + route.getCategory() +
24689a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            " group category=" + mCategory + ")");
24699a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
24709a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.add(insertAt, route);
2471d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = this;
24729a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2473f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
24749a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
2475f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell            dispatchRouteGrouped(route, this, insertAt);
24769a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
24779a1de308cea2d160778fd977825f10a07b49d738Adam Powell
24789a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
24799a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Remove a route from this group.
24809a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
24819a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to remove
24829a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
24839a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void removeRoute(RouteInfo route) {
24849a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != this) {
24859a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException("Route " + route +
24869a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        " is not a member of this group.");
24879a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
24889a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.remove(route);
2489d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = null;
24909a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2491f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
2492d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteUngrouped(route, this);
24939a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
24949a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
24959a1de308cea2d160778fd977825f10a07b49d738Adam Powell
24969a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
24979a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Remove the route at the specified index from this group.
24989a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
24999a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param index index of the route to remove
25009a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
25019a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void removeRoute(int index) {
2502d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            RouteInfo route = mRoutes.remove(index);
2503d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = null;
25049a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
2505f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
2506d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteUngrouped(route, this);
25079a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
25089a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
25099a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2510d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2511d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @return The number of routes in this group
2512d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
2513d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public int getRouteCount() {
2514d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            return mRoutes.size();
2515d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        }
2516d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2517d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2518d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Return the route in this group at the specified index
2519d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2520d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param index Index to fetch
2521d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @return The route at index
2522d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
2523d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public RouteInfo getRouteAt(int index) {
2524d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            return mRoutes.get(index);
2525d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        }
2526d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2527ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2528ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this group.
2529ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2530ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
2531ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @param icon icon drawable to use to represent this group
2532ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
2533ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setIconDrawable(Drawable icon) {
2534ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            mIcon = icon;
2535ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2536ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
2537ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
2538ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this group.
2539ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
2540ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
254171c69897ad0a55d590698bfa399bfe99c763b9dbAdam Powell         * @param resId Resource ID of an icon drawable to use to represent this group
2542ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
25437b9c912f536925ac6ec43935d6e97506851b33d6Tor Norbye        public void setIconResource(@DrawableRes int resId) {
2544ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            setIconDrawable(sStatic.mResources.getDrawable(resId));
2545ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
2546ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
25478e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
25488e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestSetVolume(int volume) {
25498e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int maxVol = getVolumeMax();
25508e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (maxVol == 0) {
25518e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                return;
25528e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
25538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
25548e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final float scaledVolume = (float) volume / maxVol;
25558e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int routeCount = getRouteCount();
25568e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            for (int i = 0; i < routeCount; i++) {
25578e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final RouteInfo route = getRouteAt(i);
25588e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int routeVol = (int) (scaledVolume * route.getVolumeMax());
25598e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                route.requestSetVolume(routeVol);
25608e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
25618e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (volume != mVolume) {
25628e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVolume = volume;
25638e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                dispatchRouteVolumeChanged(this);
25648e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
25658e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
25668e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
25678e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
25688e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void requestUpdateVolume(int direction) {
25698e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int maxVol = getVolumeMax();
25708e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (maxVol == 0) {
25718e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                return;
25728e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
25738e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
25748e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            final int routeCount = getRouteCount();
2575f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            int volume = 0;
25768e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            for (int i = 0; i < routeCount; i++) {
25778e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final RouteInfo route = getRouteAt(i);
25788e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                route.requestUpdateVolume(direction);
2579f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                final int routeVol = route.getVolume();
2580f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                if (routeVol > volume) {
2581f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                    volume = routeVol;
2582f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                }
25838e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
25848e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (volume != mVolume) {
25858e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                mVolume = volume;
25868e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                dispatchRouteVolumeChanged(this);
25878e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
25888e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
25898e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
25909a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void memberNameChanged(RouteInfo info, CharSequence name) {
25919a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
25929a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
25939a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
25949a1de308cea2d160778fd977825f10a07b49d738Adam Powell
25959a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void memberStatusChanged(RouteInfo info, CharSequence status) {
25969a1de308cea2d160778fd977825f10a07b49d738Adam Powell            setStatusInt(status);
25979a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
25989a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2599f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        void memberVolumeChanged(RouteInfo info) {
2600f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            updateVolume();
2601f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        }
2602f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell
2603f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        void updateVolume() {
2604f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            // A group always represents the highest component volume value.
2605f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            final int routeCount = getRouteCount();
2606f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            int volume = 0;
2607f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            for (int i = 0; i < routeCount; i++) {
2608f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                final int routeVol = getRouteAt(i).getVolume();
2609f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                if (routeVol > volume) {
2610f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                    volume = routeVol;
2611f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                }
2612f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            }
2613f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            if (volume != mVolume) {
2614f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                mVolume = volume;
2615f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell                dispatchRouteVolumeChanged(this);
2616f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell            }
2617f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell        }
2618f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell
2619d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        @Override
2620d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        void routeUpdated() {
2621d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            int types = 0;
2622d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            final int count = mRoutes.size();
2623b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            if (count == 0) {
2624b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell                // Don't keep empty groups in the router.
262569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                MediaRouter.removeRouteStatic(this);
2626b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell                return;
2627b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            }
2628b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
26298e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            int maxVolume = 0;
26308e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            boolean isLocal = true;
26318e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            boolean isFixedVolume = true;
2632d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            for (int i = 0; i < count; i++) {
26338e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final RouteInfo route = mRoutes.get(i);
26348e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                types |= route.mSupportedTypes;
26358e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int routeMaxVolume = route.getVolumeMax();
26368e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (routeMaxVolume > maxVolume) {
26378e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    maxVolume = routeMaxVolume;
26388e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
26398e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                isLocal &= route.getPlaybackType() == PLAYBACK_TYPE_LOCAL;
26408e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                isFixedVolume &= route.getVolumeHandling() == PLAYBACK_VOLUME_FIXED;
2641d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            }
26428e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mPlaybackType = isLocal ? PLAYBACK_TYPE_LOCAL : PLAYBACK_TYPE_REMOTE;
26438e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeHandling = isFixedVolume ? PLAYBACK_VOLUME_FIXED : PLAYBACK_VOLUME_VARIABLE;
2644d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            mSupportedTypes = types;
26458e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            mVolumeMax = maxVolume;
2646d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            mIcon = count == 1 ? mRoutes.get(0).getIconDrawable() : null;
2647d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            super.routeUpdated();
2648d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        }
2649d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
26509a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void updateName() {
26519a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final StringBuilder sb = new StringBuilder();
26529a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final int count = mRoutes.size();
26539a1de308cea2d160778fd977825f10a07b49d738Adam Powell            for (int i = 0; i < count; i++) {
26549a1de308cea2d160778fd977825f10a07b49d738Adam Powell                final RouteInfo info = mRoutes.get(i);
2655b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell                // TODO: There's probably a much more correct way to localize this.
2656eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon                if (i > 0) {
2657eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon                    sb.append(", ");
2658eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon                }
2659eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon                sb.append(info.getName());
26609a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
26619a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = sb.toString();
26629a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = false;
26639a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2664d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
2665d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        @Override
2666d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        public String toString() {
2667d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            StringBuilder sb = new StringBuilder(super.toString());
2668d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            sb.append('[');
2669d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            final int count = mRoutes.size();
2670d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            for (int i = 0; i < count; i++) {
2671d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                if (i > 0) sb.append(", ");
2672d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                sb.append(mRoutes.get(i));
2673d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            }
2674d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            sb.append(']');
2675d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            return sb.toString();
2676d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        }
26779a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
26789a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26799a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
26809a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Definition of a category of routes. All routes belong to a category.
26819a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
2682b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class RouteCategory {
26839a1de308cea2d160778fd977825f10a07b49d738Adam Powell        CharSequence mName;
26840d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        int mNameResId;
26859a1de308cea2d160778fd977825f10a07b49d738Adam Powell        int mTypes;
26869a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final boolean mGroupable;
2687705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        boolean mIsSystem;
26889a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26899a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteCategory(CharSequence name, int types, boolean groupable) {
26909a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = name;
26919a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mTypes = types;
26929a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mGroupable = groupable;
26939a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
26949a1de308cea2d160778fd977825f10a07b49d738Adam Powell
26950d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        RouteCategory(int nameResId, int types, boolean groupable) {
26960d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mNameResId = nameResId;
26970d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mTypes = types;
26980d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mGroupable = groupable;
26990d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
27000d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell
27019a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
27029a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return the name of this route category
27039a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
27049a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getName() {
27050d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(sStatic.mResources);
27060d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
27075d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
27080d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        /**
27090d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * Return the properly localized/configuration dependent name of this RouteCategory.
27105d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik         *
27110d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @param context Context to resolve name resources
27120d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @return the name of this route category
27130d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         */
27140d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public CharSequence getName(Context context) {
27150d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(context.getResources());
27160d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
27175d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik
27180d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        CharSequence getName(Resources res) {
27190d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            if (mNameResId != 0) {
27200d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                return res.getText(mNameResId);
27210d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            }
27229a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mName;
27239a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27249a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27259a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
2726d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Return the current list of routes in this category that have been added
2727d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * to the MediaRouter.
27289a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2729d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * <p>This list will not include routes that are nested within RouteGroups.
2730d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * A RouteGroup is treated as a single route within its category.</p>
2731d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2732d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param out a List to fill with the routes in this category. If this parameter is
2733d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *            non-null, it will be cleared, filled with the current routes with this
2734d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *            category, and returned. If this parameter is null, a new List will be
2735d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *            allocated to report the category's current routes.
2736d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @return A list with the routes in this category that have been added to the MediaRouter.
27379a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
2738d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public List<RouteInfo> getRoutes(List<RouteInfo> out) {
2739d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            if (out == null) {
2740d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                out = new ArrayList<RouteInfo>();
2741d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            } else {
2742d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                out.clear();
2743d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            }
2744d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2745b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final int count = getRouteCountStatic();
2746d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            for (int i = 0; i < count; i++) {
2747b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                final RouteInfo route = getRouteAtStatic(i);
2748d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                if (route.mCategory == this) {
2749d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                    out.add(route);
2750d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                }
2751d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            }
2752d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            return out;
27539a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27549a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27559a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
27569a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return Flag set describing the route types supported by this category
27579a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
27589a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int getSupportedTypes() {
27599a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mTypes;
27609a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27619a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27629a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
27639a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Return whether or not this category supports grouping.
27649a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
27659a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * <p>If this method returns true, all routes obtained from this category
2766d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * via calls to {@link #getRouteAt(int)} will be {@link MediaRouter.RouteGroup}s.</p>
27679a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
27689a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return true if this category supports
27699a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
27709a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public boolean isGroupable() {
27719a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mGroupable;
27729a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27739a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2774705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        /**
2775705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * @return true if this is the category reserved for system routes.
2776705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         * @hide
2777705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell         */
2778705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        public boolean isSystem() {
2779705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            return mIsSystem;
2780705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
2781705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell
278269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
27839a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public String toString() {
2784eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon            return "RouteCategory{ name=" + getName() + " types=" + typesToString(mTypes) +
2785d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                    " groupable=" + mGroupable + " }";
27869a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
27879a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
27889a1de308cea2d160778fd977825f10a07b49d738Adam Powell
27899a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static class CallbackInfo {
27909a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int type;
279166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        public int flags;
2792b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        public final Callback cb;
2793b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        public final MediaRouter router;
27949a1de308cea2d160778fd977825f10a07b49d738Adam Powell
279566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        public CallbackInfo(Callback cb, int type, int flags, MediaRouter router) {
27969a1de308cea2d160778fd977825f10a07b49d738Adam Powell            this.cb = cb;
27979a1de308cea2d160778fd977825f10a07b49d738Adam Powell            this.type = type;
279866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            this.flags = flags;
2799b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            this.router = router;
28009a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
280166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown
280266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        public boolean filterRouteEvent(RouteInfo route) {
2803af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            return filterRouteEvent(route.mSupportedTypes);
2804af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        }
2805af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
2806af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        public boolean filterRouteEvent(int supportedTypes) {
280766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown            return (flags & CALLBACK_FLAG_UNFILTERED_EVENTS) != 0
2808af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    || (type & supportedTypes) != 0;
280966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown        }
28109a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
28119a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28129a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
28139a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Interface for receiving events about media routing changes.
28149a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * All methods of this interface will be called from the application's main thread.
281566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * <p>
281666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * A Callback will only receive events relevant to routes that the callback
281766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * was registered for unless the {@link MediaRouter#CALLBACK_FLAG_UNFILTERED_EVENTS}
281866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * flag was specified in {@link MediaRouter#addCallback(int, Callback, int)}.
281966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * </p>
28209a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
282166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown     * @see MediaRouter#addCallback(int, Callback, int)
28229a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see MediaRouter#removeCallback(Callback)
28239a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
28240d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    public static abstract class Callback {
28259a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
28269a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when the supplied route becomes selected as the active route
28279a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * for the given route type.
28289a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2829d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
28309a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param type Type flag set indicating the routes that have been selected
28319a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been selected for the given route types
28329a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
28330d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteSelected(MediaRouter router, int type, RouteInfo info);
28349a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28359a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
28369a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when the supplied route becomes unselected as the active route
28379a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * for the given route type.
28389a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2839d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
28409a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param type Type flag set indicating the routes that have been unselected
28419a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been unselected for the given route types
28429a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
28430d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteUnselected(MediaRouter router, int type, RouteInfo info);
28449a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28459a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
28469a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when a route for the specified type was added.
28479a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2848d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
28499a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has become available for use
28509a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
28510d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteAdded(MediaRouter router, RouteInfo info);
28529a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28539a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
28549a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when a route for the specified type was removed.
28559a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2856d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
28579a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been removed from availability
28589a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
28590d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteRemoved(MediaRouter router, RouteInfo info);
28609a1de308cea2d160778fd977825f10a07b49d738Adam Powell
28619a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
28629a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when an aspect of the indicated route has changed.
28639a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
28649a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * <p>This will not indicate that the types supported by this route have
28659a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * changed, only that cosmetic info such as name or status have been updated.</p>
28669a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
2867d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
28689a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info The route that was changed
28699a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
28700d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteChanged(MediaRouter router, RouteInfo info);
2871d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2872d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2873d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Called when a route is added to a group.
2874d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2875d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
2876d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param info The route that was added
2877d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param group The group the route was added to
2878d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param index The route index within group that info was added at
2879d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
28800d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
28810d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                int index);
2882d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
2883d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
2884d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Called when a route is removed from a group.
2885d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
2886d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
2887d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param info The route that was removed
2888d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param group The group the route was removed from
2889d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
28900d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group);
28918e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
28928e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        /**
28938e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * Called when a route's volume changes.
28948e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         *
28958e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param router the MediaRouter reporting the event
28968e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         * @param info The route with altered volume
28978e37a85bf3dc39519942698dc90a3951306b934bAdam Powell         */
28988e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public abstract void onRouteVolumeChanged(MediaRouter router, RouteInfo info);
289992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown
290092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        /**
290192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * Called when a route's presentation display changes.
290292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * <p>
290392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * This method is called whenever the route's presentation display becomes
290492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * available, is removes or has changes to some of its properties (such as its size).
290592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * </p>
290692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
290792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @param router the MediaRouter reporting the event
290892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @param info The route whose presentation display changed
290992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         *
291092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         * @see RouteInfo#getPresentationDisplay()
291192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown         */
291292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
291392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown        }
29149a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
29159a1de308cea2d160778fd977825f10a07b49d738Adam Powell
29169a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
29170d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * Stub implementation of {@link MediaRouter.Callback}.
29180d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * Each abstract method is defined as a no-op. Override just the ones
29199a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * you need.
29209a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
29210d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    public static class SimpleCallback extends Callback {
29229a1de308cea2d160778fd977825f10a07b49d738Adam Powell
29239a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2924d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
29259a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
29269a1de308cea2d160778fd977825f10a07b49d738Adam Powell
29279a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2928d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
29299a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
29309a1de308cea2d160778fd977825f10a07b49d738Adam Powell
29319a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2932d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteAdded(MediaRouter router, RouteInfo info) {
29339a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
29349a1de308cea2d160778fd977825f10a07b49d738Adam Powell
29359a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2936d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteRemoved(MediaRouter router, RouteInfo info) {
29379a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
29389a1de308cea2d160778fd977825f10a07b49d738Adam Powell
29399a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2940d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteChanged(MediaRouter router, RouteInfo info) {
29419a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
29429a1de308cea2d160778fd977825f10a07b49d738Adam Powell
29439a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2944d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
2945d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                int index) {
29469a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
29479a1de308cea2d160778fd977825f10a07b49d738Adam Powell
29489a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
2949d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
29509a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2951d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
29528e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
29538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void onRouteVolumeChanged(MediaRouter router, RouteInfo info) {
29548e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
29559a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
29561357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
29571357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    static class VolumeCallbackInfo {
29581357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final VolumeCallback vcb;
29591357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final RouteInfo route;
29601357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
29611357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public VolumeCallbackInfo(VolumeCallback vcb, RouteInfo route) {
29621357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            this.vcb = vcb;
29631357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            this.route = route;
29641357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
29651357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    }
29661357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
29671357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    /**
29681357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * Interface for receiving events about volume changes.
29691357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * All methods of this interface will be called from the application's main thread.
29701357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     *
29711357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * <p>A VolumeCallback will only receive events relevant to routes that the callback
29721357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * was registered for.</p>
29731357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     *
29741357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * @see UserRouteInfo#setVolumeCallback(VolumeCallback)
29751357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     */
29761357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    public static abstract class VolumeCallback {
29771357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
29781357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Called when the volume for the route should be increased or decreased.
29791357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param info the route affected by this event
29801357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param direction an integer indicating whether the volume is to be increased
29811357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     (positive value) or decreased (negative value).
29821357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     For bundled changes, the absolute value indicates the number of changes
29831357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     in the same direction, e.g. +3 corresponds to three "volume up" changes.
29841357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
29851357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public abstract void onVolumeUpdateRequest(RouteInfo info, int direction);
29861357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
29871357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Called when the volume for the route should be set to the given value
29881357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param info the route affected by this event
29891357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volume an integer indicating the new volume value that should be used, always
29901357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     between 0 and the value set by {@link UserRouteInfo#setVolumeMax(int)}.
29911357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
29921357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public abstract void onVolumeSetRequest(RouteInfo info, int volume);
29931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    }
29941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
29958e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    static class VolumeChangeReceiver extends BroadcastReceiver {
29968e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        @Override
29978e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        public void onReceive(Context context, Intent intent) {
29988e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            if (intent.getAction().equals(AudioManager.VOLUME_CHANGED_ACTION)) {
29998e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
30008e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                        -1);
30018e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (streamType != AudioManager.STREAM_MUSIC) {
30028e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    return;
30038e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
30048e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
30058e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int newVolume = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
30068e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                final int oldVolume = intent.getIntExtra(
30078e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                        AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0);
30088e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                if (newVolume != oldVolume) {
30098e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                    systemVolumeChanged(newVolume);
30108e37a85bf3dc39519942698dc90a3951306b934bAdam Powell                }
30118e37a85bf3dc39519942698dc90a3951306b934bAdam Powell            }
30128e37a85bf3dc39519942698dc90a3951306b934bAdam Powell        }
3013705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    }
30148e37a85bf3dc39519942698dc90a3951306b934bAdam Powell
3015705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell    static class WifiDisplayStatusChangedReceiver extends BroadcastReceiver {
3016705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        @Override
3017705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        public void onReceive(Context context, Intent intent) {
3018705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            if (intent.getAction().equals(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)) {
3019705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                updateWifiDisplayStatus((WifiDisplayStatus) intent.getParcelableExtra(
3020705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell                        DisplayManager.EXTRA_WIFI_DISPLAY_STATUS));
3021705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell            }
3022705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell        }
30238e37a85bf3dc39519942698dc90a3951306b934bAdam Powell    }
30249a1de308cea2d160778fd977825f10a07b49d738Adam Powell}
3025