1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.media;
18 
19 
20 import android.Manifest;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.RequiresPermission;
24 import android.app.ActivityManager;
25 import android.app.UserSwitchObserver;
26 import android.bluetooth.BluetoothA2dp;
27 import android.bluetooth.BluetoothDevice;
28 import android.content.BroadcastReceiver;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.IntentFilter;
32 import android.content.pm.PackageManager;
33 import android.content.res.Resources;
34 import android.media.AudioPlaybackConfiguration;
35 import android.media.AudioRoutesInfo;
36 import android.media.AudioSystem;
37 import android.media.IAudioRoutesObserver;
38 import android.media.IAudioService;
39 import android.media.IMediaRouter2;
40 import android.media.IMediaRouter2Manager;
41 import android.media.IMediaRouterClient;
42 import android.media.IMediaRouterService;
43 import android.media.MediaRoute2Info;
44 import android.media.MediaRouter;
45 import android.media.MediaRouter2.ScanningState;
46 import android.media.MediaRouterClientState;
47 import android.media.RemoteDisplayState;
48 import android.media.RemoteDisplayState.RemoteDisplayInfo;
49 import android.media.RouteDiscoveryPreference;
50 import android.media.RouteListingPreference;
51 import android.media.RoutingSessionInfo;
52 import android.os.Binder;
53 import android.os.Bundle;
54 import android.os.Handler;
55 import android.os.HandlerThread;
56 import android.os.IBinder;
57 import android.os.Looper;
58 import android.os.Message;
59 import android.os.RemoteException;
60 import android.os.ServiceManager;
61 import android.os.SystemClock;
62 import android.os.UserHandle;
63 import android.text.TextUtils;
64 import android.util.ArrayMap;
65 import android.util.IntArray;
66 import android.util.Log;
67 import android.util.Slog;
68 import android.util.SparseArray;
69 import android.util.TimeUtils;
70 
71 import com.android.internal.annotations.GuardedBy;
72 import com.android.internal.util.DumpUtils;
73 import com.android.media.flags.Flags;
74 import com.android.server.LocalServices;
75 import com.android.server.Watchdog;
76 import com.android.server.pm.UserManagerInternal;
77 
78 import java.io.FileDescriptor;
79 import java.io.PrintWriter;
80 import java.util.ArrayList;
81 import java.util.Collections;
82 import java.util.List;
83 import java.util.Objects;
84 
85 /**
86  * Provides a mechanism for discovering media routes and manages media playback
87  * behalf of applications.
88  * <p>
89  * Currently supports discovering remote displays via remote display provider
90  * services that have been registered by applications.
91  * </p>
92  */
93 public final class MediaRouterService extends IMediaRouterService.Stub
94         implements Watchdog.Monitor {
95     private static final String TAG = "MediaRouterService";
96     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
97     private static final String WORKER_THREAD_NAME = "MediaRouterServiceThread";
98 
99     /**
100      * Timeout in milliseconds for a selected route to transition from a disconnected state to a
101      * connecting state. If we don't observe any progress within this interval, then we will give up
102      * and unselect the route.
103      */
104     private static final long CONNECTING_TIMEOUT = 5000;
105 
106     /**
107      * Timeout in milliseconds for a selected route to transition from a connecting state to a
108      * connected state. If we don't observe any progress within this interval, then we will give up
109      * and unselect the route.
110      */
111     private static final long CONNECTED_TIMEOUT = 60000;
112 
113     private final Context mContext;
114     private final Looper mLooper;
115 
116     // State guarded by mLock.
117     private final Object mLock = new Object();
118 
119     private final UserManagerInternal mUserManagerInternal;
120 
121     @GuardedBy("mLock")
122     private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
123     @GuardedBy("mLock")
124     private final ArrayMap<IBinder, ClientRecord> mAllClientRecords = new ArrayMap<>();
125     @GuardedBy("mLock")
126     private int mCurrentActiveUserId = -1;
127 
128     private final IAudioService mAudioService;
129     private final AudioPlayerStateMonitor mAudioPlayerStateMonitor;
130     private final Handler mHandler;
131     private final IntArray mActivePlayerMinPriorityQueue = new IntArray();
132     private final IntArray mActivePlayerUidMinPriorityQueue = new IntArray();
133 
134     private final BroadcastReceiver mReceiver = new MediaRouterServiceBroadcastReceiver();
135     BluetoothDevice mActiveBluetoothDevice;
136     int mAudioRouteMainType = AudioRoutesInfo.MAIN_SPEAKER;
137     boolean mGlobalBluetoothA2dpOn = false;
138 
139     //TODO: remove this when it's finished
140     private final MediaRouter2ServiceImpl mService2;
141     private final String mDefaultAudioRouteId;
142     private final String mBluetoothA2dpRouteId;
143 
144     @RequiresPermission(Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS)
MediaRouterService(Context context)145     public MediaRouterService(Context context) {
146         if (Flags.enableMr2ServiceNonMainBgThread()) {
147             HandlerThread handlerThread = new HandlerThread(WORKER_THREAD_NAME);
148             handlerThread.start();
149             mLooper = handlerThread.getLooper();
150         } else {
151             mLooper = Looper.myLooper();
152         }
153         mHandler = new Handler(mLooper);
154         mService2 = new MediaRouter2ServiceImpl(context, mLooper);
155         mContext = context;
156         Watchdog.getInstance().addMonitor(this);
157         Resources res = context.getResources();
158         mDefaultAudioRouteId = res.getString(com.android.internal.R.string.default_audio_route_id);
159         mBluetoothA2dpRouteId =
160                 res.getString(com.android.internal.R.string.bluetooth_a2dp_audio_route_id);
161 
162         mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
163         mAudioService = IAudioService.Stub.asInterface(
164                 ServiceManager.getService(Context.AUDIO_SERVICE));
165         mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance(context);
166         mAudioPlayerStateMonitor.registerListener(
167                 new AudioPlayerActiveStateChangedListenerImpl(), mHandler);
168 
169         try {
170             mAudioService.startWatchingRoutes(new AudioRoutesObserverImpl());
171         } catch (RemoteException e) {
172             Slog.w(TAG, "RemoteException in the audio service.");
173         }
174 
175         IntentFilter intentFilter = new IntentFilter(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
176         context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null);
177     }
178 
179     /**
180      * Initializes the MediaRouter service.
181      *
182      * @throws RemoteException If an error occurs while registering the {@link UserSwitchObserver}.
183      */
184     @RequiresPermission(
185             anyOf = {
186                 "android.permission.INTERACT_ACROSS_USERS",
187                 "android.permission.INTERACT_ACROSS_USERS_FULL"
188             })
systemRunning()189     public void systemRunning() throws RemoteException {
190         ActivityManager.getService()
191                 .registerUserSwitchObserver(
192                         new UserSwitchObserver() {
193                             @Override
194                             public void onUserSwitchComplete(int newUserId) {
195                                 updateRunningUserAndProfiles(newUserId);
196                             }
197                         },
198                         TAG);
199         updateRunningUserAndProfiles(ActivityManager.getCurrentUser());
200     }
201 
202     @Override
monitor()203     public void monitor() {
204         synchronized (mLock) { /* check for deadlock */ }
205     }
206 
207     // Binder call
208     @Override
registerClientAsUser( IMediaRouterClient client, @NonNull String packageName, int userId)209     public void registerClientAsUser(
210             IMediaRouterClient client, @NonNull String packageName, int userId) {
211         final int uid = Binder.getCallingUid();
212         if (!validatePackageName(uid, packageName)) {
213             throw new SecurityException("packageName must match the calling uid");
214         }
215 
216         final int pid = Binder.getCallingPid();
217         final int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
218                 false /*allowAll*/, true /*requireFull*/, "registerClientAsUser", packageName);
219         final boolean trusted = mContext.checkCallingOrSelfPermission(
220                 android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) ==
221                 PackageManager.PERMISSION_GRANTED;
222         final long token = Binder.clearCallingIdentity();
223         try {
224             synchronized (mLock) {
225                 registerClientLocked(client, uid, pid, packageName, resolvedUserId, trusted);
226             }
227         } finally {
228             Binder.restoreCallingIdentity(token);
229         }
230     }
231 
232     // Binder call
233     @Override
registerClientGroupId(IMediaRouterClient client, String groupId)234     public void registerClientGroupId(IMediaRouterClient client, String groupId) {
235         if (mContext.checkCallingOrSelfPermission(
236                 android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
237                 != PackageManager.PERMISSION_GRANTED) {
238             Log.w(TAG, "Ignoring client group request because "
239                     + "the client doesn't have the CONFIGURE_WIFI_DISPLAY permission.");
240             return;
241         }
242         final long token = Binder.clearCallingIdentity();
243         try {
244             synchronized (mLock) {
245                 registerClientGroupIdLocked(client, groupId);
246             }
247         } finally {
248             Binder.restoreCallingIdentity(token);
249         }
250     }
251 
252     // Binder call
253     @Override
unregisterClient(IMediaRouterClient client)254     public void unregisterClient(IMediaRouterClient client) {
255         final long token = Binder.clearCallingIdentity();
256         try {
257             synchronized (mLock) {
258                 unregisterClientLocked(client, false);
259             }
260         } finally {
261             Binder.restoreCallingIdentity(token);
262         }
263     }
264 
265     // Binder call
266     @Override
getState(IMediaRouterClient client)267     public MediaRouterClientState getState(IMediaRouterClient client) {
268         final long token = Binder.clearCallingIdentity();
269         try {
270             synchronized (mLock) {
271                 return getStateLocked(client);
272             }
273         } finally {
274             Binder.restoreCallingIdentity(token);
275         }
276     }
277 
278     // Binder call
279     @Override
isPlaybackActive(IMediaRouterClient client)280     public boolean isPlaybackActive(IMediaRouterClient client) {
281         final long token = Binder.clearCallingIdentity();
282         try {
283             ClientRecord clientRecord;
284             synchronized (mLock) {
285                 clientRecord = mAllClientRecords.get(client.asBinder());
286             }
287             if (clientRecord != null) {
288                 return mAudioPlayerStateMonitor.isPlaybackActive(clientRecord.mUid);
289             }
290             return false;
291         } finally {
292             Binder.restoreCallingIdentity(token);
293         }
294     }
295 
296     // Binder call
297     @Override
setBluetoothA2dpOn(IMediaRouterClient client, boolean on)298     public void setBluetoothA2dpOn(IMediaRouterClient client, boolean on) {
299         if (client == null) {
300             throw new IllegalArgumentException("client must not be null");
301         }
302 
303         final long token = Binder.clearCallingIdentity();
304         try {
305             mAudioService.setBluetoothA2dpOn(on);
306         } catch (RemoteException ex) {
307             Slog.w(TAG, "RemoteException while calling setBluetoothA2dpOn. on=" + on);
308         } finally {
309             Binder.restoreCallingIdentity(token);
310         }
311     }
312 
313     // Binder call
314     @Override
setDiscoveryRequest(IMediaRouterClient client, int routeTypes, boolean activeScan)315     public void setDiscoveryRequest(IMediaRouterClient client,
316             int routeTypes, boolean activeScan) {
317         final long token = Binder.clearCallingIdentity();
318         try {
319             synchronized (mLock) {
320                 setDiscoveryRequestLocked(client, routeTypes, activeScan);
321             }
322         } finally {
323             Binder.restoreCallingIdentity(token);
324         }
325     }
326 
327     // Binder call
328     // A null routeId means that the client wants to unselect its current route.
329     // The explicit flag indicates whether the change was explicitly requested by the
330     // user or the application which may cause changes to propagate out to the rest
331     // of the system.  Should be false when the change is in response to a new
332     // selected route or a default selection.
333     @Override
setSelectedRoute(IMediaRouterClient client, String routeId, boolean explicit)334     public void setSelectedRoute(IMediaRouterClient client, String routeId, boolean explicit) {
335         final long token = Binder.clearCallingIdentity();
336         try {
337             synchronized (mLock) {
338                 setSelectedRouteLocked(client, routeId, explicit);
339             }
340         } finally {
341             Binder.restoreCallingIdentity(token);
342         }
343     }
344 
345     // Binder call
346     @Override
requestSetVolume(IMediaRouterClient client, String routeId, int volume)347     public void requestSetVolume(IMediaRouterClient client, String routeId, int volume) {
348         Objects.requireNonNull(routeId, "routeId must not be null");
349 
350         final long token = Binder.clearCallingIdentity();
351         try {
352             synchronized (mLock) {
353                 requestSetVolumeLocked(client, routeId, volume);
354             }
355         } finally {
356             Binder.restoreCallingIdentity(token);
357         }
358     }
359 
360     // Binder call
361     @Override
requestUpdateVolume(IMediaRouterClient client, String routeId, int direction)362     public void requestUpdateVolume(IMediaRouterClient client, String routeId, int direction) {
363         Objects.requireNonNull(routeId, "routeId must not be null");
364 
365         final long token = Binder.clearCallingIdentity();
366         try {
367             synchronized (mLock) {
368                 requestUpdateVolumeLocked(client, routeId, direction);
369             }
370         } finally {
371             Binder.restoreCallingIdentity(token);
372         }
373     }
374 
375     // Binder call
376     @Override
dump(FileDescriptor fd, final PrintWriter pw, String[] args)377     public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
378         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
379 
380         pw.println("MEDIA ROUTER SERVICE (dumpsys media_router)");
381         pw.println();
382         pw.println("Global state");
383         pw.println("  mCurrentUserId=" + mCurrentActiveUserId);
384 
385         synchronized (mLock) {
386             final int count = mUserRecords.size();
387             for (int i = 0; i < count; i++) {
388                 UserRecord userRecord = mUserRecords.valueAt(i);
389                 pw.println();
390                 userRecord.dump(pw, "");
391             }
392         }
393 
394         pw.println();
395         mService2.dump(pw, "");
396     }
397 
398     // Binder call
399     @Override
getSystemRoutes(@onNull String callerPackageName, boolean isProxyRouter)400     public List<MediaRoute2Info> getSystemRoutes(@NonNull String callerPackageName,
401             boolean isProxyRouter) {
402         if (!validatePackageName(Binder.getCallingUid(), callerPackageName)) {
403             throw new SecurityException("callerPackageName does not match calling uid.");
404         }
405         return mService2.getSystemRoutes(callerPackageName, isProxyRouter);
406     }
407 
408     // Binder call
409     @Override
getSystemSessionInfo()410     public RoutingSessionInfo getSystemSessionInfo() {
411         return mService2.getSystemSessionInfo(
412                 /* callerPackageName */ null,
413                 /* targetPackageName */ null, /* setDeviceRouteSelected */
414                 false);
415     }
416 
417     // Binder call
418     @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
419     @Override
showMediaOutputSwitcherWithRouter2(@onNull String packageName)420     public boolean showMediaOutputSwitcherWithRouter2(@NonNull String packageName) {
421         int uid = Binder.getCallingUid();
422         if (!validatePackageName(uid, packageName)) {
423             throw new SecurityException("packageName must match the calling identity");
424         }
425         return mService2.showMediaOutputSwitcherWithRouter2(packageName);
426     }
427 
428     // Binder call
429     @Override
registerRouter2(IMediaRouter2 router, String packageName)430     public void registerRouter2(IMediaRouter2 router, String packageName) {
431         final int uid = Binder.getCallingUid();
432         if (!validatePackageName(uid, packageName)) {
433             throw new SecurityException("packageName must match the calling uid");
434         }
435         mService2.registerRouter2(router, packageName);
436     }
437 
438     // Binder call
439     @Override
unregisterRouter2(IMediaRouter2 router)440     public void unregisterRouter2(IMediaRouter2 router) {
441         mService2.unregisterRouter2(router);
442     }
443 
444     // Binder call
445     @Override
446     @RequiresPermission(
447             anyOf = {
448                 Manifest.permission.MEDIA_ROUTING_CONTROL,
449                 Manifest.permission.MEDIA_CONTENT_CONTROL
450             },
451             conditional = true)
updateScanningStateWithRouter2( IMediaRouter2 router, @ScanningState int scanningState)452     public void updateScanningStateWithRouter2(
453             IMediaRouter2 router, @ScanningState int scanningState) {
454         mService2.updateScanningState(router, scanningState);
455     }
456 
457     // Binder call
458     @Override
setDiscoveryRequestWithRouter2(IMediaRouter2 router, RouteDiscoveryPreference request)459     public void setDiscoveryRequestWithRouter2(IMediaRouter2 router,
460             RouteDiscoveryPreference request) {
461         mService2.setDiscoveryRequestWithRouter2(router, request);
462     }
463 
464     // Binder call
465     @Override
setRouteListingPreference( @onNull IMediaRouter2 router, @Nullable RouteListingPreference routeListingPreference)466     public void setRouteListingPreference(
467             @NonNull IMediaRouter2 router,
468             @Nullable RouteListingPreference routeListingPreference) {
469         mService2.setRouteListingPreference(router, routeListingPreference);
470     }
471 
472     // Binder call
473     @Override
setRouteVolumeWithRouter2(IMediaRouter2 router, MediaRoute2Info route, int volume)474     public void setRouteVolumeWithRouter2(IMediaRouter2 router,
475             MediaRoute2Info route, int volume) {
476         mService2.setRouteVolumeWithRouter2(router, route, volume);
477     }
478 
479     // Binder call
480     @Override
requestCreateSessionWithRouter2( IMediaRouter2 router, int requestId, long managerRequestId, RoutingSessionInfo oldSession, MediaRoute2Info route, Bundle sessionHints)481     public void requestCreateSessionWithRouter2(
482             IMediaRouter2 router,
483             int requestId,
484             long managerRequestId,
485             RoutingSessionInfo oldSession,
486             MediaRoute2Info route,
487             Bundle sessionHints) {
488         mService2.requestCreateSessionWithRouter2(
489                 router, requestId, managerRequestId, oldSession, route, sessionHints);
490     }
491 
492     // Binder call
493     @Override
selectRouteWithRouter2(IMediaRouter2 router, String sessionId, MediaRoute2Info route)494     public void selectRouteWithRouter2(IMediaRouter2 router, String sessionId,
495             MediaRoute2Info route) {
496         mService2.selectRouteWithRouter2(router, sessionId, route);
497     }
498 
499     // Binder call
500     @Override
deselectRouteWithRouter2(IMediaRouter2 router, String sessionId, MediaRoute2Info route)501     public void deselectRouteWithRouter2(IMediaRouter2 router, String sessionId,
502             MediaRoute2Info route) {
503         mService2.deselectRouteWithRouter2(router, sessionId, route);
504     }
505 
506     // Binder call
507     @Override
transferToRouteWithRouter2(IMediaRouter2 router, String sessionId, MediaRoute2Info route)508     public void transferToRouteWithRouter2(IMediaRouter2 router, String sessionId,
509             MediaRoute2Info route) {
510         mService2.transferToRouteWithRouter2(router, sessionId, route);
511     }
512 
513     // Binder call
514     @Override
setSessionVolumeWithRouter2(IMediaRouter2 router, String sessionId, int volume)515     public void setSessionVolumeWithRouter2(IMediaRouter2 router, String sessionId, int volume) {
516         mService2.setSessionVolumeWithRouter2(router, sessionId, volume);
517     }
518 
519     // Binder call
520     @Override
releaseSessionWithRouter2(IMediaRouter2 router, String sessionId)521     public void releaseSessionWithRouter2(IMediaRouter2 router, String sessionId) {
522         mService2.releaseSessionWithRouter2(router, sessionId);
523     }
524 
525     // Binder call
526     @Override
getRemoteSessions(IMediaRouter2Manager manager)527     public List<RoutingSessionInfo> getRemoteSessions(IMediaRouter2Manager manager) {
528         return mService2.getRemoteSessions(manager);
529     }
530 
531     // Binder call
532     @Override
getSystemSessionInfoForPackage( @onNull String callerPackageName, @Nullable String targetPackageName)533     public RoutingSessionInfo getSystemSessionInfoForPackage(
534             @NonNull String callerPackageName, @Nullable String targetPackageName) {
535         final int uid = Binder.getCallingUid();
536         final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
537 
538         if (!validatePackageName(uid, callerPackageName)) {
539             throw new SecurityException("callerPackageName does not match calling uid.");
540         }
541 
542         boolean setDeviceRouteSelected = false;
543         synchronized (mLock) {
544             UserRecord userRecord = mUserRecords.get(userId);
545             List<ClientRecord> userClientRecords =
546                     userRecord != null ? userRecord.mClientRecords : Collections.emptyList();
547             for (ClientRecord clientRecord : userClientRecords) {
548                 if (TextUtils.equals(clientRecord.mPackageName, targetPackageName)) {
549                     if (mDefaultAudioRouteId.equals(clientRecord.mSelectedRouteId)) {
550                         setDeviceRouteSelected = true;
551                         break;
552                     }
553                 }
554             }
555         }
556         return mService2.getSystemSessionInfo(
557                 callerPackageName, targetPackageName, setDeviceRouteSelected);
558     }
559 
560     // Binder call
561     @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
562     @Override
registerManager(IMediaRouter2Manager manager, String callerPackageName)563     public void registerManager(IMediaRouter2Manager manager, String callerPackageName) {
564         final int uid = Binder.getCallingUid();
565         if (!validatePackageName(uid, callerPackageName)) {
566             throw new SecurityException("callerPackageName must match the calling uid");
567         }
568         mService2.registerManager(manager, callerPackageName);
569     }
570 
571     @Override
registerProxyRouter( @onNull IMediaRouter2Manager manager, @NonNull String callerPackageName, @NonNull String targetPackageName, @NonNull UserHandle targetUser)572     public void registerProxyRouter(
573             @NonNull IMediaRouter2Manager manager,
574             @NonNull String callerPackageName,
575             @NonNull String targetPackageName,
576             @NonNull UserHandle targetUser) {
577         final int uid = Binder.getCallingUid();
578         if (!validatePackageName(uid, callerPackageName)) {
579             throw new SecurityException("callerPackageName must match the calling uid");
580         }
581         mService2.registerProxyRouter(manager, callerPackageName, targetPackageName, targetUser);
582     }
583 
584     // Binder call
585     @Override
unregisterManager(IMediaRouter2Manager manager)586     public void unregisterManager(IMediaRouter2Manager manager) {
587         mService2.unregisterManager(manager);
588     }
589 
590     // Binder call
591     @Override
updateScanningState( IMediaRouter2Manager manager, @ScanningState int scanningState)592     public void updateScanningState(
593             IMediaRouter2Manager manager, @ScanningState int scanningState) {
594         mService2.updateScanningState(manager, scanningState);
595     }
596 
597     // Binder call
598     @Override
setRouteVolumeWithManager(IMediaRouter2Manager manager, int requestId, MediaRoute2Info route, int volume)599     public void setRouteVolumeWithManager(IMediaRouter2Manager manager, int requestId,
600             MediaRoute2Info route, int volume) {
601         mService2.setRouteVolumeWithManager(manager, requestId, route, volume);
602     }
603 
604     // Binder call
605     @Override
requestCreateSessionWithManager( IMediaRouter2Manager manager, int requestId, RoutingSessionInfo oldSession, MediaRoute2Info route, UserHandle transferInitiatorUserHandle, String transferInitiatorPackageName)606     public void requestCreateSessionWithManager(
607             IMediaRouter2Manager manager,
608             int requestId,
609             RoutingSessionInfo oldSession,
610             MediaRoute2Info route,
611             UserHandle transferInitiatorUserHandle,
612             String transferInitiatorPackageName) {
613         mService2.requestCreateSessionWithManager(
614                 manager,
615                 requestId,
616                 oldSession,
617                 route,
618                 transferInitiatorUserHandle,
619                 transferInitiatorPackageName);
620     }
621 
622     // Binder call
623     @Override
selectRouteWithManager(IMediaRouter2Manager manager, int requestId, String sessionId, MediaRoute2Info route)624     public void selectRouteWithManager(IMediaRouter2Manager manager, int requestId,
625             String sessionId, MediaRoute2Info route) {
626         mService2.selectRouteWithManager(manager, requestId, sessionId, route);
627     }
628 
629     // Binder call
630     @Override
deselectRouteWithManager(IMediaRouter2Manager manager, int requestId, String sessionId, MediaRoute2Info route)631     public void deselectRouteWithManager(IMediaRouter2Manager manager, int requestId,
632             String sessionId, MediaRoute2Info route) {
633         mService2.deselectRouteWithManager(manager, requestId, sessionId, route);
634     }
635 
636     // Binder call
637     @Override
transferToRouteWithManager( IMediaRouter2Manager manager, int requestId, String sessionId, MediaRoute2Info route, UserHandle transferInitiatorUserHandle, String transferInitiatorPackageName)638     public void transferToRouteWithManager(
639             IMediaRouter2Manager manager,
640             int requestId,
641             String sessionId,
642             MediaRoute2Info route,
643             UserHandle transferInitiatorUserHandle,
644             String transferInitiatorPackageName) {
645         mService2.transferToRouteWithManager(
646                 manager,
647                 requestId,
648                 sessionId,
649                 route,
650                 transferInitiatorUserHandle,
651                 transferInitiatorPackageName);
652     }
653 
654     // Binder call
655     @Override
setSessionVolumeWithManager(IMediaRouter2Manager manager, int requestId, String sessionId, int volume)656     public void setSessionVolumeWithManager(IMediaRouter2Manager manager, int requestId,
657             String sessionId, int volume) {
658         mService2.setSessionVolumeWithManager(manager, requestId, sessionId, volume);
659     }
660 
661     // Binder call
662     @Override
releaseSessionWithManager(IMediaRouter2Manager manager, int requestId, String sessionId)663     public void releaseSessionWithManager(IMediaRouter2Manager manager, int requestId,
664             String sessionId) {
665         mService2.releaseSessionWithManager(manager, requestId, sessionId);
666     }
667 
668     @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
669     @Override
showMediaOutputSwitcherWithProxyRouter( @onNull IMediaRouter2Manager proxyRouter)670     public boolean showMediaOutputSwitcherWithProxyRouter(
671             @NonNull IMediaRouter2Manager proxyRouter) {
672         return mService2.showMediaOutputSwitcherWithProxyRouter(proxyRouter);
673     }
674 
restoreBluetoothA2dp()675     void restoreBluetoothA2dp() {
676         try {
677             boolean a2dpOn;
678             BluetoothDevice btDevice;
679             synchronized (mLock) {
680                 a2dpOn = mGlobalBluetoothA2dpOn;
681                 btDevice = mActiveBluetoothDevice;
682             }
683             // We don't need to change a2dp status when bluetooth is not connected.
684             if (btDevice != null) {
685                 if (DEBUG) {
686                     Slog.d(TAG, "restoreBluetoothA2dp(" + a2dpOn + ")");
687                 }
688                 mAudioService.setBluetoothA2dpOn(a2dpOn);
689             }
690         } catch (RemoteException e) {
691             Slog.w(TAG, "RemoteException while calling setBluetoothA2dpOn.");
692         }
693     }
694 
restoreRoute(int uid)695     void restoreRoute(int uid) {
696         ClientRecord clientRecord = null;
697         synchronized (mLock) {
698             UserRecord userRecord = mUserRecords.get(
699                     UserHandle.getUserHandleForUid(uid).getIdentifier());
700             if (userRecord != null && userRecord.mClientRecords != null) {
701                 for (ClientRecord cr : userRecord.mClientRecords) {
702                     if (validatePackageName(uid, cr.mPackageName)) {
703                         clientRecord = cr;
704                         break;
705                     }
706                 }
707             }
708         }
709         if (clientRecord != null) {
710             try {
711                 clientRecord.mClient.onRestoreRoute();
712             } catch (RemoteException e) {
713                 Slog.w(TAG, "Failed to call onRestoreRoute. Client probably died.");
714             }
715         } else {
716             restoreBluetoothA2dp();
717         }
718     }
719 
720     /**
721      * Starts all {@link UserRecord user records} associated with the active user (whose ID is
722      * {@code newActiveUserId}) or the active user's profiles.
723      *
724      * <p>All other records are stopped, and those without associated client records are removed.
725      */
updateRunningUserAndProfiles(int newActiveUserId)726     private void updateRunningUserAndProfiles(int newActiveUserId) {
727         synchronized (mLock) {
728             if (mCurrentActiveUserId != newActiveUserId) {
729                 mCurrentActiveUserId = newActiveUserId;
730                 // disposeUserIfNeededLocked might modify the collection, hence clone
731                 final var userRecords = mUserRecords.clone();
732                 for (int i = 0; i < userRecords.size(); i++) {
733                     int userId = userRecords.keyAt(i);
734                     UserRecord userRecord = userRecords.valueAt(i);
735                     if (isUserActiveLocked(userId)) {
736                         // userId corresponds to the active user, or one of its profiles. We
737                         // ensure the associated structures are initialized.
738                         userRecord.mHandler.sendEmptyMessage(UserHandler.MSG_START);
739                     } else {
740                         userRecord.mHandler.sendEmptyMessage(UserHandler.MSG_STOP);
741                         disposeUserIfNeededLocked(userRecord);
742                     }
743                 }
744             }
745         }
746         mService2.updateRunningUserAndProfiles(newActiveUserId);
747     }
748 
clientDied(ClientRecord clientRecord)749     void clientDied(ClientRecord clientRecord) {
750         synchronized (mLock) {
751             unregisterClientLocked(clientRecord.mClient, true);
752         }
753     }
754 
755     @GuardedBy("mLock")
registerClientLocked( IMediaRouterClient client, int uid, int pid, @NonNull String packageName, int userId, boolean trusted)756     private void registerClientLocked(
757             IMediaRouterClient client,
758             int uid,
759             int pid,
760             @NonNull String packageName,
761             int userId,
762             boolean trusted) {
763         final IBinder binder = client.asBinder();
764         ClientRecord clientRecord = mAllClientRecords.get(binder);
765         if (clientRecord == null) {
766             boolean newUser = false;
767             UserRecord userRecord = mUserRecords.get(userId);
768             if (userRecord == null) {
769                 userRecord = new UserRecord(userId);
770                 newUser = true;
771             }
772             clientRecord = new ClientRecord(userRecord, client, uid, pid, packageName, trusted);
773             try {
774                 binder.linkToDeath(clientRecord, 0);
775             } catch (RemoteException ex) {
776                 throw new RuntimeException("Media router client died prematurely.", ex);
777             }
778 
779             if (newUser) {
780                 mUserRecords.put(userId, userRecord);
781                 initializeUserLocked(userRecord);
782             }
783 
784             userRecord.mClientRecords.add(clientRecord);
785             mAllClientRecords.put(binder, clientRecord);
786             initializeClientLocked(clientRecord);
787         }
788     }
789 
790     @GuardedBy("mLock")
registerClientGroupIdLocked(IMediaRouterClient client, String groupId)791     private void registerClientGroupIdLocked(IMediaRouterClient client, String groupId) {
792         final IBinder binder = client.asBinder();
793         ClientRecord clientRecord = mAllClientRecords.get(binder);
794         if (clientRecord == null) {
795             Log.w(TAG, "Ignoring group id register request of a unregistered client.");
796             return;
797         }
798         if (TextUtils.equals(clientRecord.mGroupId, groupId)) {
799             return;
800         }
801         UserRecord userRecord = clientRecord.mUserRecord;
802         if (clientRecord.mGroupId != null) {
803             userRecord.removeFromGroup(clientRecord.mGroupId, clientRecord);
804         }
805         clientRecord.mGroupId = groupId;
806         if (groupId != null) {
807             userRecord.addToGroup(groupId, clientRecord);
808             userRecord
809                     .mHandler
810                     .obtainMessage(UserHandler.MSG_NOTIFY_GROUP_ROUTE_SELECTED, groupId)
811                     .sendToTarget();
812         }
813     }
814 
815     @GuardedBy("mLock")
unregisterClientLocked(IMediaRouterClient client, boolean died)816     private void unregisterClientLocked(IMediaRouterClient client, boolean died) {
817         ClientRecord clientRecord = mAllClientRecords.remove(client.asBinder());
818         if (clientRecord != null) {
819             UserRecord userRecord = clientRecord.mUserRecord;
820             userRecord.mClientRecords.remove(clientRecord);
821             if (clientRecord.mGroupId != null) {
822                 userRecord.removeFromGroup(clientRecord.mGroupId, clientRecord);
823                 clientRecord.mGroupId = null;
824             }
825             disposeClientLocked(clientRecord, died);
826             disposeUserIfNeededLocked(userRecord); // since client removed from user
827         }
828     }
829 
830     @GuardedBy("mLock")
getStateLocked(IMediaRouterClient client)831     private MediaRouterClientState getStateLocked(IMediaRouterClient client) {
832         ClientRecord clientRecord = mAllClientRecords.get(client.asBinder());
833         if (clientRecord != null) {
834             return clientRecord.getState();
835         }
836         return null;
837     }
838 
839     @GuardedBy("mLock")
setDiscoveryRequestLocked(IMediaRouterClient client, int routeTypes, boolean activeScan)840     private void setDiscoveryRequestLocked(IMediaRouterClient client,
841             int routeTypes, boolean activeScan) {
842         final IBinder binder = client.asBinder();
843         ClientRecord clientRecord = mAllClientRecords.get(binder);
844         if (clientRecord != null) {
845             // Only let the system discover remote display routes for now.
846             if (!clientRecord.mTrusted) {
847                 routeTypes &= ~MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY;
848             }
849 
850             if (clientRecord.mRouteTypes != routeTypes
851                     || clientRecord.mActiveScan != activeScan) {
852                 if (DEBUG) {
853                     Slog.d(TAG, clientRecord + ": Set discovery request, routeTypes=0x"
854                             + Integer.toHexString(routeTypes) + ", activeScan=" + activeScan);
855                 }
856                 clientRecord.mRouteTypes = routeTypes;
857                 clientRecord.mActiveScan = activeScan;
858                 clientRecord.mUserRecord.mHandler.sendEmptyMessage(
859                         UserHandler.MSG_UPDATE_DISCOVERY_REQUEST);
860             }
861         }
862     }
863 
864     @GuardedBy("mLock")
setSelectedRouteLocked(IMediaRouterClient client, String routeId, boolean explicit)865     private void setSelectedRouteLocked(IMediaRouterClient client,
866             String routeId, boolean explicit) {
867         ClientRecord clientRecord = mAllClientRecords.get(client.asBinder());
868         if (clientRecord != null) {
869             // In order not to handle system routes as a global route,
870             // set the IDs null if system routes.
871             final String oldRouteId = (mDefaultAudioRouteId.equals(clientRecord.mSelectedRouteId)
872                     || mBluetoothA2dpRouteId.equals(clientRecord.mSelectedRouteId))
873                     ? null : clientRecord.mSelectedRouteId;
874             clientRecord.mSelectedRouteId = routeId;
875             if (mDefaultAudioRouteId.equals(routeId) || mBluetoothA2dpRouteId.equals(routeId)) {
876                 routeId = null;
877             }
878             if (!Objects.equals(routeId, oldRouteId)) {
879                 if (DEBUG) {
880                     Slog.d(TAG, clientRecord + ": Set selected route, routeId=" + routeId
881                             + ", oldRouteId=" + oldRouteId
882                             + ", explicit=" + explicit);
883                 }
884 
885                 // Only let the system connect to new global routes for now.
886                 // A similar check exists in the display manager for wifi display.
887                 if (explicit && clientRecord.mTrusted) {
888                     if (oldRouteId != null) {
889                         clientRecord.mUserRecord.mHandler.obtainMessage(
890                                 UserHandler.MSG_UNSELECT_ROUTE, oldRouteId).sendToTarget();
891                     }
892                     if (routeId != null) {
893                         clientRecord.mUserRecord.mHandler.obtainMessage(
894                                 UserHandler.MSG_SELECT_ROUTE, routeId).sendToTarget();
895                     }
896                     if (clientRecord.mGroupId != null) {
897                         ClientGroup group =
898                                 clientRecord.mUserRecord.mClientGroupMap.get(clientRecord.mGroupId);
899                         if (group != null) {
900                             group.mSelectedRouteId = routeId;
901                             clientRecord
902                                     .mUserRecord
903                                     .mHandler
904                                     .obtainMessage(
905                                             UserHandler.MSG_NOTIFY_GROUP_ROUTE_SELECTED,
906                                             clientRecord.mGroupId)
907                                     .sendToTarget();
908                         }
909                     }
910                 }
911             }
912         }
913     }
914 
915     @GuardedBy("mLock")
requestSetVolumeLocked(IMediaRouterClient client, String routeId, int volume)916     private void requestSetVolumeLocked(IMediaRouterClient client,
917             String routeId, int volume) {
918         final IBinder binder = client.asBinder();
919         ClientRecord clientRecord = mAllClientRecords.get(binder);
920         if (clientRecord != null) {
921             clientRecord.mUserRecord.mHandler.obtainMessage(
922                     UserHandler.MSG_REQUEST_SET_VOLUME, volume, 0, routeId).sendToTarget();
923         }
924     }
925 
926     @GuardedBy("mLock")
requestUpdateVolumeLocked(IMediaRouterClient client, String routeId, int direction)927     private void requestUpdateVolumeLocked(IMediaRouterClient client,
928             String routeId, int direction) {
929         final IBinder binder = client.asBinder();
930         ClientRecord clientRecord = mAllClientRecords.get(binder);
931         if (clientRecord != null) {
932             clientRecord.mUserRecord.mHandler.obtainMessage(
933                     UserHandler.MSG_REQUEST_UPDATE_VOLUME, direction, 0, routeId).sendToTarget();
934         }
935     }
936 
937     @GuardedBy("mLock")
initializeUserLocked(UserRecord userRecord)938     private void initializeUserLocked(UserRecord userRecord) {
939         if (DEBUG) {
940             Slog.d(TAG, userRecord + ": Initialized");
941         }
942         if (isUserActiveLocked(userRecord.mUserId)) {
943             userRecord.mHandler.sendEmptyMessage(UserHandler.MSG_START);
944         }
945     }
946 
947     @GuardedBy("mLock")
disposeUserIfNeededLocked(UserRecord userRecord)948     private void disposeUserIfNeededLocked(UserRecord userRecord) {
949         // If there are no records left and the user is no longer current then go ahead
950         // and purge the user record and all of its associated state.  If the user is current
951         // then leave it alone since we might be connected to a route or want to query
952         // the same route information again soon.
953         if (!isUserActiveLocked(userRecord.mUserId) && userRecord.mClientRecords.isEmpty()) {
954             if (DEBUG) {
955                 Slog.d(TAG, userRecord + ": Disposed");
956             }
957             mUserRecords.remove(userRecord.mUserId);
958             // Note: User already stopped (by switchUser) so no need to send stop message here.
959         }
960     }
961 
962     /**
963      * Returns {@code true} if the given {@code userId} corresponds to the active user or a profile
964      * of the active user, returns {@code false} otherwise.
965      */
966     @GuardedBy("mLock")
isUserActiveLocked(int userId)967     private boolean isUserActiveLocked(int userId) {
968         return mUserManagerInternal.getProfileParentId(userId) == mCurrentActiveUserId;
969     }
970 
971     @GuardedBy("mLock")
initializeClientLocked(ClientRecord clientRecord)972     private void initializeClientLocked(ClientRecord clientRecord) {
973         if (DEBUG) {
974             Slog.d(TAG, clientRecord + ": Registered");
975         }
976     }
977 
978     @GuardedBy("mLock")
disposeClientLocked(ClientRecord clientRecord, boolean died)979     private void disposeClientLocked(ClientRecord clientRecord, boolean died) {
980         if (DEBUG) {
981             if (died) {
982                 Slog.d(TAG, clientRecord + ": Died!");
983             } else {
984                 Slog.d(TAG, clientRecord + ": Unregistered");
985             }
986         }
987         if (clientRecord.mRouteTypes != 0 || clientRecord.mActiveScan) {
988             clientRecord.mUserRecord.mHandler.sendEmptyMessage(
989                     UserHandler.MSG_UPDATE_DISCOVERY_REQUEST);
990         }
991         clientRecord.dispose();
992     }
993 
994     /**
995      * Validates whether the provided package name matches a given uid. Returns false if the package
996      * name is null.
997      */
validatePackageName(int uid, String packageName)998     private boolean validatePackageName(int uid, String packageName) {
999         if (packageName != null) {
1000             String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
1001             if (packageNames != null) {
1002                 for (String n : packageNames) {
1003                     if (n.equals(packageName)) {
1004                         return true;
1005                     }
1006                 }
1007             }
1008         }
1009         return false;
1010     }
1011 
1012     final class MediaRouterServiceBroadcastReceiver extends BroadcastReceiver {
1013         @Override
onReceive(Context context, Intent intent)1014         public void onReceive(Context context, Intent intent) {
1015             if (intent.getAction().equals(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED)) {
1016                 BluetoothDevice btDevice =
1017                         intent.getParcelableExtra(
1018                                 BluetoothDevice.EXTRA_DEVICE,
1019                                 android.bluetooth.BluetoothDevice.class);
1020                 synchronized (mLock) {
1021                     mActiveBluetoothDevice = btDevice;
1022                     mGlobalBluetoothA2dpOn = btDevice != null;
1023                 }
1024             }
1025         }
1026     }
1027 
1028     /**
1029      * Information about a particular client of the media router.
1030      * The contents of this object is guarded by mLock.
1031      */
1032     final class ClientRecord implements DeathRecipient {
1033         public final UserRecord mUserRecord;
1034         public final IMediaRouterClient mClient;
1035         public final int mUid;
1036         public final int mPid;
1037         public final String mPackageName;
1038         public final boolean mTrusted;
1039         public List<String> mControlCategories;
1040 
1041         public int mRouteTypes;
1042         public boolean mActiveScan;
1043         public String mSelectedRouteId;
1044         public String mGroupId;
1045 
ClientRecord( UserRecord userRecord, IMediaRouterClient client, int uid, int pid, @NonNull String packageName, boolean trusted)1046         ClientRecord(
1047                 UserRecord userRecord,
1048                 IMediaRouterClient client,
1049                 int uid,
1050                 int pid,
1051                 @NonNull String packageName,
1052                 boolean trusted) {
1053             mUserRecord = userRecord;
1054             mClient = client;
1055             mUid = uid;
1056             mPid = pid;
1057             mPackageName = packageName;
1058             mTrusted = trusted;
1059         }
1060 
dispose()1061         public void dispose() {
1062             mClient.asBinder().unlinkToDeath(this, 0);
1063         }
1064 
1065         @Override
binderDied()1066         public void binderDied() {
1067             clientDied(this);
1068         }
1069 
getState()1070         MediaRouterClientState getState() {
1071             return mTrusted ? mUserRecord.mRouterState : null;
1072         }
1073 
dump(PrintWriter pw, String prefix)1074         public void dump(PrintWriter pw, String prefix) {
1075             pw.println(prefix + this);
1076 
1077             final String indent = prefix + "  ";
1078             pw.println(indent + "mTrusted=" + mTrusted);
1079             pw.println(indent + "mRouteTypes=0x" + Integer.toHexString(mRouteTypes));
1080             pw.println(indent + "mActiveScan=" + mActiveScan);
1081             pw.println(indent + "mSelectedRouteId=" + mSelectedRouteId);
1082         }
1083 
1084         @Override
toString()1085         public String toString() {
1086             return "Client " + mPackageName + " (pid " + mPid + ")";
1087         }
1088     }
1089 
1090     final class ClientGroup {
1091         public String mSelectedRouteId;
1092         public final List<ClientRecord> mClientRecords = new ArrayList<>();
1093     }
1094 
1095     /**
1096      * Information about a particular user.
1097      * The contents of this object is guarded by mLock.
1098      */
1099     final class UserRecord {
1100         public final int mUserId;
1101         public final ArrayList<ClientRecord> mClientRecords = new ArrayList<>();
1102         public final UserHandler mHandler;
1103         public MediaRouterClientState mRouterState;
1104         private final ArrayMap<String, ClientGroup> mClientGroupMap = new ArrayMap<>();
1105 
UserRecord(int userId)1106         public UserRecord(int userId) {
1107             mUserId = userId;
1108             mHandler = new UserHandler(MediaRouterService.this, this, mLooper);
1109         }
1110 
dump(final PrintWriter pw, String prefix)1111         public void dump(final PrintWriter pw, String prefix) {
1112             pw.println(prefix + this);
1113 
1114             final String indent = prefix + "  ";
1115             final int clientCount = mClientRecords.size();
1116             if (clientCount != 0) {
1117                 for (int i = 0; i < clientCount; i++) {
1118                     mClientRecords.get(i).dump(pw, indent);
1119                 }
1120             } else {
1121                 pw.println(indent + "<no clients>");
1122             }
1123 
1124             pw.println(indent + "State");
1125             pw.println(indent + "mRouterState=" + mRouterState);
1126 
1127             if (!mHandler.runWithScissors(new Runnable() {
1128                 @Override
1129                 public void run() {
1130                     mHandler.dump(pw, indent);
1131                 }
1132             }, 1000)) {
1133                 pw.println(indent + "<could not dump handler state>");
1134             }
1135         }
1136 
addToGroup(String groupId, ClientRecord clientRecord)1137         public void addToGroup(String groupId, ClientRecord clientRecord) {
1138             ClientGroup group = mClientGroupMap.get(groupId);
1139             if (group == null) {
1140                 group = new ClientGroup();
1141                 mClientGroupMap.put(groupId, group);
1142             }
1143             group.mClientRecords.add(clientRecord);
1144         }
1145 
removeFromGroup(String groupId, ClientRecord clientRecord)1146         public void removeFromGroup(String groupId, ClientRecord clientRecord) {
1147             ClientGroup group = mClientGroupMap.get(groupId);
1148             if (group != null) {
1149                 group.mClientRecords.remove(clientRecord);
1150                 if (group.mClientRecords.size() == 0) {
1151                     mClientGroupMap.remove(groupId);
1152                 }
1153             }
1154         }
1155 
1156         @Override
toString()1157         public String toString() {
1158             return "User " + mUserId;
1159         }
1160     }
1161 
1162     /**
1163      * Media router handler
1164      * <p>
1165      * Since remote display providers are designed to be single-threaded by nature,
1166      * this class encapsulates all of the associated functionality and exports state
1167      * to the service as it evolves.
1168      * </p><p>
1169      * This class is currently hardcoded to work with remote display providers but
1170      * it is intended to be eventually extended to support more general route providers
1171      * similar to the support library media router.
1172      * </p>
1173      */
1174     static final class UserHandler extends Handler
1175             implements RemoteDisplayProviderWatcher.Callback,
1176             RemoteDisplayProviderProxy.Callback {
1177         public static final int MSG_START = 1;
1178         public static final int MSG_STOP = 2;
1179         public static final int MSG_UPDATE_DISCOVERY_REQUEST = 3;
1180         public static final int MSG_SELECT_ROUTE = 4;
1181         public static final int MSG_UNSELECT_ROUTE = 5;
1182         public static final int MSG_REQUEST_SET_VOLUME = 6;
1183         public static final int MSG_REQUEST_UPDATE_VOLUME = 7;
1184         private static final int MSG_UPDATE_CLIENT_STATE = 8;
1185         private static final int MSG_CONNECTION_TIMED_OUT = 9;
1186         private static final int MSG_NOTIFY_GROUP_ROUTE_SELECTED = 10;
1187 
1188         private static final int TIMEOUT_REASON_NOT_AVAILABLE = 1;
1189         private static final int TIMEOUT_REASON_CONNECTION_LOST = 2;
1190         private static final int TIMEOUT_REASON_WAITING_FOR_CONNECTING = 3;
1191         private static final int TIMEOUT_REASON_WAITING_FOR_CONNECTED = 4;
1192 
1193         // The relative order of these constants is important and expresses progress
1194         // through the process of connecting to a route.
1195         private static final int PHASE_NOT_AVAILABLE = -1;
1196         private static final int PHASE_NOT_CONNECTED = 0;
1197         private static final int PHASE_CONNECTING = 1;
1198         private static final int PHASE_CONNECTED = 2;
1199 
1200         private final MediaRouterService mService;
1201         private final UserRecord mUserRecord;
1202         private final RemoteDisplayProviderWatcher mWatcher;
1203         private final ArrayList<ProviderRecord> mProviderRecords =
1204                 new ArrayList<ProviderRecord>();
1205         private final ArrayList<IMediaRouterClient> mTempClients =
1206                 new ArrayList<IMediaRouterClient>();
1207 
1208         private boolean mRunning;
1209         private int mDiscoveryMode = RemoteDisplayState.DISCOVERY_MODE_NONE;
1210         private RouteRecord mSelectedRouteRecord;
1211         private int mConnectionPhase = PHASE_NOT_AVAILABLE;
1212         private int mConnectionTimeoutReason;
1213         private long mConnectionTimeoutStartTime;
1214         private boolean mClientStateUpdateScheduled;
1215 
UserHandler(MediaRouterService service, UserRecord userRecord, Looper looper)1216         private UserHandler(MediaRouterService service, UserRecord userRecord, Looper looper) {
1217             super(looper, null, true);
1218             mService = service;
1219             mUserRecord = userRecord;
1220             mWatcher = new RemoteDisplayProviderWatcher(service.mContext, this,
1221                     this, mUserRecord.mUserId);
1222         }
1223 
1224         @Override
handleMessage(Message msg)1225         public void handleMessage(Message msg) {
1226             switch (msg.what) {
1227                 case MSG_START: {
1228                     start();
1229                     break;
1230                 }
1231                 case MSG_STOP: {
1232                     stop();
1233                     break;
1234                 }
1235                 case MSG_UPDATE_DISCOVERY_REQUEST: {
1236                     updateDiscoveryRequest();
1237                     break;
1238                 }
1239                 case MSG_SELECT_ROUTE: {
1240                     selectRoute((String)msg.obj);
1241                     break;
1242                 }
1243                 case MSG_UNSELECT_ROUTE: {
1244                     unselectRoute((String)msg.obj);
1245                     break;
1246                 }
1247                 case MSG_REQUEST_SET_VOLUME: {
1248                     requestSetVolume((String)msg.obj, msg.arg1);
1249                     break;
1250                 }
1251                 case MSG_REQUEST_UPDATE_VOLUME: {
1252                     requestUpdateVolume((String)msg.obj, msg.arg1);
1253                     break;
1254                 }
1255                 case MSG_UPDATE_CLIENT_STATE: {
1256                     updateClientState();
1257                     break;
1258                 }
1259                 case MSG_CONNECTION_TIMED_OUT: {
1260                     connectionTimedOut();
1261                     break;
1262                 }
1263                 case MSG_NOTIFY_GROUP_ROUTE_SELECTED: {
1264                     notifyGroupRouteSelected((String) msg.obj);
1265                     break;
1266                 }
1267             }
1268         }
1269 
dump(PrintWriter pw, String prefix)1270         public void dump(PrintWriter pw, String prefix) {
1271             pw.println(prefix + "Handler");
1272 
1273             final String indent = prefix + "  ";
1274             pw.println(indent + "mRunning=" + mRunning);
1275             pw.println(indent + "mDiscoveryMode=" + mDiscoveryMode);
1276             pw.println(indent + "mSelectedRouteRecord=" + mSelectedRouteRecord);
1277             pw.println(indent + "mConnectionPhase=" + mConnectionPhase);
1278             pw.println(indent + "mConnectionTimeoutReason=" + mConnectionTimeoutReason);
1279             pw.println(indent + "mConnectionTimeoutStartTime=" + (mConnectionTimeoutReason != 0 ?
1280                     TimeUtils.formatUptime(mConnectionTimeoutStartTime) : "<n/a>"));
1281 
1282             mWatcher.dump(pw, prefix);
1283 
1284             final int providerCount = mProviderRecords.size();
1285             if (providerCount != 0) {
1286                 for (int i = 0; i < providerCount; i++) {
1287                     mProviderRecords.get(i).dump(pw, prefix);
1288                 }
1289             } else {
1290                 pw.println(indent + "<no providers>");
1291             }
1292         }
1293 
start()1294         private void start() {
1295             if (!mRunning) {
1296                 mRunning = true;
1297                 mWatcher.start(); // also starts all providers
1298             }
1299         }
1300 
stop()1301         private void stop() {
1302             if (mRunning) {
1303                 mRunning = false;
1304                 unselectSelectedRoute();
1305                 mWatcher.stop(); // also stops all providers
1306             }
1307         }
1308 
updateDiscoveryRequest()1309         private void updateDiscoveryRequest() {
1310             int routeTypes = 0;
1311             boolean activeScan = false;
1312             synchronized (mService.mLock) {
1313                 final int count = mUserRecord.mClientRecords.size();
1314                 for (int i = 0; i < count; i++) {
1315                     ClientRecord clientRecord = mUserRecord.mClientRecords.get(i);
1316                     routeTypes |= clientRecord.mRouteTypes;
1317                     activeScan |= clientRecord.mActiveScan;
1318                 }
1319             }
1320 
1321             final int newDiscoveryMode;
1322             if ((routeTypes & MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY) != 0) {
1323                 if (activeScan) {
1324                     newDiscoveryMode = RemoteDisplayState.DISCOVERY_MODE_ACTIVE;
1325                 } else {
1326                     newDiscoveryMode = RemoteDisplayState.DISCOVERY_MODE_PASSIVE;
1327                 }
1328             } else {
1329                 newDiscoveryMode = RemoteDisplayState.DISCOVERY_MODE_NONE;
1330             }
1331 
1332             if (mDiscoveryMode != newDiscoveryMode) {
1333                 mDiscoveryMode = newDiscoveryMode;
1334                 final int count = mProviderRecords.size();
1335                 for (int i = 0; i < count; i++) {
1336                     mProviderRecords.get(i).getProvider().setDiscoveryMode(mDiscoveryMode);
1337                 }
1338             }
1339         }
1340 
selectRoute(String routeId)1341         private void selectRoute(String routeId) {
1342             if (routeId != null
1343                     && (mSelectedRouteRecord == null
1344                             || !routeId.equals(mSelectedRouteRecord.getUniqueId()))) {
1345                 RouteRecord routeRecord = findRouteRecord(routeId);
1346                 if (routeRecord != null) {
1347                     unselectSelectedRoute();
1348 
1349                     Slog.i(TAG, "Selected route:" + routeRecord);
1350                     mSelectedRouteRecord = routeRecord;
1351                     checkSelectedRouteState();
1352                     routeRecord.getProvider().setSelectedDisplay(routeRecord.getDescriptorId());
1353 
1354                     scheduleUpdateClientState();
1355                 }
1356             }
1357         }
1358 
unselectRoute(String routeId)1359         private void unselectRoute(String routeId) {
1360             if (routeId != null
1361                     && mSelectedRouteRecord != null
1362                     && routeId.equals(mSelectedRouteRecord.getUniqueId())) {
1363                 unselectSelectedRoute();
1364             }
1365         }
1366 
unselectSelectedRoute()1367         private void unselectSelectedRoute() {
1368             if (mSelectedRouteRecord != null) {
1369                 Slog.i(TAG, "Unselected route:" + mSelectedRouteRecord);
1370                 mSelectedRouteRecord.getProvider().setSelectedDisplay(null);
1371                 mSelectedRouteRecord = null;
1372                 checkSelectedRouteState();
1373 
1374                 scheduleUpdateClientState();
1375             }
1376         }
1377 
requestSetVolume(String routeId, int volume)1378         private void requestSetVolume(String routeId, int volume) {
1379             if (mSelectedRouteRecord != null
1380                     && routeId.equals(mSelectedRouteRecord.getUniqueId())) {
1381                 mSelectedRouteRecord.getProvider().setDisplayVolume(volume);
1382             }
1383         }
1384 
requestUpdateVolume(String routeId, int direction)1385         private void requestUpdateVolume(String routeId, int direction) {
1386             if (mSelectedRouteRecord != null
1387                     && routeId.equals(mSelectedRouteRecord.getUniqueId())) {
1388                 mSelectedRouteRecord.getProvider().adjustDisplayVolume(direction);
1389             }
1390         }
1391 
1392         @Override
addProvider(RemoteDisplayProviderProxy provider)1393         public void addProvider(RemoteDisplayProviderProxy provider) {
1394             provider.setCallback(this);
1395             provider.setDiscoveryMode(mDiscoveryMode);
1396             provider.setSelectedDisplay(null); // just to be safe
1397 
1398             ProviderRecord providerRecord = new ProviderRecord(provider);
1399             mProviderRecords.add(providerRecord);
1400             providerRecord.updateDescriptor(provider.getDisplayState());
1401 
1402             scheduleUpdateClientState();
1403         }
1404 
1405         @Override
removeProvider(RemoteDisplayProviderProxy provider)1406         public void removeProvider(RemoteDisplayProviderProxy provider) {
1407             int index = findProviderRecord(provider);
1408             if (index >= 0) {
1409                 ProviderRecord providerRecord = mProviderRecords.remove(index);
1410                 providerRecord.updateDescriptor(null); // mark routes invalid
1411                 provider.setCallback(null);
1412                 provider.setDiscoveryMode(RemoteDisplayState.DISCOVERY_MODE_NONE);
1413 
1414                 checkSelectedRouteState();
1415                 scheduleUpdateClientState();
1416             }
1417         }
1418 
1419         @Override
onDisplayStateChanged(RemoteDisplayProviderProxy provider, RemoteDisplayState state)1420         public void onDisplayStateChanged(RemoteDisplayProviderProxy provider,
1421                 RemoteDisplayState state) {
1422             updateProvider(provider, state);
1423         }
1424 
updateProvider(RemoteDisplayProviderProxy provider, RemoteDisplayState state)1425         private void updateProvider(RemoteDisplayProviderProxy provider,
1426                 RemoteDisplayState state) {
1427             int index = findProviderRecord(provider);
1428             if (index >= 0) {
1429                 ProviderRecord providerRecord = mProviderRecords.get(index);
1430                 if (providerRecord.updateDescriptor(state)) {
1431                     checkSelectedRouteState();
1432                     scheduleUpdateClientState();
1433                 }
1434             }
1435         }
1436 
1437         /**
1438          * This function is called whenever the state of the selected route may have changed.
1439          * It checks the state and updates timeouts or unselects the route as appropriate.
1440          */
checkSelectedRouteState()1441         private void checkSelectedRouteState() {
1442             // Unschedule timeouts when the route is unselected.
1443             if (mSelectedRouteRecord == null) {
1444                 mConnectionPhase = PHASE_NOT_AVAILABLE;
1445                 updateConnectionTimeout(0);
1446                 return;
1447             }
1448 
1449             // Ensure that the route is still present and enabled.
1450             if (!mSelectedRouteRecord.isValid()
1451                     || !mSelectedRouteRecord.isEnabled()) {
1452                 updateConnectionTimeout(TIMEOUT_REASON_NOT_AVAILABLE);
1453                 return;
1454             }
1455 
1456             // Make sure we haven't lost our connection.
1457             final int oldPhase = mConnectionPhase;
1458             mConnectionPhase = getConnectionPhase(mSelectedRouteRecord.getStatus());
1459             if (oldPhase >= PHASE_CONNECTING && mConnectionPhase < PHASE_CONNECTING) {
1460                 updateConnectionTimeout(TIMEOUT_REASON_CONNECTION_LOST);
1461                 return;
1462             }
1463 
1464             // Check the route status.
1465             switch (mConnectionPhase) {
1466                 case PHASE_CONNECTED:
1467                     if (oldPhase != PHASE_CONNECTED) {
1468                         Slog.i(TAG, "Connected to route: " + mSelectedRouteRecord);
1469                     }
1470                     updateConnectionTimeout(0);
1471                     break;
1472                 case PHASE_CONNECTING:
1473                     if (oldPhase != PHASE_CONNECTING) {
1474                         Slog.i(TAG, "Connecting to route: " + mSelectedRouteRecord);
1475                     }
1476                     updateConnectionTimeout(TIMEOUT_REASON_WAITING_FOR_CONNECTED);
1477                     break;
1478                 case PHASE_NOT_CONNECTED:
1479                     updateConnectionTimeout(TIMEOUT_REASON_WAITING_FOR_CONNECTING);
1480                     break;
1481                 case PHASE_NOT_AVAILABLE:
1482                 default:
1483                     updateConnectionTimeout(TIMEOUT_REASON_NOT_AVAILABLE);
1484                     break;
1485             }
1486         }
1487 
updateConnectionTimeout(int reason)1488         private void updateConnectionTimeout(int reason) {
1489             if (reason != mConnectionTimeoutReason) {
1490                 if (mConnectionTimeoutReason != 0) {
1491                     removeMessages(MSG_CONNECTION_TIMED_OUT);
1492                 }
1493                 mConnectionTimeoutReason = reason;
1494                 mConnectionTimeoutStartTime = SystemClock.uptimeMillis();
1495                 switch (reason) {
1496                     case TIMEOUT_REASON_NOT_AVAILABLE:
1497                     case TIMEOUT_REASON_CONNECTION_LOST:
1498                         // Route became unavailable or connection lost.
1499                         // Unselect it immediately.
1500                         sendEmptyMessage(MSG_CONNECTION_TIMED_OUT);
1501                         break;
1502                     case TIMEOUT_REASON_WAITING_FOR_CONNECTING:
1503                         // Waiting for route to start connecting.
1504                         sendEmptyMessageDelayed(MSG_CONNECTION_TIMED_OUT, CONNECTING_TIMEOUT);
1505                         break;
1506                     case TIMEOUT_REASON_WAITING_FOR_CONNECTED:
1507                         // Waiting for route to complete connection.
1508                         sendEmptyMessageDelayed(MSG_CONNECTION_TIMED_OUT, CONNECTED_TIMEOUT);
1509                         break;
1510                 }
1511             }
1512         }
1513 
connectionTimedOut()1514         private void connectionTimedOut() {
1515             if (mConnectionTimeoutReason == 0 || mSelectedRouteRecord == null) {
1516                 // Shouldn't get here.  There must be a bug somewhere.
1517                 Log.wtf(TAG, "Handled connection timeout for no reason.");
1518                 return;
1519             }
1520 
1521             switch (mConnectionTimeoutReason) {
1522                 case TIMEOUT_REASON_NOT_AVAILABLE:
1523                     Slog.i(TAG, "Selected route no longer available: "
1524                             + mSelectedRouteRecord);
1525                     break;
1526                 case TIMEOUT_REASON_CONNECTION_LOST:
1527                     Slog.i(TAG, "Selected route connection lost: "
1528                             + mSelectedRouteRecord);
1529                     break;
1530                 case TIMEOUT_REASON_WAITING_FOR_CONNECTING:
1531                     Slog.i(TAG, "Selected route timed out while waiting for "
1532                             + "connection attempt to begin after "
1533                             + (SystemClock.uptimeMillis() - mConnectionTimeoutStartTime)
1534                             + " ms: " + mSelectedRouteRecord);
1535                     break;
1536                 case TIMEOUT_REASON_WAITING_FOR_CONNECTED:
1537                     Slog.i(TAG, "Selected route timed out while connecting after "
1538                             + (SystemClock.uptimeMillis() - mConnectionTimeoutStartTime)
1539                             + " ms: " + mSelectedRouteRecord);
1540                     break;
1541             }
1542             mConnectionTimeoutReason = 0;
1543 
1544             unselectSelectedRoute();
1545         }
1546 
scheduleUpdateClientState()1547         private void scheduleUpdateClientState() {
1548             if (!mClientStateUpdateScheduled) {
1549                 mClientStateUpdateScheduled = true;
1550                 sendEmptyMessage(MSG_UPDATE_CLIENT_STATE);
1551             }
1552         }
1553 
updateClientState()1554         private void updateClientState() {
1555             mClientStateUpdateScheduled = false;
1556 
1557             // Build a new client state for trusted clients.
1558             MediaRouterClientState routerState = new MediaRouterClientState();
1559             final int providerCount = mProviderRecords.size();
1560             for (int i = 0; i < providerCount; i++) {
1561                 mProviderRecords.get(i).appendClientState(routerState);
1562             }
1563             try {
1564                 synchronized (mService.mLock) {
1565                     // Update the UserRecord.
1566                     mUserRecord.mRouterState = routerState;
1567 
1568                     // Collect all clients.
1569                     final int count = mUserRecord.mClientRecords.size();
1570                     for (int i = 0; i < count; i++) {
1571                         mTempClients.add(mUserRecord.mClientRecords.get(i).mClient);
1572                     }
1573                 }
1574 
1575                 // Notify all clients (outside of the lock).
1576                 final int count = mTempClients.size();
1577                 for (int i = 0; i < count; i++) {
1578                     try {
1579                         mTempClients.get(i).onStateChanged();
1580                     } catch (RemoteException ex) {
1581                         Slog.w(TAG, "Failed to call onStateChanged. Client probably died.");
1582                     }
1583                 }
1584             } finally {
1585                 // Clear the list in preparation for the next time.
1586                 mTempClients.clear();
1587             }
1588         }
1589 
notifyGroupRouteSelected(String groupId)1590         private void notifyGroupRouteSelected(String groupId) {
1591             try {
1592                 String selectedRouteId;
1593                 synchronized (mService.mLock) {
1594                     ClientGroup group = mUserRecord.mClientGroupMap.get(groupId);
1595                     if (group == null) {
1596                         return;
1597                     }
1598                     selectedRouteId = group.mSelectedRouteId;
1599                     final int count = group.mClientRecords.size();
1600                     for (int i = 0; i < count; i++) {
1601                         ClientRecord clientRecord = group.mClientRecords.get(i);
1602                         if (!TextUtils.equals(selectedRouteId, clientRecord.mSelectedRouteId)) {
1603                             mTempClients.add(clientRecord.mClient);
1604                         }
1605                     }
1606                 }
1607 
1608                 final int count = mTempClients.size();
1609                 for (int i = 0; i < count; i++) {
1610                     try {
1611                         mTempClients.get(i).onGroupRouteSelected(selectedRouteId);
1612                     } catch (RemoteException ex) {
1613                         Slog.w(TAG, "Failed to call onSelectedRouteChanged. Client probably died.");
1614                     }
1615                 }
1616             } finally {
1617                 mTempClients.clear();
1618             }
1619         }
1620 
findProviderRecord(RemoteDisplayProviderProxy provider)1621         private int findProviderRecord(RemoteDisplayProviderProxy provider) {
1622             final int count = mProviderRecords.size();
1623             for (int i = 0; i < count; i++) {
1624                 ProviderRecord record = mProviderRecords.get(i);
1625                 if (record.getProvider() == provider) {
1626                     return i;
1627                 }
1628             }
1629             return -1;
1630         }
1631 
findRouteRecord(String uniqueId)1632         private RouteRecord findRouteRecord(String uniqueId) {
1633             final int count = mProviderRecords.size();
1634             for (int i = 0; i < count; i++) {
1635                 RouteRecord record = mProviderRecords.get(i).findRouteByUniqueId(uniqueId);
1636                 if (record != null) {
1637                     return record;
1638                 }
1639             }
1640             return null;
1641         }
1642 
getConnectionPhase(int status)1643         private static int getConnectionPhase(int status) {
1644             switch (status) {
1645                 case MediaRouter.RouteInfo.STATUS_NONE:
1646                 case MediaRouter.RouteInfo.STATUS_CONNECTED:
1647                     return PHASE_CONNECTED;
1648                 case MediaRouter.RouteInfo.STATUS_CONNECTING:
1649                     return PHASE_CONNECTING;
1650                 case MediaRouter.RouteInfo.STATUS_SCANNING:
1651                 case MediaRouter.RouteInfo.STATUS_AVAILABLE:
1652                     return PHASE_NOT_CONNECTED;
1653                 case MediaRouter.RouteInfo.STATUS_NOT_AVAILABLE:
1654                 case MediaRouter.RouteInfo.STATUS_IN_USE:
1655                 default:
1656                     return PHASE_NOT_AVAILABLE;
1657             }
1658         }
1659 
1660         static final class ProviderRecord {
1661             private final RemoteDisplayProviderProxy mProvider;
1662             private final String mUniquePrefix;
1663             private final ArrayList<RouteRecord> mRoutes = new ArrayList<RouteRecord>();
1664             private RemoteDisplayState mDescriptor;
1665 
ProviderRecord(RemoteDisplayProviderProxy provider)1666             public ProviderRecord(RemoteDisplayProviderProxy provider) {
1667                 mProvider = provider;
1668                 mUniquePrefix = provider.getFlattenedComponentName() + ":";
1669             }
1670 
getProvider()1671             public RemoteDisplayProviderProxy getProvider() {
1672                 return mProvider;
1673             }
1674 
getUniquePrefix()1675             public String getUniquePrefix() {
1676                 return mUniquePrefix;
1677             }
1678 
updateDescriptor(RemoteDisplayState descriptor)1679             public boolean updateDescriptor(RemoteDisplayState descriptor) {
1680                 boolean changed = false;
1681                 if (mDescriptor != descriptor) {
1682                     mDescriptor = descriptor;
1683 
1684                     // Update all existing routes and reorder them to match
1685                     // the order of their descriptors.
1686                     int targetIndex = 0;
1687                     if (descriptor != null) {
1688                         if (descriptor.isValid()) {
1689                             final List<RemoteDisplayInfo> routeDescriptors = descriptor.displays;
1690                             final int routeCount = routeDescriptors.size();
1691                             for (int i = 0; i < routeCount; i++) {
1692                                 final RemoteDisplayInfo routeDescriptor =
1693                                         routeDescriptors.get(i);
1694                                 final String descriptorId = routeDescriptor.id;
1695                                 final int sourceIndex = findRouteByDescriptorId(descriptorId);
1696                                 if (sourceIndex < 0) {
1697                                     // Add the route to the provider.
1698                                     String uniqueId = assignRouteUniqueId(descriptorId);
1699                                     RouteRecord route =
1700                                             new RouteRecord(this, descriptorId, uniqueId);
1701                                     mRoutes.add(targetIndex++, route);
1702                                     route.updateDescriptor(routeDescriptor);
1703                                     changed = true;
1704                                 } else if (sourceIndex < targetIndex) {
1705                                     // Ignore route with duplicate id.
1706                                     Slog.w(TAG, "Ignoring route descriptor with duplicate id: "
1707                                             + routeDescriptor);
1708                                 } else {
1709                                     // Reorder existing route within the list.
1710                                     RouteRecord route = mRoutes.get(sourceIndex);
1711                                     Collections.swap(mRoutes, sourceIndex, targetIndex++);
1712                                     changed |= route.updateDescriptor(routeDescriptor);
1713                                 }
1714                             }
1715                         } else {
1716                             Slog.w(TAG, "Ignoring invalid descriptor from media route provider: "
1717                                     + mProvider.getFlattenedComponentName());
1718                         }
1719                     }
1720 
1721                     // Dispose all remaining routes that do not have matching descriptors.
1722                     for (int i = mRoutes.size() - 1; i >= targetIndex; i--) {
1723                         RouteRecord route = mRoutes.remove(i);
1724                         route.updateDescriptor(null); // mark route invalid
1725                         changed = true;
1726                     }
1727                 }
1728                 return changed;
1729             }
1730 
appendClientState(MediaRouterClientState state)1731             public void appendClientState(MediaRouterClientState state) {
1732                 final int routeCount = mRoutes.size();
1733                 for (int i = 0; i < routeCount; i++) {
1734                     state.routes.add(mRoutes.get(i).getInfo());
1735                 }
1736             }
1737 
findRouteByUniqueId(String uniqueId)1738             public RouteRecord findRouteByUniqueId(String uniqueId) {
1739                 final int routeCount = mRoutes.size();
1740                 for (int i = 0; i < routeCount; i++) {
1741                     RouteRecord route = mRoutes.get(i);
1742                     if (route.getUniqueId().equals(uniqueId)) {
1743                         return route;
1744                     }
1745                 }
1746                 return null;
1747             }
1748 
findRouteByDescriptorId(String descriptorId)1749             private int findRouteByDescriptorId(String descriptorId) {
1750                 final int routeCount = mRoutes.size();
1751                 for (int i = 0; i < routeCount; i++) {
1752                     RouteRecord route = mRoutes.get(i);
1753                     if (route.getDescriptorId().equals(descriptorId)) {
1754                         return i;
1755                     }
1756                 }
1757                 return -1;
1758             }
1759 
dump(PrintWriter pw, String prefix)1760             public void dump(PrintWriter pw, String prefix) {
1761                 pw.println(prefix + this);
1762 
1763                 final String indent = prefix + "  ";
1764                 mProvider.dump(pw, indent);
1765 
1766                 final int routeCount = mRoutes.size();
1767                 if (routeCount != 0) {
1768                     for (int i = 0; i < routeCount; i++) {
1769                         mRoutes.get(i).dump(pw, indent);
1770                     }
1771                 } else {
1772                     pw.println(indent + "<no routes>");
1773                 }
1774             }
1775 
1776             @Override
toString()1777             public String toString() {
1778                 return "Provider " + mProvider.getFlattenedComponentName();
1779             }
1780 
assignRouteUniqueId(String descriptorId)1781             private String assignRouteUniqueId(String descriptorId) {
1782                 return mUniquePrefix + descriptorId;
1783             }
1784         }
1785 
1786         static final class RouteRecord {
1787             private final ProviderRecord mProviderRecord;
1788             private final String mDescriptorId;
1789             private final MediaRouterClientState.RouteInfo mMutableInfo;
1790             private MediaRouterClientState.RouteInfo mImmutableInfo;
1791             private RemoteDisplayInfo mDescriptor;
1792 
RouteRecord(ProviderRecord providerRecord, String descriptorId, String uniqueId)1793             public RouteRecord(ProviderRecord providerRecord,
1794                     String descriptorId, String uniqueId) {
1795                 mProviderRecord = providerRecord;
1796                 mDescriptorId = descriptorId;
1797                 mMutableInfo = new MediaRouterClientState.RouteInfo(uniqueId);
1798             }
1799 
getProvider()1800             public RemoteDisplayProviderProxy getProvider() {
1801                 return mProviderRecord.getProvider();
1802             }
1803 
getProviderRecord()1804             public ProviderRecord getProviderRecord() {
1805                 return mProviderRecord;
1806             }
1807 
getDescriptorId()1808             public String getDescriptorId() {
1809                 return mDescriptorId;
1810             }
1811 
getUniqueId()1812             public String getUniqueId() {
1813                 return mMutableInfo.id;
1814             }
1815 
getInfo()1816             public MediaRouterClientState.RouteInfo getInfo() {
1817                 if (mImmutableInfo == null) {
1818                     mImmutableInfo = new MediaRouterClientState.RouteInfo(mMutableInfo);
1819                 }
1820                 return mImmutableInfo;
1821             }
1822 
isValid()1823             public boolean isValid() {
1824                 return mDescriptor != null;
1825             }
1826 
isEnabled()1827             public boolean isEnabled() {
1828                 return mMutableInfo.enabled;
1829             }
1830 
getStatus()1831             public int getStatus() {
1832                 return mMutableInfo.statusCode;
1833             }
1834 
updateDescriptor(RemoteDisplayInfo descriptor)1835             public boolean updateDescriptor(RemoteDisplayInfo descriptor) {
1836                 boolean changed = false;
1837                 if (mDescriptor != descriptor) {
1838                     mDescriptor = descriptor;
1839                     if (descriptor != null) {
1840                         final String name = computeName(descriptor);
1841                         if (!Objects.equals(mMutableInfo.name, name)) {
1842                             mMutableInfo.name = name;
1843                             changed = true;
1844                         }
1845                         final String description = computeDescription(descriptor);
1846                         if (!Objects.equals(mMutableInfo.description, description)) {
1847                             mMutableInfo.description = description;
1848                             changed = true;
1849                         }
1850                         final int supportedTypes = computeSupportedTypes(descriptor);
1851                         if (mMutableInfo.supportedTypes != supportedTypes) {
1852                             mMutableInfo.supportedTypes = supportedTypes;
1853                             changed = true;
1854                         }
1855                         final boolean enabled = computeEnabled(descriptor);
1856                         if (mMutableInfo.enabled != enabled) {
1857                             mMutableInfo.enabled = enabled;
1858                             changed = true;
1859                         }
1860                         final int statusCode = computeStatusCode(descriptor);
1861                         if (mMutableInfo.statusCode != statusCode) {
1862                             mMutableInfo.statusCode = statusCode;
1863                             changed = true;
1864                         }
1865                         final int playbackType = computePlaybackType(descriptor);
1866                         if (mMutableInfo.playbackType != playbackType) {
1867                             mMutableInfo.playbackType = playbackType;
1868                             changed = true;
1869                         }
1870                         final int playbackStream = computePlaybackStream(descriptor);
1871                         if (mMutableInfo.playbackStream != playbackStream) {
1872                             mMutableInfo.playbackStream = playbackStream;
1873                             changed = true;
1874                         }
1875                         final int volume = computeVolume(descriptor);
1876                         if (mMutableInfo.volume != volume) {
1877                             mMutableInfo.volume = volume;
1878                             changed = true;
1879                         }
1880                         final int volumeMax = computeVolumeMax(descriptor);
1881                         if (mMutableInfo.volumeMax != volumeMax) {
1882                             mMutableInfo.volumeMax = volumeMax;
1883                             changed = true;
1884                         }
1885                         final int volumeHandling = computeVolumeHandling(descriptor);
1886                         if (mMutableInfo.volumeHandling != volumeHandling) {
1887                             mMutableInfo.volumeHandling = volumeHandling;
1888                             changed = true;
1889                         }
1890                         final int presentationDisplayId = computePresentationDisplayId(descriptor);
1891                         if (mMutableInfo.presentationDisplayId != presentationDisplayId) {
1892                             mMutableInfo.presentationDisplayId = presentationDisplayId;
1893                             changed = true;
1894                         }
1895                     }
1896                 }
1897                 if (changed) {
1898                     mImmutableInfo = null;
1899                 }
1900                 return changed;
1901             }
1902 
dump(PrintWriter pw, String prefix)1903             public void dump(PrintWriter pw, String prefix) {
1904                 pw.println(prefix + this);
1905 
1906                 final String indent = prefix + "  ";
1907                 pw.println(indent + "mMutableInfo=" + mMutableInfo);
1908                 pw.println(indent + "mDescriptorId=" + mDescriptorId);
1909                 pw.println(indent + "mDescriptor=" + mDescriptor);
1910             }
1911 
1912             @Override
toString()1913             public String toString() {
1914                 return "Route " + mMutableInfo.name + " (" + mMutableInfo.id + ")";
1915             }
1916 
computeName(RemoteDisplayInfo descriptor)1917             private static String computeName(RemoteDisplayInfo descriptor) {
1918                 // Note that isValid() already ensures the name is non-empty.
1919                 return descriptor.name;
1920             }
1921 
computeDescription(RemoteDisplayInfo descriptor)1922             private static String computeDescription(RemoteDisplayInfo descriptor) {
1923                 final String description = descriptor.description;
1924                 return TextUtils.isEmpty(description) ? null : description;
1925             }
1926 
computeSupportedTypes(RemoteDisplayInfo descriptor)1927             private static int computeSupportedTypes(RemoteDisplayInfo descriptor) {
1928                 return MediaRouter.ROUTE_TYPE_LIVE_AUDIO
1929                         | MediaRouter.ROUTE_TYPE_LIVE_VIDEO
1930                         | MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY;
1931             }
1932 
computeEnabled(RemoteDisplayInfo descriptor)1933             private static boolean computeEnabled(RemoteDisplayInfo descriptor) {
1934                 switch (descriptor.status) {
1935                     case RemoteDisplayInfo.STATUS_CONNECTED:
1936                     case RemoteDisplayInfo.STATUS_CONNECTING:
1937                     case RemoteDisplayInfo.STATUS_AVAILABLE:
1938                         return true;
1939                     default:
1940                         return false;
1941                 }
1942             }
1943 
computeStatusCode(RemoteDisplayInfo descriptor)1944             private static int computeStatusCode(RemoteDisplayInfo descriptor) {
1945                 switch (descriptor.status) {
1946                     case RemoteDisplayInfo.STATUS_NOT_AVAILABLE:
1947                         return MediaRouter.RouteInfo.STATUS_NOT_AVAILABLE;
1948                     case RemoteDisplayInfo.STATUS_AVAILABLE:
1949                         return MediaRouter.RouteInfo.STATUS_AVAILABLE;
1950                     case RemoteDisplayInfo.STATUS_IN_USE:
1951                         return MediaRouter.RouteInfo.STATUS_IN_USE;
1952                     case RemoteDisplayInfo.STATUS_CONNECTING:
1953                         return MediaRouter.RouteInfo.STATUS_CONNECTING;
1954                     case RemoteDisplayInfo.STATUS_CONNECTED:
1955                         return MediaRouter.RouteInfo.STATUS_CONNECTED;
1956                     default:
1957                         return MediaRouter.RouteInfo.STATUS_NONE;
1958                 }
1959             }
1960 
computePlaybackType(RemoteDisplayInfo descriptor)1961             private static int computePlaybackType(RemoteDisplayInfo descriptor) {
1962                 return MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE;
1963             }
1964 
computePlaybackStream(RemoteDisplayInfo descriptor)1965             private static int computePlaybackStream(RemoteDisplayInfo descriptor) {
1966                 return AudioSystem.STREAM_MUSIC;
1967             }
1968 
computeVolume(RemoteDisplayInfo descriptor)1969             private static int computeVolume(RemoteDisplayInfo descriptor) {
1970                 final int volume = descriptor.volume;
1971                 final int volumeMax = descriptor.volumeMax;
1972                 if (volume < 0) {
1973                     return 0;
1974                 } else if (volume > volumeMax) {
1975                     return volumeMax;
1976                 }
1977                 return volume;
1978             }
1979 
computeVolumeMax(RemoteDisplayInfo descriptor)1980             private static int computeVolumeMax(RemoteDisplayInfo descriptor) {
1981                 final int volumeMax = descriptor.volumeMax;
1982                 return volumeMax > 0 ? volumeMax : 0;
1983             }
1984 
computeVolumeHandling(RemoteDisplayInfo descriptor)1985             private static int computeVolumeHandling(RemoteDisplayInfo descriptor) {
1986                 final int volumeHandling = descriptor.volumeHandling;
1987                 switch (volumeHandling) {
1988                     case RemoteDisplayInfo.PLAYBACK_VOLUME_VARIABLE:
1989                         return MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE;
1990                     case RemoteDisplayInfo.PLAYBACK_VOLUME_FIXED:
1991                     default:
1992                         return MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED;
1993                 }
1994             }
1995 
computePresentationDisplayId(RemoteDisplayInfo descriptor)1996             private static int computePresentationDisplayId(RemoteDisplayInfo descriptor) {
1997                 // The MediaRouter class validates that the id corresponds to an extant
1998                 // presentation display.  So all we do here is canonicalize the null case.
1999                 final int displayId = descriptor.presentationDisplayId;
2000                 return displayId < 0 ? -1 : displayId;
2001             }
2002         }
2003     }
2004 
2005     private class AudioPlayerActiveStateChangedListenerImpl
2006             implements AudioPlayerStateMonitor.OnAudioPlayerActiveStateChangedListener {
2007 
2008         private static final long WAIT_MS = 500;
2009         private final Runnable mRestoreBluetoothA2dpRunnable =
2010                 MediaRouterService.this::restoreBluetoothA2dp;
2011 
2012         @Override
onAudioPlayerActiveStateChanged( @onNull AudioPlaybackConfiguration config, boolean isRemoved)2013         public void onAudioPlayerActiveStateChanged(
2014                 @NonNull AudioPlaybackConfiguration config, boolean isRemoved) {
2015             boolean active = !isRemoved && config.isActive();
2016             int uid = config.getClientUid();
2017 
2018             int idx = mActivePlayerMinPriorityQueue.indexOf(config.getPlayerInterfaceId());
2019             // Keep the latest active player and its uid at the end of the queue.
2020             if (idx >= 0) {
2021                 mActivePlayerMinPriorityQueue.remove(idx);
2022                 mActivePlayerUidMinPriorityQueue.remove(idx);
2023             }
2024 
2025             int restoreUid = -1;
2026             if (active) {
2027                 mActivePlayerMinPriorityQueue.add(config.getPlayerInterfaceId());
2028                 mActivePlayerUidMinPriorityQueue.add(uid);
2029                 restoreUid = uid;
2030             } else if (mActivePlayerUidMinPriorityQueue.size() > 0) {
2031                 restoreUid =
2032                         mActivePlayerUidMinPriorityQueue.get(
2033                                 mActivePlayerUidMinPriorityQueue.size() - 1);
2034             }
2035 
2036             mHandler.removeCallbacks(mRestoreBluetoothA2dpRunnable);
2037             if (restoreUid >= 0) {
2038                 restoreRoute(restoreUid);
2039                 if (DEBUG) {
2040                     Slog.d(
2041                             TAG,
2042                             "onAudioPlayerActiveStateChanged: "
2043                                     + "uid="
2044                                     + uid
2045                                     + ", active="
2046                                     + active
2047                                     + ", restoreUid="
2048                                     + restoreUid);
2049                 }
2050             } else {
2051                 mHandler.postDelayed(mRestoreBluetoothA2dpRunnable, WAIT_MS);
2052                 if (DEBUG) {
2053                     Slog.d(
2054                             TAG,
2055                             "onAudioPlayerActiveStateChanged: "
2056                                     + "uid="
2057                                     + uid
2058                                     + ", active="
2059                                     + active
2060                                     + ", delaying");
2061                 }
2062             }
2063         }
2064     }
2065 
2066     private class AudioRoutesObserverImpl extends IAudioRoutesObserver.Stub {
2067 
2068         private static final int HEADSET_FLAGS =
2069                 AudioRoutesInfo.MAIN_HEADSET
2070                         | AudioRoutesInfo.MAIN_HEADPHONES
2071                         | AudioRoutesInfo.MAIN_USB;
2072 
2073         @Override
dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes)2074         public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
2075             synchronized (mLock) {
2076                 if (newRoutes.mainType != mAudioRouteMainType) {
2077                     if ((newRoutes.mainType & HEADSET_FLAGS) == 0) {
2078                         // headset was plugged out.
2079                         mGlobalBluetoothA2dpOn =
2080                                 newRoutes.bluetoothName != null || mActiveBluetoothDevice != null;
2081                     } else {
2082                         // headset was plugged in.
2083                         mGlobalBluetoothA2dpOn = false;
2084                     }
2085                     mAudioRouteMainType = newRoutes.mainType;
2086                 }
2087                 // The new audio routes info could be delivered with several seconds delay.
2088                 // In order to avoid such delay, Bluetooth device info will be updated
2089                 // via MediaRouterServiceBroadcastReceiver.
2090             }
2091         }
2092     }
2093 }
2094