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