1 /* 2 * Copyright 2019 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_SERVICE; 20 import static android.content.Intent.ACTION_SCREEN_OFF; 21 import static android.content.Intent.ACTION_SCREEN_ON; 22 import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR; 23 import static android.media.MediaRouter2Utils.getOriginalId; 24 import static android.media.MediaRouter2Utils.getProviderId; 25 26 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; 27 28 import android.Manifest; 29 import android.annotation.NonNull; 30 import android.annotation.Nullable; 31 import android.app.ActivityManager; 32 import android.content.BroadcastReceiver; 33 import android.content.Context; 34 import android.content.Intent; 35 import android.content.IntentFilter; 36 import android.content.pm.PackageManager; 37 import android.media.IMediaRouter2; 38 import android.media.IMediaRouter2Manager; 39 import android.media.MediaRoute2Info; 40 import android.media.MediaRoute2ProviderInfo; 41 import android.media.MediaRoute2ProviderService; 42 import android.media.MediaRouter2Manager; 43 import android.media.RouteDiscoveryPreference; 44 import android.media.RoutingSessionInfo; 45 import android.os.Binder; 46 import android.os.Bundle; 47 import android.os.Handler; 48 import android.os.IBinder; 49 import android.os.Looper; 50 import android.os.PowerManager; 51 import android.os.RemoteException; 52 import android.os.UserHandle; 53 import android.text.TextUtils; 54 import android.util.ArrayMap; 55 import android.util.Log; 56 import android.util.Slog; 57 import android.util.SparseArray; 58 59 import com.android.internal.annotations.GuardedBy; 60 import com.android.internal.util.function.pooled.PooledLambda; 61 import com.android.server.LocalServices; 62 import com.android.server.pm.UserManagerInternal; 63 64 import java.io.PrintWriter; 65 import java.lang.ref.WeakReference; 66 import java.util.ArrayList; 67 import java.util.Collection; 68 import java.util.Collections; 69 import java.util.HashSet; 70 import java.util.List; 71 import java.util.Map; 72 import java.util.Objects; 73 import java.util.Set; 74 import java.util.concurrent.CopyOnWriteArrayList; 75 import java.util.concurrent.atomic.AtomicInteger; 76 import java.util.stream.Collectors; 77 78 /** 79 * Implements features related to {@link android.media.MediaRouter2} and 80 * {@link android.media.MediaRouter2Manager}. 81 */ 82 class MediaRouter2ServiceImpl { 83 private static final String TAG = "MR2ServiceImpl"; 84 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 85 86 // TODO: (In Android S or later) if we add callback methods for generic failures 87 // in MediaRouter2, remove this constant and replace the usages with the real request IDs. 88 private static final long DUMMY_REQUEST_ID = -1; 89 private static final int PACKAGE_IMPORTANCE_FOR_DISCOVERY = IMPORTANCE_FOREGROUND_SERVICE; 90 91 private final Context mContext; 92 private final UserManagerInternal mUserManagerInternal; 93 private final Object mLock = new Object(); 94 final AtomicInteger mNextRouterOrManagerId = new AtomicInteger(1); 95 final ActivityManager mActivityManager; 96 final PowerManager mPowerManager; 97 98 @GuardedBy("mLock") 99 private final SparseArray<UserRecord> mUserRecords = new SparseArray<>(); 100 @GuardedBy("mLock") 101 private final ArrayMap<IBinder, RouterRecord> mAllRouterRecords = new ArrayMap<>(); 102 @GuardedBy("mLock") 103 private final ArrayMap<IBinder, ManagerRecord> mAllManagerRecords = new ArrayMap<>(); 104 @GuardedBy("mLock") 105 private int mCurrentActiveUserId = -1; 106 107 private final ActivityManager.OnUidImportanceListener mOnUidImportanceListener = 108 (uid, importance) -> { 109 synchronized (mLock) { 110 final int count = mUserRecords.size(); 111 for (int i = 0; i < count; i++) { 112 mUserRecords.valueAt(i).mHandler.maybeUpdateDiscoveryPreferenceForUid(uid); 113 } 114 } 115 }; 116 117 private final BroadcastReceiver mScreenOnOffReceiver = new BroadcastReceiver() { 118 @Override 119 public void onReceive(Context context, Intent intent) { 120 synchronized (mLock) { 121 final int count = mUserRecords.size(); 122 for (int i = 0; i < count; i++) { 123 UserHandler userHandler = mUserRecords.valueAt(i).mHandler; 124 userHandler.sendMessage(PooledLambda.obtainMessage( 125 UserHandler::updateDiscoveryPreferenceOnHandler, userHandler)); 126 } 127 } 128 } 129 }; 130 MediaRouter2ServiceImpl(Context context)131 /* package */ MediaRouter2ServiceImpl(Context context) { 132 mContext = context; 133 mActivityManager = mContext.getSystemService(ActivityManager.class); 134 mActivityManager.addOnUidImportanceListener(mOnUidImportanceListener, 135 PACKAGE_IMPORTANCE_FOR_DISCOVERY); 136 mPowerManager = mContext.getSystemService(PowerManager.class); 137 mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); 138 139 IntentFilter screenOnOffIntentFilter = new IntentFilter(); 140 screenOnOffIntentFilter.addAction(ACTION_SCREEN_ON); 141 screenOnOffIntentFilter.addAction(ACTION_SCREEN_OFF); 142 143 mContext.registerReceiver(mScreenOnOffReceiver, screenOnOffIntentFilter); 144 } 145 146 //////////////////////////////////////////////////////////////// 147 //// Calls from MediaRouter2 148 //// - Should not have @NonNull/@Nullable on any arguments 149 //////////////////////////////////////////////////////////////// 150 151 @NonNull enforceMediaContentControlPermission()152 public void enforceMediaContentControlPermission() { 153 final int pid = Binder.getCallingPid(); 154 final int uid = Binder.getCallingUid(); 155 final long token = Binder.clearCallingIdentity(); 156 157 try { 158 mContext.enforcePermission(Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid, 159 "Must hold MEDIA_CONTENT_CONTROL permission."); 160 } finally { 161 Binder.restoreCallingIdentity(token); 162 } 163 } 164 165 @NonNull getSystemRoutes()166 public List<MediaRoute2Info> getSystemRoutes() { 167 final int uid = Binder.getCallingUid(); 168 final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); 169 final boolean hasModifyAudioRoutingPermission = mContext.checkCallingOrSelfPermission( 170 android.Manifest.permission.MODIFY_AUDIO_ROUTING) 171 == PackageManager.PERMISSION_GRANTED; 172 173 final long token = Binder.clearCallingIdentity(); 174 try { 175 Collection<MediaRoute2Info> systemRoutes; 176 synchronized (mLock) { 177 UserRecord userRecord = getOrCreateUserRecordLocked(userId); 178 if (hasModifyAudioRoutingPermission) { 179 MediaRoute2ProviderInfo providerInfo = 180 userRecord.mHandler.mSystemProvider.getProviderInfo(); 181 if (providerInfo != null) { 182 systemRoutes = providerInfo.getRoutes(); 183 } else { 184 systemRoutes = Collections.emptyList(); 185 } 186 } else { 187 systemRoutes = new ArrayList<>(); 188 systemRoutes.add(userRecord.mHandler.mSystemProvider.getDefaultRoute()); 189 } 190 } 191 return new ArrayList<>(systemRoutes); 192 } finally { 193 Binder.restoreCallingIdentity(token); 194 } 195 } 196 197 @NonNull getSystemSessionInfo( @ullable String packageName, boolean setDeviceRouteSelected)198 public RoutingSessionInfo getSystemSessionInfo( 199 @Nullable String packageName, boolean setDeviceRouteSelected) { 200 final int uid = Binder.getCallingUid(); 201 final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); 202 final boolean hasModifyAudioRoutingPermission = mContext.checkCallingOrSelfPermission( 203 android.Manifest.permission.MODIFY_AUDIO_ROUTING) 204 == PackageManager.PERMISSION_GRANTED; 205 206 final long token = Binder.clearCallingIdentity(); 207 try { 208 RoutingSessionInfo systemSessionInfo = null; 209 synchronized (mLock) { 210 UserRecord userRecord = getOrCreateUserRecordLocked(userId); 211 List<RoutingSessionInfo> sessionInfos; 212 if (hasModifyAudioRoutingPermission) { 213 if (setDeviceRouteSelected) { 214 systemSessionInfo = userRecord.mHandler.mSystemProvider 215 .generateDeviceRouteSelectedSessionInfo(packageName); 216 } else { 217 sessionInfos = userRecord.mHandler.mSystemProvider.getSessionInfos(); 218 if (sessionInfos != null && !sessionInfos.isEmpty()) { 219 systemSessionInfo = new RoutingSessionInfo.Builder(sessionInfos.get(0)) 220 .setClientPackageName(packageName).build(); 221 } else { 222 Slog.w(TAG, "System provider does not have any session info."); 223 } 224 } 225 } else { 226 systemSessionInfo = new RoutingSessionInfo.Builder( 227 userRecord.mHandler.mSystemProvider.getDefaultSessionInfo()) 228 .setClientPackageName(packageName).build(); 229 } 230 } 231 return systemSessionInfo; 232 } finally { 233 Binder.restoreCallingIdentity(token); 234 } 235 } 236 registerRouter2(IMediaRouter2 router, String packageName)237 public void registerRouter2(IMediaRouter2 router, String packageName) { 238 Objects.requireNonNull(router, "router must not be null"); 239 if (TextUtils.isEmpty(packageName)) { 240 throw new IllegalArgumentException("packageName must not be empty"); 241 } 242 243 final int uid = Binder.getCallingUid(); 244 final int pid = Binder.getCallingPid(); 245 final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); 246 final boolean hasConfigureWifiDisplayPermission = mContext.checkCallingOrSelfPermission( 247 android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) 248 == PackageManager.PERMISSION_GRANTED; 249 final boolean hasModifyAudioRoutingPermission = mContext.checkCallingOrSelfPermission( 250 android.Manifest.permission.MODIFY_AUDIO_ROUTING) 251 == PackageManager.PERMISSION_GRANTED; 252 253 final long token = Binder.clearCallingIdentity(); 254 try { 255 synchronized (mLock) { 256 registerRouter2Locked(router, uid, pid, packageName, userId, 257 hasConfigureWifiDisplayPermission, hasModifyAudioRoutingPermission); 258 } 259 } finally { 260 Binder.restoreCallingIdentity(token); 261 } 262 } 263 unregisterRouter2(IMediaRouter2 router)264 public void unregisterRouter2(IMediaRouter2 router) { 265 Objects.requireNonNull(router, "router must not be null"); 266 267 final long token = Binder.clearCallingIdentity(); 268 try { 269 synchronized (mLock) { 270 unregisterRouter2Locked(router, false); 271 } 272 } finally { 273 Binder.restoreCallingIdentity(token); 274 } 275 } 276 setDiscoveryRequestWithRouter2(IMediaRouter2 router, RouteDiscoveryPreference preference)277 public void setDiscoveryRequestWithRouter2(IMediaRouter2 router, 278 RouteDiscoveryPreference preference) { 279 Objects.requireNonNull(router, "router must not be null"); 280 Objects.requireNonNull(preference, "preference must not be null"); 281 282 final long token = Binder.clearCallingIdentity(); 283 try { 284 synchronized (mLock) { 285 RouterRecord routerRecord = mAllRouterRecords.get(router.asBinder()); 286 if (routerRecord == null) { 287 Slog.w(TAG, "Ignoring updating discoveryRequest of null routerRecord."); 288 return; 289 } 290 setDiscoveryRequestWithRouter2Locked(routerRecord, preference); 291 } 292 } finally { 293 Binder.restoreCallingIdentity(token); 294 } 295 } 296 setRouteVolumeWithRouter2(IMediaRouter2 router, MediaRoute2Info route, int volume)297 public void setRouteVolumeWithRouter2(IMediaRouter2 router, 298 MediaRoute2Info route, int volume) { 299 Objects.requireNonNull(router, "router must not be null"); 300 Objects.requireNonNull(route, "route must not be null"); 301 302 final long token = Binder.clearCallingIdentity(); 303 try { 304 synchronized (mLock) { 305 setRouteVolumeWithRouter2Locked(router, route, volume); 306 } 307 } finally { 308 Binder.restoreCallingIdentity(token); 309 } 310 } 311 requestCreateSessionWithRouter2(IMediaRouter2 router, int requestId, long managerRequestId, RoutingSessionInfo oldSession, MediaRoute2Info route, Bundle sessionHints)312 public void requestCreateSessionWithRouter2(IMediaRouter2 router, int requestId, 313 long managerRequestId, RoutingSessionInfo oldSession, 314 MediaRoute2Info route, Bundle sessionHints) { 315 Objects.requireNonNull(router, "router must not be null"); 316 Objects.requireNonNull(oldSession, "oldSession must not be null"); 317 Objects.requireNonNull(route, "route must not be null"); 318 319 final long token = Binder.clearCallingIdentity(); 320 try { 321 synchronized (mLock) { 322 requestCreateSessionWithRouter2Locked(requestId, managerRequestId, 323 router, oldSession, route, sessionHints); 324 } 325 } finally { 326 Binder.restoreCallingIdentity(token); 327 } 328 } 329 selectRouteWithRouter2(IMediaRouter2 router, String uniqueSessionId, MediaRoute2Info route)330 public void selectRouteWithRouter2(IMediaRouter2 router, String uniqueSessionId, 331 MediaRoute2Info route) { 332 Objects.requireNonNull(router, "router must not be null"); 333 Objects.requireNonNull(route, "route must not be null"); 334 if (TextUtils.isEmpty(uniqueSessionId)) { 335 throw new IllegalArgumentException("uniqueSessionId must not be empty"); 336 } 337 338 final long token = Binder.clearCallingIdentity(); 339 try { 340 synchronized (mLock) { 341 selectRouteWithRouter2Locked(router, uniqueSessionId, route); 342 } 343 } finally { 344 Binder.restoreCallingIdentity(token); 345 } 346 } 347 deselectRouteWithRouter2(IMediaRouter2 router, String uniqueSessionId, MediaRoute2Info route)348 public void deselectRouteWithRouter2(IMediaRouter2 router, String uniqueSessionId, 349 MediaRoute2Info route) { 350 Objects.requireNonNull(router, "router must not be null"); 351 Objects.requireNonNull(route, "route must not be null"); 352 if (TextUtils.isEmpty(uniqueSessionId)) { 353 throw new IllegalArgumentException("uniqueSessionId must not be empty"); 354 } 355 356 final long token = Binder.clearCallingIdentity(); 357 try { 358 synchronized (mLock) { 359 deselectRouteWithRouter2Locked(router, uniqueSessionId, route); 360 } 361 } finally { 362 Binder.restoreCallingIdentity(token); 363 } 364 } 365 transferToRouteWithRouter2(IMediaRouter2 router, String uniqueSessionId, MediaRoute2Info route)366 public void transferToRouteWithRouter2(IMediaRouter2 router, String uniqueSessionId, 367 MediaRoute2Info route) { 368 Objects.requireNonNull(router, "router must not be null"); 369 Objects.requireNonNull(route, "route must not be null"); 370 if (TextUtils.isEmpty(uniqueSessionId)) { 371 throw new IllegalArgumentException("uniqueSessionId must not be empty"); 372 } 373 374 final long token = Binder.clearCallingIdentity(); 375 try { 376 synchronized (mLock) { 377 transferToRouteWithRouter2Locked(router, uniqueSessionId, route); 378 } 379 } finally { 380 Binder.restoreCallingIdentity(token); 381 } 382 } 383 setSessionVolumeWithRouter2(IMediaRouter2 router, String uniqueSessionId, int volume)384 public void setSessionVolumeWithRouter2(IMediaRouter2 router, String uniqueSessionId, 385 int volume) { 386 Objects.requireNonNull(router, "router must not be null"); 387 Objects.requireNonNull(uniqueSessionId, "uniqueSessionId must not be null"); 388 389 final long token = Binder.clearCallingIdentity(); 390 try { 391 synchronized (mLock) { 392 setSessionVolumeWithRouter2Locked(router, uniqueSessionId, volume); 393 } 394 } finally { 395 Binder.restoreCallingIdentity(token); 396 } 397 } 398 releaseSessionWithRouter2(IMediaRouter2 router, String uniqueSessionId)399 public void releaseSessionWithRouter2(IMediaRouter2 router, String uniqueSessionId) { 400 Objects.requireNonNull(router, "router must not be null"); 401 if (TextUtils.isEmpty(uniqueSessionId)) { 402 throw new IllegalArgumentException("uniqueSessionId must not be empty"); 403 } 404 405 final long token = Binder.clearCallingIdentity(); 406 try { 407 synchronized (mLock) { 408 releaseSessionWithRouter2Locked(router, uniqueSessionId); 409 } 410 } finally { 411 Binder.restoreCallingIdentity(token); 412 } 413 } 414 415 //////////////////////////////////////////////////////////////// 416 //// Calls from MediaRouter2Manager 417 //// - Should not have @NonNull/@Nullable on any arguments 418 //////////////////////////////////////////////////////////////// 419 420 @NonNull getRemoteSessions(IMediaRouter2Manager manager)421 public List<RoutingSessionInfo> getRemoteSessions(IMediaRouter2Manager manager) { 422 Objects.requireNonNull(manager, "manager must not be null"); 423 final long token = Binder.clearCallingIdentity(); 424 try { 425 synchronized (mLock) { 426 return getRemoteSessionsLocked(manager); 427 } 428 } finally { 429 Binder.restoreCallingIdentity(token); 430 } 431 } 432 registerManager(IMediaRouter2Manager manager, String packageName)433 public void registerManager(IMediaRouter2Manager manager, String packageName) { 434 Objects.requireNonNull(manager, "manager must not be null"); 435 if (TextUtils.isEmpty(packageName)) { 436 throw new IllegalArgumentException("packageName must not be empty"); 437 } 438 439 final int uid = Binder.getCallingUid(); 440 final int pid = Binder.getCallingPid(); 441 final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); 442 443 final long token = Binder.clearCallingIdentity(); 444 try { 445 synchronized (mLock) { 446 registerManagerLocked(manager, uid, pid, packageName, userId); 447 } 448 } finally { 449 Binder.restoreCallingIdentity(token); 450 } 451 } 452 unregisterManager(IMediaRouter2Manager manager)453 public void unregisterManager(IMediaRouter2Manager manager) { 454 Objects.requireNonNull(manager, "manager must not be null"); 455 456 final long token = Binder.clearCallingIdentity(); 457 try { 458 synchronized (mLock) { 459 unregisterManagerLocked(manager, false); 460 } 461 } finally { 462 Binder.restoreCallingIdentity(token); 463 } 464 } 465 startScan(IMediaRouter2Manager manager)466 public void startScan(IMediaRouter2Manager manager) { 467 Objects.requireNonNull(manager, "manager must not be null"); 468 final long token = Binder.clearCallingIdentity(); 469 try { 470 synchronized (mLock) { 471 startScanLocked(manager); 472 } 473 } finally { 474 Binder.restoreCallingIdentity(token); 475 } 476 } 477 stopScan(IMediaRouter2Manager manager)478 public void stopScan(IMediaRouter2Manager manager) { 479 Objects.requireNonNull(manager, "manager must not be null"); 480 final long token = Binder.clearCallingIdentity(); 481 try { 482 synchronized (mLock) { 483 stopScanLocked(manager); 484 } 485 } finally { 486 Binder.restoreCallingIdentity(token); 487 } 488 } 489 setRouteVolumeWithManager(IMediaRouter2Manager manager, int requestId, MediaRoute2Info route, int volume)490 public void setRouteVolumeWithManager(IMediaRouter2Manager manager, int requestId, 491 MediaRoute2Info route, int volume) { 492 Objects.requireNonNull(manager, "manager must not be null"); 493 Objects.requireNonNull(route, "route must not be null"); 494 495 final long token = Binder.clearCallingIdentity(); 496 try { 497 synchronized (mLock) { 498 setRouteVolumeWithManagerLocked(requestId, manager, route, volume); 499 } 500 } finally { 501 Binder.restoreCallingIdentity(token); 502 } 503 } 504 requestCreateSessionWithManager(IMediaRouter2Manager manager, int requestId, RoutingSessionInfo oldSession, MediaRoute2Info route)505 public void requestCreateSessionWithManager(IMediaRouter2Manager manager, int requestId, 506 RoutingSessionInfo oldSession, MediaRoute2Info route) { 507 Objects.requireNonNull(manager, "manager must not be null"); 508 Objects.requireNonNull(oldSession, "oldSession must not be null"); 509 510 final long token = Binder.clearCallingIdentity(); 511 try { 512 synchronized (mLock) { 513 requestCreateSessionWithManagerLocked(requestId, manager, oldSession, route); 514 } 515 } finally { 516 Binder.restoreCallingIdentity(token); 517 } 518 } 519 selectRouteWithManager(IMediaRouter2Manager manager, int requestId, String uniqueSessionId, MediaRoute2Info route)520 public void selectRouteWithManager(IMediaRouter2Manager manager, int requestId, 521 String uniqueSessionId, MediaRoute2Info route) { 522 Objects.requireNonNull(manager, "manager must not be null"); 523 if (TextUtils.isEmpty(uniqueSessionId)) { 524 throw new IllegalArgumentException("uniqueSessionId must not be empty"); 525 } 526 Objects.requireNonNull(route, "route must not be null"); 527 528 final long token = Binder.clearCallingIdentity(); 529 try { 530 synchronized (mLock) { 531 selectRouteWithManagerLocked(requestId, manager, uniqueSessionId, route); 532 } 533 } finally { 534 Binder.restoreCallingIdentity(token); 535 } 536 } 537 deselectRouteWithManager(IMediaRouter2Manager manager, int requestId, String uniqueSessionId, MediaRoute2Info route)538 public void deselectRouteWithManager(IMediaRouter2Manager manager, int requestId, 539 String uniqueSessionId, MediaRoute2Info route) { 540 Objects.requireNonNull(manager, "manager must not be null"); 541 if (TextUtils.isEmpty(uniqueSessionId)) { 542 throw new IllegalArgumentException("uniqueSessionId must not be empty"); 543 } 544 Objects.requireNonNull(route, "route must not be null"); 545 546 final long token = Binder.clearCallingIdentity(); 547 try { 548 synchronized (mLock) { 549 deselectRouteWithManagerLocked(requestId, manager, uniqueSessionId, route); 550 } 551 } finally { 552 Binder.restoreCallingIdentity(token); 553 } 554 } 555 transferToRouteWithManager(IMediaRouter2Manager manager, int requestId, String uniqueSessionId, MediaRoute2Info route)556 public void transferToRouteWithManager(IMediaRouter2Manager manager, int requestId, 557 String uniqueSessionId, MediaRoute2Info route) { 558 Objects.requireNonNull(manager, "manager must not be null"); 559 if (TextUtils.isEmpty(uniqueSessionId)) { 560 throw new IllegalArgumentException("uniqueSessionId must not be empty"); 561 } 562 Objects.requireNonNull(route, "route must not be null"); 563 564 final long token = Binder.clearCallingIdentity(); 565 try { 566 synchronized (mLock) { 567 transferToRouteWithManagerLocked(requestId, manager, uniqueSessionId, route); 568 } 569 } finally { 570 Binder.restoreCallingIdentity(token); 571 } 572 } 573 setSessionVolumeWithManager(IMediaRouter2Manager manager, int requestId, String uniqueSessionId, int volume)574 public void setSessionVolumeWithManager(IMediaRouter2Manager manager, int requestId, 575 String uniqueSessionId, int volume) { 576 Objects.requireNonNull(manager, "manager must not be null"); 577 if (TextUtils.isEmpty(uniqueSessionId)) { 578 throw new IllegalArgumentException("uniqueSessionId must not be empty"); 579 } 580 581 final long token = Binder.clearCallingIdentity(); 582 try { 583 synchronized (mLock) { 584 setSessionVolumeWithManagerLocked(requestId, manager, uniqueSessionId, volume); 585 } 586 } finally { 587 Binder.restoreCallingIdentity(token); 588 } 589 } 590 releaseSessionWithManager(IMediaRouter2Manager manager, int requestId, String uniqueSessionId)591 public void releaseSessionWithManager(IMediaRouter2Manager manager, int requestId, 592 String uniqueSessionId) { 593 Objects.requireNonNull(manager, "manager must not be null"); 594 if (TextUtils.isEmpty(uniqueSessionId)) { 595 throw new IllegalArgumentException("uniqueSessionId must not be empty"); 596 } 597 598 final long token = Binder.clearCallingIdentity(); 599 try { 600 synchronized (mLock) { 601 releaseSessionWithManagerLocked(requestId, manager, uniqueSessionId); 602 } 603 } finally { 604 Binder.restoreCallingIdentity(token); 605 } 606 } 607 updateRunningUserAndProfiles(int newActiveUserId)608 /* package */ void updateRunningUserAndProfiles(int newActiveUserId) { 609 synchronized (mLock) { 610 if (mCurrentActiveUserId != newActiveUserId) { 611 Slog.i(TAG, TextUtils.formatSimple( 612 "switchUser | user: %d", newActiveUserId)); 613 614 mCurrentActiveUserId = newActiveUserId; 615 for (int i = 0; i < mUserRecords.size(); i++) { 616 int userId = mUserRecords.keyAt(i); 617 UserRecord userRecord = mUserRecords.valueAt(i); 618 if (isUserActiveLocked(userId)) { 619 // userId corresponds to the active user, or one of its profiles. We 620 // ensure the associated structures are initialized. 621 userRecord.mHandler.sendMessage( 622 obtainMessage(UserHandler::start, userRecord.mHandler)); 623 } else { 624 userRecord.mHandler.sendMessage( 625 obtainMessage(UserHandler::stop, userRecord.mHandler)); 626 disposeUserIfNeededLocked(userRecord); 627 } 628 } 629 } 630 } 631 } 632 routerDied(@onNull RouterRecord routerRecord)633 void routerDied(@NonNull RouterRecord routerRecord) { 634 synchronized (mLock) { 635 unregisterRouter2Locked(routerRecord.mRouter, true); 636 } 637 } 638 managerDied(@onNull ManagerRecord managerRecord)639 void managerDied(@NonNull ManagerRecord managerRecord) { 640 synchronized (mLock) { 641 unregisterManagerLocked(managerRecord.mManager, true); 642 } 643 } 644 645 /** 646 * Returns {@code true} if the given {@code userId} corresponds to the active user or a profile 647 * of the active user, returns {@code false} otherwise. 648 */ 649 @GuardedBy("mLock") isUserActiveLocked(int userId)650 private boolean isUserActiveLocked(int userId) { 651 return mUserManagerInternal.getProfileParentId(userId) == mCurrentActiveUserId; 652 } 653 654 //////////////////////////////////////////////////////////////// 655 //// ***Locked methods related to MediaRouter2 656 //// - Should have @NonNull/@Nullable on all arguments 657 //////////////////////////////////////////////////////////////// 658 659 @GuardedBy("mLock") registerRouter2Locked(@onNull IMediaRouter2 router, int uid, int pid, @NonNull String packageName, int userId, boolean hasConfigureWifiDisplayPermission, boolean hasModifyAudioRoutingPermission)660 private void registerRouter2Locked(@NonNull IMediaRouter2 router, int uid, int pid, 661 @NonNull String packageName, int userId, boolean hasConfigureWifiDisplayPermission, 662 boolean hasModifyAudioRoutingPermission) { 663 final IBinder binder = router.asBinder(); 664 if (mAllRouterRecords.get(binder) != null) { 665 Slog.w(TAG, "registerRouter2Locked: Same router already exists. packageName=" 666 + packageName); 667 return; 668 } 669 670 UserRecord userRecord = getOrCreateUserRecordLocked(userId); 671 RouterRecord routerRecord = new RouterRecord(userRecord, router, uid, pid, packageName, 672 hasConfigureWifiDisplayPermission, hasModifyAudioRoutingPermission); 673 try { 674 binder.linkToDeath(routerRecord, 0); 675 } catch (RemoteException ex) { 676 throw new RuntimeException("MediaRouter2 died prematurely.", ex); 677 } 678 679 userRecord.mRouterRecords.add(routerRecord); 680 mAllRouterRecords.put(binder, routerRecord); 681 682 userRecord.mHandler.sendMessage( 683 obtainMessage(UserHandler::notifyRouterRegistered, 684 userRecord.mHandler, routerRecord)); 685 686 Slog.i(TAG, TextUtils.formatSimple( 687 "registerRouter2 | package: %s, uid: %d, pid: %d, router: %d", 688 packageName, uid, pid, routerRecord.mRouterId)); 689 } 690 691 @GuardedBy("mLock") unregisterRouter2Locked(@onNull IMediaRouter2 router, boolean died)692 private void unregisterRouter2Locked(@NonNull IMediaRouter2 router, boolean died) { 693 RouterRecord routerRecord = mAllRouterRecords.remove(router.asBinder()); 694 if (routerRecord == null) { 695 Slog.w(TAG, "Ignoring unregistering unknown router2"); 696 return; 697 } 698 699 Slog.i(TAG, TextUtils.formatSimple( 700 "unregisterRouter2 | package: %s, router: %d", 701 routerRecord.mPackageName, 702 routerRecord.mRouterId)); 703 704 UserRecord userRecord = routerRecord.mUserRecord; 705 userRecord.mRouterRecords.remove(routerRecord); 706 routerRecord.mUserRecord.mHandler.sendMessage( 707 obtainMessage(UserHandler::notifyDiscoveryPreferenceChangedToManagers, 708 routerRecord.mUserRecord.mHandler, 709 routerRecord.mPackageName, null)); 710 userRecord.mHandler.sendMessage( 711 obtainMessage(UserHandler::updateDiscoveryPreferenceOnHandler, 712 userRecord.mHandler)); 713 routerRecord.dispose(); 714 disposeUserIfNeededLocked(userRecord); // since router removed from user 715 } 716 setDiscoveryRequestWithRouter2Locked(@onNull RouterRecord routerRecord, @NonNull RouteDiscoveryPreference discoveryRequest)717 private void setDiscoveryRequestWithRouter2Locked(@NonNull RouterRecord routerRecord, 718 @NonNull RouteDiscoveryPreference discoveryRequest) { 719 if (routerRecord.mDiscoveryPreference.equals(discoveryRequest)) { 720 return; 721 } 722 723 Slog.i(TAG, TextUtils.formatSimple( 724 "setDiscoveryRequestWithRouter2 | router: %d, discovery request: %s", 725 routerRecord.mRouterId, discoveryRequest.toString())); 726 727 routerRecord.mDiscoveryPreference = discoveryRequest; 728 routerRecord.mUserRecord.mHandler.sendMessage( 729 obtainMessage(UserHandler::notifyDiscoveryPreferenceChangedToManagers, 730 routerRecord.mUserRecord.mHandler, 731 routerRecord.mPackageName, 732 routerRecord.mDiscoveryPreference)); 733 routerRecord.mUserRecord.mHandler.sendMessage( 734 obtainMessage(UserHandler::updateDiscoveryPreferenceOnHandler, 735 routerRecord.mUserRecord.mHandler)); 736 } 737 setRouteVolumeWithRouter2Locked(@onNull IMediaRouter2 router, @NonNull MediaRoute2Info route, int volume)738 private void setRouteVolumeWithRouter2Locked(@NonNull IMediaRouter2 router, 739 @NonNull MediaRoute2Info route, int volume) { 740 final IBinder binder = router.asBinder(); 741 RouterRecord routerRecord = mAllRouterRecords.get(binder); 742 743 if (routerRecord != null) { 744 Slog.i(TAG, TextUtils.formatSimple( 745 "setRouteVolumeWithRouter2 | router: %d, volume: %d", 746 routerRecord.mRouterId, volume)); 747 748 routerRecord.mUserRecord.mHandler.sendMessage( 749 obtainMessage(UserHandler::setRouteVolumeOnHandler, 750 routerRecord.mUserRecord.mHandler, 751 DUMMY_REQUEST_ID, route, volume)); 752 } 753 } 754 requestCreateSessionWithRouter2Locked(int requestId, long managerRequestId, @NonNull IMediaRouter2 router, @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route, @Nullable Bundle sessionHints)755 private void requestCreateSessionWithRouter2Locked(int requestId, long managerRequestId, 756 @NonNull IMediaRouter2 router, @NonNull RoutingSessionInfo oldSession, 757 @NonNull MediaRoute2Info route, @Nullable Bundle sessionHints) { 758 final IBinder binder = router.asBinder(); 759 final RouterRecord routerRecord = mAllRouterRecords.get(binder); 760 761 if (routerRecord == null) { 762 return; 763 } 764 765 if (managerRequestId != MediaRoute2ProviderService.REQUEST_ID_NONE) { 766 ManagerRecord manager = routerRecord.mUserRecord.mHandler.findManagerWithId( 767 toRequesterId(managerRequestId)); 768 if (manager == null || manager.mLastSessionCreationRequest == null) { 769 Slog.w(TAG, "requestCreateSessionWithRouter2Locked: " 770 + "Ignoring unknown request."); 771 routerRecord.mUserRecord.mHandler.notifySessionCreationFailedToRouter( 772 routerRecord, requestId); 773 return; 774 } 775 if (!TextUtils.equals(manager.mLastSessionCreationRequest.mOldSession.getId(), 776 oldSession.getId())) { 777 Slog.w(TAG, "requestCreateSessionWithRouter2Locked: " 778 + "Ignoring unmatched routing session."); 779 routerRecord.mUserRecord.mHandler.notifySessionCreationFailedToRouter( 780 routerRecord, requestId); 781 return; 782 } 783 if (!TextUtils.equals(manager.mLastSessionCreationRequest.mRoute.getId(), 784 route.getId())) { 785 // When media router has no permission 786 if (!routerRecord.mHasModifyAudioRoutingPermission 787 && manager.mLastSessionCreationRequest.mRoute.isSystemRoute() 788 && route.isSystemRoute()) { 789 route = manager.mLastSessionCreationRequest.mRoute; 790 } else { 791 Slog.w(TAG, "requestCreateSessionWithRouter2Locked: " 792 + "Ignoring unmatched route."); 793 routerRecord.mUserRecord.mHandler.notifySessionCreationFailedToRouter( 794 routerRecord, requestId); 795 return; 796 } 797 } 798 manager.mLastSessionCreationRequest = null; 799 } else { 800 if (route.isSystemRoute() && !routerRecord.mHasModifyAudioRoutingPermission 801 && !TextUtils.equals(route.getId(), 802 routerRecord.mUserRecord.mHandler.mSystemProvider.getDefaultRoute().getId())) { 803 Slog.w(TAG, "MODIFY_AUDIO_ROUTING permission is required to transfer to" 804 + route); 805 routerRecord.mUserRecord.mHandler.notifySessionCreationFailedToRouter( 806 routerRecord, requestId); 807 return; 808 } 809 } 810 811 long uniqueRequestId = toUniqueRequestId(routerRecord.mRouterId, requestId); 812 routerRecord.mUserRecord.mHandler.sendMessage( 813 obtainMessage(UserHandler::requestCreateSessionWithRouter2OnHandler, 814 routerRecord.mUserRecord.mHandler, 815 uniqueRequestId, managerRequestId, routerRecord, oldSession, route, 816 sessionHints)); 817 } 818 selectRouteWithRouter2Locked(@onNull IMediaRouter2 router, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route)819 private void selectRouteWithRouter2Locked(@NonNull IMediaRouter2 router, 820 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { 821 final IBinder binder = router.asBinder(); 822 final RouterRecord routerRecord = mAllRouterRecords.get(binder); 823 824 if (routerRecord == null) { 825 return; 826 } 827 828 Slog.i(TAG, TextUtils.formatSimple( 829 "selectRouteWithRouter2 | router: %d, route: %s", 830 routerRecord.mRouterId, route.getId())); 831 832 routerRecord.mUserRecord.mHandler.sendMessage( 833 obtainMessage(UserHandler::selectRouteOnHandler, 834 routerRecord.mUserRecord.mHandler, 835 DUMMY_REQUEST_ID, routerRecord, uniqueSessionId, route)); 836 } 837 deselectRouteWithRouter2Locked(@onNull IMediaRouter2 router, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route)838 private void deselectRouteWithRouter2Locked(@NonNull IMediaRouter2 router, 839 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { 840 final IBinder binder = router.asBinder(); 841 final RouterRecord routerRecord = mAllRouterRecords.get(binder); 842 843 if (routerRecord == null) { 844 return; 845 } 846 847 Slog.i(TAG, TextUtils.formatSimple( 848 "deselectRouteWithRouter2 | router: %d, route: %s", 849 routerRecord.mRouterId, route.getId())); 850 851 routerRecord.mUserRecord.mHandler.sendMessage( 852 obtainMessage(UserHandler::deselectRouteOnHandler, 853 routerRecord.mUserRecord.mHandler, 854 DUMMY_REQUEST_ID, routerRecord, uniqueSessionId, route)); 855 } 856 transferToRouteWithRouter2Locked(@onNull IMediaRouter2 router, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route)857 private void transferToRouteWithRouter2Locked(@NonNull IMediaRouter2 router, 858 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { 859 final IBinder binder = router.asBinder(); 860 final RouterRecord routerRecord = mAllRouterRecords.get(binder); 861 862 if (routerRecord == null) { 863 return; 864 } 865 866 Slog.i(TAG, TextUtils.formatSimple( 867 "transferToRouteWithRouter2 | router: %d, route: %s", 868 routerRecord.mRouterId, route.getId())); 869 870 String defaultRouteId = 871 routerRecord.mUserRecord.mHandler.mSystemProvider.getDefaultRoute().getId(); 872 if (route.isSystemRoute() && !routerRecord.mHasModifyAudioRoutingPermission 873 && !TextUtils.equals(route.getId(), defaultRouteId)) { 874 routerRecord.mUserRecord.mHandler.sendMessage( 875 obtainMessage(UserHandler::notifySessionCreationFailedToRouter, 876 routerRecord.mUserRecord.mHandler, 877 routerRecord, toOriginalRequestId(DUMMY_REQUEST_ID))); 878 } else { 879 routerRecord.mUserRecord.mHandler.sendMessage( 880 obtainMessage(UserHandler::transferToRouteOnHandler, 881 routerRecord.mUserRecord.mHandler, 882 DUMMY_REQUEST_ID, routerRecord, uniqueSessionId, route)); 883 } 884 } 885 setSessionVolumeWithRouter2Locked(@onNull IMediaRouter2 router, @NonNull String uniqueSessionId, int volume)886 private void setSessionVolumeWithRouter2Locked(@NonNull IMediaRouter2 router, 887 @NonNull String uniqueSessionId, int volume) { 888 final IBinder binder = router.asBinder(); 889 RouterRecord routerRecord = mAllRouterRecords.get(binder); 890 891 if (routerRecord == null) { 892 return; 893 } 894 895 Slog.i(TAG, TextUtils.formatSimple( 896 "setSessionVolumeWithRouter2 | router: %d, session: %s, volume: %d", 897 routerRecord.mRouterId, uniqueSessionId, volume)); 898 899 routerRecord.mUserRecord.mHandler.sendMessage( 900 obtainMessage(UserHandler::setSessionVolumeOnHandler, 901 routerRecord.mUserRecord.mHandler, 902 DUMMY_REQUEST_ID, uniqueSessionId, volume)); 903 } 904 releaseSessionWithRouter2Locked(@onNull IMediaRouter2 router, @NonNull String uniqueSessionId)905 private void releaseSessionWithRouter2Locked(@NonNull IMediaRouter2 router, 906 @NonNull String uniqueSessionId) { 907 final IBinder binder = router.asBinder(); 908 final RouterRecord routerRecord = mAllRouterRecords.get(binder); 909 910 if (routerRecord == null) { 911 return; 912 } 913 914 Slog.i(TAG, TextUtils.formatSimple( 915 "releaseSessionWithRouter2 | router: %d, session: %s", 916 routerRecord.mRouterId, uniqueSessionId)); 917 918 routerRecord.mUserRecord.mHandler.sendMessage( 919 obtainMessage(UserHandler::releaseSessionOnHandler, 920 routerRecord.mUserRecord.mHandler, 921 DUMMY_REQUEST_ID, routerRecord, uniqueSessionId)); 922 } 923 924 //////////////////////////////////////////////////////////// 925 //// ***Locked methods related to MediaRouter2Manager 926 //// - Should have @NonNull/@Nullable on all arguments 927 //////////////////////////////////////////////////////////// 928 getRemoteSessionsLocked( @onNull IMediaRouter2Manager manager)929 private List<RoutingSessionInfo> getRemoteSessionsLocked( 930 @NonNull IMediaRouter2Manager manager) { 931 final IBinder binder = manager.asBinder(); 932 ManagerRecord managerRecord = mAllManagerRecords.get(binder); 933 934 if (managerRecord == null) { 935 Slog.w(TAG, "getRemoteSessionLocked: Ignoring unknown manager"); 936 return Collections.emptyList(); 937 } 938 939 List<RoutingSessionInfo> sessionInfos = new ArrayList<>(); 940 for (MediaRoute2Provider provider : managerRecord.mUserRecord.mHandler.mRouteProviders) { 941 if (!provider.mIsSystemRouteProvider) { 942 sessionInfos.addAll(provider.getSessionInfos()); 943 } 944 } 945 return sessionInfos; 946 } 947 948 @GuardedBy("mLock") registerManagerLocked(@onNull IMediaRouter2Manager manager, int uid, int pid, @NonNull String packageName, int userId)949 private void registerManagerLocked(@NonNull IMediaRouter2Manager manager, 950 int uid, int pid, @NonNull String packageName, int userId) { 951 final IBinder binder = manager.asBinder(); 952 ManagerRecord managerRecord = mAllManagerRecords.get(binder); 953 954 if (managerRecord != null) { 955 Slog.w(TAG, "registerManagerLocked: Same manager already exists. packageName=" 956 + packageName); 957 return; 958 } 959 960 Slog.i(TAG, TextUtils.formatSimple( 961 "registerManager | uid: %d, pid: %d, package: %s, user: %d", 962 uid, pid, packageName, userId)); 963 964 mContext.enforcePermission(Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid, 965 "Must hold MEDIA_CONTENT_CONTROL permission."); 966 967 UserRecord userRecord = getOrCreateUserRecordLocked(userId); 968 managerRecord = new ManagerRecord(userRecord, manager, uid, pid, packageName); 969 try { 970 binder.linkToDeath(managerRecord, 0); 971 } catch (RemoteException ex) { 972 throw new RuntimeException("Media router manager died prematurely.", ex); 973 } 974 975 userRecord.mManagerRecords.add(managerRecord); 976 mAllManagerRecords.put(binder, managerRecord); 977 978 // Note: Features should be sent first before the routes. If not, the 979 // RouteCallback#onRoutesAdded() for system MR2 will never be called with initial routes 980 // due to the lack of features. 981 for (RouterRecord routerRecord : userRecord.mRouterRecords) { 982 // TODO: UserRecord <-> routerRecord, why do they reference each other? 983 // How about removing mUserRecord from routerRecord? 984 routerRecord.mUserRecord.mHandler.sendMessage( 985 obtainMessage(UserHandler::notifyDiscoveryPreferenceChangedToManager, 986 routerRecord.mUserRecord.mHandler, routerRecord, manager)); 987 } 988 989 userRecord.mHandler.sendMessage(obtainMessage(UserHandler::notifyRoutesToManager, 990 userRecord.mHandler, manager)); 991 } 992 unregisterManagerLocked(@onNull IMediaRouter2Manager manager, boolean died)993 private void unregisterManagerLocked(@NonNull IMediaRouter2Manager manager, boolean died) { 994 ManagerRecord managerRecord = mAllManagerRecords.remove(manager.asBinder()); 995 if (managerRecord == null) { 996 return; 997 } 998 UserRecord userRecord = managerRecord.mUserRecord; 999 1000 Slog.i(TAG, TextUtils.formatSimple( 1001 "unregisterManager | package: %s, user: %d, manager: %d", 1002 managerRecord.mPackageName, 1003 userRecord.mUserId, 1004 managerRecord.mManagerId)); 1005 1006 userRecord.mManagerRecords.remove(managerRecord); 1007 managerRecord.dispose(); 1008 disposeUserIfNeededLocked(userRecord); // since manager removed from user 1009 } 1010 startScanLocked(@onNull IMediaRouter2Manager manager)1011 private void startScanLocked(@NonNull IMediaRouter2Manager manager) { 1012 final IBinder binder = manager.asBinder(); 1013 ManagerRecord managerRecord = mAllManagerRecords.get(binder); 1014 if (managerRecord == null) { 1015 return; 1016 } 1017 1018 Slog.i(TAG, TextUtils.formatSimple( 1019 "startScan | manager: %d", managerRecord.mManagerId)); 1020 1021 managerRecord.startScan(); 1022 } 1023 stopScanLocked(@onNull IMediaRouter2Manager manager)1024 private void stopScanLocked(@NonNull IMediaRouter2Manager manager) { 1025 final IBinder binder = manager.asBinder(); 1026 ManagerRecord managerRecord = mAllManagerRecords.get(binder); 1027 if (managerRecord == null) { 1028 return; 1029 } 1030 1031 Slog.i(TAG, TextUtils.formatSimple( 1032 "stopScan | manager: %d", managerRecord.mManagerId)); 1033 1034 managerRecord.stopScan(); 1035 } 1036 setRouteVolumeWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager, @NonNull MediaRoute2Info route, int volume)1037 private void setRouteVolumeWithManagerLocked(int requestId, 1038 @NonNull IMediaRouter2Manager manager, 1039 @NonNull MediaRoute2Info route, int volume) { 1040 final IBinder binder = manager.asBinder(); 1041 ManagerRecord managerRecord = mAllManagerRecords.get(binder); 1042 1043 if (managerRecord == null) { 1044 return; 1045 } 1046 1047 Slog.i(TAG, TextUtils.formatSimple( 1048 "setRouteVolumeWithManager | manager: %d, route: %s, volume: %d", 1049 managerRecord.mManagerId, route.getId(), volume)); 1050 1051 long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); 1052 managerRecord.mUserRecord.mHandler.sendMessage( 1053 obtainMessage(UserHandler::setRouteVolumeOnHandler, 1054 managerRecord.mUserRecord.mHandler, 1055 uniqueRequestId, route, volume)); 1056 } 1057 requestCreateSessionWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager, @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route)1058 private void requestCreateSessionWithManagerLocked(int requestId, 1059 @NonNull IMediaRouter2Manager manager, 1060 @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route) { 1061 ManagerRecord managerRecord = mAllManagerRecords.get(manager.asBinder()); 1062 if (managerRecord == null) { 1063 return; 1064 } 1065 1066 Slog.i(TAG, TextUtils.formatSimple( 1067 "requestCreateSessionWithManager | manager: %d, route: %s", 1068 managerRecord.mManagerId, route.getId())); 1069 1070 String packageName = oldSession.getClientPackageName(); 1071 1072 RouterRecord routerRecord = managerRecord.mUserRecord.findRouterRecordLocked(packageName); 1073 if (routerRecord == null) { 1074 Slog.w(TAG, "requestCreateSessionWithManagerLocked: Ignoring session creation for " 1075 + "unknown router."); 1076 try { 1077 managerRecord.mManager.notifyRequestFailed(requestId, REASON_UNKNOWN_ERROR); 1078 } catch (RemoteException ex) { 1079 Slog.w(TAG, "requestCreateSessionWithManagerLocked: Failed to notify failure. " 1080 + "Manager probably died."); 1081 } 1082 return; 1083 } 1084 1085 long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); 1086 if (managerRecord.mLastSessionCreationRequest != null) { 1087 managerRecord.mUserRecord.mHandler.notifyRequestFailedToManager( 1088 managerRecord.mManager, 1089 toOriginalRequestId(managerRecord.mLastSessionCreationRequest 1090 .mManagerRequestId), 1091 REASON_UNKNOWN_ERROR); 1092 managerRecord.mLastSessionCreationRequest = null; 1093 } 1094 managerRecord.mLastSessionCreationRequest = new SessionCreationRequest(routerRecord, 1095 MediaRoute2ProviderService.REQUEST_ID_NONE, uniqueRequestId, 1096 oldSession, route); 1097 1098 // Before requesting to the provider, get session hints from the media router. 1099 // As a return, media router will request to create a session. 1100 routerRecord.mUserRecord.mHandler.sendMessage( 1101 obtainMessage(UserHandler::requestRouterCreateSessionOnHandler, 1102 routerRecord.mUserRecord.mHandler, 1103 uniqueRequestId, routerRecord, managerRecord, oldSession, route)); 1104 } 1105 selectRouteWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route)1106 private void selectRouteWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager, 1107 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { 1108 final IBinder binder = manager.asBinder(); 1109 ManagerRecord managerRecord = mAllManagerRecords.get(binder); 1110 1111 if (managerRecord == null) { 1112 return; 1113 } 1114 1115 Slog.i(TAG, TextUtils.formatSimple( 1116 "selectRouteWithManager | manager: %d, session: %s, route: %s", 1117 managerRecord.mManagerId, uniqueSessionId, route.getId())); 1118 1119 // Can be null if the session is system's or RCN. 1120 RouterRecord routerRecord = managerRecord.mUserRecord.mHandler 1121 .findRouterWithSessionLocked(uniqueSessionId); 1122 1123 long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); 1124 managerRecord.mUserRecord.mHandler.sendMessage( 1125 obtainMessage(UserHandler::selectRouteOnHandler, 1126 managerRecord.mUserRecord.mHandler, 1127 uniqueRequestId, routerRecord, uniqueSessionId, route)); 1128 } 1129 deselectRouteWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route)1130 private void deselectRouteWithManagerLocked(int requestId, 1131 @NonNull IMediaRouter2Manager manager, 1132 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { 1133 final IBinder binder = manager.asBinder(); 1134 ManagerRecord managerRecord = mAllManagerRecords.get(binder); 1135 1136 if (managerRecord == null) { 1137 return; 1138 } 1139 1140 Slog.i(TAG, TextUtils.formatSimple( 1141 "deselectRouteWithManager | manager: %d, session: %s, route: %s", 1142 managerRecord.mManagerId, uniqueSessionId, route.getId())); 1143 1144 // Can be null if the session is system's or RCN. 1145 RouterRecord routerRecord = managerRecord.mUserRecord.mHandler 1146 .findRouterWithSessionLocked(uniqueSessionId); 1147 1148 long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); 1149 managerRecord.mUserRecord.mHandler.sendMessage( 1150 obtainMessage(UserHandler::deselectRouteOnHandler, 1151 managerRecord.mUserRecord.mHandler, 1152 uniqueRequestId, routerRecord, uniqueSessionId, route)); 1153 } 1154 transferToRouteWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route)1155 private void transferToRouteWithManagerLocked(int requestId, 1156 @NonNull IMediaRouter2Manager manager, 1157 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { 1158 final IBinder binder = manager.asBinder(); 1159 ManagerRecord managerRecord = mAllManagerRecords.get(binder); 1160 1161 if (managerRecord == null) { 1162 return; 1163 } 1164 1165 Slog.i(TAG, TextUtils.formatSimple( 1166 "transferToRouteWithManager | manager: %d, session: %s, route: %s", 1167 managerRecord.mManagerId, uniqueSessionId, route.getId())); 1168 1169 // Can be null if the session is system's or RCN. 1170 RouterRecord routerRecord = managerRecord.mUserRecord.mHandler 1171 .findRouterWithSessionLocked(uniqueSessionId); 1172 1173 long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); 1174 managerRecord.mUserRecord.mHandler.sendMessage( 1175 obtainMessage(UserHandler::transferToRouteOnHandler, 1176 managerRecord.mUserRecord.mHandler, 1177 uniqueRequestId, routerRecord, uniqueSessionId, route)); 1178 } 1179 setSessionVolumeWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager, @NonNull String uniqueSessionId, int volume)1180 private void setSessionVolumeWithManagerLocked(int requestId, 1181 @NonNull IMediaRouter2Manager manager, 1182 @NonNull String uniqueSessionId, int volume) { 1183 final IBinder binder = manager.asBinder(); 1184 ManagerRecord managerRecord = mAllManagerRecords.get(binder); 1185 1186 if (managerRecord == null) { 1187 return; 1188 } 1189 1190 Slog.i(TAG, TextUtils.formatSimple( 1191 "setSessionVolumeWithManager | manager: %d, session: %s, volume: %d", 1192 managerRecord.mManagerId, uniqueSessionId, volume)); 1193 1194 long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); 1195 managerRecord.mUserRecord.mHandler.sendMessage( 1196 obtainMessage(UserHandler::setSessionVolumeOnHandler, 1197 managerRecord.mUserRecord.mHandler, 1198 uniqueRequestId, uniqueSessionId, volume)); 1199 } 1200 releaseSessionWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager, @NonNull String uniqueSessionId)1201 private void releaseSessionWithManagerLocked(int requestId, 1202 @NonNull IMediaRouter2Manager manager, 1203 @NonNull String uniqueSessionId) { 1204 final IBinder binder = manager.asBinder(); 1205 ManagerRecord managerRecord = mAllManagerRecords.get(binder); 1206 1207 if (managerRecord == null) { 1208 return; 1209 } 1210 1211 Slog.i(TAG, TextUtils.formatSimple( 1212 "releaseSessionWithManager | manager: %d, session: %s", 1213 managerRecord.mManagerId, uniqueSessionId)); 1214 1215 RouterRecord routerRecord = managerRecord.mUserRecord.mHandler 1216 .findRouterWithSessionLocked(uniqueSessionId); 1217 1218 long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); 1219 managerRecord.mUserRecord.mHandler.sendMessage( 1220 obtainMessage(UserHandler::releaseSessionOnHandler, 1221 managerRecord.mUserRecord.mHandler, 1222 uniqueRequestId, routerRecord, uniqueSessionId)); 1223 } 1224 1225 //////////////////////////////////////////////////////////// 1226 //// ***Locked methods used by both router2 and manager 1227 //// - Should have @NonNull/@Nullable on all arguments 1228 //////////////////////////////////////////////////////////// 1229 1230 @GuardedBy("mLock") getOrCreateUserRecordLocked(int userId)1231 private UserRecord getOrCreateUserRecordLocked(int userId) { 1232 UserRecord userRecord = mUserRecords.get(userId); 1233 if (userRecord == null) { 1234 userRecord = new UserRecord(userId); 1235 mUserRecords.put(userId, userRecord); 1236 userRecord.init(); 1237 if (isUserActiveLocked(userId)) { 1238 userRecord.mHandler.sendMessage( 1239 obtainMessage(UserHandler::start, userRecord.mHandler)); 1240 } 1241 } 1242 return userRecord; 1243 } 1244 1245 @GuardedBy("mLock") disposeUserIfNeededLocked(@onNull UserRecord userRecord)1246 private void disposeUserIfNeededLocked(@NonNull UserRecord userRecord) { 1247 // If there are no records left and the user is no longer current then go ahead 1248 // and purge the user record and all of its associated state. If the user is current 1249 // then leave it alone since we might be connected to a route or want to query 1250 // the same route information again soon. 1251 if (!isUserActiveLocked(userRecord.mUserId) 1252 && userRecord.mRouterRecords.isEmpty() 1253 && userRecord.mManagerRecords.isEmpty()) { 1254 if (DEBUG) { 1255 Slog.d(TAG, userRecord + ": Disposed"); 1256 } 1257 userRecord.mHandler.sendMessage( 1258 obtainMessage(UserHandler::stop, userRecord.mHandler)); 1259 mUserRecords.remove(userRecord.mUserId); 1260 // Note: User already stopped (by switchUser) so no need to send stop message here. 1261 } 1262 } 1263 toUniqueRequestId(int requesterId, int originalRequestId)1264 static long toUniqueRequestId(int requesterId, int originalRequestId) { 1265 return ((long) requesterId << 32) | originalRequestId; 1266 } 1267 toRequesterId(long uniqueRequestId)1268 static int toRequesterId(long uniqueRequestId) { 1269 return (int) (uniqueRequestId >> 32); 1270 } 1271 toOriginalRequestId(long uniqueRequestId)1272 static int toOriginalRequestId(long uniqueRequestId) { 1273 return (int) uniqueRequestId; 1274 } 1275 1276 final class UserRecord { 1277 public final int mUserId; 1278 //TODO: make records private for thread-safety 1279 final ArrayList<RouterRecord> mRouterRecords = new ArrayList<>(); 1280 final ArrayList<ManagerRecord> mManagerRecords = new ArrayList<>(); 1281 RouteDiscoveryPreference mCompositeDiscoveryPreference = RouteDiscoveryPreference.EMPTY; 1282 final UserHandler mHandler; 1283 UserRecord(int userId)1284 UserRecord(int userId) { 1285 mUserId = userId; 1286 mHandler = new UserHandler(MediaRouter2ServiceImpl.this, this); 1287 } 1288 init()1289 void init() { 1290 mHandler.init(); 1291 } 1292 1293 // TODO: This assumes that only one router exists in a package. 1294 // Do this in Android S or later. findRouterRecordLocked(String packageName)1295 RouterRecord findRouterRecordLocked(String packageName) { 1296 for (RouterRecord routerRecord : mRouterRecords) { 1297 if (TextUtils.equals(routerRecord.mPackageName, packageName)) { 1298 return routerRecord; 1299 } 1300 } 1301 return null; 1302 } 1303 } 1304 1305 final class RouterRecord implements IBinder.DeathRecipient { 1306 public final UserRecord mUserRecord; 1307 public final String mPackageName; 1308 public final List<Integer> mSelectRouteSequenceNumbers; 1309 public final IMediaRouter2 mRouter; 1310 public final int mUid; 1311 public final int mPid; 1312 public final boolean mHasConfigureWifiDisplayPermission; 1313 public final boolean mHasModifyAudioRoutingPermission; 1314 public final int mRouterId; 1315 1316 public RouteDiscoveryPreference mDiscoveryPreference; 1317 public MediaRoute2Info mSelectedRoute; 1318 RouterRecord(UserRecord userRecord, IMediaRouter2 router, int uid, int pid, String packageName, boolean hasConfigureWifiDisplayPermission, boolean hasModifyAudioRoutingPermission)1319 RouterRecord(UserRecord userRecord, IMediaRouter2 router, int uid, int pid, 1320 String packageName, boolean hasConfigureWifiDisplayPermission, 1321 boolean hasModifyAudioRoutingPermission) { 1322 mUserRecord = userRecord; 1323 mPackageName = packageName; 1324 mSelectRouteSequenceNumbers = new ArrayList<>(); 1325 mDiscoveryPreference = RouteDiscoveryPreference.EMPTY; 1326 mRouter = router; 1327 mUid = uid; 1328 mPid = pid; 1329 mHasConfigureWifiDisplayPermission = hasConfigureWifiDisplayPermission; 1330 mHasModifyAudioRoutingPermission = hasModifyAudioRoutingPermission; 1331 mRouterId = mNextRouterOrManagerId.getAndIncrement(); 1332 } 1333 dispose()1334 public void dispose() { 1335 mRouter.asBinder().unlinkToDeath(this, 0); 1336 } 1337 1338 @Override binderDied()1339 public void binderDied() { 1340 routerDied(this); 1341 } 1342 } 1343 1344 final class ManagerRecord implements IBinder.DeathRecipient { 1345 public final UserRecord mUserRecord; 1346 public final IMediaRouter2Manager mManager; 1347 public final int mUid; 1348 public final int mPid; 1349 public final String mPackageName; 1350 public final int mManagerId; 1351 public SessionCreationRequest mLastSessionCreationRequest; 1352 public boolean mIsScanning; 1353 ManagerRecord(UserRecord userRecord, IMediaRouter2Manager manager, int uid, int pid, String packageName)1354 ManagerRecord(UserRecord userRecord, IMediaRouter2Manager manager, 1355 int uid, int pid, String packageName) { 1356 mUserRecord = userRecord; 1357 mManager = manager; 1358 mUid = uid; 1359 mPid = pid; 1360 mPackageName = packageName; 1361 mManagerId = mNextRouterOrManagerId.getAndIncrement(); 1362 } 1363 dispose()1364 public void dispose() { 1365 mManager.asBinder().unlinkToDeath(this, 0); 1366 } 1367 1368 @Override binderDied()1369 public void binderDied() { 1370 managerDied(this); 1371 } 1372 dump(PrintWriter pw, String prefix)1373 public void dump(PrintWriter pw, String prefix) { 1374 pw.println(prefix + this); 1375 } 1376 startScan()1377 public void startScan() { 1378 if (mIsScanning) { 1379 return; 1380 } 1381 mIsScanning = true; 1382 mUserRecord.mHandler.sendMessage(PooledLambda.obtainMessage( 1383 UserHandler::updateDiscoveryPreferenceOnHandler, mUserRecord.mHandler)); 1384 } 1385 stopScan()1386 public void stopScan() { 1387 if (!mIsScanning) { 1388 return; 1389 } 1390 mIsScanning = false; 1391 mUserRecord.mHandler.sendMessage(PooledLambda.obtainMessage( 1392 UserHandler::updateDiscoveryPreferenceOnHandler, mUserRecord.mHandler)); 1393 } 1394 1395 @Override toString()1396 public String toString() { 1397 return "Manager " + mPackageName + " (pid " + mPid + ")"; 1398 } 1399 } 1400 1401 static final class UserHandler extends Handler implements 1402 MediaRoute2ProviderWatcher.Callback, 1403 MediaRoute2Provider.Callback { 1404 1405 private final WeakReference<MediaRouter2ServiceImpl> mServiceRef; 1406 private final UserRecord mUserRecord; 1407 private final MediaRoute2ProviderWatcher mWatcher; 1408 1409 private final SystemMediaRoute2Provider mSystemProvider; 1410 private final ArrayList<MediaRoute2Provider> mRouteProviders = 1411 new ArrayList<>(); 1412 1413 private final List<MediaRoute2ProviderInfo> mLastProviderInfos = new ArrayList<>(); 1414 private final CopyOnWriteArrayList<SessionCreationRequest> mSessionCreationRequests = 1415 new CopyOnWriteArrayList<>(); 1416 private final Map<String, RouterRecord> mSessionToRouterMap = new ArrayMap<>(); 1417 1418 private boolean mRunning; 1419 1420 // TODO: (In Android S+) Pull out SystemMediaRoute2Provider out of UserHandler. UserHandler(@onNull MediaRouter2ServiceImpl service, @NonNull UserRecord userRecord)1421 UserHandler(@NonNull MediaRouter2ServiceImpl service, @NonNull UserRecord userRecord) { 1422 super(Looper.getMainLooper(), null, true); 1423 mServiceRef = new WeakReference<>(service); 1424 mUserRecord = userRecord; 1425 mSystemProvider = new SystemMediaRoute2Provider(service.mContext, 1426 UserHandle.of(userRecord.mUserId)); 1427 mRouteProviders.add(mSystemProvider); 1428 mWatcher = new MediaRoute2ProviderWatcher(service.mContext, this, 1429 this, mUserRecord.mUserId); 1430 } 1431 init()1432 void init() { 1433 mSystemProvider.setCallback(this); 1434 } 1435 start()1436 private void start() { 1437 if (!mRunning) { 1438 mRunning = true; 1439 mSystemProvider.start(); 1440 mWatcher.start(); 1441 } 1442 } 1443 stop()1444 private void stop() { 1445 if (mRunning) { 1446 mRunning = false; 1447 mWatcher.stop(); // also stops all providers 1448 mSystemProvider.stop(); 1449 } 1450 } 1451 1452 @Override onAddProviderService(@onNull MediaRoute2ProviderServiceProxy proxy)1453 public void onAddProviderService(@NonNull MediaRoute2ProviderServiceProxy proxy) { 1454 proxy.setCallback(this); 1455 mRouteProviders.add(proxy); 1456 proxy.updateDiscoveryPreference(mUserRecord.mCompositeDiscoveryPreference); 1457 } 1458 1459 @Override onRemoveProviderService(@onNull MediaRoute2ProviderServiceProxy proxy)1460 public void onRemoveProviderService(@NonNull MediaRoute2ProviderServiceProxy proxy) { 1461 mRouteProviders.remove(proxy); 1462 } 1463 1464 @Override onProviderStateChanged(@onNull MediaRoute2Provider provider)1465 public void onProviderStateChanged(@NonNull MediaRoute2Provider provider) { 1466 sendMessage(PooledLambda.obtainMessage(UserHandler::onProviderStateChangedOnHandler, 1467 this, provider)); 1468 } 1469 1470 @Override onSessionCreated(@onNull MediaRoute2Provider provider, long uniqueRequestId, @NonNull RoutingSessionInfo sessionInfo)1471 public void onSessionCreated(@NonNull MediaRoute2Provider provider, 1472 long uniqueRequestId, @NonNull RoutingSessionInfo sessionInfo) { 1473 sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionCreatedOnHandler, 1474 this, provider, uniqueRequestId, sessionInfo)); 1475 } 1476 1477 @Override onSessionUpdated(@onNull MediaRoute2Provider provider, @NonNull RoutingSessionInfo sessionInfo)1478 public void onSessionUpdated(@NonNull MediaRoute2Provider provider, 1479 @NonNull RoutingSessionInfo sessionInfo) { 1480 sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionInfoChangedOnHandler, 1481 this, provider, sessionInfo)); 1482 } 1483 1484 @Override onSessionReleased(@onNull MediaRoute2Provider provider, @NonNull RoutingSessionInfo sessionInfo)1485 public void onSessionReleased(@NonNull MediaRoute2Provider provider, 1486 @NonNull RoutingSessionInfo sessionInfo) { 1487 sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionReleasedOnHandler, 1488 this, provider, sessionInfo)); 1489 } 1490 1491 @Override onRequestFailed(@onNull MediaRoute2Provider provider, long uniqueRequestId, int reason)1492 public void onRequestFailed(@NonNull MediaRoute2Provider provider, long uniqueRequestId, 1493 int reason) { 1494 sendMessage(PooledLambda.obtainMessage(UserHandler::onRequestFailedOnHandler, 1495 this, provider, uniqueRequestId, reason)); 1496 } 1497 1498 @Nullable findRouterWithSessionLocked(@onNull String uniqueSessionId)1499 public RouterRecord findRouterWithSessionLocked(@NonNull String uniqueSessionId) { 1500 return mSessionToRouterMap.get(uniqueSessionId); 1501 } 1502 1503 @Nullable findManagerWithId(int managerId)1504 public ManagerRecord findManagerWithId(int managerId) { 1505 for (ManagerRecord manager : getManagerRecords()) { 1506 if (manager.mManagerId == managerId) { 1507 return manager; 1508 } 1509 } 1510 return null; 1511 } 1512 maybeUpdateDiscoveryPreferenceForUid(int uid)1513 public void maybeUpdateDiscoveryPreferenceForUid(int uid) { 1514 MediaRouter2ServiceImpl service = mServiceRef.get(); 1515 if (service == null) { 1516 return; 1517 } 1518 boolean isUidRelevant; 1519 synchronized (service.mLock) { 1520 isUidRelevant = mUserRecord.mRouterRecords.stream().anyMatch( 1521 router -> router.mUid == uid) 1522 | mUserRecord.mManagerRecords.stream().anyMatch( 1523 manager -> manager.mUid == uid); 1524 } 1525 if (isUidRelevant) { 1526 sendMessage(PooledLambda.obtainMessage( 1527 UserHandler::updateDiscoveryPreferenceOnHandler, this)); 1528 } 1529 } 1530 onProviderStateChangedOnHandler(@onNull MediaRoute2Provider provider)1531 private void onProviderStateChangedOnHandler(@NonNull MediaRoute2Provider provider) { 1532 int providerInfoIndex = getLastProviderInfoIndex(provider.getUniqueId()); 1533 MediaRoute2ProviderInfo currentInfo = provider.getProviderInfo(); 1534 MediaRoute2ProviderInfo prevInfo = 1535 (providerInfoIndex < 0) ? null : mLastProviderInfos.get(providerInfoIndex); 1536 if (Objects.equals(prevInfo, currentInfo)) return; 1537 1538 List<MediaRoute2Info> addedRoutes = new ArrayList<>(); 1539 List<MediaRoute2Info> removedRoutes = new ArrayList<>(); 1540 List<MediaRoute2Info> changedRoutes = new ArrayList<>(); 1541 if (prevInfo == null) { 1542 mLastProviderInfos.add(currentInfo); 1543 addedRoutes.addAll(currentInfo.getRoutes()); 1544 } else if (currentInfo == null) { 1545 mLastProviderInfos.remove(prevInfo); 1546 removedRoutes.addAll(prevInfo.getRoutes()); 1547 } else { 1548 mLastProviderInfos.set(providerInfoIndex, currentInfo); 1549 final Collection<MediaRoute2Info> prevRoutes = prevInfo.getRoutes(); 1550 final Collection<MediaRoute2Info> currentRoutes = currentInfo.getRoutes(); 1551 1552 for (MediaRoute2Info route : currentRoutes) { 1553 if (!route.isValid()) { 1554 Slog.w(TAG, "onProviderStateChangedOnHandler: Ignoring invalid route : " 1555 + route); 1556 continue; 1557 } 1558 MediaRoute2Info prevRoute = prevInfo.getRoute(route.getOriginalId()); 1559 if (prevRoute == null) { 1560 addedRoutes.add(route); 1561 } else if (!Objects.equals(prevRoute, route)) { 1562 changedRoutes.add(route); 1563 } 1564 } 1565 1566 for (MediaRoute2Info prevRoute : prevInfo.getRoutes()) { 1567 if (currentInfo.getRoute(prevRoute.getOriginalId()) == null) { 1568 removedRoutes.add(prevRoute); 1569 } 1570 } 1571 } 1572 1573 List<IMediaRouter2> routersWithModifyAudioRoutingPermission = getRouters(true); 1574 List<IMediaRouter2> routersWithoutModifyAudioRoutingPermission = getRouters(false); 1575 1576 if (!addedRoutes.isEmpty()) { 1577 // If routes were added, currentInfo cannot be null. 1578 Slog.i(TAG, 1579 toLoggingMessage( 1580 /* source= */ "addProviderRoutes", 1581 currentInfo.getUniqueId(), 1582 (ArrayList) addedRoutes)); 1583 } 1584 if (!removedRoutes.isEmpty()) { 1585 // If routes were removed, prevInfo cannot be null. 1586 Slog.i(TAG, 1587 toLoggingMessage( 1588 /* source= */ "removeProviderRoutes", 1589 prevInfo.getUniqueId(), 1590 (ArrayList) removedRoutes)); 1591 } 1592 1593 List<IMediaRouter2Manager> managers = getManagers(); 1594 List<MediaRoute2Info> defaultRoute = new ArrayList<>(); 1595 defaultRoute.add(mSystemProvider.getDefaultRoute()); 1596 1597 if (addedRoutes.size() > 0) { 1598 notifyRoutesAddedToRouters(routersWithModifyAudioRoutingPermission, addedRoutes); 1599 if (!provider.mIsSystemRouteProvider) { 1600 notifyRoutesAddedToRouters(routersWithoutModifyAudioRoutingPermission, 1601 addedRoutes); 1602 } else if (prevInfo == null) { 1603 notifyRoutesAddedToRouters(routersWithoutModifyAudioRoutingPermission, 1604 defaultRoute); 1605 } // 'else' is handled as changed routes 1606 notifyRoutesAddedToManagers(managers, addedRoutes); 1607 } 1608 if (removedRoutes.size() > 0) { 1609 notifyRoutesRemovedToRouters(routersWithModifyAudioRoutingPermission, 1610 removedRoutes); 1611 if (!provider.mIsSystemRouteProvider) { 1612 notifyRoutesRemovedToRouters(routersWithoutModifyAudioRoutingPermission, 1613 removedRoutes); 1614 } 1615 notifyRoutesRemovedToManagers(managers, removedRoutes); 1616 } 1617 if (changedRoutes.size() > 0) { 1618 notifyRoutesChangedToRouters(routersWithModifyAudioRoutingPermission, 1619 changedRoutes); 1620 if (!provider.mIsSystemRouteProvider) { 1621 notifyRoutesChangedToRouters(routersWithoutModifyAudioRoutingPermission, 1622 changedRoutes); 1623 } else if (prevInfo != null) { 1624 notifyRoutesChangedToRouters(routersWithoutModifyAudioRoutingPermission, 1625 defaultRoute); 1626 } // 'else' is handled as added routes 1627 notifyRoutesChangedToManagers(managers, changedRoutes); 1628 } 1629 } 1630 toLoggingMessage( String source, String providerId, ArrayList<MediaRoute2Info> routes)1631 private static String toLoggingMessage( 1632 String source, String providerId, ArrayList<MediaRoute2Info> routes) { 1633 String routesString = 1634 routes.stream() 1635 .map(it -> String.format("%s | %s", it.getOriginalId(), it.getName())) 1636 .collect(Collectors.joining(/* delimiter= */ ", ")); 1637 return TextUtils.formatSimple("%s | provider: %s, routes: [%s]", 1638 source, providerId, routesString); 1639 } 1640 getLastProviderInfoIndex(@onNull String providerId)1641 private int getLastProviderInfoIndex(@NonNull String providerId) { 1642 for (int i = 0; i < mLastProviderInfos.size(); i++) { 1643 MediaRoute2ProviderInfo providerInfo = mLastProviderInfos.get(i); 1644 if (TextUtils.equals(providerInfo.getUniqueId(), providerId)) { 1645 return i; 1646 } 1647 } 1648 return -1; 1649 } 1650 requestRouterCreateSessionOnHandler(long uniqueRequestId, @NonNull RouterRecord routerRecord, @NonNull ManagerRecord managerRecord, @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route)1651 private void requestRouterCreateSessionOnHandler(long uniqueRequestId, 1652 @NonNull RouterRecord routerRecord, @NonNull ManagerRecord managerRecord, 1653 @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route) { 1654 try { 1655 if (route.isSystemRoute() && !routerRecord.mHasModifyAudioRoutingPermission) { 1656 routerRecord.mRouter.requestCreateSessionByManager(uniqueRequestId, 1657 oldSession, mSystemProvider.getDefaultRoute()); 1658 } else { 1659 routerRecord.mRouter.requestCreateSessionByManager(uniqueRequestId, 1660 oldSession, route); 1661 } 1662 } catch (RemoteException ex) { 1663 Slog.w(TAG, "getSessionHintsForCreatingSessionOnHandler: " 1664 + "Failed to request. Router probably died.", ex); 1665 notifyRequestFailedToManager(managerRecord.mManager, 1666 toOriginalRequestId(uniqueRequestId), REASON_UNKNOWN_ERROR); 1667 } 1668 } 1669 requestCreateSessionWithRouter2OnHandler(long uniqueRequestId, long managerRequestId, @NonNull RouterRecord routerRecord, @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route, @Nullable Bundle sessionHints)1670 private void requestCreateSessionWithRouter2OnHandler(long uniqueRequestId, 1671 long managerRequestId, @NonNull RouterRecord routerRecord, 1672 @NonNull RoutingSessionInfo oldSession, 1673 @NonNull MediaRoute2Info route, @Nullable Bundle sessionHints) { 1674 1675 final MediaRoute2Provider provider = findProvider(route.getProviderId()); 1676 if (provider == null) { 1677 Slog.w(TAG, "requestCreateSessionWithRouter2OnHandler: Ignoring session " 1678 + "creation request since no provider found for given route=" + route); 1679 notifySessionCreationFailedToRouter(routerRecord, 1680 toOriginalRequestId(uniqueRequestId)); 1681 return; 1682 } 1683 1684 SessionCreationRequest request = 1685 new SessionCreationRequest(routerRecord, uniqueRequestId, 1686 managerRequestId, oldSession, route); 1687 mSessionCreationRequests.add(request); 1688 1689 provider.requestCreateSession(uniqueRequestId, routerRecord.mPackageName, 1690 route.getOriginalId(), sessionHints); 1691 } 1692 1693 // routerRecord can be null if the session is system's or RCN. selectRouteOnHandler(long uniqueRequestId, @Nullable RouterRecord routerRecord, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route)1694 private void selectRouteOnHandler(long uniqueRequestId, @Nullable RouterRecord routerRecord, 1695 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { 1696 if (!checkArgumentsForSessionControl(routerRecord, uniqueSessionId, route, 1697 "selecting")) { 1698 return; 1699 } 1700 1701 final String providerId = route.getProviderId(); 1702 final MediaRoute2Provider provider = findProvider(providerId); 1703 if (provider == null) { 1704 return; 1705 } 1706 provider.selectRoute(uniqueRequestId, getOriginalId(uniqueSessionId), 1707 route.getOriginalId()); 1708 } 1709 1710 // routerRecord can be null if the session is system's or RCN. deselectRouteOnHandler(long uniqueRequestId, @Nullable RouterRecord routerRecord, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route)1711 private void deselectRouteOnHandler(long uniqueRequestId, 1712 @Nullable RouterRecord routerRecord, 1713 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { 1714 if (!checkArgumentsForSessionControl(routerRecord, uniqueSessionId, route, 1715 "deselecting")) { 1716 return; 1717 } 1718 1719 final String providerId = route.getProviderId(); 1720 final MediaRoute2Provider provider = findProvider(providerId); 1721 if (provider == null) { 1722 return; 1723 } 1724 1725 provider.deselectRoute(uniqueRequestId, getOriginalId(uniqueSessionId), 1726 route.getOriginalId()); 1727 } 1728 1729 // routerRecord can be null if the session is system's or RCN. transferToRouteOnHandler(long uniqueRequestId, @Nullable RouterRecord routerRecord, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route)1730 private void transferToRouteOnHandler(long uniqueRequestId, 1731 @Nullable RouterRecord routerRecord, 1732 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { 1733 if (!checkArgumentsForSessionControl(routerRecord, uniqueSessionId, route, 1734 "transferring to")) { 1735 return; 1736 } 1737 1738 final String providerId = route.getProviderId(); 1739 final MediaRoute2Provider provider = findProvider(providerId); 1740 if (provider == null) { 1741 return; 1742 } 1743 provider.transferToRoute(uniqueRequestId, getOriginalId(uniqueSessionId), 1744 route.getOriginalId()); 1745 } 1746 1747 // routerRecord is null if and only if the session is created without the request, which 1748 // includes the system's session and RCN cases. checkArgumentsForSessionControl(@ullable RouterRecord routerRecord, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route, @NonNull String description)1749 private boolean checkArgumentsForSessionControl(@Nullable RouterRecord routerRecord, 1750 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route, 1751 @NonNull String description) { 1752 final String providerId = route.getProviderId(); 1753 final MediaRoute2Provider provider = findProvider(providerId); 1754 if (provider == null) { 1755 Slog.w(TAG, "Ignoring " + description + " route since no provider found for " 1756 + "given route=" + route); 1757 return false; 1758 } 1759 1760 // Bypass checking router if it's the system session (routerRecord should be null) 1761 if (TextUtils.equals(getProviderId(uniqueSessionId), mSystemProvider.getUniqueId())) { 1762 return true; 1763 } 1764 1765 RouterRecord matchingRecord = mSessionToRouterMap.get(uniqueSessionId); 1766 if (matchingRecord != routerRecord) { 1767 Slog.w(TAG, "Ignoring " + description + " route from non-matching router. " 1768 + "packageName=" + routerRecord.mPackageName + " route=" + route); 1769 return false; 1770 } 1771 1772 final String sessionId = getOriginalId(uniqueSessionId); 1773 if (sessionId == null) { 1774 Slog.w(TAG, "Failed to get original session id from unique session id. " 1775 + "uniqueSessionId=" + uniqueSessionId); 1776 return false; 1777 } 1778 1779 return true; 1780 } 1781 setRouteVolumeOnHandler(long uniqueRequestId, @NonNull MediaRoute2Info route, int volume)1782 private void setRouteVolumeOnHandler(long uniqueRequestId, @NonNull MediaRoute2Info route, 1783 int volume) { 1784 final MediaRoute2Provider provider = findProvider(route.getProviderId()); 1785 if (provider == null) { 1786 Slog.w(TAG, "setRouteVolumeOnHandler: Couldn't find provider for route=" + route); 1787 return; 1788 } 1789 provider.setRouteVolume(uniqueRequestId, route.getOriginalId(), volume); 1790 } 1791 setSessionVolumeOnHandler(long uniqueRequestId, @NonNull String uniqueSessionId, int volume)1792 private void setSessionVolumeOnHandler(long uniqueRequestId, 1793 @NonNull String uniqueSessionId, int volume) { 1794 final MediaRoute2Provider provider = findProvider(getProviderId(uniqueSessionId)); 1795 if (provider == null) { 1796 Slog.w(TAG, "setSessionVolumeOnHandler: Couldn't find provider for session id=" 1797 + uniqueSessionId); 1798 return; 1799 } 1800 provider.setSessionVolume(uniqueRequestId, getOriginalId(uniqueSessionId), volume); 1801 } 1802 releaseSessionOnHandler(long uniqueRequestId, @Nullable RouterRecord routerRecord, @NonNull String uniqueSessionId)1803 private void releaseSessionOnHandler(long uniqueRequestId, 1804 @Nullable RouterRecord routerRecord, @NonNull String uniqueSessionId) { 1805 final RouterRecord matchingRecord = mSessionToRouterMap.get(uniqueSessionId); 1806 if (matchingRecord != routerRecord) { 1807 Slog.w(TAG, "Ignoring releasing session from non-matching router. packageName=" 1808 + (routerRecord == null ? null : routerRecord.mPackageName) 1809 + " uniqueSessionId=" + uniqueSessionId); 1810 return; 1811 } 1812 1813 final String providerId = getProviderId(uniqueSessionId); 1814 if (providerId == null) { 1815 Slog.w(TAG, "Ignoring releasing session with invalid unique session ID. " 1816 + "uniqueSessionId=" + uniqueSessionId); 1817 return; 1818 } 1819 1820 final String sessionId = getOriginalId(uniqueSessionId); 1821 if (sessionId == null) { 1822 Slog.w(TAG, "Ignoring releasing session with invalid unique session ID. " 1823 + "uniqueSessionId=" + uniqueSessionId + " providerId=" + providerId); 1824 return; 1825 } 1826 1827 final MediaRoute2Provider provider = findProvider(providerId); 1828 if (provider == null) { 1829 Slog.w(TAG, "Ignoring releasing session since no provider found for given " 1830 + "providerId=" + providerId); 1831 return; 1832 } 1833 1834 provider.releaseSession(uniqueRequestId, sessionId); 1835 } 1836 onSessionCreatedOnHandler(@onNull MediaRoute2Provider provider, long uniqueRequestId, @NonNull RoutingSessionInfo sessionInfo)1837 private void onSessionCreatedOnHandler(@NonNull MediaRoute2Provider provider, 1838 long uniqueRequestId, @NonNull RoutingSessionInfo sessionInfo) { 1839 SessionCreationRequest matchingRequest = null; 1840 1841 for (SessionCreationRequest request : mSessionCreationRequests) { 1842 if (request.mUniqueRequestId == uniqueRequestId 1843 && TextUtils.equals( 1844 request.mRoute.getProviderId(), provider.getUniqueId())) { 1845 matchingRequest = request; 1846 break; 1847 } 1848 } 1849 1850 long managerRequestId = (matchingRequest == null) 1851 ? MediaRoute2ProviderService.REQUEST_ID_NONE 1852 : matchingRequest.mManagerRequestId; 1853 notifySessionCreatedToManagers(managerRequestId, sessionInfo); 1854 1855 if (matchingRequest == null) { 1856 Slog.w(TAG, "Ignoring session creation result for unknown request. " 1857 + "uniqueRequestId=" + uniqueRequestId + ", sessionInfo=" + sessionInfo); 1858 return; 1859 } 1860 1861 mSessionCreationRequests.remove(matchingRequest); 1862 // Not to show old session 1863 MediaRoute2Provider oldProvider = 1864 findProvider(matchingRequest.mOldSession.getProviderId()); 1865 if (oldProvider != null) { 1866 oldProvider.prepareReleaseSession(matchingRequest.mOldSession.getId()); 1867 } else { 1868 Slog.w(TAG, "onSessionCreatedOnHandler: Can't find provider for an old session. " 1869 + "session=" + matchingRequest.mOldSession); 1870 } 1871 1872 // Succeeded 1873 if (sessionInfo.isSystemSession() 1874 && !matchingRequest.mRouterRecord.mHasModifyAudioRoutingPermission) { 1875 notifySessionCreatedToRouter(matchingRequest.mRouterRecord, 1876 toOriginalRequestId(uniqueRequestId), 1877 mSystemProvider.getDefaultSessionInfo()); 1878 } else { 1879 notifySessionCreatedToRouter(matchingRequest.mRouterRecord, 1880 toOriginalRequestId(uniqueRequestId), sessionInfo); 1881 } 1882 mSessionToRouterMap.put(sessionInfo.getId(), matchingRequest.mRouterRecord); 1883 } 1884 onSessionInfoChangedOnHandler(@onNull MediaRoute2Provider provider, @NonNull RoutingSessionInfo sessionInfo)1885 private void onSessionInfoChangedOnHandler(@NonNull MediaRoute2Provider provider, 1886 @NonNull RoutingSessionInfo sessionInfo) { 1887 List<IMediaRouter2Manager> managers = getManagers(); 1888 notifySessionUpdatedToManagers(managers, sessionInfo); 1889 1890 // For system provider, notify all routers. 1891 if (provider == mSystemProvider) { 1892 MediaRouter2ServiceImpl service = mServiceRef.get(); 1893 if (service == null) { 1894 return; 1895 } 1896 notifySessionInfoChangedToRouters(getRouters(true), sessionInfo); 1897 notifySessionInfoChangedToRouters(getRouters(false), 1898 mSystemProvider.getDefaultSessionInfo()); 1899 return; 1900 } 1901 1902 RouterRecord routerRecord = mSessionToRouterMap.get(sessionInfo.getId()); 1903 if (routerRecord == null) { 1904 Slog.w(TAG, "onSessionInfoChangedOnHandler: No matching router found for session=" 1905 + sessionInfo); 1906 return; 1907 } 1908 notifySessionInfoChangedToRouter(routerRecord, sessionInfo); 1909 } 1910 onSessionReleasedOnHandler(@onNull MediaRoute2Provider provider, @NonNull RoutingSessionInfo sessionInfo)1911 private void onSessionReleasedOnHandler(@NonNull MediaRoute2Provider provider, 1912 @NonNull RoutingSessionInfo sessionInfo) { 1913 List<IMediaRouter2Manager> managers = getManagers(); 1914 notifySessionReleasedToManagers(managers, sessionInfo); 1915 1916 RouterRecord routerRecord = mSessionToRouterMap.get(sessionInfo.getId()); 1917 if (routerRecord == null) { 1918 Slog.w(TAG, "onSessionReleasedOnHandler: No matching router found for session=" 1919 + sessionInfo); 1920 return; 1921 } 1922 notifySessionReleasedToRouter(routerRecord, sessionInfo); 1923 } 1924 onRequestFailedOnHandler(@onNull MediaRoute2Provider provider, long uniqueRequestId, int reason)1925 private void onRequestFailedOnHandler(@NonNull MediaRoute2Provider provider, 1926 long uniqueRequestId, int reason) { 1927 if (handleSessionCreationRequestFailed(provider, uniqueRequestId, reason)) { 1928 return; 1929 } 1930 1931 final int requesterId = toRequesterId(uniqueRequestId); 1932 ManagerRecord manager = findManagerWithId(requesterId); 1933 if (manager != null) { 1934 notifyRequestFailedToManager( 1935 manager.mManager, toOriginalRequestId(uniqueRequestId), reason); 1936 return; 1937 } 1938 1939 // Currently, only the manager can get notified of failures. 1940 // TODO: Notify router too when the related callback is introduced. 1941 } 1942 handleSessionCreationRequestFailed(@onNull MediaRoute2Provider provider, long uniqueRequestId, int reason)1943 private boolean handleSessionCreationRequestFailed(@NonNull MediaRoute2Provider provider, 1944 long uniqueRequestId, int reason) { 1945 // Check whether the failure is about creating a session 1946 SessionCreationRequest matchingRequest = null; 1947 for (SessionCreationRequest request : mSessionCreationRequests) { 1948 if (request.mUniqueRequestId == uniqueRequestId && TextUtils.equals( 1949 request.mRoute.getProviderId(), provider.getUniqueId())) { 1950 matchingRequest = request; 1951 break; 1952 } 1953 } 1954 1955 if (matchingRequest == null) { 1956 // The failure is not about creating a session. 1957 return false; 1958 } 1959 1960 mSessionCreationRequests.remove(matchingRequest); 1961 1962 // Notify the requester about the failure. 1963 // The call should be made by either MediaRouter2 or MediaRouter2Manager. 1964 if (matchingRequest.mManagerRequestId == MediaRouter2Manager.REQUEST_ID_NONE) { 1965 notifySessionCreationFailedToRouter( 1966 matchingRequest.mRouterRecord, toOriginalRequestId(uniqueRequestId)); 1967 } else { 1968 final int requesterId = toRequesterId(matchingRequest.mManagerRequestId); 1969 ManagerRecord manager = findManagerWithId(requesterId); 1970 if (manager != null) { 1971 notifyRequestFailedToManager(manager.mManager, 1972 toOriginalRequestId(matchingRequest.mManagerRequestId), reason); 1973 } 1974 } 1975 return true; 1976 } 1977 notifySessionCreatedToRouter(@onNull RouterRecord routerRecord, int requestId, @NonNull RoutingSessionInfo sessionInfo)1978 private void notifySessionCreatedToRouter(@NonNull RouterRecord routerRecord, 1979 int requestId, @NonNull RoutingSessionInfo sessionInfo) { 1980 try { 1981 routerRecord.mRouter.notifySessionCreated(requestId, sessionInfo); 1982 } catch (RemoteException ex) { 1983 Slog.w(TAG, "Failed to notify router of the session creation." 1984 + " Router probably died.", ex); 1985 } 1986 } 1987 notifySessionCreationFailedToRouter(@onNull RouterRecord routerRecord, int requestId)1988 private void notifySessionCreationFailedToRouter(@NonNull RouterRecord routerRecord, 1989 int requestId) { 1990 try { 1991 routerRecord.mRouter.notifySessionCreated(requestId, 1992 /* sessionInfo= */ null); 1993 } catch (RemoteException ex) { 1994 Slog.w(TAG, "Failed to notify router of the session creation failure." 1995 + " Router probably died.", ex); 1996 } 1997 } 1998 notifySessionInfoChangedToRouter(@onNull RouterRecord routerRecord, @NonNull RoutingSessionInfo sessionInfo)1999 private void notifySessionInfoChangedToRouter(@NonNull RouterRecord routerRecord, 2000 @NonNull RoutingSessionInfo sessionInfo) { 2001 try { 2002 routerRecord.mRouter.notifySessionInfoChanged(sessionInfo); 2003 } catch (RemoteException ex) { 2004 Slog.w(TAG, "Failed to notify router of the session info change." 2005 + " Router probably died.", ex); 2006 } 2007 } 2008 notifySessionReleasedToRouter(@onNull RouterRecord routerRecord, @NonNull RoutingSessionInfo sessionInfo)2009 private void notifySessionReleasedToRouter(@NonNull RouterRecord routerRecord, 2010 @NonNull RoutingSessionInfo sessionInfo) { 2011 try { 2012 routerRecord.mRouter.notifySessionReleased(sessionInfo); 2013 } catch (RemoteException ex) { 2014 Slog.w(TAG, "Failed to notify router of the session release." 2015 + " Router probably died.", ex); 2016 } 2017 } 2018 getAllRouters()2019 private List<IMediaRouter2> getAllRouters() { 2020 final List<IMediaRouter2> routers = new ArrayList<>(); 2021 MediaRouter2ServiceImpl service = mServiceRef.get(); 2022 if (service == null) { 2023 return routers; 2024 } 2025 synchronized (service.mLock) { 2026 for (RouterRecord routerRecord : mUserRecord.mRouterRecords) { 2027 routers.add(routerRecord.mRouter); 2028 } 2029 } 2030 return routers; 2031 } 2032 getRouters(boolean hasModifyAudioRoutingPermission)2033 private List<IMediaRouter2> getRouters(boolean hasModifyAudioRoutingPermission) { 2034 final List<IMediaRouter2> routers = new ArrayList<>(); 2035 MediaRouter2ServiceImpl service = mServiceRef.get(); 2036 if (service == null) { 2037 return routers; 2038 } 2039 synchronized (service.mLock) { 2040 for (RouterRecord routerRecord : mUserRecord.mRouterRecords) { 2041 if (hasModifyAudioRoutingPermission 2042 == routerRecord.mHasModifyAudioRoutingPermission) { 2043 routers.add(routerRecord.mRouter); 2044 } 2045 } 2046 } 2047 return routers; 2048 } 2049 getManagers()2050 private List<IMediaRouter2Manager> getManagers() { 2051 final List<IMediaRouter2Manager> managers = new ArrayList<>(); 2052 MediaRouter2ServiceImpl service = mServiceRef.get(); 2053 if (service == null) { 2054 return managers; 2055 } 2056 synchronized (service.mLock) { 2057 for (ManagerRecord managerRecord : mUserRecord.mManagerRecords) { 2058 managers.add(managerRecord.mManager); 2059 } 2060 } 2061 return managers; 2062 } 2063 getRouterRecords()2064 private List<RouterRecord> getRouterRecords() { 2065 MediaRouter2ServiceImpl service = mServiceRef.get(); 2066 if (service == null) { 2067 return Collections.emptyList(); 2068 } 2069 synchronized (service.mLock) { 2070 return new ArrayList<>(mUserRecord.mRouterRecords); 2071 } 2072 } 2073 getManagerRecords()2074 private List<ManagerRecord> getManagerRecords() { 2075 MediaRouter2ServiceImpl service = mServiceRef.get(); 2076 if (service == null) { 2077 return Collections.emptyList(); 2078 } 2079 synchronized (service.mLock) { 2080 return new ArrayList<>(mUserRecord.mManagerRecords); 2081 } 2082 } 2083 notifyRouterRegistered(@onNull RouterRecord routerRecord)2084 private void notifyRouterRegistered(@NonNull RouterRecord routerRecord) { 2085 List<MediaRoute2Info> currentRoutes = new ArrayList<>(); 2086 2087 MediaRoute2ProviderInfo systemProviderInfo = null; 2088 for (MediaRoute2ProviderInfo providerInfo : mLastProviderInfos) { 2089 // TODO: Create MediaRoute2ProviderInfo#isSystemProvider() 2090 if (TextUtils.equals(providerInfo.getUniqueId(), mSystemProvider.getUniqueId())) { 2091 // Adding routes from system provider will be handled below, so skip it here. 2092 systemProviderInfo = providerInfo; 2093 continue; 2094 } 2095 currentRoutes.addAll(providerInfo.getRoutes()); 2096 } 2097 2098 RoutingSessionInfo currentSystemSessionInfo; 2099 if (routerRecord.mHasModifyAudioRoutingPermission) { 2100 if (systemProviderInfo != null) { 2101 currentRoutes.addAll(systemProviderInfo.getRoutes()); 2102 } else { 2103 // This shouldn't happen. 2104 Slog.wtf(TAG, "System route provider not found."); 2105 } 2106 currentSystemSessionInfo = mSystemProvider.getSessionInfos().get(0); 2107 } else { 2108 currentRoutes.add(mSystemProvider.getDefaultRoute()); 2109 currentSystemSessionInfo = mSystemProvider.getDefaultSessionInfo(); 2110 } 2111 2112 if (currentRoutes.size() == 0) { 2113 return; 2114 } 2115 2116 try { 2117 routerRecord.mRouter.notifyRouterRegistered( 2118 currentRoutes, currentSystemSessionInfo); 2119 } catch (RemoteException ex) { 2120 Slog.w(TAG, "Failed to notify router registered. Router probably died.", ex); 2121 } 2122 } 2123 notifyRoutesAddedToRouters(@onNull List<IMediaRouter2> routers, @NonNull List<MediaRoute2Info> routes)2124 private void notifyRoutesAddedToRouters(@NonNull List<IMediaRouter2> routers, 2125 @NonNull List<MediaRoute2Info> routes) { 2126 for (IMediaRouter2 router : routers) { 2127 try { 2128 router.notifyRoutesAdded(routes); 2129 } catch (RemoteException ex) { 2130 Slog.w(TAG, "Failed to notify routes added. Router probably died.", ex); 2131 } 2132 } 2133 } 2134 notifyRoutesRemovedToRouters(@onNull List<IMediaRouter2> routers, @NonNull List<MediaRoute2Info> routes)2135 private void notifyRoutesRemovedToRouters(@NonNull List<IMediaRouter2> routers, 2136 @NonNull List<MediaRoute2Info> routes) { 2137 for (IMediaRouter2 router : routers) { 2138 try { 2139 router.notifyRoutesRemoved(routes); 2140 } catch (RemoteException ex) { 2141 Slog.w(TAG, "Failed to notify routes removed. Router probably died.", ex); 2142 } 2143 } 2144 } 2145 notifyRoutesChangedToRouters(@onNull List<IMediaRouter2> routers, @NonNull List<MediaRoute2Info> routes)2146 private void notifyRoutesChangedToRouters(@NonNull List<IMediaRouter2> routers, 2147 @NonNull List<MediaRoute2Info> routes) { 2148 for (IMediaRouter2 router : routers) { 2149 try { 2150 router.notifyRoutesChanged(routes); 2151 } catch (RemoteException ex) { 2152 Slog.w(TAG, "Failed to notify routes changed. Router probably died.", ex); 2153 } 2154 } 2155 } 2156 notifySessionInfoChangedToRouters(@onNull List<IMediaRouter2> routers, @NonNull RoutingSessionInfo sessionInfo)2157 private void notifySessionInfoChangedToRouters(@NonNull List<IMediaRouter2> routers, 2158 @NonNull RoutingSessionInfo sessionInfo) { 2159 for (IMediaRouter2 router : routers) { 2160 try { 2161 router.notifySessionInfoChanged(sessionInfo); 2162 } catch (RemoteException ex) { 2163 Slog.w(TAG, "Failed to notify session info changed. Router probably died.", ex); 2164 } 2165 } 2166 } 2167 notifyRoutesToManager(@onNull IMediaRouter2Manager manager)2168 private void notifyRoutesToManager(@NonNull IMediaRouter2Manager manager) { 2169 List<MediaRoute2Info> routes = new ArrayList<>(); 2170 for (MediaRoute2ProviderInfo providerInfo : mLastProviderInfos) { 2171 routes.addAll(providerInfo.getRoutes()); 2172 } 2173 if (routes.size() == 0) { 2174 return; 2175 } 2176 try { 2177 manager.notifyRoutesAdded(routes); 2178 } catch (RemoteException ex) { 2179 Slog.w(TAG, "Failed to notify all routes. Manager probably died.", ex); 2180 } 2181 } 2182 notifyRoutesAddedToManagers(@onNull List<IMediaRouter2Manager> managers, @NonNull List<MediaRoute2Info> routes)2183 private void notifyRoutesAddedToManagers(@NonNull List<IMediaRouter2Manager> managers, 2184 @NonNull List<MediaRoute2Info> routes) { 2185 for (IMediaRouter2Manager manager : managers) { 2186 try { 2187 manager.notifyRoutesAdded(routes); 2188 } catch (RemoteException ex) { 2189 Slog.w(TAG, "Failed to notify routes added. Manager probably died.", ex); 2190 } 2191 } 2192 } 2193 notifyRoutesRemovedToManagers(@onNull List<IMediaRouter2Manager> managers, @NonNull List<MediaRoute2Info> routes)2194 private void notifyRoutesRemovedToManagers(@NonNull List<IMediaRouter2Manager> managers, 2195 @NonNull List<MediaRoute2Info> routes) { 2196 for (IMediaRouter2Manager manager : managers) { 2197 try { 2198 manager.notifyRoutesRemoved(routes); 2199 } catch (RemoteException ex) { 2200 Slog.w(TAG, "Failed to notify routes removed. Manager probably died.", ex); 2201 } 2202 } 2203 } 2204 notifyRoutesChangedToManagers(@onNull List<IMediaRouter2Manager> managers, @NonNull List<MediaRoute2Info> routes)2205 private void notifyRoutesChangedToManagers(@NonNull List<IMediaRouter2Manager> managers, 2206 @NonNull List<MediaRoute2Info> routes) { 2207 for (IMediaRouter2Manager manager : managers) { 2208 try { 2209 manager.notifyRoutesChanged(routes); 2210 } catch (RemoteException ex) { 2211 Slog.w(TAG, "Failed to notify routes changed. Manager probably died.", ex); 2212 } 2213 } 2214 } 2215 notifySessionCreatedToManagers(long managerRequestId, @NonNull RoutingSessionInfo session)2216 private void notifySessionCreatedToManagers(long managerRequestId, 2217 @NonNull RoutingSessionInfo session) { 2218 int requesterId = toRequesterId(managerRequestId); 2219 int originalRequestId = toOriginalRequestId(managerRequestId); 2220 2221 for (ManagerRecord manager : getManagerRecords()) { 2222 try { 2223 manager.mManager.notifySessionCreated( 2224 ((manager.mManagerId == requesterId) ? originalRequestId : 2225 MediaRouter2Manager.REQUEST_ID_NONE), session); 2226 } catch (RemoteException ex) { 2227 Slog.w(TAG, "notifySessionCreatedToManagers: " 2228 + "Failed to notify. Manager probably died.", ex); 2229 } 2230 } 2231 } 2232 notifySessionUpdatedToManagers( @onNull List<IMediaRouter2Manager> managers, @NonNull RoutingSessionInfo sessionInfo)2233 private void notifySessionUpdatedToManagers( 2234 @NonNull List<IMediaRouter2Manager> managers, 2235 @NonNull RoutingSessionInfo sessionInfo) { 2236 for (IMediaRouter2Manager manager : managers) { 2237 try { 2238 manager.notifySessionUpdated(sessionInfo); 2239 } catch (RemoteException ex) { 2240 Slog.w(TAG, "notifySessionUpdatedToManagers: " 2241 + "Failed to notify. Manager probably died.", ex); 2242 } 2243 } 2244 } 2245 notifySessionReleasedToManagers( @onNull List<IMediaRouter2Manager> managers, @NonNull RoutingSessionInfo sessionInfo)2246 private void notifySessionReleasedToManagers( 2247 @NonNull List<IMediaRouter2Manager> managers, 2248 @NonNull RoutingSessionInfo sessionInfo) { 2249 for (IMediaRouter2Manager manager : managers) { 2250 try { 2251 manager.notifySessionReleased(sessionInfo); 2252 } catch (RemoteException ex) { 2253 Slog.w(TAG, "notifySessionReleasedToManagers: " 2254 + "Failed to notify. Manager probably died.", ex); 2255 } 2256 } 2257 } 2258 notifyDiscoveryPreferenceChangedToManager(@onNull RouterRecord routerRecord, @NonNull IMediaRouter2Manager manager)2259 private void notifyDiscoveryPreferenceChangedToManager(@NonNull RouterRecord routerRecord, 2260 @NonNull IMediaRouter2Manager manager) { 2261 try { 2262 manager.notifyDiscoveryPreferenceChanged(routerRecord.mPackageName, 2263 routerRecord.mDiscoveryPreference); 2264 } catch (RemoteException ex) { 2265 Slog.w(TAG, "Failed to notify preferred features changed." 2266 + " Manager probably died.", ex); 2267 } 2268 } 2269 notifyDiscoveryPreferenceChangedToManagers(@onNull String routerPackageName, @Nullable RouteDiscoveryPreference discoveryPreference)2270 private void notifyDiscoveryPreferenceChangedToManagers(@NonNull String routerPackageName, 2271 @Nullable RouteDiscoveryPreference discoveryPreference) { 2272 MediaRouter2ServiceImpl service = mServiceRef.get(); 2273 if (service == null) { 2274 return; 2275 } 2276 List<IMediaRouter2Manager> managers = new ArrayList<>(); 2277 synchronized (service.mLock) { 2278 for (ManagerRecord managerRecord : mUserRecord.mManagerRecords) { 2279 managers.add(managerRecord.mManager); 2280 } 2281 } 2282 for (IMediaRouter2Manager manager : managers) { 2283 try { 2284 manager.notifyDiscoveryPreferenceChanged(routerPackageName, 2285 discoveryPreference); 2286 } catch (RemoteException ex) { 2287 Slog.w(TAG, "Failed to notify preferred features changed." 2288 + " Manager probably died.", ex); 2289 } 2290 } 2291 } 2292 notifyRequestFailedToManager(@onNull IMediaRouter2Manager manager, int requestId, int reason)2293 private void notifyRequestFailedToManager(@NonNull IMediaRouter2Manager manager, 2294 int requestId, int reason) { 2295 try { 2296 manager.notifyRequestFailed(requestId, reason); 2297 } catch (RemoteException ex) { 2298 Slog.w(TAG, "Failed to notify manager of the request failure." 2299 + " Manager probably died.", ex); 2300 } 2301 } 2302 updateDiscoveryPreferenceOnHandler()2303 private void updateDiscoveryPreferenceOnHandler() { 2304 MediaRouter2ServiceImpl service = mServiceRef.get(); 2305 if (service == null) { 2306 return; 2307 } 2308 List<RouteDiscoveryPreference> discoveryPreferences = Collections.emptyList(); 2309 List<RouterRecord> routerRecords = getRouterRecords(); 2310 List<ManagerRecord> managerRecords = getManagerRecords(); 2311 2312 boolean shouldBindProviders = false; 2313 2314 if (service.mPowerManager.isInteractive()) { 2315 boolean isManagerScanning = managerRecords.stream().anyMatch(manager -> 2316 manager.mIsScanning && service.mActivityManager 2317 .getPackageImportance(manager.mPackageName) 2318 <= PACKAGE_IMPORTANCE_FOR_DISCOVERY); 2319 2320 if (isManagerScanning) { 2321 discoveryPreferences = routerRecords.stream() 2322 .map(record -> record.mDiscoveryPreference) 2323 .collect(Collectors.toList()); 2324 shouldBindProviders = true; 2325 } else { 2326 discoveryPreferences = routerRecords.stream().filter(record -> 2327 service.mActivityManager.getPackageImportance(record.mPackageName) 2328 <= PACKAGE_IMPORTANCE_FOR_DISCOVERY) 2329 .map(record -> record.mDiscoveryPreference) 2330 .collect(Collectors.toList()); 2331 } 2332 } 2333 2334 for (MediaRoute2Provider provider : mRouteProviders) { 2335 if (provider instanceof MediaRoute2ProviderServiceProxy) { 2336 ((MediaRoute2ProviderServiceProxy) provider) 2337 .setManagerScanning(shouldBindProviders); 2338 } 2339 } 2340 2341 // Build a composite RouteDiscoveryPreference that matches all of the routes 2342 // that match one or more of the individual discovery preferences. It may also 2343 // match additional routes. The composite RouteDiscoveryPreference can be used 2344 // to query route providers once to obtain all of the routes of interest, which 2345 // can be subsequently filtered for the individual discovery preferences. 2346 Set<String> preferredFeatures = new HashSet<>(); 2347 boolean activeScan = false; 2348 for (RouteDiscoveryPreference preference : discoveryPreferences) { 2349 preferredFeatures.addAll(preference.getPreferredFeatures()); 2350 activeScan |= preference.shouldPerformActiveScan(); 2351 } 2352 RouteDiscoveryPreference newPreference = new RouteDiscoveryPreference.Builder( 2353 List.copyOf(preferredFeatures), activeScan).build(); 2354 2355 synchronized (service.mLock) { 2356 if (newPreference.equals(mUserRecord.mCompositeDiscoveryPreference)) { 2357 return; 2358 } 2359 mUserRecord.mCompositeDiscoveryPreference = newPreference; 2360 } 2361 for (MediaRoute2Provider provider : mRouteProviders) { 2362 provider.updateDiscoveryPreference(mUserRecord.mCompositeDiscoveryPreference); 2363 } 2364 } 2365 findProvider(@ullable String providerId)2366 private MediaRoute2Provider findProvider(@Nullable String providerId) { 2367 for (MediaRoute2Provider provider : mRouteProviders) { 2368 if (TextUtils.equals(provider.getUniqueId(), providerId)) { 2369 return provider; 2370 } 2371 } 2372 return null; 2373 } 2374 2375 } 2376 static final class SessionCreationRequest { 2377 public final RouterRecord mRouterRecord; 2378 public final long mUniqueRequestId; 2379 public final long mManagerRequestId; 2380 public final RoutingSessionInfo mOldSession; 2381 public final MediaRoute2Info mRoute; 2382 SessionCreationRequest(@onNull RouterRecord routerRecord, long uniqueRequestId, long managerRequestId, @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route)2383 SessionCreationRequest(@NonNull RouterRecord routerRecord, long uniqueRequestId, 2384 long managerRequestId, @NonNull RoutingSessionInfo oldSession, 2385 @NonNull MediaRoute2Info route) { 2386 mRouterRecord = routerRecord; 2387 mUniqueRequestId = uniqueRequestId; 2388 mManagerRequestId = managerRequestId; 2389 mOldSession = oldSession; 2390 mRoute = route; 2391 } 2392 } 2393 } 2394