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; 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.MediaRouter2.SCANNING_STATE_NOT_SCANNING; 24 import static android.media.MediaRouter2.SCANNING_STATE_SCANNING_FULL; 25 import static android.media.MediaRouter2.SCANNING_STATE_WHILE_INTERACTIVE; 26 import static android.media.MediaRouter2Utils.getOriginalId; 27 import static android.media.MediaRouter2Utils.getProviderId; 28 29 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; 30 31 import android.Manifest; 32 import android.annotation.NonNull; 33 import android.annotation.Nullable; 34 import android.annotation.RequiresPermission; 35 import android.app.ActivityManager; 36 import android.app.AppOpsManager; 37 import android.content.BroadcastReceiver; 38 import android.content.ComponentName; 39 import android.content.Context; 40 import android.content.Intent; 41 import android.content.IntentFilter; 42 import android.content.PermissionChecker; 43 import android.content.pm.PackageManager; 44 import android.media.IMediaRouter2; 45 import android.media.IMediaRouter2Manager; 46 import android.media.MediaRoute2Info; 47 import android.media.MediaRoute2ProviderInfo; 48 import android.media.MediaRoute2ProviderService; 49 import android.media.MediaRouter2.ScanningState; 50 import android.media.MediaRouter2Manager; 51 import android.media.RouteDiscoveryPreference; 52 import android.media.RouteListingPreference; 53 import android.media.RoutingSessionInfo; 54 import android.os.Binder; 55 import android.os.Bundle; 56 import android.os.Handler; 57 import android.os.IBinder; 58 import android.os.Looper; 59 import android.os.PowerManager; 60 import android.os.RemoteException; 61 import android.os.UserHandle; 62 import android.text.TextUtils; 63 import android.util.ArrayMap; 64 import android.util.Log; 65 import android.util.Slog; 66 import android.util.SparseArray; 67 68 import com.android.internal.annotations.GuardedBy; 69 import com.android.internal.util.function.pooled.PooledLambda; 70 import com.android.media.flags.Flags; 71 import com.android.server.LocalServices; 72 import com.android.server.pm.UserManagerInternal; 73 import com.android.server.statusbar.StatusBarManagerInternal; 74 75 import java.io.PrintWriter; 76 import java.lang.ref.WeakReference; 77 import java.util.ArrayList; 78 import java.util.Arrays; 79 import java.util.Collection; 80 import java.util.Collections; 81 import java.util.HashSet; 82 import java.util.List; 83 import java.util.Map; 84 import java.util.Objects; 85 import java.util.Optional; 86 import java.util.Set; 87 import java.util.concurrent.CopyOnWriteArrayList; 88 import java.util.concurrent.atomic.AtomicBoolean; 89 import java.util.concurrent.atomic.AtomicInteger; 90 import java.util.stream.Collectors; 91 92 /** 93 * Implements features related to {@link android.media.MediaRouter2} and 94 * {@link android.media.MediaRouter2Manager}. 95 */ 96 class MediaRouter2ServiceImpl { 97 private static final String TAG = "MR2ServiceImpl"; 98 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 99 100 // TODO: (In Android S or later) if we add callback methods for generic failures 101 // in MediaRouter2, remove this constant and replace the usages with the real request IDs. 102 private static final long DUMMY_REQUEST_ID = -1; 103 104 private static final int REQUIRED_PACKAGE_IMPORTANCE_FOR_SCANNING = IMPORTANCE_FOREGROUND; 105 106 /** 107 * Contains the list of bluetooth permissions that are required to do system routing. 108 * 109 * <p>Alternatively, apps that hold {@link android.Manifest.permission#MODIFY_AUDIO_ROUTING} are 110 * also allowed to do system routing. 111 */ 112 private static final String[] BLUETOOTH_PERMISSIONS_FOR_SYSTEM_ROUTING = 113 new String[] { 114 Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.BLUETOOTH_SCAN 115 }; 116 117 private final Context mContext; 118 private final Looper mLooper; 119 private final UserManagerInternal mUserManagerInternal; 120 private final Object mLock = new Object(); 121 private final AppOpsManager mAppOpsManager; 122 private final StatusBarManagerInternal mStatusBarManagerInternal; 123 final AtomicInteger mNextRouterOrManagerId = new AtomicInteger(1); 124 final ActivityManager mActivityManager; 125 final PowerManager mPowerManager; 126 127 @GuardedBy("mLock") 128 private final SparseArray<UserRecord> mUserRecords = new SparseArray<>(); 129 @GuardedBy("mLock") 130 private final ArrayMap<IBinder, RouterRecord> mAllRouterRecords = new ArrayMap<>(); 131 @GuardedBy("mLock") 132 private final ArrayMap<IBinder, ManagerRecord> mAllManagerRecords = new ArrayMap<>(); 133 @GuardedBy("mLock") 134 private int mCurrentActiveUserId = -1; 135 136 private final ActivityManager.OnUidImportanceListener mOnUidImportanceListener = 137 (uid, importance) -> { 138 synchronized (mLock) { 139 final int count = mUserRecords.size(); 140 for (int i = 0; i < count; i++) { 141 mUserRecords.valueAt(i).mHandler.maybeUpdateDiscoveryPreferenceForUid(uid); 142 } 143 } 144 }; 145 146 private final BroadcastReceiver mScreenOnOffReceiver = new BroadcastReceiver() { 147 @Override 148 public void onReceive(Context context, Intent intent) { 149 synchronized (mLock) { 150 final int count = mUserRecords.size(); 151 for (int i = 0; i < count; i++) { 152 UserHandler userHandler = mUserRecords.valueAt(i).mHandler; 153 userHandler.sendMessage(PooledLambda.obtainMessage( 154 UserHandler::updateDiscoveryPreferenceOnHandler, userHandler)); 155 } 156 } 157 } 158 }; 159 160 private final AppOpsManager.OnOpChangedListener mOnOpChangedListener = 161 new AppOpsManager.OnOpChangedListener() { 162 @Override 163 public void onOpChanged(String op, String packageName) { 164 // Do nothing. 165 } 166 167 @Override 168 public void onOpChanged( 169 @NonNull String op, @NonNull String packageName, int userId) { 170 if (!TextUtils.equals(op, AppOpsManager.OPSTR_MEDIA_ROUTING_CONTROL)) { 171 return; 172 } 173 synchronized (mLock) { 174 revokeManagerRecordAccessIfNeededLocked(packageName, userId); 175 } 176 } 177 }; 178 179 @RequiresPermission( 180 allOf = { 181 Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS, 182 Manifest.permission.WATCH_APPOPS 183 }) MediaRouter2ServiceImpl(@onNull Context context, @NonNull Looper looper)184 /* package */ MediaRouter2ServiceImpl(@NonNull Context context, @NonNull Looper looper) { 185 mContext = context; 186 mLooper = looper; 187 mActivityManager = mContext.getSystemService(ActivityManager.class); 188 mActivityManager.addOnUidImportanceListener(mOnUidImportanceListener, 189 REQUIRED_PACKAGE_IMPORTANCE_FOR_SCANNING); 190 mPowerManager = mContext.getSystemService(PowerManager.class); 191 mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); 192 mAppOpsManager = mContext.getSystemService(AppOpsManager.class); 193 mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class); 194 195 IntentFilter screenOnOffIntentFilter = new IntentFilter(); 196 screenOnOffIntentFilter.addAction(ACTION_SCREEN_ON); 197 screenOnOffIntentFilter.addAction(ACTION_SCREEN_OFF); 198 mContext.registerReceiver(mScreenOnOffReceiver, screenOnOffIntentFilter); 199 200 // Passing null package name to listen to all events. 201 mAppOpsManager.startWatchingMode( 202 AppOpsManager.OP_MEDIA_ROUTING_CONTROL, 203 /* packageName */ null, 204 mOnOpChangedListener); 205 206 mContext.getPackageManager().addOnPermissionsChangeListener(this::onPermissionsChanged); 207 } 208 209 /** 210 * Called when there's a change in the permissions of an app. 211 * 212 * @param uid The uid of the app whose permissions changed. 213 */ onPermissionsChanged(int uid)214 private void onPermissionsChanged(int uid) { 215 synchronized (mLock) { 216 Optional<RouterRecord> affectedRouter = 217 mAllRouterRecords.values().stream().filter(it -> it.mUid == uid).findFirst(); 218 if (affectedRouter.isPresent()) { 219 affectedRouter.get().maybeUpdateSystemRoutingPermissionLocked(); 220 } 221 } 222 } 223 224 // Start of methods that implement MediaRouter2 operations. 225 226 @NonNull getSystemRoutes(@onNull String callerPackageName, boolean isProxyRouter)227 public List<MediaRoute2Info> getSystemRoutes(@NonNull String callerPackageName, 228 boolean isProxyRouter) { 229 final int uid = Binder.getCallingUid(); 230 final int pid = Binder.getCallingPid(); 231 final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); 232 233 boolean hasSystemRoutingPermissions; 234 if (!isProxyRouter) { 235 hasSystemRoutingPermissions = checkCallerHasSystemRoutingPermissions(pid, uid); 236 } else { 237 // Request from ProxyRouter. 238 hasSystemRoutingPermissions = 239 checkCallerHasPrivilegedRoutingPermissions(pid, uid, callerPackageName); 240 } 241 242 final long token = Binder.clearCallingIdentity(); 243 try { 244 Collection<MediaRoute2Info> systemRoutes; 245 synchronized (mLock) { 246 UserRecord userRecord = getOrCreateUserRecordLocked(userId); 247 if (hasSystemRoutingPermissions) { 248 MediaRoute2ProviderInfo providerInfo = 249 userRecord.mHandler.mSystemProvider.getProviderInfo(); 250 if (providerInfo != null) { 251 systemRoutes = providerInfo.getRoutes(); 252 } else { 253 systemRoutes = Collections.emptyList(); 254 } 255 } else { 256 systemRoutes = new ArrayList<>(); 257 systemRoutes.add(userRecord.mHandler.mSystemProvider.getDefaultRoute()); 258 } 259 } 260 return new ArrayList<>(systemRoutes); 261 } finally { 262 Binder.restoreCallingIdentity(token); 263 } 264 } 265 266 @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS) showMediaOutputSwitcherWithRouter2(@onNull String packageName)267 public boolean showMediaOutputSwitcherWithRouter2(@NonNull String packageName) { 268 UserHandle userHandle = Binder.getCallingUserHandle(); 269 final long token = Binder.clearCallingIdentity(); 270 try { 271 return showOutputSwitcher(packageName, userHandle); 272 } finally { 273 Binder.restoreCallingIdentity(token); 274 } 275 } 276 registerRouter2(@onNull IMediaRouter2 router, @NonNull String packageName)277 public void registerRouter2(@NonNull IMediaRouter2 router, @NonNull String packageName) { 278 Objects.requireNonNull(router, "router must not be null"); 279 if (TextUtils.isEmpty(packageName)) { 280 throw new IllegalArgumentException("packageName must not be empty"); 281 } 282 283 final int uid = Binder.getCallingUid(); 284 final int pid = Binder.getCallingPid(); 285 final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); 286 final boolean hasConfigureWifiDisplayPermission = 287 mContext.checkCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY) 288 == PackageManager.PERMISSION_GRANTED; 289 final boolean hasModifyAudioRoutingPermission = 290 checkCallerHasModifyAudioRoutingPermission(pid, uid); 291 boolean hasMediaContentControlPermission = checkMediaContentControlPermission(uid, pid); 292 boolean hasMediaRoutingControlPermission = 293 checkMediaRoutingControlPermission(uid, pid, packageName); 294 295 final long token = Binder.clearCallingIdentity(); 296 try { 297 synchronized (mLock) { 298 registerRouter2Locked( 299 router, 300 uid, 301 pid, 302 packageName, 303 userId, 304 hasConfigureWifiDisplayPermission, 305 hasModifyAudioRoutingPermission, 306 hasMediaContentControlPermission, 307 hasMediaRoutingControlPermission); 308 } 309 } finally { 310 Binder.restoreCallingIdentity(token); 311 } 312 } 313 unregisterRouter2(@onNull IMediaRouter2 router)314 public void unregisterRouter2(@NonNull IMediaRouter2 router) { 315 Objects.requireNonNull(router, "router must not be null"); 316 317 final long token = Binder.clearCallingIdentity(); 318 try { 319 synchronized (mLock) { 320 unregisterRouter2Locked(router, false); 321 } 322 } finally { 323 Binder.restoreCallingIdentity(token); 324 } 325 } 326 327 @RequiresPermission( 328 anyOf = { 329 Manifest.permission.MEDIA_ROUTING_CONTROL, 330 Manifest.permission.MEDIA_CONTENT_CONTROL 331 }, 332 conditional = true) updateScanningState( @onNull IMediaRouter2 router, @ScanningState int scanningState)333 public void updateScanningState( 334 @NonNull IMediaRouter2 router, @ScanningState int scanningState) { 335 Objects.requireNonNull(router, "router must not be null"); 336 validateScanningStateValue(scanningState); 337 338 final long token = Binder.clearCallingIdentity(); 339 try { 340 synchronized (mLock) { 341 updateScanningStateLocked(router, scanningState); 342 } 343 } finally { 344 Binder.restoreCallingIdentity(token); 345 } 346 } 347 setDiscoveryRequestWithRouter2(@onNull IMediaRouter2 router, @NonNull RouteDiscoveryPreference preference)348 public void setDiscoveryRequestWithRouter2(@NonNull IMediaRouter2 router, 349 @NonNull RouteDiscoveryPreference preference) { 350 Objects.requireNonNull(router, "router must not be null"); 351 Objects.requireNonNull(preference, "preference must not be null"); 352 353 final long token = Binder.clearCallingIdentity(); 354 try { 355 synchronized (mLock) { 356 RouterRecord routerRecord = mAllRouterRecords.get(router.asBinder()); 357 if (routerRecord == null) { 358 Slog.w(TAG, "Ignoring updating discoveryRequest of null routerRecord."); 359 return; 360 } 361 setDiscoveryRequestWithRouter2Locked(routerRecord, preference); 362 } 363 } finally { 364 Binder.restoreCallingIdentity(token); 365 } 366 } 367 setRouteListingPreference( @onNull IMediaRouter2 router, @Nullable RouteListingPreference routeListingPreference)368 public void setRouteListingPreference( 369 @NonNull IMediaRouter2 router, 370 @Nullable RouteListingPreference routeListingPreference) { 371 ComponentName linkedItemLandingComponent = 372 routeListingPreference != null 373 ? routeListingPreference.getLinkedItemComponentName() 374 : null; 375 if (linkedItemLandingComponent != null) { 376 int callingUid = Binder.getCallingUid(); 377 MediaServerUtils.enforcePackageName( 378 mContext, linkedItemLandingComponent.getPackageName(), callingUid); 379 if (!MediaServerUtils.isValidActivityComponentName( 380 mContext, 381 linkedItemLandingComponent, 382 RouteListingPreference.ACTION_TRANSFER_MEDIA, 383 Binder.getCallingUserHandle())) { 384 throw new IllegalArgumentException( 385 "Unable to resolve " 386 + linkedItemLandingComponent 387 + " to a valid activity for " 388 + RouteListingPreference.ACTION_TRANSFER_MEDIA); 389 } 390 } 391 392 final long token = Binder.clearCallingIdentity(); 393 try { 394 synchronized (mLock) { 395 RouterRecord routerRecord = mAllRouterRecords.get(router.asBinder()); 396 if (routerRecord == null) { 397 Slog.w(TAG, "Ignoring updating route listing of null routerRecord."); 398 return; 399 } 400 setRouteListingPreferenceLocked(routerRecord, routeListingPreference); 401 } 402 } finally { 403 Binder.restoreCallingIdentity(token); 404 } 405 } 406 setRouteVolumeWithRouter2(@onNull IMediaRouter2 router, @NonNull MediaRoute2Info route, int volume)407 public void setRouteVolumeWithRouter2(@NonNull IMediaRouter2 router, 408 @NonNull MediaRoute2Info route, int volume) { 409 Objects.requireNonNull(router, "router must not be null"); 410 Objects.requireNonNull(route, "route must not be null"); 411 412 final long token = Binder.clearCallingIdentity(); 413 try { 414 synchronized (mLock) { 415 setRouteVolumeWithRouter2Locked(router, route, volume); 416 } 417 } finally { 418 Binder.restoreCallingIdentity(token); 419 } 420 } 421 requestCreateSessionWithRouter2( @onNull IMediaRouter2 router, int requestId, long managerRequestId, @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route, Bundle sessionHints)422 public void requestCreateSessionWithRouter2( 423 @NonNull IMediaRouter2 router, 424 int requestId, 425 long managerRequestId, 426 @NonNull RoutingSessionInfo oldSession, 427 @NonNull MediaRoute2Info route, 428 Bundle sessionHints) { 429 Objects.requireNonNull(router, "router must not be null"); 430 Objects.requireNonNull(oldSession, "oldSession must not be null"); 431 Objects.requireNonNull(route, "route must not be null"); 432 433 final long token = Binder.clearCallingIdentity(); 434 try { 435 synchronized (mLock) { 436 requestCreateSessionWithRouter2Locked( 437 requestId, 438 managerRequestId, 439 router, 440 oldSession, 441 route, 442 sessionHints); 443 } 444 } finally { 445 Binder.restoreCallingIdentity(token); 446 } 447 } 448 selectRouteWithRouter2(@onNull IMediaRouter2 router, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route)449 public void selectRouteWithRouter2(@NonNull IMediaRouter2 router, 450 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { 451 Objects.requireNonNull(router, "router must not be null"); 452 Objects.requireNonNull(route, "route must not be null"); 453 if (TextUtils.isEmpty(uniqueSessionId)) { 454 throw new IllegalArgumentException("uniqueSessionId must not be empty"); 455 } 456 457 final long token = Binder.clearCallingIdentity(); 458 try { 459 synchronized (mLock) { 460 selectRouteWithRouter2Locked(router, uniqueSessionId, route); 461 } 462 } finally { 463 Binder.restoreCallingIdentity(token); 464 } 465 } 466 deselectRouteWithRouter2(@onNull IMediaRouter2 router, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route)467 public void deselectRouteWithRouter2(@NonNull IMediaRouter2 router, 468 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { 469 Objects.requireNonNull(router, "router must not be null"); 470 Objects.requireNonNull(route, "route must not be null"); 471 if (TextUtils.isEmpty(uniqueSessionId)) { 472 throw new IllegalArgumentException("uniqueSessionId must not be empty"); 473 } 474 475 final long token = Binder.clearCallingIdentity(); 476 try { 477 synchronized (mLock) { 478 deselectRouteWithRouter2Locked(router, uniqueSessionId, route); 479 } 480 } finally { 481 Binder.restoreCallingIdentity(token); 482 } 483 } 484 transferToRouteWithRouter2(@onNull IMediaRouter2 router, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route)485 public void transferToRouteWithRouter2(@NonNull IMediaRouter2 router, 486 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { 487 Objects.requireNonNull(router, "router must not be null"); 488 Objects.requireNonNull(route, "route must not be null"); 489 if (TextUtils.isEmpty(uniqueSessionId)) { 490 throw new IllegalArgumentException("uniqueSessionId must not be empty"); 491 } 492 493 UserHandle userHandle = Binder.getCallingUserHandle(); 494 final long token = Binder.clearCallingIdentity(); 495 try { 496 synchronized (mLock) { 497 transferToRouteWithRouter2Locked(router, userHandle, uniqueSessionId, route); 498 } 499 } finally { 500 Binder.restoreCallingIdentity(token); 501 } 502 } 503 setSessionVolumeWithRouter2(@onNull IMediaRouter2 router, @NonNull String uniqueSessionId, int volume)504 public void setSessionVolumeWithRouter2(@NonNull IMediaRouter2 router, 505 @NonNull String uniqueSessionId, int volume) { 506 Objects.requireNonNull(router, "router must not be null"); 507 Objects.requireNonNull(uniqueSessionId, "uniqueSessionId must not be null"); 508 if (TextUtils.isEmpty(uniqueSessionId)) { 509 throw new IllegalArgumentException("uniqueSessionId must not be empty"); 510 } 511 512 final long token = Binder.clearCallingIdentity(); 513 try { 514 synchronized (mLock) { 515 setSessionVolumeWithRouter2Locked(router, uniqueSessionId, volume); 516 } 517 } finally { 518 Binder.restoreCallingIdentity(token); 519 } 520 } 521 releaseSessionWithRouter2(@onNull IMediaRouter2 router, @NonNull String uniqueSessionId)522 public void releaseSessionWithRouter2(@NonNull IMediaRouter2 router, 523 @NonNull String uniqueSessionId) { 524 Objects.requireNonNull(router, "router must not be null"); 525 if (TextUtils.isEmpty(uniqueSessionId)) { 526 throw new IllegalArgumentException("uniqueSessionId must not be empty"); 527 } 528 529 final long token = Binder.clearCallingIdentity(); 530 try { 531 synchronized (mLock) { 532 releaseSessionWithRouter2Locked(router, uniqueSessionId); 533 } 534 } finally { 535 Binder.restoreCallingIdentity(token); 536 } 537 } 538 539 // End of methods that implement MediaRouter2 operations. 540 541 // Start of methods that implement MediaRouter2Manager operations. 542 543 @NonNull getRemoteSessions(@onNull IMediaRouter2Manager manager)544 public List<RoutingSessionInfo> getRemoteSessions(@NonNull IMediaRouter2Manager manager) { 545 Objects.requireNonNull(manager, "manager must not be null"); 546 final long token = Binder.clearCallingIdentity(); 547 try { 548 synchronized (mLock) { 549 return getRemoteSessionsLocked(manager); 550 } 551 } finally { 552 Binder.restoreCallingIdentity(token); 553 } 554 } 555 556 @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) registerManager(@onNull IMediaRouter2Manager manager, @NonNull String callerPackageName)557 public void registerManager(@NonNull IMediaRouter2Manager manager, 558 @NonNull String callerPackageName) { 559 Objects.requireNonNull(manager, "manager must not be null"); 560 if (TextUtils.isEmpty(callerPackageName)) { 561 throw new IllegalArgumentException("callerPackageName must not be empty"); 562 } 563 564 final int callerUid = Binder.getCallingUid(); 565 final int callerPid = Binder.getCallingPid(); 566 final UserHandle callerUser = Binder.getCallingUserHandle(); 567 568 enforcePrivilegedRoutingPermissions(callerUid, callerPid, callerPackageName); 569 570 final long token = Binder.clearCallingIdentity(); 571 try { 572 synchronized (mLock) { 573 registerManagerLocked( 574 manager, 575 callerUid, 576 callerPid, 577 callerPackageName, 578 /* targetPackageName */ null, 579 callerUser); 580 } 581 } finally { 582 Binder.restoreCallingIdentity(token); 583 } 584 } 585 586 @RequiresPermission( 587 anyOf = { 588 Manifest.permission.MEDIA_CONTENT_CONTROL, 589 Manifest.permission.MEDIA_ROUTING_CONTROL 590 }) registerProxyRouter( @onNull IMediaRouter2Manager manager, @NonNull String callerPackageName, @NonNull String targetPackageName, @NonNull UserHandle targetUser)591 public void registerProxyRouter( 592 @NonNull IMediaRouter2Manager manager, 593 @NonNull String callerPackageName, 594 @NonNull String targetPackageName, 595 @NonNull UserHandle targetUser) { 596 Objects.requireNonNull(manager, "manager must not be null"); 597 Objects.requireNonNull(targetUser, "targetUser must not be null"); 598 599 if (TextUtils.isEmpty(targetPackageName)) { 600 throw new IllegalArgumentException("targetPackageName must not be empty"); 601 } 602 603 int callerUid = Binder.getCallingUid(); 604 int callerPid = Binder.getCallingPid(); 605 final long token = Binder.clearCallingIdentity(); 606 607 try { 608 enforcePrivilegedRoutingPermissions(callerUid, callerPid, callerPackageName); 609 enforceCrossUserPermissions(callerUid, callerPid, targetUser); 610 if (!verifyPackageExistsForUser(targetPackageName, targetUser)) { 611 throw new IllegalArgumentException( 612 "targetPackageName does not exist: " + targetPackageName); 613 } 614 615 synchronized (mLock) { 616 registerManagerLocked( 617 manager, 618 callerUid, 619 callerPid, 620 callerPackageName, 621 targetPackageName, 622 targetUser); 623 } 624 } finally { 625 Binder.restoreCallingIdentity(token); 626 } 627 } 628 unregisterManager(@onNull IMediaRouter2Manager manager)629 public void unregisterManager(@NonNull IMediaRouter2Manager manager) { 630 Objects.requireNonNull(manager, "manager must not be null"); 631 632 final long token = Binder.clearCallingIdentity(); 633 try { 634 synchronized (mLock) { 635 unregisterManagerLocked(manager, false); 636 } 637 } finally { 638 Binder.restoreCallingIdentity(token); 639 } 640 } 641 updateScanningState( @onNull IMediaRouter2Manager manager, @ScanningState int scanningState)642 public void updateScanningState( 643 @NonNull IMediaRouter2Manager manager, @ScanningState int scanningState) { 644 Objects.requireNonNull(manager, "manager must not be null"); 645 validateScanningStateValue(scanningState); 646 647 final long token = Binder.clearCallingIdentity(); 648 try { 649 synchronized (mLock) { 650 updateScanningStateLocked(manager, scanningState); 651 } 652 } finally { 653 Binder.restoreCallingIdentity(token); 654 } 655 } 656 setRouteVolumeWithManager(@onNull IMediaRouter2Manager manager, int requestId, @NonNull MediaRoute2Info route, int volume)657 public void setRouteVolumeWithManager(@NonNull IMediaRouter2Manager manager, int requestId, 658 @NonNull MediaRoute2Info route, int volume) { 659 Objects.requireNonNull(manager, "manager must not be null"); 660 Objects.requireNonNull(route, "route must not be null"); 661 662 final long token = Binder.clearCallingIdentity(); 663 try { 664 synchronized (mLock) { 665 setRouteVolumeWithManagerLocked(requestId, manager, route, volume); 666 } 667 } finally { 668 Binder.restoreCallingIdentity(token); 669 } 670 } 671 requestCreateSessionWithManager( @onNull IMediaRouter2Manager manager, int requestId, @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route, @NonNull UserHandle transferInitiatorUserHandle, @NonNull String transferInitiatorPackageName)672 public void requestCreateSessionWithManager( 673 @NonNull IMediaRouter2Manager manager, 674 int requestId, 675 @NonNull RoutingSessionInfo oldSession, 676 @NonNull MediaRoute2Info route, 677 @NonNull UserHandle transferInitiatorUserHandle, 678 @NonNull String transferInitiatorPackageName) { 679 Objects.requireNonNull(manager, "manager must not be null"); 680 Objects.requireNonNull(oldSession, "oldSession must not be null"); 681 Objects.requireNonNull(route, "route must not be null"); 682 Objects.requireNonNull(transferInitiatorUserHandle); 683 684 final long token = Binder.clearCallingIdentity(); 685 try { 686 synchronized (mLock) { 687 requestCreateSessionWithManagerLocked( 688 requestId, 689 manager, 690 oldSession, 691 route, 692 transferInitiatorUserHandle, 693 transferInitiatorPackageName); 694 } 695 } finally { 696 Binder.restoreCallingIdentity(token); 697 } 698 } 699 selectRouteWithManager(@onNull IMediaRouter2Manager manager, int requestId, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route)700 public void selectRouteWithManager(@NonNull IMediaRouter2Manager manager, int requestId, 701 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { 702 Objects.requireNonNull(manager, "manager must not be null"); 703 if (TextUtils.isEmpty(uniqueSessionId)) { 704 throw new IllegalArgumentException("uniqueSessionId must not be empty"); 705 } 706 Objects.requireNonNull(route, "route must not be null"); 707 708 final long token = Binder.clearCallingIdentity(); 709 try { 710 synchronized (mLock) { 711 selectRouteWithManagerLocked(requestId, manager, uniqueSessionId, route); 712 } 713 } finally { 714 Binder.restoreCallingIdentity(token); 715 } 716 } 717 deselectRouteWithManager(@onNull IMediaRouter2Manager manager, int requestId, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route)718 public void deselectRouteWithManager(@NonNull IMediaRouter2Manager manager, int requestId, 719 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { 720 Objects.requireNonNull(manager, "manager must not be null"); 721 if (TextUtils.isEmpty(uniqueSessionId)) { 722 throw new IllegalArgumentException("uniqueSessionId must not be empty"); 723 } 724 Objects.requireNonNull(route, "route must not be null"); 725 726 final long token = Binder.clearCallingIdentity(); 727 try { 728 synchronized (mLock) { 729 deselectRouteWithManagerLocked(requestId, manager, uniqueSessionId, route); 730 } 731 } finally { 732 Binder.restoreCallingIdentity(token); 733 } 734 } 735 transferToRouteWithManager( @onNull IMediaRouter2Manager manager, int requestId, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route, @NonNull UserHandle transferInitiatorUserHandle, @NonNull String transferInitiatorPackageName)736 public void transferToRouteWithManager( 737 @NonNull IMediaRouter2Manager manager, 738 int requestId, 739 @NonNull String uniqueSessionId, 740 @NonNull MediaRoute2Info route, 741 @NonNull UserHandle transferInitiatorUserHandle, 742 @NonNull String transferInitiatorPackageName) { 743 Objects.requireNonNull(manager, "manager must not be null"); 744 if (TextUtils.isEmpty(uniqueSessionId)) { 745 throw new IllegalArgumentException("uniqueSessionId must not be empty"); 746 } 747 Objects.requireNonNull(route, "route must not be null"); 748 Objects.requireNonNull(transferInitiatorUserHandle); 749 Objects.requireNonNull(transferInitiatorPackageName); 750 751 final long token = Binder.clearCallingIdentity(); 752 try { 753 synchronized (mLock) { 754 transferToRouteWithManagerLocked( 755 requestId, 756 manager, 757 uniqueSessionId, 758 route, 759 RoutingSessionInfo.TRANSFER_REASON_SYSTEM_REQUEST, 760 transferInitiatorUserHandle, 761 transferInitiatorPackageName); 762 } 763 } finally { 764 Binder.restoreCallingIdentity(token); 765 } 766 } 767 setSessionVolumeWithManager(@onNull IMediaRouter2Manager manager, int requestId, @NonNull String uniqueSessionId, int volume)768 public void setSessionVolumeWithManager(@NonNull IMediaRouter2Manager manager, int requestId, 769 @NonNull String uniqueSessionId, int volume) { 770 Objects.requireNonNull(manager, "manager must not be null"); 771 if (TextUtils.isEmpty(uniqueSessionId)) { 772 throw new IllegalArgumentException("uniqueSessionId must not be empty"); 773 } 774 775 final long token = Binder.clearCallingIdentity(); 776 try { 777 synchronized (mLock) { 778 setSessionVolumeWithManagerLocked(requestId, manager, uniqueSessionId, volume); 779 } 780 } finally { 781 Binder.restoreCallingIdentity(token); 782 } 783 } 784 releaseSessionWithManager(@onNull IMediaRouter2Manager manager, int requestId, @NonNull String uniqueSessionId)785 public void releaseSessionWithManager(@NonNull IMediaRouter2Manager manager, int requestId, 786 @NonNull String uniqueSessionId) { 787 Objects.requireNonNull(manager, "manager must not be null"); 788 if (TextUtils.isEmpty(uniqueSessionId)) { 789 throw new IllegalArgumentException("uniqueSessionId must not be empty"); 790 } 791 792 final long token = Binder.clearCallingIdentity(); 793 try { 794 synchronized (mLock) { 795 releaseSessionWithManagerLocked(requestId, manager, uniqueSessionId); 796 } 797 } finally { 798 Binder.restoreCallingIdentity(token); 799 } 800 } 801 802 @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS) showMediaOutputSwitcherWithProxyRouter( @onNull IMediaRouter2Manager proxyRouter)803 public boolean showMediaOutputSwitcherWithProxyRouter( 804 @NonNull IMediaRouter2Manager proxyRouter) { 805 Objects.requireNonNull(proxyRouter, "Proxy router must not be null"); 806 807 final long token = Binder.clearCallingIdentity(); 808 try { 809 synchronized (mLock) { 810 final IBinder binder = proxyRouter.asBinder(); 811 ManagerRecord proxyRouterRecord = mAllManagerRecords.get(binder); 812 813 if (proxyRouterRecord.mTargetPackageName == null) { 814 throw new UnsupportedOperationException( 815 "Only proxy routers can show the Output Switcher."); 816 } 817 818 return showOutputSwitcher( 819 proxyRouterRecord.mTargetPackageName, 820 UserHandle.of(proxyRouterRecord.mUserRecord.mUserId)); 821 } 822 } finally { 823 Binder.restoreCallingIdentity(token); 824 } 825 } 826 827 // End of methods that implement MediaRouter2Manager operations. 828 829 // Start of methods that implements operations for both MediaRouter2 and MediaRouter2Manager. 830 831 @Nullable getSystemSessionInfo( @onNull String callerPackageName, @Nullable String targetPackageName, boolean setDeviceRouteSelected)832 public RoutingSessionInfo getSystemSessionInfo( 833 @NonNull String callerPackageName, 834 @Nullable String targetPackageName, 835 boolean setDeviceRouteSelected) { 836 final int uid = Binder.getCallingUid(); 837 final int pid = Binder.getCallingPid(); 838 final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); 839 840 boolean hasSystemRoutingPermissions; 841 if (targetPackageName == null) { 842 hasSystemRoutingPermissions = checkCallerHasSystemRoutingPermissions(pid, uid); 843 } else { 844 // Request from ProxyRouter. 845 hasSystemRoutingPermissions = 846 checkCallerHasPrivilegedRoutingPermissions(pid, uid, callerPackageName); 847 } 848 849 final long token = Binder.clearCallingIdentity(); 850 try { 851 synchronized (mLock) { 852 UserRecord userRecord = getOrCreateUserRecordLocked(userId); 853 List<RoutingSessionInfo> sessionInfos; 854 if (hasSystemRoutingPermissions) { 855 if (setDeviceRouteSelected) { 856 // Return a fake system session that shows the device route as selected and 857 // available bluetooth routes as transferable. 858 return userRecord.mHandler.mSystemProvider 859 .generateDeviceRouteSelectedSessionInfo(targetPackageName); 860 } else { 861 sessionInfos = userRecord.mHandler.mSystemProvider.getSessionInfos(); 862 if (!sessionInfos.isEmpty()) { 863 // Return a copy of the current system session with no modification, 864 // except setting the client package name. 865 return new RoutingSessionInfo.Builder(sessionInfos.get(0)) 866 .setClientPackageName(targetPackageName) 867 .build(); 868 } else { 869 Slog.w(TAG, "System provider does not have any session info."); 870 } 871 } 872 } else { 873 return new RoutingSessionInfo.Builder( 874 userRecord.mHandler.mSystemProvider.getDefaultSessionInfo()) 875 .setClientPackageName(targetPackageName) 876 .build(); 877 } 878 } 879 return null; 880 } finally { 881 Binder.restoreCallingIdentity(token); 882 } 883 } 884 checkCallerHasSystemRoutingPermissions(int pid, int uid)885 private boolean checkCallerHasSystemRoutingPermissions(int pid, int uid) { 886 return checkCallerHasModifyAudioRoutingPermission(pid, uid) 887 || checkCallerHasBluetoothPermissions(pid, uid); 888 } 889 checkCallerHasPrivilegedRoutingPermissions( int pid, int uid, @NonNull String callerPackageName)890 private boolean checkCallerHasPrivilegedRoutingPermissions( 891 int pid, int uid, @NonNull String callerPackageName) { 892 return checkMediaContentControlPermission(uid, pid) 893 || checkMediaRoutingControlPermission(uid, pid, callerPackageName); 894 } 895 checkCallerHasModifyAudioRoutingPermission(int pid, int uid)896 private boolean checkCallerHasModifyAudioRoutingPermission(int pid, int uid) { 897 return mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_ROUTING, pid, uid) 898 == PackageManager.PERMISSION_GRANTED; 899 } 900 checkCallerHasBluetoothPermissions(int pid, int uid)901 private boolean checkCallerHasBluetoothPermissions(int pid, int uid) { 902 boolean hasBluetoothRoutingPermission = true; 903 for (String permission : BLUETOOTH_PERMISSIONS_FOR_SYSTEM_ROUTING) { 904 hasBluetoothRoutingPermission &= 905 mContext.checkPermission(permission, pid, uid) 906 == PackageManager.PERMISSION_GRANTED; 907 } 908 return hasBluetoothRoutingPermission; 909 } 910 911 @RequiresPermission( 912 anyOf = { 913 Manifest.permission.MEDIA_ROUTING_CONTROL, 914 Manifest.permission.MEDIA_CONTENT_CONTROL 915 }) enforcePrivilegedRoutingPermissions( int callerUid, int callerPid, @NonNull String callerPackageName)916 private void enforcePrivilegedRoutingPermissions( 917 int callerUid, int callerPid, @NonNull String callerPackageName) { 918 if (checkMediaContentControlPermission(callerUid, callerPid)) { 919 return; 920 } 921 922 if (!checkMediaRoutingControlPermission(callerUid, callerPid, callerPackageName)) { 923 throw new SecurityException( 924 "Must hold MEDIA_CONTENT_CONTROL or MEDIA_ROUTING_CONTROL permissions."); 925 } 926 } 927 checkMediaContentControlPermission(int callerUid, int callerPid)928 private boolean checkMediaContentControlPermission(int callerUid, int callerPid) { 929 return mContext.checkPermission( 930 Manifest.permission.MEDIA_CONTENT_CONTROL, callerPid, callerUid) 931 == PackageManager.PERMISSION_GRANTED; 932 } 933 checkMediaRoutingControlPermission( int callerUid, int callerPid, @NonNull String callerPackageName)934 private boolean checkMediaRoutingControlPermission( 935 int callerUid, int callerPid, @NonNull String callerPackageName) { 936 if (!Flags.enablePrivilegedRoutingForMediaRoutingControl()) { 937 return false; 938 } 939 940 return PermissionChecker.checkPermissionForDataDelivery( 941 mContext, 942 Manifest.permission.MEDIA_ROUTING_CONTROL, 943 callerPid, 944 callerUid, 945 callerPackageName, 946 /* attributionTag */ null, 947 /* message */ "Checking permissions for registering manager in" 948 + " MediaRouter2ServiceImpl.") 949 == PermissionChecker.PERMISSION_GRANTED; 950 } 951 952 @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS) verifyPackageExistsForUser( @onNull String clientPackageName, @NonNull UserHandle user)953 private boolean verifyPackageExistsForUser( 954 @NonNull String clientPackageName, @NonNull UserHandle user) { 955 try { 956 PackageManager pm = mContext.getPackageManager(); 957 pm.getPackageInfoAsUser( 958 clientPackageName, PackageManager.PackageInfoFlags.of(0), user.getIdentifier()); 959 return true; 960 } catch (PackageManager.NameNotFoundException ex) { 961 return false; 962 } 963 } 964 965 /** 966 * Enforces the caller has {@link Manifest.permission#INTERACT_ACROSS_USERS_FULL} if the 967 * caller's user is different from the target user. 968 */ enforceCrossUserPermissions( int callerUid, int callerPid, @NonNull UserHandle targetUser)969 private void enforceCrossUserPermissions( 970 int callerUid, int callerPid, @NonNull UserHandle targetUser) { 971 int callerUserId = UserHandle.getUserId(callerUid); 972 973 if (targetUser.getIdentifier() != callerUserId) { 974 mContext.enforcePermission( 975 Manifest.permission.INTERACT_ACROSS_USERS_FULL, 976 callerPid, 977 callerUid, 978 "Must hold INTERACT_ACROSS_USERS_FULL to control an app in a different" 979 + " userId."); 980 } 981 } 982 983 @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS) showOutputSwitcher( @onNull String packageName, @NonNull UserHandle userHandle)984 private boolean showOutputSwitcher( 985 @NonNull String packageName, @NonNull UserHandle userHandle) { 986 if (mActivityManager.getPackageImportance(packageName) > IMPORTANCE_FOREGROUND) { 987 Slog.w(TAG, "showMediaOutputSwitcher only works when called from foreground"); 988 return false; 989 } 990 synchronized (mLock) { 991 mStatusBarManagerInternal.showMediaOutputSwitcher(packageName, userHandle); 992 } 993 return true; 994 } 995 996 // End of methods that implements operations for both MediaRouter2 and MediaRouter2Manager. 997 dump(@onNull PrintWriter pw, @NonNull String prefix)998 public void dump(@NonNull PrintWriter pw, @NonNull String prefix) { 999 pw.println(prefix + "MediaRouter2ServiceImpl"); 1000 1001 String indent = prefix + " "; 1002 1003 synchronized (mLock) { 1004 pw.println(indent + "mNextRouterOrManagerId=" + mNextRouterOrManagerId.get()); 1005 pw.println(indent + "mCurrentActiveUserId=" + mCurrentActiveUserId); 1006 1007 pw.println(indent + "UserRecords:"); 1008 if (mUserRecords.size() > 0) { 1009 for (int i = 0; i < mUserRecords.size(); i++) { 1010 mUserRecords.valueAt(i).dump(pw, indent + " "); 1011 } 1012 } else { 1013 pw.println(indent + " <no user records>"); 1014 } 1015 } 1016 } 1017 updateRunningUserAndProfiles(int newActiveUserId)1018 /* package */ void updateRunningUserAndProfiles(int newActiveUserId) { 1019 synchronized (mLock) { 1020 if (mCurrentActiveUserId != newActiveUserId) { 1021 Slog.i(TAG, TextUtils.formatSimple( 1022 "switchUser | user: %d", newActiveUserId)); 1023 1024 mCurrentActiveUserId = newActiveUserId; 1025 // disposeUserIfNeededLocked might modify the collection, hence clone 1026 final var userRecords = mUserRecords.clone(); 1027 for (int i = 0; i < userRecords.size(); i++) { 1028 int userId = userRecords.keyAt(i); 1029 UserRecord userRecord = userRecords.valueAt(i); 1030 if (isUserActiveLocked(userId)) { 1031 // userId corresponds to the active user, or one of its profiles. We 1032 // ensure the associated structures are initialized. 1033 userRecord.mHandler.sendMessage( 1034 obtainMessage(UserHandler::start, userRecord.mHandler)); 1035 } else { 1036 userRecord.mHandler.sendMessage( 1037 obtainMessage(UserHandler::stop, userRecord.mHandler)); 1038 disposeUserIfNeededLocked(userRecord); 1039 } 1040 } 1041 } 1042 } 1043 } 1044 routerDied(@onNull RouterRecord routerRecord)1045 void routerDied(@NonNull RouterRecord routerRecord) { 1046 synchronized (mLock) { 1047 unregisterRouter2Locked(routerRecord.mRouter, true); 1048 } 1049 } 1050 managerDied(@onNull ManagerRecord managerRecord)1051 void managerDied(@NonNull ManagerRecord managerRecord) { 1052 synchronized (mLock) { 1053 unregisterManagerLocked(managerRecord.mManager, true); 1054 } 1055 } 1056 1057 /** 1058 * Returns {@code true} if the given {@code userId} corresponds to the active user or a profile 1059 * of the active user, returns {@code false} otherwise. 1060 */ 1061 @GuardedBy("mLock") isUserActiveLocked(int userId)1062 private boolean isUserActiveLocked(int userId) { 1063 return mUserManagerInternal.getProfileParentId(userId) == mCurrentActiveUserId; 1064 } 1065 1066 @GuardedBy("mLock") revokeManagerRecordAccessIfNeededLocked(@onNull String packageName, int userId)1067 private void revokeManagerRecordAccessIfNeededLocked(@NonNull String packageName, int userId) { 1068 UserRecord userRecord = mUserRecords.get(userId); 1069 if (userRecord == null) { 1070 return; 1071 } 1072 1073 List<ManagerRecord> managers = 1074 userRecord.mManagerRecords.stream() 1075 .filter(r -> !r.mHasMediaContentControl) 1076 .filter(r -> TextUtils.equals(r.mOwnerPackageName, packageName)) 1077 .collect(Collectors.toList()); 1078 1079 if (managers.isEmpty()) { 1080 return; 1081 } 1082 1083 ManagerRecord record = managers.getFirst(); 1084 1085 // Uid and package name are shared across all manager records in the list. 1086 boolean isAppOpAllowed = 1087 mAppOpsManager.unsafeCheckOpNoThrow( 1088 AppOpsManager.OPSTR_MEDIA_ROUTING_CONTROL, 1089 record.mOwnerUid, 1090 record.mOwnerPackageName) 1091 == AppOpsManager.MODE_ALLOWED; 1092 1093 if (isAppOpAllowed) { 1094 return; 1095 } 1096 1097 for (ManagerRecord manager : managers) { 1098 boolean isRegularPermission = 1099 mContext.checkPermission( 1100 Manifest.permission.MEDIA_ROUTING_CONTROL, 1101 manager.mOwnerPid, 1102 manager.mOwnerUid) 1103 == PackageManager.PERMISSION_GRANTED; 1104 1105 if (isRegularPermission) { 1106 // We should check the regular permission for all manager records, as different PIDs 1107 // might yield different permission results. 1108 continue; 1109 } 1110 1111 Log.w( 1112 TAG, 1113 TextUtils.formatSimple( 1114 "Revoking access to manager record id: %d, package: %s, userId:" 1115 + " %d", 1116 manager.mManagerId, manager.mOwnerPackageName, userRecord.mUserId)); 1117 1118 unregisterManagerLocked(manager.mManager, /* died */ false); 1119 1120 try { 1121 manager.mManager.invalidateInstance(); 1122 } catch (RemoteException ex) { 1123 Slog.w(TAG, "Failed to notify manager= " + manager + " of permission revocation."); 1124 } 1125 } 1126 } 1127 1128 // Start of locked methods that are used by MediaRouter2. 1129 1130 @GuardedBy("mLock") registerRouter2Locked( @onNull IMediaRouter2 router, int uid, int pid, @NonNull String packageName, int userId, boolean hasConfigureWifiDisplayPermission, boolean hasModifyAudioRoutingPermission, boolean hasMediaContentControlPermission, boolean hasMediaRoutingControlPermission)1131 private void registerRouter2Locked( 1132 @NonNull IMediaRouter2 router, 1133 int uid, 1134 int pid, 1135 @NonNull String packageName, 1136 int userId, 1137 boolean hasConfigureWifiDisplayPermission, 1138 boolean hasModifyAudioRoutingPermission, 1139 boolean hasMediaContentControlPermission, 1140 boolean hasMediaRoutingControlPermission) { 1141 final IBinder binder = router.asBinder(); 1142 if (mAllRouterRecords.get(binder) != null) { 1143 Slog.w(TAG, "registerRouter2Locked: Same router already exists. packageName=" 1144 + packageName); 1145 return; 1146 } 1147 1148 UserRecord userRecord = getOrCreateUserRecordLocked(userId); 1149 RouterRecord routerRecord = 1150 new RouterRecord( 1151 userRecord, 1152 router, 1153 uid, 1154 pid, 1155 packageName, 1156 hasConfigureWifiDisplayPermission, 1157 hasModifyAudioRoutingPermission, 1158 hasMediaContentControlPermission, 1159 hasMediaRoutingControlPermission); 1160 try { 1161 binder.linkToDeath(routerRecord, 0); 1162 } catch (RemoteException ex) { 1163 throw new RuntimeException("MediaRouter2 died prematurely.", ex); 1164 } 1165 1166 userRecord.mRouterRecords.add(routerRecord); 1167 mAllRouterRecords.put(binder, routerRecord); 1168 1169 userRecord.mHandler.sendMessage( 1170 obtainMessage(UserHandler::notifyRouterRegistered, 1171 userRecord.mHandler, routerRecord)); 1172 1173 Slog.i( 1174 TAG, 1175 TextUtils.formatSimple( 1176 "registerRouter2 | package: %s, uid: %d, pid: %d, router id: %d," 1177 + " hasMediaRoutingControl: %b", 1178 packageName, 1179 uid, 1180 pid, 1181 routerRecord.mRouterId, 1182 hasMediaRoutingControlPermission)); 1183 } 1184 1185 @GuardedBy("mLock") unregisterRouter2Locked(@onNull IMediaRouter2 router, boolean died)1186 private void unregisterRouter2Locked(@NonNull IMediaRouter2 router, boolean died) { 1187 RouterRecord routerRecord = mAllRouterRecords.remove(router.asBinder()); 1188 if (routerRecord == null) { 1189 Slog.w( 1190 TAG, 1191 TextUtils.formatSimple( 1192 "Ignoring unregistering unknown router: %s, died: %b", router, died)); 1193 return; 1194 } 1195 1196 Slog.i( 1197 TAG, 1198 TextUtils.formatSimple( 1199 "unregisterRouter2 | package: %s, router id: %d, died: %b", 1200 routerRecord.mPackageName, routerRecord.mRouterId, died)); 1201 1202 UserRecord userRecord = routerRecord.mUserRecord; 1203 userRecord.mRouterRecords.remove(routerRecord); 1204 routerRecord.mUserRecord.mHandler.sendMessage( 1205 obtainMessage(UserHandler::notifyDiscoveryPreferenceChangedToManagers, 1206 routerRecord.mUserRecord.mHandler, 1207 routerRecord.mPackageName, null)); 1208 routerRecord.mUserRecord.mHandler.sendMessage( 1209 obtainMessage( 1210 UserHandler::notifyRouteListingPreferenceChangeToManagers, 1211 routerRecord.mUserRecord.mHandler, 1212 routerRecord.mPackageName, 1213 /* routeListingPreference= */ null)); 1214 userRecord.mHandler.sendMessage( 1215 obtainMessage(UserHandler::updateDiscoveryPreferenceOnHandler, 1216 userRecord.mHandler)); 1217 routerRecord.dispose(); 1218 disposeUserIfNeededLocked(userRecord); // since router removed from user 1219 } 1220 1221 @RequiresPermission( 1222 anyOf = { 1223 Manifest.permission.MEDIA_ROUTING_CONTROL, 1224 Manifest.permission.MEDIA_CONTENT_CONTROL 1225 }, 1226 conditional = true) 1227 @GuardedBy("mLock") updateScanningStateLocked( @onNull IMediaRouter2 router, @ScanningState int scanningState)1228 private void updateScanningStateLocked( 1229 @NonNull IMediaRouter2 router, @ScanningState int scanningState) { 1230 final IBinder binder = router.asBinder(); 1231 RouterRecord routerRecord = mAllRouterRecords.get(binder); 1232 if (routerRecord == null) { 1233 Slog.w(TAG, "Router record not found. Ignoring updateScanningState call."); 1234 return; 1235 } 1236 1237 boolean enableScanViaMediaContentControl = 1238 Flags.enableFullScanWithMediaContentControl() 1239 && routerRecord.mHasMediaContentControlPermission; 1240 if (scanningState == SCANNING_STATE_SCANNING_FULL 1241 && !enableScanViaMediaContentControl 1242 && !routerRecord.mHasMediaRoutingControl) { 1243 throw new SecurityException("Screen off scan requires MEDIA_ROUTING_CONTROL"); 1244 } 1245 1246 Slog.i( 1247 TAG, 1248 TextUtils.formatSimple( 1249 "updateScanningStateLocked | router: %d, packageName: %s, scanningState:" 1250 + " %d", 1251 routerRecord.mRouterId, 1252 routerRecord.mPackageName, 1253 getScanningStateString(scanningState))); 1254 1255 routerRecord.updateScanningState(scanningState); 1256 } 1257 1258 @GuardedBy("mLock") setDiscoveryRequestWithRouter2Locked(@onNull RouterRecord routerRecord, @NonNull RouteDiscoveryPreference discoveryRequest)1259 private void setDiscoveryRequestWithRouter2Locked(@NonNull RouterRecord routerRecord, 1260 @NonNull RouteDiscoveryPreference discoveryRequest) { 1261 if (routerRecord.mDiscoveryPreference.equals(discoveryRequest)) { 1262 return; 1263 } 1264 1265 Slog.i( 1266 TAG, 1267 TextUtils.formatSimple( 1268 "setDiscoveryRequestWithRouter2 | router: %s(id: %d), discovery request:" 1269 + " %s", 1270 routerRecord.mPackageName, 1271 routerRecord.mRouterId, 1272 discoveryRequest.toString())); 1273 1274 routerRecord.mDiscoveryPreference = discoveryRequest; 1275 routerRecord.mUserRecord.mHandler.sendMessage( 1276 obtainMessage(UserHandler::notifyDiscoveryPreferenceChangedToManagers, 1277 routerRecord.mUserRecord.mHandler, 1278 routerRecord.mPackageName, 1279 routerRecord.mDiscoveryPreference)); 1280 routerRecord.mUserRecord.mHandler.sendMessage( 1281 obtainMessage(UserHandler::updateDiscoveryPreferenceOnHandler, 1282 routerRecord.mUserRecord.mHandler)); 1283 } 1284 1285 @GuardedBy("mLock") setRouteListingPreferenceLocked( RouterRecord routerRecord, @Nullable RouteListingPreference routeListingPreference)1286 private void setRouteListingPreferenceLocked( 1287 RouterRecord routerRecord, @Nullable RouteListingPreference routeListingPreference) { 1288 routerRecord.mRouteListingPreference = routeListingPreference; 1289 String routeListingAsString = 1290 routeListingPreference != null 1291 ? routeListingPreference.getItems().stream() 1292 .map(RouteListingPreference.Item::getRouteId) 1293 .collect(Collectors.joining(",")) 1294 : null; 1295 1296 Slog.i( 1297 TAG, 1298 TextUtils.formatSimple( 1299 "setRouteListingPreference | router: %s(id: %d), route listing preference:" 1300 + " [%s]", 1301 routerRecord.mPackageName, routerRecord.mRouterId, routeListingAsString)); 1302 1303 routerRecord.mUserRecord.mHandler.sendMessage( 1304 obtainMessage( 1305 UserHandler::notifyRouteListingPreferenceChangeToManagers, 1306 routerRecord.mUserRecord.mHandler, 1307 routerRecord.mPackageName, 1308 routeListingPreference)); 1309 } 1310 1311 @GuardedBy("mLock") setRouteVolumeWithRouter2Locked(@onNull IMediaRouter2 router, @NonNull MediaRoute2Info route, int volume)1312 private void setRouteVolumeWithRouter2Locked(@NonNull IMediaRouter2 router, 1313 @NonNull MediaRoute2Info route, int volume) { 1314 final IBinder binder = router.asBinder(); 1315 RouterRecord routerRecord = mAllRouterRecords.get(binder); 1316 1317 if (routerRecord != null) { 1318 Slog.i( 1319 TAG, 1320 TextUtils.formatSimple( 1321 "setRouteVolumeWithRouter2 | router: %s(id: %d), volume: %d", 1322 routerRecord.mPackageName, routerRecord.mRouterId, volume)); 1323 1324 routerRecord.mUserRecord.mHandler.sendMessage( 1325 obtainMessage(UserHandler::setRouteVolumeOnHandler, 1326 routerRecord.mUserRecord.mHandler, 1327 DUMMY_REQUEST_ID, route, volume)); 1328 } 1329 } 1330 1331 @GuardedBy("mLock") requestCreateSessionWithRouter2Locked( int requestId, long managerRequestId, @NonNull IMediaRouter2 router, @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route, @Nullable Bundle sessionHints)1332 private void requestCreateSessionWithRouter2Locked( 1333 int requestId, 1334 long managerRequestId, 1335 @NonNull IMediaRouter2 router, 1336 @NonNull RoutingSessionInfo oldSession, 1337 @NonNull MediaRoute2Info route, 1338 @Nullable Bundle sessionHints) { 1339 final IBinder binder = router.asBinder(); 1340 final RouterRecord routerRecord = mAllRouterRecords.get(binder); 1341 1342 if (routerRecord == null) { 1343 return; 1344 } 1345 1346 Slog.i( 1347 TAG, 1348 TextUtils.formatSimple( 1349 "requestCreateSessionWithRouter2 | router: %s(id: %d), old session id: %s," 1350 + " new session's route id: %s, request id: %d", 1351 routerRecord.mPackageName, 1352 routerRecord.mRouterId, 1353 oldSession.getId(), 1354 route.getId(), 1355 requestId)); 1356 1357 UserHandler userHandler = routerRecord.mUserRecord.mHandler; 1358 if (managerRequestId != MediaRoute2ProviderService.REQUEST_ID_NONE) { 1359 ManagerRecord manager = userHandler.findManagerWithId(toRequesterId(managerRequestId)); 1360 if (manager == null || manager.mLastSessionCreationRequest == null) { 1361 Slog.w(TAG, "requestCreateSessionWithRouter2Locked: " 1362 + "Ignoring unknown request."); 1363 userHandler.notifySessionCreationFailedToRouter(routerRecord, requestId); 1364 return; 1365 } 1366 if (!TextUtils.equals(manager.mLastSessionCreationRequest.mOldSession.getId(), 1367 oldSession.getId())) { 1368 Slog.w(TAG, "requestCreateSessionWithRouter2Locked: " 1369 + "Ignoring unmatched routing session."); 1370 userHandler.notifySessionCreationFailedToRouter(routerRecord, requestId); 1371 return; 1372 } 1373 if (!TextUtils.equals(manager.mLastSessionCreationRequest.mRoute.getId(), 1374 route.getId())) { 1375 // When media router has no permission 1376 if (!routerRecord.hasSystemRoutingPermission() 1377 && manager.mLastSessionCreationRequest.mRoute.isSystemRoute() 1378 && route.isSystemRoute()) { 1379 route = manager.mLastSessionCreationRequest.mRoute; 1380 } else { 1381 Slog.w(TAG, "requestCreateSessionWithRouter2Locked: " 1382 + "Ignoring unmatched route."); 1383 userHandler.notifySessionCreationFailedToRouter(routerRecord, requestId); 1384 return; 1385 } 1386 } 1387 manager.mLastSessionCreationRequest = null; 1388 } else { 1389 String defaultRouteId = userHandler.mSystemProvider.getDefaultRoute().getId(); 1390 if (route.isSystemRoute() 1391 && !routerRecord.hasSystemRoutingPermission() 1392 && !TextUtils.equals(route.getId(), defaultRouteId)) { 1393 Slog.w(TAG, "MODIFY_AUDIO_ROUTING permission is required to transfer to" 1394 + route); 1395 userHandler.notifySessionCreationFailedToRouter(routerRecord, requestId); 1396 return; 1397 } 1398 } 1399 1400 long uniqueRequestId = toUniqueRequestId(routerRecord.mRouterId, requestId); 1401 userHandler.sendMessage( 1402 obtainMessage( 1403 UserHandler::requestCreateSessionWithRouter2OnHandler, 1404 userHandler, 1405 uniqueRequestId, 1406 managerRequestId, 1407 routerRecord, 1408 oldSession, 1409 route, 1410 sessionHints)); 1411 } 1412 1413 @GuardedBy("mLock") selectRouteWithRouter2Locked(@onNull IMediaRouter2 router, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route)1414 private void selectRouteWithRouter2Locked(@NonNull IMediaRouter2 router, 1415 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { 1416 final IBinder binder = router.asBinder(); 1417 final RouterRecord routerRecord = mAllRouterRecords.get(binder); 1418 1419 if (routerRecord == null) { 1420 return; 1421 } 1422 1423 Slog.i( 1424 TAG, 1425 TextUtils.formatSimple( 1426 "selectRouteWithRouter2 | router: %s(id: %d), route: %s", 1427 routerRecord.mPackageName, routerRecord.mRouterId, route.getId())); 1428 1429 routerRecord.mUserRecord.mHandler.sendMessage( 1430 obtainMessage(UserHandler::selectRouteOnHandler, 1431 routerRecord.mUserRecord.mHandler, 1432 DUMMY_REQUEST_ID, routerRecord, uniqueSessionId, route)); 1433 } 1434 1435 @GuardedBy("mLock") deselectRouteWithRouter2Locked(@onNull IMediaRouter2 router, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route)1436 private void deselectRouteWithRouter2Locked(@NonNull IMediaRouter2 router, 1437 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { 1438 final IBinder binder = router.asBinder(); 1439 final RouterRecord routerRecord = mAllRouterRecords.get(binder); 1440 1441 if (routerRecord == null) { 1442 return; 1443 } 1444 1445 Slog.i( 1446 TAG, 1447 TextUtils.formatSimple( 1448 "deselectRouteWithRouter2 | router: %s(id: %d), route: %s", 1449 routerRecord.mPackageName, routerRecord.mRouterId, route.getId())); 1450 1451 routerRecord.mUserRecord.mHandler.sendMessage( 1452 obtainMessage(UserHandler::deselectRouteOnHandler, 1453 routerRecord.mUserRecord.mHandler, 1454 DUMMY_REQUEST_ID, routerRecord, uniqueSessionId, route)); 1455 } 1456 1457 @GuardedBy("mLock") transferToRouteWithRouter2Locked( @onNull IMediaRouter2 router, @NonNull UserHandle transferInitiatorUserHandle, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route)1458 private void transferToRouteWithRouter2Locked( 1459 @NonNull IMediaRouter2 router, 1460 @NonNull UserHandle transferInitiatorUserHandle, 1461 @NonNull String uniqueSessionId, 1462 @NonNull MediaRoute2Info route) { 1463 final IBinder binder = router.asBinder(); 1464 final RouterRecord routerRecord = mAllRouterRecords.get(binder); 1465 1466 if (routerRecord == null) { 1467 return; 1468 } 1469 1470 Slog.i( 1471 TAG, 1472 TextUtils.formatSimple( 1473 "transferToRouteWithRouter2 | router: %s(id: %d), route: %s", 1474 routerRecord.mPackageName, routerRecord.mRouterId, route.getId())); 1475 1476 UserHandler userHandler = routerRecord.mUserRecord.mHandler; 1477 String defaultRouteId = userHandler.mSystemProvider.getDefaultRoute().getId(); 1478 if (route.isSystemRoute() 1479 && !routerRecord.hasSystemRoutingPermission() 1480 && !TextUtils.equals(route.getId(), defaultRouteId)) { 1481 userHandler.sendMessage( 1482 obtainMessage( 1483 UserHandler::notifySessionCreationFailedToRouter, 1484 userHandler, 1485 routerRecord, 1486 toOriginalRequestId(DUMMY_REQUEST_ID))); 1487 } else { 1488 userHandler.sendMessage( 1489 obtainMessage( 1490 UserHandler::transferToRouteOnHandler, 1491 userHandler, 1492 DUMMY_REQUEST_ID, 1493 transferInitiatorUserHandle, 1494 routerRecord.mPackageName, 1495 routerRecord, 1496 uniqueSessionId, 1497 route, 1498 RoutingSessionInfo.TRANSFER_REASON_APP)); 1499 } 1500 } 1501 1502 @GuardedBy("mLock") setSessionVolumeWithRouter2Locked(@onNull IMediaRouter2 router, @NonNull String uniqueSessionId, int volume)1503 private void setSessionVolumeWithRouter2Locked(@NonNull IMediaRouter2 router, 1504 @NonNull String uniqueSessionId, int volume) { 1505 final IBinder binder = router.asBinder(); 1506 RouterRecord routerRecord = mAllRouterRecords.get(binder); 1507 1508 if (routerRecord == null) { 1509 return; 1510 } 1511 1512 Slog.i( 1513 TAG, 1514 TextUtils.formatSimple( 1515 "setSessionVolumeWithRouter2 | router: %s(id: %d), session: %s, volume: %d", 1516 routerRecord.mPackageName, 1517 routerRecord.mRouterId, 1518 uniqueSessionId, 1519 volume)); 1520 1521 routerRecord.mUserRecord.mHandler.sendMessage( 1522 obtainMessage(UserHandler::setSessionVolumeOnHandler, 1523 routerRecord.mUserRecord.mHandler, 1524 DUMMY_REQUEST_ID, uniqueSessionId, volume)); 1525 } 1526 1527 @GuardedBy("mLock") releaseSessionWithRouter2Locked(@onNull IMediaRouter2 router, @NonNull String uniqueSessionId)1528 private void releaseSessionWithRouter2Locked(@NonNull IMediaRouter2 router, 1529 @NonNull String uniqueSessionId) { 1530 final IBinder binder = router.asBinder(); 1531 final RouterRecord routerRecord = mAllRouterRecords.get(binder); 1532 1533 if (routerRecord == null) { 1534 return; 1535 } 1536 1537 Slog.i( 1538 TAG, 1539 TextUtils.formatSimple( 1540 "releaseSessionWithRouter2 | router: %s(id: %d), session: %s", 1541 routerRecord.mPackageName, routerRecord.mRouterId, uniqueSessionId)); 1542 1543 routerRecord.mUserRecord.mHandler.sendMessage( 1544 obtainMessage(UserHandler::releaseSessionOnHandler, 1545 routerRecord.mUserRecord.mHandler, 1546 DUMMY_REQUEST_ID, routerRecord, uniqueSessionId)); 1547 } 1548 1549 // End of locked methods that are used by MediaRouter2. 1550 1551 // Start of locked methods that are used by MediaRouter2Manager. 1552 1553 @GuardedBy("mLock") getRemoteSessionsLocked( @onNull IMediaRouter2Manager manager)1554 private List<RoutingSessionInfo> getRemoteSessionsLocked( 1555 @NonNull IMediaRouter2Manager manager) { 1556 final IBinder binder = manager.asBinder(); 1557 ManagerRecord managerRecord = mAllManagerRecords.get(binder); 1558 1559 if (managerRecord == null) { 1560 Slog.w(TAG, "getRemoteSessionLocked: Ignoring unknown manager"); 1561 return Collections.emptyList(); 1562 } 1563 1564 List<RoutingSessionInfo> sessionInfos = new ArrayList<>(); 1565 for (MediaRoute2Provider provider : managerRecord.mUserRecord.mHandler.mRouteProviders) { 1566 if (!provider.mIsSystemRouteProvider) { 1567 sessionInfos.addAll(provider.getSessionInfos()); 1568 } 1569 } 1570 return sessionInfos; 1571 } 1572 1573 @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) 1574 @GuardedBy("mLock") registerManagerLocked( @onNull IMediaRouter2Manager manager, int callerUid, int callerPid, @NonNull String callerPackageName, @Nullable String targetPackageName, @NonNull UserHandle targetUser)1575 private void registerManagerLocked( 1576 @NonNull IMediaRouter2Manager manager, 1577 int callerUid, 1578 int callerPid, 1579 @NonNull String callerPackageName, 1580 @Nullable String targetPackageName, 1581 @NonNull UserHandle targetUser) { 1582 final IBinder binder = manager.asBinder(); 1583 ManagerRecord managerRecord = mAllManagerRecords.get(binder); 1584 1585 if (managerRecord != null) { 1586 Slog.w(TAG, "registerManagerLocked: Same manager already exists. callerPackageName=" 1587 + callerPackageName); 1588 return; 1589 } 1590 1591 boolean hasMediaRoutingControl = 1592 checkMediaRoutingControlPermission(callerUid, callerPid, callerPackageName); 1593 1594 boolean hasMediaContentControl = checkMediaContentControlPermission(callerUid, callerPid); 1595 1596 Slog.i( 1597 TAG, 1598 TextUtils.formatSimple( 1599 "registerManager | callerUid: %d, callerPid: %d, callerPackage: %s," 1600 + " targetPackageName: %s, targetUserId: %d, hasMediaRoutingControl:" 1601 + " %b", 1602 callerUid, 1603 callerPid, 1604 callerPackageName, 1605 targetPackageName, 1606 targetUser, 1607 hasMediaRoutingControl)); 1608 1609 UserRecord userRecord = getOrCreateUserRecordLocked(targetUser.getIdentifier()); 1610 1611 managerRecord = 1612 new ManagerRecord( 1613 userRecord, 1614 manager, 1615 callerUid, 1616 callerPid, 1617 callerPackageName, 1618 targetPackageName, 1619 hasMediaRoutingControl, 1620 hasMediaContentControl); 1621 try { 1622 binder.linkToDeath(managerRecord, 0); 1623 } catch (RemoteException ex) { 1624 throw new RuntimeException("Media router manager died prematurely.", ex); 1625 } 1626 1627 userRecord.mManagerRecords.add(managerRecord); 1628 mAllManagerRecords.put(binder, managerRecord); 1629 1630 // Note: Features should be sent first before the routes. If not, the 1631 // RouteCallback#onRoutesAdded() for system MR2 will never be called with initial routes 1632 // due to the lack of features. 1633 for (RouterRecord routerRecord : userRecord.mRouterRecords) { 1634 // Send route listing preferences before discovery preferences and routes to avoid an 1635 // inconsistent state where there are routes to show, but the manager thinks 1636 // the app has not expressed a preference for listing. 1637 userRecord.mHandler.sendMessage( 1638 obtainMessage( 1639 UserHandler::notifyRouteListingPreferenceChangeToManagers, 1640 routerRecord.mUserRecord.mHandler, 1641 routerRecord.mPackageName, 1642 routerRecord.mRouteListingPreference)); 1643 // TODO: UserRecord <-> routerRecord, why do they reference each other? 1644 // How about removing mUserRecord from routerRecord? 1645 routerRecord.mUserRecord.mHandler.sendMessage( 1646 obtainMessage( 1647 UserHandler::notifyDiscoveryPreferenceChangedToManager, 1648 routerRecord.mUserRecord.mHandler, 1649 routerRecord, 1650 manager)); 1651 } 1652 1653 userRecord.mHandler.sendMessage( 1654 obtainMessage( 1655 UserHandler::notifyInitialRoutesToManager, userRecord.mHandler, manager)); 1656 } 1657 1658 @GuardedBy("mLock") unregisterManagerLocked(@onNull IMediaRouter2Manager manager, boolean died)1659 private void unregisterManagerLocked(@NonNull IMediaRouter2Manager manager, boolean died) { 1660 ManagerRecord managerRecord = mAllManagerRecords.remove(manager.asBinder()); 1661 if (managerRecord == null) { 1662 Slog.w( 1663 TAG, 1664 TextUtils.formatSimple( 1665 "Ignoring unregistering unknown manager: %s, died: %b", manager, died)); 1666 return; 1667 } 1668 UserRecord userRecord = managerRecord.mUserRecord; 1669 1670 Slog.i( 1671 TAG, 1672 TextUtils.formatSimple( 1673 "unregisterManager | package: %s, user: %d, manager: %d, died: %b", 1674 managerRecord.mOwnerPackageName, 1675 userRecord.mUserId, 1676 managerRecord.mManagerId, 1677 died)); 1678 1679 userRecord.mManagerRecords.remove(managerRecord); 1680 managerRecord.dispose(); 1681 disposeUserIfNeededLocked(userRecord); // since manager removed from user 1682 } 1683 1684 @GuardedBy("mLock") updateScanningStateLocked( @onNull IMediaRouter2Manager manager, @ScanningState int scanningState)1685 private void updateScanningStateLocked( 1686 @NonNull IMediaRouter2Manager manager, @ScanningState int scanningState) { 1687 final IBinder binder = manager.asBinder(); 1688 ManagerRecord managerRecord = mAllManagerRecords.get(binder); 1689 if (managerRecord == null) { 1690 Slog.w(TAG, "Manager record not found. Ignoring updateScanningState call."); 1691 return; 1692 } 1693 1694 boolean enableScanViaMediaContentControl = 1695 Flags.enableFullScanWithMediaContentControl() 1696 && managerRecord.mHasMediaContentControl; 1697 if (!managerRecord.mHasMediaRoutingControl 1698 && !enableScanViaMediaContentControl 1699 && scanningState == SCANNING_STATE_SCANNING_FULL) { 1700 throw new SecurityException("Screen off scan requires MEDIA_ROUTING_CONTROL"); 1701 } 1702 1703 Slog.i( 1704 TAG, 1705 TextUtils.formatSimple( 1706 "updateScanningState | manager: %d, ownerPackageName: %s," 1707 + " targetPackageName: %s, scanningState: %d", 1708 managerRecord.mManagerId, 1709 managerRecord.mOwnerPackageName, 1710 managerRecord.mTargetPackageName, 1711 getScanningStateString(scanningState))); 1712 1713 managerRecord.updateScanningState(scanningState); 1714 } 1715 1716 @GuardedBy("mLock") setRouteVolumeWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager, @NonNull MediaRoute2Info route, int volume)1717 private void setRouteVolumeWithManagerLocked(int requestId, 1718 @NonNull IMediaRouter2Manager manager, 1719 @NonNull MediaRoute2Info route, int volume) { 1720 final IBinder binder = manager.asBinder(); 1721 ManagerRecord managerRecord = mAllManagerRecords.get(binder); 1722 1723 if (managerRecord == null) { 1724 return; 1725 } 1726 1727 Slog.i(TAG, TextUtils.formatSimple( 1728 "setRouteVolumeWithManager | manager: %d, route: %s, volume: %d", 1729 managerRecord.mManagerId, route.getId(), volume)); 1730 1731 long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); 1732 managerRecord.mUserRecord.mHandler.sendMessage( 1733 obtainMessage(UserHandler::setRouteVolumeOnHandler, 1734 managerRecord.mUserRecord.mHandler, 1735 uniqueRequestId, route, volume)); 1736 } 1737 1738 @GuardedBy("mLock") requestCreateSessionWithManagerLocked( int requestId, @NonNull IMediaRouter2Manager manager, @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route, @NonNull UserHandle transferInitiatorUserHandle, @NonNull String transferInitiatorPackageName)1739 private void requestCreateSessionWithManagerLocked( 1740 int requestId, 1741 @NonNull IMediaRouter2Manager manager, 1742 @NonNull RoutingSessionInfo oldSession, 1743 @NonNull MediaRoute2Info route, 1744 @NonNull UserHandle transferInitiatorUserHandle, 1745 @NonNull String transferInitiatorPackageName) { 1746 ManagerRecord managerRecord = mAllManagerRecords.get(manager.asBinder()); 1747 if (managerRecord == null) { 1748 return; 1749 } 1750 1751 Slog.i(TAG, TextUtils.formatSimple( 1752 "requestCreateSessionWithManager | manager: %d, route: %s", 1753 managerRecord.mManagerId, route.getId())); 1754 1755 String packageName = oldSession.getClientPackageName(); 1756 1757 RouterRecord routerRecord = managerRecord.mUserRecord.findRouterRecordLocked(packageName); 1758 if (routerRecord == null) { 1759 Slog.w(TAG, "requestCreateSessionWithManagerLocked: Ignoring session creation for " 1760 + "unknown router."); 1761 try { 1762 managerRecord.mManager.notifyRequestFailed(requestId, REASON_UNKNOWN_ERROR); 1763 } catch (RemoteException ex) { 1764 Slog.w(TAG, "requestCreateSessionWithManagerLocked: Failed to notify failure. " 1765 + "Manager probably died."); 1766 } 1767 return; 1768 } 1769 1770 long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); 1771 SessionCreationRequest lastRequest = managerRecord.mLastSessionCreationRequest; 1772 if (lastRequest != null) { 1773 Slog.i( 1774 TAG, 1775 TextUtils.formatSimple( 1776 "requestCreateSessionWithManagerLocked: Notifying failure for pending" 1777 + " session creation request - oldSession: %s, route: %s", 1778 lastRequest.mOldSession, lastRequest.mRoute)); 1779 managerRecord.mUserRecord.mHandler.notifyRequestFailedToManager( 1780 managerRecord.mManager, 1781 toOriginalRequestId(lastRequest.mManagerRequestId), 1782 REASON_UNKNOWN_ERROR); 1783 } 1784 managerRecord.mLastSessionCreationRequest = new SessionCreationRequest(routerRecord, 1785 MediaRoute2ProviderService.REQUEST_ID_NONE, uniqueRequestId, 1786 oldSession, route); 1787 1788 // Before requesting to the provider, get session hints from the media router. 1789 // As a return, media router will request to create a session. 1790 routerRecord.mUserRecord.mHandler.sendMessage( 1791 obtainMessage( 1792 UserHandler::requestRouterCreateSessionOnHandler, 1793 routerRecord.mUserRecord.mHandler, 1794 uniqueRequestId, 1795 routerRecord, 1796 managerRecord, 1797 oldSession, 1798 route, 1799 transferInitiatorUserHandle, 1800 transferInitiatorPackageName)); 1801 } 1802 1803 @GuardedBy("mLock") selectRouteWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route)1804 private void selectRouteWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager, 1805 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { 1806 final IBinder binder = manager.asBinder(); 1807 ManagerRecord managerRecord = mAllManagerRecords.get(binder); 1808 1809 if (managerRecord == null) { 1810 return; 1811 } 1812 1813 Slog.i(TAG, TextUtils.formatSimple( 1814 "selectRouteWithManager | manager: %d, session: %s, route: %s", 1815 managerRecord.mManagerId, uniqueSessionId, route.getId())); 1816 1817 // Can be null if the session is system's or RCN. 1818 RouterRecord routerRecord = managerRecord.mUserRecord.mHandler 1819 .findRouterWithSessionLocked(uniqueSessionId); 1820 1821 long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); 1822 managerRecord.mUserRecord.mHandler.sendMessage( 1823 obtainMessage(UserHandler::selectRouteOnHandler, 1824 managerRecord.mUserRecord.mHandler, 1825 uniqueRequestId, routerRecord, uniqueSessionId, route)); 1826 } 1827 1828 @GuardedBy("mLock") deselectRouteWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route)1829 private void deselectRouteWithManagerLocked(int requestId, 1830 @NonNull IMediaRouter2Manager manager, 1831 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { 1832 final IBinder binder = manager.asBinder(); 1833 ManagerRecord managerRecord = mAllManagerRecords.get(binder); 1834 1835 if (managerRecord == null) { 1836 return; 1837 } 1838 1839 Slog.i(TAG, TextUtils.formatSimple( 1840 "deselectRouteWithManager | manager: %d, session: %s, route: %s", 1841 managerRecord.mManagerId, uniqueSessionId, route.getId())); 1842 1843 // Can be null if the session is system's or RCN. 1844 RouterRecord routerRecord = managerRecord.mUserRecord.mHandler 1845 .findRouterWithSessionLocked(uniqueSessionId); 1846 1847 long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); 1848 managerRecord.mUserRecord.mHandler.sendMessage( 1849 obtainMessage(UserHandler::deselectRouteOnHandler, 1850 managerRecord.mUserRecord.mHandler, 1851 uniqueRequestId, routerRecord, uniqueSessionId, route)); 1852 } 1853 1854 @GuardedBy("mLock") transferToRouteWithManagerLocked( int requestId, @NonNull IMediaRouter2Manager manager, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route, @RoutingSessionInfo.TransferReason int transferReason, @NonNull UserHandle transferInitiatorUserHandle, @NonNull String transferInitiatorPackageName)1855 private void transferToRouteWithManagerLocked( 1856 int requestId, 1857 @NonNull IMediaRouter2Manager manager, 1858 @NonNull String uniqueSessionId, 1859 @NonNull MediaRoute2Info route, 1860 @RoutingSessionInfo.TransferReason int transferReason, 1861 @NonNull UserHandle transferInitiatorUserHandle, 1862 @NonNull String transferInitiatorPackageName) { 1863 final IBinder binder = manager.asBinder(); 1864 ManagerRecord managerRecord = mAllManagerRecords.get(binder); 1865 1866 if (managerRecord == null) { 1867 return; 1868 } 1869 1870 Slog.i(TAG, TextUtils.formatSimple( 1871 "transferToRouteWithManager | manager: %d, session: %s, route: %s", 1872 managerRecord.mManagerId, uniqueSessionId, route.getId())); 1873 1874 // Can be null if the session is system's or RCN. 1875 RouterRecord routerRecord = managerRecord.mUserRecord.mHandler 1876 .findRouterWithSessionLocked(uniqueSessionId); 1877 1878 long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); 1879 managerRecord.mUserRecord.mHandler.sendMessage( 1880 obtainMessage( 1881 UserHandler::transferToRouteOnHandler, 1882 managerRecord.mUserRecord.mHandler, 1883 uniqueRequestId, 1884 transferInitiatorUserHandle, 1885 transferInitiatorPackageName, 1886 routerRecord, 1887 uniqueSessionId, 1888 route, 1889 transferReason)); 1890 } 1891 1892 @GuardedBy("mLock") setSessionVolumeWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager, @NonNull String uniqueSessionId, int volume)1893 private void setSessionVolumeWithManagerLocked(int requestId, 1894 @NonNull IMediaRouter2Manager manager, 1895 @NonNull String uniqueSessionId, int volume) { 1896 final IBinder binder = manager.asBinder(); 1897 ManagerRecord managerRecord = mAllManagerRecords.get(binder); 1898 1899 if (managerRecord == null) { 1900 return; 1901 } 1902 1903 Slog.i(TAG, TextUtils.formatSimple( 1904 "setSessionVolumeWithManager | manager: %d, session: %s, volume: %d", 1905 managerRecord.mManagerId, uniqueSessionId, volume)); 1906 1907 long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); 1908 managerRecord.mUserRecord.mHandler.sendMessage( 1909 obtainMessage(UserHandler::setSessionVolumeOnHandler, 1910 managerRecord.mUserRecord.mHandler, 1911 uniqueRequestId, uniqueSessionId, volume)); 1912 } 1913 1914 @GuardedBy("mLock") releaseSessionWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager, @NonNull String uniqueSessionId)1915 private void releaseSessionWithManagerLocked(int requestId, 1916 @NonNull IMediaRouter2Manager manager, @NonNull String uniqueSessionId) { 1917 final IBinder binder = manager.asBinder(); 1918 ManagerRecord managerRecord = mAllManagerRecords.get(binder); 1919 1920 if (managerRecord == null) { 1921 return; 1922 } 1923 1924 Slog.i(TAG, TextUtils.formatSimple( 1925 "releaseSessionWithManager | manager: %d, session: %s", 1926 managerRecord.mManagerId, uniqueSessionId)); 1927 1928 RouterRecord routerRecord = managerRecord.mUserRecord.mHandler 1929 .findRouterWithSessionLocked(uniqueSessionId); 1930 1931 long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); 1932 managerRecord.mUserRecord.mHandler.sendMessage( 1933 obtainMessage(UserHandler::releaseSessionOnHandler, 1934 managerRecord.mUserRecord.mHandler, 1935 uniqueRequestId, routerRecord, uniqueSessionId)); 1936 } 1937 1938 // End of locked methods that are used by MediaRouter2Manager. 1939 1940 // Start of locked methods that are used by both MediaRouter2 and MediaRouter2Manager. 1941 1942 @GuardedBy("mLock") getOrCreateUserRecordLocked(int userId)1943 private UserRecord getOrCreateUserRecordLocked(int userId) { 1944 UserRecord userRecord = mUserRecords.get(userId); 1945 if (userRecord == null) { 1946 userRecord = new UserRecord(userId, mLooper); 1947 mUserRecords.put(userId, userRecord); 1948 userRecord.init(); 1949 if (isUserActiveLocked(userId)) { 1950 userRecord.mHandler.sendMessage( 1951 obtainMessage(UserHandler::start, userRecord.mHandler)); 1952 } 1953 } 1954 return userRecord; 1955 } 1956 1957 @GuardedBy("mLock") disposeUserIfNeededLocked(@onNull UserRecord userRecord)1958 private void disposeUserIfNeededLocked(@NonNull UserRecord userRecord) { 1959 // If there are no records left and the user is no longer current then go ahead 1960 // and purge the user record and all of its associated state. If the user is current 1961 // then leave it alone since we might be connected to a route or want to query 1962 // the same route information again soon. 1963 if (!isUserActiveLocked(userRecord.mUserId) 1964 && userRecord.mRouterRecords.isEmpty() 1965 && userRecord.mManagerRecords.isEmpty()) { 1966 if (DEBUG) { 1967 Slog.d(TAG, userRecord + ": Disposed"); 1968 } 1969 userRecord.mHandler.sendMessage( 1970 obtainMessage(UserHandler::stop, userRecord.mHandler)); 1971 mUserRecords.remove(userRecord.mUserId); 1972 // Note: User already stopped (by switchUser) so no need to send stop message here. 1973 } 1974 } 1975 1976 // End of locked methods that are used by both MediaRouter2 and MediaRouter2Manager. 1977 toUniqueRequestId(int requesterId, int originalRequestId)1978 static long toUniqueRequestId(int requesterId, int originalRequestId) { 1979 return ((long) requesterId << 32) | originalRequestId; 1980 } 1981 toRequesterId(long uniqueRequestId)1982 static int toRequesterId(long uniqueRequestId) { 1983 return (int) (uniqueRequestId >> 32); 1984 } 1985 toOriginalRequestId(long uniqueRequestId)1986 static int toOriginalRequestId(long uniqueRequestId) { 1987 return (int) uniqueRequestId; 1988 } 1989 getScanningStateString(@canningState int scanningState)1990 private static String getScanningStateString(@ScanningState int scanningState) { 1991 return switch (scanningState) { 1992 case SCANNING_STATE_NOT_SCANNING -> "NOT_SCANNING"; 1993 case SCANNING_STATE_WHILE_INTERACTIVE -> "SCREEN_ON_ONLY"; 1994 case SCANNING_STATE_SCANNING_FULL -> "FULL"; 1995 default -> "Invalid scanning state: " + scanningState; 1996 }; 1997 } 1998 validateScanningStateValue(@canningState int scanningState)1999 private static void validateScanningStateValue(@ScanningState int scanningState) { 2000 if (scanningState != SCANNING_STATE_NOT_SCANNING 2001 && scanningState != SCANNING_STATE_WHILE_INTERACTIVE 2002 && scanningState != SCANNING_STATE_SCANNING_FULL) { 2003 throw new IllegalArgumentException( 2004 TextUtils.formatSimple("Scanning state %d is not valid.", scanningState)); 2005 } 2006 } 2007 2008 final class UserRecord { 2009 public final int mUserId; 2010 //TODO: make records private for thread-safety 2011 final ArrayList<RouterRecord> mRouterRecords = new ArrayList<>(); 2012 final ArrayList<ManagerRecord> mManagerRecords = new ArrayList<>(); 2013 RouteDiscoveryPreference mCompositeDiscoveryPreference = RouteDiscoveryPreference.EMPTY; 2014 Set<String> mActivelyScanningPackages = Set.of(); 2015 final UserHandler mHandler; 2016 UserRecord(int userId, @NonNull Looper looper)2017 UserRecord(int userId, @NonNull Looper looper) { 2018 mUserId = userId; 2019 mHandler = 2020 new UserHandler( 2021 /* service= */ MediaRouter2ServiceImpl.this, 2022 /* userRecord= */ this, 2023 looper); 2024 } 2025 init()2026 void init() { 2027 mHandler.init(); 2028 } 2029 2030 // TODO: This assumes that only one router exists in a package. 2031 // Do this in Android S or later. 2032 @GuardedBy("mLock") findRouterRecordLocked(String packageName)2033 RouterRecord findRouterRecordLocked(String packageName) { 2034 for (RouterRecord routerRecord : mRouterRecords) { 2035 if (TextUtils.equals(routerRecord.mPackageName, packageName)) { 2036 return routerRecord; 2037 } 2038 } 2039 return null; 2040 } 2041 dump(@onNull PrintWriter pw, @NonNull String prefix)2042 public void dump(@NonNull PrintWriter pw, @NonNull String prefix) { 2043 pw.println(prefix + "UserRecord"); 2044 2045 String indent = prefix + " "; 2046 2047 pw.println(indent + "mUserId=" + mUserId); 2048 2049 pw.println(indent + "Router Records:"); 2050 if (!mRouterRecords.isEmpty()) { 2051 for (RouterRecord routerRecord : mRouterRecords) { 2052 routerRecord.dump(pw, indent + " "); 2053 } 2054 } else { 2055 pw.println(indent + "<no router records>"); 2056 } 2057 2058 pw.println(indent + "Manager Records:"); 2059 if (!mManagerRecords.isEmpty()) { 2060 for (ManagerRecord managerRecord : mManagerRecords) { 2061 managerRecord.dump(pw, indent + " "); 2062 } 2063 } else { 2064 pw.println(indent + "<no manager records>"); 2065 } 2066 2067 pw.println(indent + "Composite discovery preference:"); 2068 mCompositeDiscoveryPreference.dump(pw, indent + " "); 2069 pw.println( 2070 indent 2071 + "Packages actively scanning: " 2072 + String.join(", ", mActivelyScanningPackages)); 2073 2074 if (!mHandler.runWithScissors(() -> mHandler.dump(pw, indent), 1000)) { 2075 pw.println(indent + "<could not dump handler state>"); 2076 } 2077 } 2078 } 2079 2080 final class RouterRecord implements IBinder.DeathRecipient { 2081 public final UserRecord mUserRecord; 2082 public final String mPackageName; 2083 public final List<Integer> mSelectRouteSequenceNumbers; 2084 public final IMediaRouter2 mRouter; 2085 public final int mUid; 2086 public final int mPid; 2087 public final boolean mHasConfigureWifiDisplayPermission; 2088 public final boolean mHasModifyAudioRoutingPermission; 2089 public final boolean mHasMediaContentControlPermission; 2090 public final boolean mHasMediaRoutingControl; 2091 public final AtomicBoolean mHasBluetoothRoutingPermission; 2092 public final int mRouterId; 2093 public @ScanningState int mScanningState = SCANNING_STATE_NOT_SCANNING; 2094 2095 public RouteDiscoveryPreference mDiscoveryPreference; 2096 @Nullable public RouteListingPreference mRouteListingPreference; 2097 RouterRecord( UserRecord userRecord, IMediaRouter2 router, int uid, int pid, String packageName, boolean hasConfigureWifiDisplayPermission, boolean hasModifyAudioRoutingPermission, boolean hasMediaContentControlPermission, boolean hasMediaRoutingControl)2098 RouterRecord( 2099 UserRecord userRecord, 2100 IMediaRouter2 router, 2101 int uid, 2102 int pid, 2103 String packageName, 2104 boolean hasConfigureWifiDisplayPermission, 2105 boolean hasModifyAudioRoutingPermission, 2106 boolean hasMediaContentControlPermission, 2107 boolean hasMediaRoutingControl) { 2108 mUserRecord = userRecord; 2109 mPackageName = packageName; 2110 mSelectRouteSequenceNumbers = new ArrayList<>(); 2111 mDiscoveryPreference = RouteDiscoveryPreference.EMPTY; 2112 mRouter = router; 2113 mUid = uid; 2114 mPid = pid; 2115 mHasConfigureWifiDisplayPermission = hasConfigureWifiDisplayPermission; 2116 mHasModifyAudioRoutingPermission = hasModifyAudioRoutingPermission; 2117 mHasMediaContentControlPermission = hasMediaContentControlPermission; 2118 mHasMediaRoutingControl = hasMediaRoutingControl; 2119 mHasBluetoothRoutingPermission = 2120 new AtomicBoolean(checkCallerHasBluetoothPermissions(mPid, mUid)); 2121 mRouterId = mNextRouterOrManagerId.getAndIncrement(); 2122 } 2123 2124 /** 2125 * Returns whether the corresponding router has permission to query and control system 2126 * routes. 2127 */ hasSystemRoutingPermission()2128 public boolean hasSystemRoutingPermission() { 2129 return mHasModifyAudioRoutingPermission || mHasBluetoothRoutingPermission.get(); 2130 } 2131 isActivelyScanning()2132 public boolean isActivelyScanning() { 2133 return mScanningState == SCANNING_STATE_WHILE_INTERACTIVE 2134 || mScanningState == SCANNING_STATE_SCANNING_FULL 2135 || mDiscoveryPreference.shouldPerformActiveScan(); 2136 } 2137 2138 @GuardedBy("mLock") maybeUpdateSystemRoutingPermissionLocked()2139 public void maybeUpdateSystemRoutingPermissionLocked() { 2140 boolean oldSystemRoutingPermissionValue = hasSystemRoutingPermission(); 2141 mHasBluetoothRoutingPermission.set(checkCallerHasBluetoothPermissions(mPid, mUid)); 2142 boolean newSystemRoutingPermissionValue = hasSystemRoutingPermission(); 2143 if (oldSystemRoutingPermissionValue != newSystemRoutingPermissionValue) { 2144 Map<String, MediaRoute2Info> routesToReport = 2145 newSystemRoutingPermissionValue 2146 ? mUserRecord.mHandler.mLastNotifiedRoutesToPrivilegedRouters 2147 : mUserRecord.mHandler.mLastNotifiedRoutesToNonPrivilegedRouters; 2148 notifyRoutesUpdated(routesToReport.values().stream().toList()); 2149 2150 List<RoutingSessionInfo> sessionInfos = 2151 mUserRecord.mHandler.mSystemProvider.getSessionInfos(); 2152 RoutingSessionInfo systemSessionToReport = 2153 newSystemRoutingPermissionValue && !sessionInfos.isEmpty() 2154 ? sessionInfos.get(0) 2155 : mUserRecord.mHandler.mSystemProvider.getDefaultSessionInfo(); 2156 notifySessionInfoChanged(systemSessionToReport); 2157 } 2158 } 2159 dispose()2160 public void dispose() { 2161 mRouter.asBinder().unlinkToDeath(this, 0); 2162 } 2163 2164 @Override binderDied()2165 public void binderDied() { 2166 routerDied(this); 2167 } 2168 updateScanningState(@canningState int scanningState)2169 public void updateScanningState(@ScanningState int scanningState) { 2170 if (mScanningState == scanningState) { 2171 return; 2172 } 2173 2174 mScanningState = scanningState; 2175 2176 mUserRecord.mHandler.sendMessage( 2177 obtainMessage( 2178 UserHandler::updateDiscoveryPreferenceOnHandler, mUserRecord.mHandler)); 2179 } 2180 dump(@onNull PrintWriter pw, @NonNull String prefix)2181 public void dump(@NonNull PrintWriter pw, @NonNull String prefix) { 2182 pw.println(prefix + "RouterRecord"); 2183 2184 String indent = prefix + " "; 2185 2186 pw.println(indent + "mPackageName=" + mPackageName); 2187 pw.println(indent + "mSelectRouteSequenceNumbers=" + mSelectRouteSequenceNumbers); 2188 pw.println(indent + "mUid=" + mUid); 2189 pw.println(indent + "mPid=" + mPid); 2190 pw.println(indent + "mHasConfigureWifiDisplayPermission=" 2191 + mHasConfigureWifiDisplayPermission); 2192 pw.println( 2193 indent 2194 + "mHasModifyAudioRoutingPermission=" 2195 + mHasModifyAudioRoutingPermission); 2196 pw.println( 2197 indent 2198 + "mHasBluetoothRoutingPermission=" 2199 + mHasBluetoothRoutingPermission.get()); 2200 pw.println(indent + "hasSystemRoutingPermission=" + hasSystemRoutingPermission()); 2201 pw.println(indent + "mRouterId=" + mRouterId); 2202 2203 mDiscoveryPreference.dump(pw, indent); 2204 } 2205 2206 /** 2207 * Notifies the corresponding router that it was successfully registered. 2208 * 2209 * <p>The message sent to the router includes a snapshot of the initial state, including 2210 * known routes and the system {@link RoutingSessionInfo}. 2211 * 2212 * @param currentRoutes All currently known routes, which are filtered according to package 2213 * visibility before being sent to the router. 2214 * @param currentSystemSessionInfo The current system {@link RoutingSessionInfo}. 2215 */ notifyRegistered( List<MediaRoute2Info> currentRoutes, RoutingSessionInfo currentSystemSessionInfo)2216 public void notifyRegistered( 2217 List<MediaRoute2Info> currentRoutes, RoutingSessionInfo currentSystemSessionInfo) { 2218 try { 2219 mRouter.notifyRouterRegistered( 2220 getVisibleRoutes(currentRoutes), currentSystemSessionInfo); 2221 } catch (RemoteException ex) { 2222 Slog.w(TAG, "Failed to notify router registered. Router probably died.", ex); 2223 } 2224 } 2225 2226 /** 2227 * Sends the corresponding router an {@link 2228 * android.media.MediaRouter2.RouteCallback#onRoutesUpdated update} for the given {@code 2229 * routes}. 2230 * 2231 * <p>Only the routes that are visible to the router are sent as part of the update. 2232 */ notifyRoutesUpdated(List<MediaRoute2Info> routes)2233 public void notifyRoutesUpdated(List<MediaRoute2Info> routes) { 2234 try { 2235 mRouter.notifyRoutesUpdated(getVisibleRoutes(routes)); 2236 } catch (RemoteException ex) { 2237 Slog.w(TAG, "Failed to notify routes updated. Router probably died.", ex); 2238 } 2239 } 2240 notifySessionCreated(int requestId, @NonNull RoutingSessionInfo sessionInfo)2241 public void notifySessionCreated(int requestId, @NonNull RoutingSessionInfo sessionInfo) { 2242 try { 2243 mRouter.notifySessionCreated( 2244 requestId, maybeClearTransferInitiatorIdentity(sessionInfo)); 2245 } catch (RemoteException ex) { 2246 Slog.w( 2247 TAG, 2248 "Failed to notify router of the session creation." 2249 + " Router probably died.", 2250 ex); 2251 } 2252 } 2253 2254 /** 2255 * Sends the corresponding router an update for the given session. 2256 * 2257 * <p>Note: These updates are not directly visible to the app. 2258 */ notifySessionInfoChanged(RoutingSessionInfo sessionInfo)2259 public void notifySessionInfoChanged(RoutingSessionInfo sessionInfo) { 2260 try { 2261 mRouter.notifySessionInfoChanged(maybeClearTransferInitiatorIdentity(sessionInfo)); 2262 } catch (RemoteException ex) { 2263 Slog.w(TAG, "Failed to notify session info changed. Router probably died.", ex); 2264 } 2265 } 2266 maybeClearTransferInitiatorIdentity( @onNull RoutingSessionInfo sessionInfo)2267 private RoutingSessionInfo maybeClearTransferInitiatorIdentity( 2268 @NonNull RoutingSessionInfo sessionInfo) { 2269 UserHandle transferInitiatorUserHandle = sessionInfo.getTransferInitiatorUserHandle(); 2270 String transferInitiatorPackageName = sessionInfo.getTransferInitiatorPackageName(); 2271 2272 if (!Objects.equals(UserHandle.of(mUserRecord.mUserId), transferInitiatorUserHandle) 2273 || !Objects.equals(mPackageName, transferInitiatorPackageName)) { 2274 return new RoutingSessionInfo.Builder(sessionInfo) 2275 .setTransferInitiator(null, null) 2276 .build(); 2277 } 2278 2279 return sessionInfo; 2280 } 2281 2282 /** 2283 * Returns a filtered copy of {@code routes} that contains only the routes that are {@link 2284 * MediaRoute2Info#isVisibleTo visible} to the router corresponding to this record. 2285 */ getVisibleRoutes(@onNull List<MediaRoute2Info> routes)2286 private List<MediaRoute2Info> getVisibleRoutes(@NonNull List<MediaRoute2Info> routes) { 2287 List<MediaRoute2Info> filteredRoutes = new ArrayList<>(); 2288 for (MediaRoute2Info route : routes) { 2289 if (route.isVisibleTo(mPackageName)) { 2290 filteredRoutes.add(route); 2291 } 2292 } 2293 return filteredRoutes; 2294 } 2295 } 2296 2297 final class ManagerRecord implements IBinder.DeathRecipient { 2298 @NonNull public final UserRecord mUserRecord; 2299 @NonNull public final IMediaRouter2Manager mManager; 2300 public final int mOwnerUid; 2301 public final int mOwnerPid; 2302 @NonNull public final String mOwnerPackageName; 2303 public final int mManagerId; 2304 // TODO (b/281072508): Document behaviour around nullability for mTargetPackageName. 2305 @Nullable public final String mTargetPackageName; 2306 2307 public final boolean mHasMediaRoutingControl; 2308 public final boolean mHasMediaContentControl; 2309 @Nullable public SessionCreationRequest mLastSessionCreationRequest; 2310 2311 public @ScanningState int mScanningState = SCANNING_STATE_NOT_SCANNING; 2312 ManagerRecord( @onNull UserRecord userRecord, @NonNull IMediaRouter2Manager manager, int ownerUid, int ownerPid, @NonNull String ownerPackageName, @Nullable String targetPackageName, boolean hasMediaRoutingControl, boolean hasMediaContentControl)2313 ManagerRecord( 2314 @NonNull UserRecord userRecord, 2315 @NonNull IMediaRouter2Manager manager, 2316 int ownerUid, 2317 int ownerPid, 2318 @NonNull String ownerPackageName, 2319 @Nullable String targetPackageName, 2320 boolean hasMediaRoutingControl, 2321 boolean hasMediaContentControl) { 2322 mUserRecord = userRecord; 2323 mManager = manager; 2324 mOwnerUid = ownerUid; 2325 mOwnerPid = ownerPid; 2326 mOwnerPackageName = ownerPackageName; 2327 mTargetPackageName = targetPackageName; 2328 mManagerId = mNextRouterOrManagerId.getAndIncrement(); 2329 mHasMediaRoutingControl = hasMediaRoutingControl; 2330 mHasMediaContentControl = hasMediaContentControl; 2331 } 2332 dispose()2333 public void dispose() { 2334 mManager.asBinder().unlinkToDeath(this, 0); 2335 } 2336 2337 @Override binderDied()2338 public void binderDied() { 2339 managerDied(this); 2340 } 2341 dump(@onNull PrintWriter pw, @NonNull String prefix)2342 public void dump(@NonNull PrintWriter pw, @NonNull String prefix) { 2343 pw.println(prefix + "ManagerRecord"); 2344 2345 String indent = prefix + " "; 2346 2347 pw.println(indent + "mOwnerPackageName=" + mOwnerPackageName); 2348 pw.println(indent + "mTargetPackageName=" + mTargetPackageName); 2349 pw.println(indent + "mManagerId=" + mManagerId); 2350 pw.println(indent + "mOwnerUid=" + mOwnerUid); 2351 pw.println(indent + "mOwnerPid=" + mOwnerPid); 2352 pw.println(indent + "mScanningState=" + getScanningStateString(mScanningState)); 2353 2354 if (mLastSessionCreationRequest != null) { 2355 mLastSessionCreationRequest.dump(pw, indent); 2356 } 2357 } 2358 updateScanningState(@canningState int scanningState)2359 private void updateScanningState(@ScanningState int scanningState) { 2360 if (mScanningState == scanningState) { 2361 return; 2362 } 2363 2364 mScanningState = scanningState; 2365 2366 mUserRecord.mHandler.sendMessage( 2367 obtainMessage( 2368 UserHandler::updateDiscoveryPreferenceOnHandler, mUserRecord.mHandler)); 2369 } 2370 2371 @Override toString()2372 public String toString() { 2373 return "Manager " + mOwnerPackageName + " (pid " + mOwnerPid + ")"; 2374 } 2375 } 2376 2377 static final class UserHandler extends Handler implements 2378 MediaRoute2ProviderWatcher.Callback, 2379 MediaRoute2Provider.Callback { 2380 2381 private final WeakReference<MediaRouter2ServiceImpl> mServiceRef; 2382 private final UserRecord mUserRecord; 2383 private final MediaRoute2ProviderWatcher mWatcher; 2384 2385 private final SystemMediaRoute2Provider mSystemProvider; 2386 private final ArrayList<MediaRoute2Provider> mRouteProviders = 2387 new ArrayList<>(); 2388 2389 private final List<MediaRoute2ProviderInfo> mLastProviderInfos = new ArrayList<>(); 2390 private final CopyOnWriteArrayList<SessionCreationRequest> mSessionCreationRequests = 2391 new CopyOnWriteArrayList<>(); 2392 private final Map<String, RouterRecord> mSessionToRouterMap = new ArrayMap<>(); 2393 2394 /** 2395 * Latest list of routes sent to privileged {@link android.media.MediaRouter2 routers} and 2396 * {@link android.media.MediaRouter2Manager managers}. 2397 * 2398 * <p>Privileged routers are instances of {@link android.media.MediaRouter2 MediaRouter2} 2399 * that have {@code MODIFY_AUDIO_ROUTING} permission. 2400 * 2401 * <p>This list contains all routes exposed by route providers. This includes routes from 2402 * both system route providers and user route providers. 2403 * 2404 * <p>See {@link #getRouterRecords(boolean hasModifyAudioRoutingPermission)}. 2405 */ 2406 private final Map<String, MediaRoute2Info> mLastNotifiedRoutesToPrivilegedRouters = 2407 new ArrayMap<>(); 2408 2409 /** 2410 * Latest list of routes sent to non-privileged {@link android.media.MediaRouter2 routers}. 2411 * 2412 * <p>Non-privileged routers are instances of {@link android.media.MediaRouter2 2413 * MediaRouter2} that do <i><b>not</b></i> have {@code MODIFY_AUDIO_ROUTING} permission. 2414 * 2415 * <p>This list contains all routes exposed by user route providers. It might also include 2416 * the current default route from {@link #mSystemProvider} to expose local route updates 2417 * (e.g. volume changes) to non-privileged routers. 2418 * 2419 * <p>See {@link SystemMediaRoute2Provider#mDefaultRoute}. 2420 */ 2421 private final Map<String, MediaRoute2Info> mLastNotifiedRoutesToNonPrivilegedRouters = 2422 new ArrayMap<>(); 2423 2424 private boolean mRunning; 2425 2426 // TODO: (In Android S+) Pull out SystemMediaRoute2Provider out of UserHandler. UserHandler( @onNull MediaRouter2ServiceImpl service, @NonNull UserRecord userRecord, @NonNull Looper looper)2427 UserHandler( 2428 @NonNull MediaRouter2ServiceImpl service, 2429 @NonNull UserRecord userRecord, 2430 @NonNull Looper looper) { 2431 super(looper, /* callback= */ null, /* async= */ true); 2432 mServiceRef = new WeakReference<>(service); 2433 mUserRecord = userRecord; 2434 mSystemProvider = 2435 new SystemMediaRoute2Provider( 2436 service.mContext, UserHandle.of(userRecord.mUserId), looper); 2437 mRouteProviders.add(mSystemProvider); 2438 mWatcher = new MediaRoute2ProviderWatcher(service.mContext, this, 2439 this, mUserRecord.mUserId); 2440 } 2441 init()2442 void init() { 2443 mSystemProvider.setCallback(this); 2444 } 2445 start()2446 private void start() { 2447 if (!mRunning) { 2448 mRunning = true; 2449 mSystemProvider.start(); 2450 mWatcher.start(); 2451 } 2452 } 2453 stop()2454 private void stop() { 2455 if (mRunning) { 2456 mRunning = false; 2457 mWatcher.stop(); // also stops all providers 2458 mSystemProvider.stop(); 2459 } 2460 } 2461 2462 @Override onAddProviderService(@onNull MediaRoute2ProviderServiceProxy proxy)2463 public void onAddProviderService(@NonNull MediaRoute2ProviderServiceProxy proxy) { 2464 proxy.setCallback(this); 2465 mRouteProviders.add(proxy); 2466 proxy.updateDiscoveryPreference( 2467 mUserRecord.mActivelyScanningPackages, 2468 mUserRecord.mCompositeDiscoveryPreference); 2469 } 2470 2471 @Override onRemoveProviderService(@onNull MediaRoute2ProviderServiceProxy proxy)2472 public void onRemoveProviderService(@NonNull MediaRoute2ProviderServiceProxy proxy) { 2473 mRouteProviders.remove(proxy); 2474 } 2475 2476 @Override onProviderStateChanged(@onNull MediaRoute2Provider provider)2477 public void onProviderStateChanged(@NonNull MediaRoute2Provider provider) { 2478 sendMessage(PooledLambda.obtainMessage(UserHandler::onProviderStateChangedOnHandler, 2479 this, provider)); 2480 } 2481 2482 @Override onSessionCreated(@onNull MediaRoute2Provider provider, long uniqueRequestId, @NonNull RoutingSessionInfo sessionInfo)2483 public void onSessionCreated(@NonNull MediaRoute2Provider provider, 2484 long uniqueRequestId, @NonNull RoutingSessionInfo sessionInfo) { 2485 sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionCreatedOnHandler, 2486 this, provider, uniqueRequestId, sessionInfo)); 2487 } 2488 2489 @Override onSessionUpdated(@onNull MediaRoute2Provider provider, @NonNull RoutingSessionInfo sessionInfo)2490 public void onSessionUpdated(@NonNull MediaRoute2Provider provider, 2491 @NonNull RoutingSessionInfo sessionInfo) { 2492 sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionInfoChangedOnHandler, 2493 this, provider, sessionInfo)); 2494 } 2495 2496 @Override onSessionReleased(@onNull MediaRoute2Provider provider, @NonNull RoutingSessionInfo sessionInfo)2497 public void onSessionReleased(@NonNull MediaRoute2Provider provider, 2498 @NonNull RoutingSessionInfo sessionInfo) { 2499 sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionReleasedOnHandler, 2500 this, provider, sessionInfo)); 2501 } 2502 2503 @Override onRequestFailed(@onNull MediaRoute2Provider provider, long uniqueRequestId, int reason)2504 public void onRequestFailed(@NonNull MediaRoute2Provider provider, long uniqueRequestId, 2505 int reason) { 2506 sendMessage(PooledLambda.obtainMessage(UserHandler::onRequestFailedOnHandler, 2507 this, provider, uniqueRequestId, reason)); 2508 } 2509 2510 @GuardedBy("mLock") 2511 @Nullable findRouterWithSessionLocked(@onNull String uniqueSessionId)2512 public RouterRecord findRouterWithSessionLocked(@NonNull String uniqueSessionId) { 2513 return mSessionToRouterMap.get(uniqueSessionId); 2514 } 2515 2516 @Nullable findManagerWithId(int managerId)2517 public ManagerRecord findManagerWithId(int managerId) { 2518 for (ManagerRecord manager : getManagerRecords()) { 2519 if (manager.mManagerId == managerId) { 2520 return manager; 2521 } 2522 } 2523 return null; 2524 } 2525 maybeUpdateDiscoveryPreferenceForUid(int uid)2526 public void maybeUpdateDiscoveryPreferenceForUid(int uid) { 2527 MediaRouter2ServiceImpl service = mServiceRef.get(); 2528 if (service == null) { 2529 return; 2530 } 2531 boolean isUidRelevant; 2532 synchronized (service.mLock) { 2533 isUidRelevant = 2534 mUserRecord.mRouterRecords.stream().anyMatch(router -> router.mUid == uid) 2535 | mUserRecord.mManagerRecords.stream() 2536 .anyMatch(manager -> manager.mOwnerUid == uid); 2537 } 2538 if (isUidRelevant) { 2539 sendMessage(PooledLambda.obtainMessage( 2540 UserHandler::updateDiscoveryPreferenceOnHandler, this)); 2541 } 2542 } 2543 dump(@onNull PrintWriter pw, @NonNull String prefix)2544 public void dump(@NonNull PrintWriter pw, @NonNull String prefix) { 2545 pw.println(prefix + "UserHandler"); 2546 2547 String indent = prefix + " "; 2548 pw.println(indent + "mRunning=" + mRunning); 2549 2550 mSystemProvider.dump(pw, prefix); 2551 mWatcher.dump(pw, prefix); 2552 } 2553 onProviderStateChangedOnHandler(@onNull MediaRoute2Provider provider)2554 private void onProviderStateChangedOnHandler(@NonNull MediaRoute2Provider provider) { 2555 MediaRoute2ProviderInfo newInfo = provider.getProviderInfo(); 2556 int providerInfoIndex = 2557 indexOfRouteProviderInfoByUniqueId(provider.getUniqueId(), mLastProviderInfos); 2558 MediaRoute2ProviderInfo oldInfo = 2559 providerInfoIndex == -1 ? null : mLastProviderInfos.get(providerInfoIndex); 2560 2561 if (oldInfo == newInfo) { 2562 // Nothing to do. 2563 return; 2564 } 2565 2566 Collection<MediaRoute2Info> newRoutes; 2567 Set<String> newRouteIds; 2568 if (newInfo != null) { 2569 // Adding or updating a provider. 2570 newRoutes = newInfo.getRoutes(); 2571 newRouteIds = 2572 newRoutes.stream().map(MediaRoute2Info::getId).collect(Collectors.toSet()); 2573 if (providerInfoIndex >= 0) { 2574 mLastProviderInfos.set(providerInfoIndex, newInfo); 2575 } else { 2576 mLastProviderInfos.add(newInfo); 2577 } 2578 } else /* newInfo == null */ { 2579 // Removing a provider. 2580 mLastProviderInfos.remove(oldInfo); 2581 newRouteIds = Collections.emptySet(); 2582 newRoutes = Collections.emptySet(); 2583 } 2584 2585 // Add new routes to the maps. 2586 ArrayList<MediaRoute2Info> addedRoutes = new ArrayList<>(); 2587 boolean hasAddedOrModifiedRoutes = false; 2588 for (MediaRoute2Info newRouteInfo : newRoutes) { 2589 if (!newRouteInfo.isValid()) { 2590 Slog.w(TAG, "onProviderStateChangedOnHandler: Ignoring invalid route : " 2591 + newRouteInfo); 2592 continue; 2593 } 2594 if (!provider.mIsSystemRouteProvider) { 2595 mLastNotifiedRoutesToNonPrivilegedRouters.put( 2596 newRouteInfo.getId(), newRouteInfo); 2597 } 2598 MediaRoute2Info oldRouteInfo = 2599 mLastNotifiedRoutesToPrivilegedRouters.put( 2600 newRouteInfo.getId(), newRouteInfo); 2601 hasAddedOrModifiedRoutes |= !newRouteInfo.equals(oldRouteInfo); 2602 if (oldRouteInfo == null) { 2603 addedRoutes.add(newRouteInfo); 2604 } 2605 } 2606 2607 // Remove stale routes from the maps. 2608 ArrayList<MediaRoute2Info> removedRoutes = new ArrayList<>(); 2609 Collection<MediaRoute2Info> oldRoutes = 2610 oldInfo == null ? Collections.emptyList() : oldInfo.getRoutes(); 2611 boolean hasRemovedRoutes = false; 2612 for (MediaRoute2Info oldRoute : oldRoutes) { 2613 String oldRouteId = oldRoute.getId(); 2614 if (!newRouteIds.contains(oldRouteId)) { 2615 hasRemovedRoutes = true; 2616 mLastNotifiedRoutesToPrivilegedRouters.remove(oldRouteId); 2617 mLastNotifiedRoutesToNonPrivilegedRouters.remove(oldRouteId); 2618 removedRoutes.add(oldRoute); 2619 } 2620 } 2621 2622 if (!addedRoutes.isEmpty()) { 2623 // If routes were added, newInfo cannot be null. 2624 Slog.i(TAG, 2625 toLoggingMessage( 2626 /* source= */ "addProviderRoutes", 2627 newInfo.getUniqueId(), 2628 addedRoutes)); 2629 } 2630 if (!removedRoutes.isEmpty()) { 2631 // If routes were removed, oldInfo cannot be null. 2632 Slog.i(TAG, 2633 toLoggingMessage( 2634 /* source= */ "removeProviderRoutes", 2635 oldInfo.getUniqueId(), 2636 removedRoutes)); 2637 } 2638 2639 dispatchUpdates( 2640 hasAddedOrModifiedRoutes, 2641 hasRemovedRoutes, 2642 provider.mIsSystemRouteProvider, 2643 mSystemProvider.getDefaultRoute()); 2644 } 2645 getPackageNameFromNullableRecord( @ullable RouterRecord routerRecord)2646 private static String getPackageNameFromNullableRecord( 2647 @Nullable RouterRecord routerRecord) { 2648 return routerRecord != null ? routerRecord.mPackageName : "<null router record>"; 2649 } 2650 toLoggingMessage( String source, String providerId, ArrayList<MediaRoute2Info> routes)2651 private static String toLoggingMessage( 2652 String source, String providerId, ArrayList<MediaRoute2Info> routes) { 2653 String routesString = 2654 routes.stream() 2655 .map(it -> String.format("%s | %s", it.getOriginalId(), it.getName())) 2656 .collect(Collectors.joining(/* delimiter= */ ", ")); 2657 return TextUtils.formatSimple("%s | provider: %s, routes: [%s]", 2658 source, providerId, routesString); 2659 } 2660 2661 /** 2662 * Dispatches the latest route updates in {@link #mLastNotifiedRoutesToPrivilegedRouters} 2663 * and {@link #mLastNotifiedRoutesToNonPrivilegedRouters} to registered {@link 2664 * android.media.MediaRouter2 routers} and {@link MediaRouter2Manager managers} after a call 2665 * to {@link #onProviderStateChangedOnHandler(MediaRoute2Provider)}. Ignores if no changes 2666 * were made. 2667 * 2668 * @param hasAddedOrModifiedRoutes whether routes were added or modified. 2669 * @param hasRemovedRoutes whether routes were removed. 2670 * @param isSystemProvider whether the latest update was caused by a system provider. 2671 * @param defaultRoute the current default route in {@link #mSystemProvider}. 2672 */ dispatchUpdates( boolean hasAddedOrModifiedRoutes, boolean hasRemovedRoutes, boolean isSystemProvider, MediaRoute2Info defaultRoute)2673 private void dispatchUpdates( 2674 boolean hasAddedOrModifiedRoutes, 2675 boolean hasRemovedRoutes, 2676 boolean isSystemProvider, 2677 MediaRoute2Info defaultRoute) { 2678 2679 // Ignore if no changes. 2680 if (!hasAddedOrModifiedRoutes && !hasRemovedRoutes) { 2681 return; 2682 } 2683 List<RouterRecord> routerRecordsWithSystemRoutingPermission = 2684 getRouterRecords(/* hasSystemRoutingPermission= */ true); 2685 List<RouterRecord> routerRecordsWithoutSystemRoutingPermission = 2686 getRouterRecords(/* hasSystemRoutingPermission= */ false); 2687 List<IMediaRouter2Manager> managers = getManagers(); 2688 2689 // Managers receive all provider updates with all routes. 2690 notifyRoutesUpdatedToManagers( 2691 managers, new ArrayList<>(mLastNotifiedRoutesToPrivilegedRouters.values())); 2692 2693 // Routers with system routing access (either via {@link MODIFY_AUDIO_ROUTING} or 2694 // {@link BLUETOOTH_CONNECT} + {@link BLUETOOTH_SCAN}) receive all provider updates 2695 // with all routes. 2696 notifyRoutesUpdatedToRouterRecords( 2697 routerRecordsWithSystemRoutingPermission, 2698 new ArrayList<>(mLastNotifiedRoutesToPrivilegedRouters.values())); 2699 2700 if (!isSystemProvider) { 2701 // Regular routers receive updates from all non-system providers with all non-system 2702 // routes. 2703 notifyRoutesUpdatedToRouterRecords( 2704 routerRecordsWithoutSystemRoutingPermission, 2705 new ArrayList<>(mLastNotifiedRoutesToNonPrivilegedRouters.values())); 2706 } else if (hasAddedOrModifiedRoutes) { 2707 // On system provider updates, routers without system routing access 2708 // receive the updated default route. This is the only system route they should 2709 // receive. 2710 mLastNotifiedRoutesToNonPrivilegedRouters.put(defaultRoute.getId(), defaultRoute); 2711 notifyRoutesUpdatedToRouterRecords( 2712 routerRecordsWithoutSystemRoutingPermission, 2713 new ArrayList<>(mLastNotifiedRoutesToNonPrivilegedRouters.values())); 2714 } 2715 } 2716 2717 /** 2718 * Returns the index of the first element in {@code lastProviderInfos} that matches the 2719 * specified unique id. 2720 * 2721 * @param uniqueId unique id of {@link MediaRoute2ProviderInfo} to be found. 2722 * @param lastProviderInfos list of {@link MediaRoute2ProviderInfo}. 2723 * @return index of found element, or -1 if not found. 2724 */ indexOfRouteProviderInfoByUniqueId( @onNull String uniqueId, @NonNull List<MediaRoute2ProviderInfo> lastProviderInfos)2725 private static int indexOfRouteProviderInfoByUniqueId( 2726 @NonNull String uniqueId, 2727 @NonNull List<MediaRoute2ProviderInfo> lastProviderInfos) { 2728 for (int i = 0; i < lastProviderInfos.size(); i++) { 2729 MediaRoute2ProviderInfo providerInfo = lastProviderInfos.get(i); 2730 if (TextUtils.equals(providerInfo.getUniqueId(), uniqueId)) { 2731 return i; 2732 } 2733 } 2734 return -1; 2735 } 2736 requestRouterCreateSessionOnHandler( long uniqueRequestId, @NonNull RouterRecord routerRecord, @NonNull ManagerRecord managerRecord, @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route, @NonNull UserHandle transferInitiatorUserHandle, @NonNull String transferInitiatorPackageName)2737 private void requestRouterCreateSessionOnHandler( 2738 long uniqueRequestId, 2739 @NonNull RouterRecord routerRecord, 2740 @NonNull ManagerRecord managerRecord, 2741 @NonNull RoutingSessionInfo oldSession, 2742 @NonNull MediaRoute2Info route, 2743 @NonNull UserHandle transferInitiatorUserHandle, 2744 @NonNull String transferInitiatorPackageName) { 2745 try { 2746 if (route.isSystemRoute() && !routerRecord.hasSystemRoutingPermission()) { 2747 // The router lacks permission to modify system routing, so we hide system 2748 // route info from them. 2749 route = mSystemProvider.getDefaultRoute(); 2750 } 2751 routerRecord.mRouter.requestCreateSessionByManager( 2752 uniqueRequestId, oldSession, route); 2753 } catch (RemoteException ex) { 2754 Slog.w(TAG, "getSessionHintsForCreatingSessionOnHandler: " 2755 + "Failed to request. Router probably died.", ex); 2756 notifyRequestFailedToManager(managerRecord.mManager, 2757 toOriginalRequestId(uniqueRequestId), REASON_UNKNOWN_ERROR); 2758 } 2759 } 2760 requestCreateSessionWithRouter2OnHandler( long uniqueRequestId, long managerRequestId, @NonNull RouterRecord routerRecord, @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route, @Nullable Bundle sessionHints)2761 private void requestCreateSessionWithRouter2OnHandler( 2762 long uniqueRequestId, 2763 long managerRequestId, 2764 @NonNull RouterRecord routerRecord, 2765 @NonNull RoutingSessionInfo oldSession, 2766 @NonNull MediaRoute2Info route, 2767 @Nullable Bundle sessionHints) { 2768 2769 final MediaRoute2Provider provider = findProvider(route.getProviderId()); 2770 if (provider == null) { 2771 Slog.w(TAG, "requestCreateSessionWithRouter2OnHandler: Ignoring session " 2772 + "creation request since no provider found for given route=" + route); 2773 notifySessionCreationFailedToRouter(routerRecord, 2774 toOriginalRequestId(uniqueRequestId)); 2775 return; 2776 } 2777 2778 SessionCreationRequest request = 2779 new SessionCreationRequest(routerRecord, uniqueRequestId, 2780 managerRequestId, oldSession, route); 2781 mSessionCreationRequests.add(request); 2782 2783 int transferReason = 2784 managerRequestId != MediaRoute2ProviderService.REQUEST_ID_NONE 2785 ? RoutingSessionInfo.TRANSFER_REASON_SYSTEM_REQUEST 2786 : RoutingSessionInfo.TRANSFER_REASON_APP; 2787 2788 provider.requestCreateSession( 2789 uniqueRequestId, 2790 routerRecord.mPackageName, 2791 route.getOriginalId(), 2792 sessionHints, 2793 transferReason, 2794 UserHandle.of(routerRecord.mUserRecord.mUserId), 2795 routerRecord.mPackageName); 2796 } 2797 2798 // routerRecord can be null if the session is system's or RCN. selectRouteOnHandler(long uniqueRequestId, @Nullable RouterRecord routerRecord, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route)2799 private void selectRouteOnHandler(long uniqueRequestId, @Nullable RouterRecord routerRecord, 2800 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { 2801 if (!checkArgumentsForSessionControl(routerRecord, uniqueSessionId, route, 2802 "selecting")) { 2803 return; 2804 } 2805 2806 final String providerId = route.getProviderId(); 2807 final MediaRoute2Provider provider = findProvider(providerId); 2808 if (provider == null) { 2809 return; 2810 } 2811 provider.selectRoute(uniqueRequestId, getOriginalId(uniqueSessionId), 2812 route.getOriginalId()); 2813 } 2814 2815 // routerRecord can be null if the session is system's or RCN. deselectRouteOnHandler(long uniqueRequestId, @Nullable RouterRecord routerRecord, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route)2816 private void deselectRouteOnHandler(long uniqueRequestId, 2817 @Nullable RouterRecord routerRecord, 2818 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { 2819 if (!checkArgumentsForSessionControl(routerRecord, uniqueSessionId, route, 2820 "deselecting")) { 2821 return; 2822 } 2823 2824 final String providerId = route.getProviderId(); 2825 final MediaRoute2Provider provider = findProvider(providerId); 2826 if (provider == null) { 2827 return; 2828 } 2829 2830 provider.deselectRoute(uniqueRequestId, getOriginalId(uniqueSessionId), 2831 route.getOriginalId()); 2832 } 2833 2834 // routerRecord can be null if the session is system's or RCN. transferToRouteOnHandler( long uniqueRequestId, @NonNull UserHandle transferInitiatorUserHandle, @NonNull String transferInitiatorPackageName, @Nullable RouterRecord routerRecord, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route, @RoutingSessionInfo.TransferReason int transferReason)2835 private void transferToRouteOnHandler( 2836 long uniqueRequestId, 2837 @NonNull UserHandle transferInitiatorUserHandle, 2838 @NonNull String transferInitiatorPackageName, 2839 @Nullable RouterRecord routerRecord, 2840 @NonNull String uniqueSessionId, 2841 @NonNull MediaRoute2Info route, 2842 @RoutingSessionInfo.TransferReason int transferReason) { 2843 if (!checkArgumentsForSessionControl(routerRecord, uniqueSessionId, route, 2844 "transferring to")) { 2845 return; 2846 } 2847 2848 final String providerId = route.getProviderId(); 2849 final MediaRoute2Provider provider = findProvider(providerId); 2850 if (provider == null) { 2851 Slog.w( 2852 TAG, 2853 "Ignoring transferToRoute due to lack of matching provider for target: " 2854 + route); 2855 return; 2856 } 2857 provider.transferToRoute( 2858 uniqueRequestId, 2859 transferInitiatorUserHandle, 2860 transferInitiatorPackageName, 2861 getOriginalId(uniqueSessionId), 2862 route.getOriginalId(), 2863 transferReason); 2864 } 2865 2866 // routerRecord is null if and only if the session is created without the request, which 2867 // includes the system's session and RCN cases. checkArgumentsForSessionControl(@ullable RouterRecord routerRecord, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route, @NonNull String description)2868 private boolean checkArgumentsForSessionControl(@Nullable RouterRecord routerRecord, 2869 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route, 2870 @NonNull String description) { 2871 final String providerId = route.getProviderId(); 2872 final MediaRoute2Provider provider = findProvider(providerId); 2873 if (provider == null) { 2874 Slog.w(TAG, "Ignoring " + description + " route since no provider found for " 2875 + "given route=" + route); 2876 return false; 2877 } 2878 2879 // Bypass checking router if it's the system session (routerRecord should be null) 2880 if (TextUtils.equals(getProviderId(uniqueSessionId), mSystemProvider.getUniqueId())) { 2881 return true; 2882 } 2883 2884 RouterRecord matchingRecord = mSessionToRouterMap.get(uniqueSessionId); 2885 if (matchingRecord != routerRecord) { 2886 Slog.w( 2887 TAG, 2888 "Ignoring " 2889 + description 2890 + " route from non-matching router." 2891 + " routerRecordPackageName=" 2892 + getPackageNameFromNullableRecord(routerRecord) 2893 + " matchingRecordPackageName=" 2894 + getPackageNameFromNullableRecord(matchingRecord) 2895 + " route=" 2896 + route); 2897 return false; 2898 } 2899 2900 final String sessionId = getOriginalId(uniqueSessionId); 2901 if (sessionId == null) { 2902 Slog.w(TAG, "Failed to get original session id from unique session id. " 2903 + "uniqueSessionId=" + uniqueSessionId); 2904 return false; 2905 } 2906 2907 return true; 2908 } 2909 setRouteVolumeOnHandler(long uniqueRequestId, @NonNull MediaRoute2Info route, int volume)2910 private void setRouteVolumeOnHandler(long uniqueRequestId, @NonNull MediaRoute2Info route, 2911 int volume) { 2912 final MediaRoute2Provider provider = findProvider(route.getProviderId()); 2913 if (provider == null) { 2914 Slog.w(TAG, "setRouteVolumeOnHandler: Couldn't find provider for route=" + route); 2915 return; 2916 } 2917 provider.setRouteVolume(uniqueRequestId, route.getOriginalId(), volume); 2918 } 2919 setSessionVolumeOnHandler(long uniqueRequestId, @NonNull String uniqueSessionId, int volume)2920 private void setSessionVolumeOnHandler(long uniqueRequestId, 2921 @NonNull String uniqueSessionId, int volume) { 2922 final MediaRoute2Provider provider = findProvider(getProviderId(uniqueSessionId)); 2923 if (provider == null) { 2924 Slog.w(TAG, "setSessionVolumeOnHandler: Couldn't find provider for session id=" 2925 + uniqueSessionId); 2926 return; 2927 } 2928 provider.setSessionVolume(uniqueRequestId, getOriginalId(uniqueSessionId), volume); 2929 } 2930 releaseSessionOnHandler(long uniqueRequestId, @Nullable RouterRecord routerRecord, @NonNull String uniqueSessionId)2931 private void releaseSessionOnHandler(long uniqueRequestId, 2932 @Nullable RouterRecord routerRecord, @NonNull String uniqueSessionId) { 2933 final RouterRecord matchingRecord = mSessionToRouterMap.get(uniqueSessionId); 2934 if (matchingRecord != routerRecord) { 2935 Slog.w( 2936 TAG, 2937 "Ignoring releasing session from non-matching router." 2938 + " routerRecordPackageName=" 2939 + getPackageNameFromNullableRecord(routerRecord) 2940 + " matchingRecordPackageName=" 2941 + getPackageNameFromNullableRecord(matchingRecord) 2942 + " uniqueSessionId=" 2943 + uniqueSessionId); 2944 return; 2945 } 2946 2947 final String providerId = getProviderId(uniqueSessionId); 2948 if (providerId == null) { 2949 Slog.w(TAG, "Ignoring releasing session with invalid unique session ID. " 2950 + "uniqueSessionId=" + uniqueSessionId); 2951 return; 2952 } 2953 2954 final String sessionId = getOriginalId(uniqueSessionId); 2955 if (sessionId == null) { 2956 Slog.w(TAG, "Ignoring releasing session with invalid unique session ID. " 2957 + "uniqueSessionId=" + uniqueSessionId + " providerId=" + providerId); 2958 return; 2959 } 2960 2961 final MediaRoute2Provider provider = findProvider(providerId); 2962 if (provider == null) { 2963 Slog.w(TAG, "Ignoring releasing session since no provider found for given " 2964 + "providerId=" + providerId); 2965 return; 2966 } 2967 2968 provider.releaseSession(uniqueRequestId, sessionId); 2969 } 2970 onSessionCreatedOnHandler(@onNull MediaRoute2Provider provider, long uniqueRequestId, @NonNull RoutingSessionInfo sessionInfo)2971 private void onSessionCreatedOnHandler(@NonNull MediaRoute2Provider provider, 2972 long uniqueRequestId, @NonNull RoutingSessionInfo sessionInfo) { 2973 SessionCreationRequest matchingRequest = null; 2974 2975 for (SessionCreationRequest request : mSessionCreationRequests) { 2976 if (request.mUniqueRequestId == uniqueRequestId 2977 && TextUtils.equals( 2978 request.mRoute.getProviderId(), provider.getUniqueId())) { 2979 matchingRequest = request; 2980 break; 2981 } 2982 } 2983 2984 long managerRequestId = (matchingRequest == null) 2985 ? MediaRoute2ProviderService.REQUEST_ID_NONE 2986 : matchingRequest.mManagerRequestId; 2987 notifySessionCreatedToManagers(managerRequestId, sessionInfo); 2988 2989 if (matchingRequest == null) { 2990 Slog.w(TAG, "Ignoring session creation result for unknown request. " 2991 + "uniqueRequestId=" + uniqueRequestId + ", sessionInfo=" + sessionInfo); 2992 return; 2993 } 2994 2995 mSessionCreationRequests.remove(matchingRequest); 2996 // Not to show old session 2997 MediaRoute2Provider oldProvider = 2998 findProvider(matchingRequest.mOldSession.getProviderId()); 2999 if (oldProvider != null) { 3000 oldProvider.prepareReleaseSession(matchingRequest.mOldSession.getId()); 3001 } else { 3002 Slog.w(TAG, "onSessionCreatedOnHandler: Can't find provider for an old session. " 3003 + "session=" + matchingRequest.mOldSession); 3004 } 3005 3006 mSessionToRouterMap.put(sessionInfo.getId(), matchingRequest.mRouterRecord); 3007 if (sessionInfo.isSystemSession() 3008 && !matchingRequest.mRouterRecord.hasSystemRoutingPermission()) { 3009 // The router lacks permission to modify system routing, so we hide system routing 3010 // session info from them. 3011 sessionInfo = mSystemProvider.getDefaultSessionInfo(); 3012 } 3013 matchingRequest.mRouterRecord.notifySessionCreated( 3014 toOriginalRequestId(uniqueRequestId), sessionInfo); 3015 } 3016 onSessionInfoChangedOnHandler(@onNull MediaRoute2Provider provider, @NonNull RoutingSessionInfo sessionInfo)3017 private void onSessionInfoChangedOnHandler(@NonNull MediaRoute2Provider provider, 3018 @NonNull RoutingSessionInfo sessionInfo) { 3019 List<IMediaRouter2Manager> managers = getManagers(); 3020 notifySessionUpdatedToManagers(managers, sessionInfo); 3021 3022 // For system provider, notify all routers. 3023 if (provider == mSystemProvider) { 3024 if (mServiceRef.get() == null) { 3025 return; 3026 } 3027 notifySessionInfoChangedToRouters(getRouterRecords(true), sessionInfo); 3028 notifySessionInfoChangedToRouters( 3029 getRouterRecords(false), mSystemProvider.getDefaultSessionInfo()); 3030 return; 3031 } 3032 3033 RouterRecord routerRecord = mSessionToRouterMap.get(sessionInfo.getId()); 3034 if (routerRecord == null) { 3035 Slog.w(TAG, "onSessionInfoChangedOnHandler: No matching router found for session=" 3036 + sessionInfo); 3037 return; 3038 } 3039 notifySessionInfoChangedToRouters(Arrays.asList(routerRecord), sessionInfo); 3040 } 3041 onSessionReleasedOnHandler(@onNull MediaRoute2Provider provider, @NonNull RoutingSessionInfo sessionInfo)3042 private void onSessionReleasedOnHandler(@NonNull MediaRoute2Provider provider, 3043 @NonNull RoutingSessionInfo sessionInfo) { 3044 List<IMediaRouter2Manager> managers = getManagers(); 3045 notifySessionReleasedToManagers(managers, sessionInfo); 3046 3047 RouterRecord routerRecord = mSessionToRouterMap.get(sessionInfo.getId()); 3048 if (routerRecord == null) { 3049 Slog.w(TAG, "onSessionReleasedOnHandler: No matching router found for session=" 3050 + sessionInfo); 3051 return; 3052 } 3053 notifySessionReleasedToRouter(routerRecord, sessionInfo); 3054 } 3055 onRequestFailedOnHandler(@onNull MediaRoute2Provider provider, long uniqueRequestId, int reason)3056 private void onRequestFailedOnHandler(@NonNull MediaRoute2Provider provider, 3057 long uniqueRequestId, int reason) { 3058 if (handleSessionCreationRequestFailed(provider, uniqueRequestId, reason)) { 3059 Slog.w( 3060 TAG, 3061 TextUtils.formatSimple( 3062 "onRequestFailedOnHandler | Finished handling session creation" 3063 + " request failed for provider: %s, uniqueRequestId: %d," 3064 + " reason: %d", 3065 provider.getUniqueId(), uniqueRequestId, reason)); 3066 return; 3067 } 3068 3069 final int requesterId = toRequesterId(uniqueRequestId); 3070 ManagerRecord manager = findManagerWithId(requesterId); 3071 if (manager != null) { 3072 notifyRequestFailedToManager( 3073 manager.mManager, toOriginalRequestId(uniqueRequestId), reason); 3074 } 3075 3076 // Currently, only manager records can get notified of failures. 3077 // TODO(b/282936553): Notify regular routers of request failures. 3078 } 3079 handleSessionCreationRequestFailed(@onNull MediaRoute2Provider provider, long uniqueRequestId, int reason)3080 private boolean handleSessionCreationRequestFailed(@NonNull MediaRoute2Provider provider, 3081 long uniqueRequestId, int reason) { 3082 // Check whether the failure is about creating a session 3083 SessionCreationRequest matchingRequest = null; 3084 for (SessionCreationRequest request : mSessionCreationRequests) { 3085 if (request.mUniqueRequestId == uniqueRequestId && TextUtils.equals( 3086 request.mRoute.getProviderId(), provider.getUniqueId())) { 3087 matchingRequest = request; 3088 break; 3089 } 3090 } 3091 3092 if (matchingRequest == null) { 3093 // The failure is not about creating a session. 3094 Slog.w( 3095 TAG, 3096 TextUtils.formatSimple( 3097 "handleSessionCreationRequestFailed | No matching request found for" 3098 + " provider: %s, uniqueRequestId: %d, reason: %d", 3099 provider.getUniqueId(), uniqueRequestId, reason)); 3100 return false; 3101 } 3102 3103 mSessionCreationRequests.remove(matchingRequest); 3104 3105 // Notify the requester about the failure. 3106 // The call should be made by either MediaRouter2 or MediaRouter2Manager. 3107 if (matchingRequest.mManagerRequestId == MediaRouter2Manager.REQUEST_ID_NONE) { 3108 notifySessionCreationFailedToRouter( 3109 matchingRequest.mRouterRecord, toOriginalRequestId(uniqueRequestId)); 3110 } else { 3111 final int requesterId = toRequesterId(matchingRequest.mManagerRequestId); 3112 ManagerRecord manager = findManagerWithId(requesterId); 3113 if (manager != null) { 3114 notifyRequestFailedToManager(manager.mManager, 3115 toOriginalRequestId(matchingRequest.mManagerRequestId), reason); 3116 } 3117 } 3118 return true; 3119 } 3120 notifySessionCreationFailedToRouter(@onNull RouterRecord routerRecord, int requestId)3121 private void notifySessionCreationFailedToRouter(@NonNull RouterRecord routerRecord, 3122 int requestId) { 3123 try { 3124 routerRecord.mRouter.notifySessionCreated(requestId, 3125 /* sessionInfo= */ null); 3126 } catch (RemoteException ex) { 3127 Slog.w(TAG, "Failed to notify router of the session creation failure." 3128 + " Router probably died.", ex); 3129 } 3130 } 3131 notifySessionReleasedToRouter(@onNull RouterRecord routerRecord, @NonNull RoutingSessionInfo sessionInfo)3132 private void notifySessionReleasedToRouter(@NonNull RouterRecord routerRecord, 3133 @NonNull RoutingSessionInfo sessionInfo) { 3134 try { 3135 routerRecord.mRouter.notifySessionReleased(sessionInfo); 3136 } catch (RemoteException ex) { 3137 Slog.w(TAG, "Failed to notify router of the session release." 3138 + " Router probably died.", ex); 3139 } 3140 } 3141 getManagers()3142 private List<IMediaRouter2Manager> getManagers() { 3143 final List<IMediaRouter2Manager> managers = new ArrayList<>(); 3144 MediaRouter2ServiceImpl service = mServiceRef.get(); 3145 if (service == null) { 3146 return managers; 3147 } 3148 synchronized (service.mLock) { 3149 for (ManagerRecord managerRecord : mUserRecord.mManagerRecords) { 3150 managers.add(managerRecord.mManager); 3151 } 3152 } 3153 return managers; 3154 } 3155 getRouterRecords()3156 private List<RouterRecord> getRouterRecords() { 3157 MediaRouter2ServiceImpl service = mServiceRef.get(); 3158 if (service == null) { 3159 return Collections.emptyList(); 3160 } 3161 synchronized (service.mLock) { 3162 return new ArrayList<>(mUserRecord.mRouterRecords); 3163 } 3164 } 3165 getRouterRecords(boolean hasSystemRoutingPermission)3166 private List<RouterRecord> getRouterRecords(boolean hasSystemRoutingPermission) { 3167 MediaRouter2ServiceImpl service = mServiceRef.get(); 3168 List<RouterRecord> routerRecords = new ArrayList<>(); 3169 if (service == null) { 3170 return routerRecords; 3171 } 3172 synchronized (service.mLock) { 3173 for (RouterRecord routerRecord : mUserRecord.mRouterRecords) { 3174 if (hasSystemRoutingPermission 3175 == routerRecord.hasSystemRoutingPermission()) { 3176 routerRecords.add(routerRecord); 3177 } 3178 } 3179 return routerRecords; 3180 } 3181 } 3182 getManagerRecords()3183 private List<ManagerRecord> getManagerRecords() { 3184 MediaRouter2ServiceImpl service = mServiceRef.get(); 3185 if (service == null) { 3186 return Collections.emptyList(); 3187 } 3188 synchronized (service.mLock) { 3189 return new ArrayList<>(mUserRecord.mManagerRecords); 3190 } 3191 } 3192 notifyRouterRegistered(@onNull RouterRecord routerRecord)3193 private void notifyRouterRegistered(@NonNull RouterRecord routerRecord) { 3194 List<MediaRoute2Info> currentRoutes = new ArrayList<>(); 3195 3196 MediaRoute2ProviderInfo systemProviderInfo = null; 3197 for (MediaRoute2ProviderInfo providerInfo : mLastProviderInfos) { 3198 // TODO: Create MediaRoute2ProviderInfo#isSystemProvider() 3199 if (TextUtils.equals(providerInfo.getUniqueId(), mSystemProvider.getUniqueId())) { 3200 // Adding routes from system provider will be handled below, so skip it here. 3201 systemProviderInfo = providerInfo; 3202 continue; 3203 } 3204 currentRoutes.addAll(providerInfo.getRoutes()); 3205 } 3206 3207 RoutingSessionInfo currentSystemSessionInfo; 3208 if (routerRecord.hasSystemRoutingPermission()) { 3209 if (systemProviderInfo != null) { 3210 currentRoutes.addAll(systemProviderInfo.getRoutes()); 3211 } else { 3212 // This shouldn't happen. 3213 Slog.wtf(TAG, "System route provider not found."); 3214 } 3215 currentSystemSessionInfo = mSystemProvider.getSessionInfos().get(0); 3216 } else { 3217 currentRoutes.add(mSystemProvider.getDefaultRoute()); 3218 currentSystemSessionInfo = mSystemProvider.getDefaultSessionInfo(); 3219 } 3220 3221 if (!currentRoutes.isEmpty()) { 3222 routerRecord.notifyRegistered(currentRoutes, currentSystemSessionInfo); 3223 } 3224 } 3225 notifyRoutesUpdatedToRouterRecords( @onNull List<RouterRecord> routerRecords, @NonNull List<MediaRoute2Info> routes)3226 private static void notifyRoutesUpdatedToRouterRecords( 3227 @NonNull List<RouterRecord> routerRecords, 3228 @NonNull List<MediaRoute2Info> routes) { 3229 for (RouterRecord routerRecord : routerRecords) { 3230 routerRecord.notifyRoutesUpdated(routes); 3231 } 3232 } 3233 notifySessionInfoChangedToRouters( @onNull List<RouterRecord> routerRecords, @NonNull RoutingSessionInfo sessionInfo)3234 private void notifySessionInfoChangedToRouters( 3235 @NonNull List<RouterRecord> routerRecords, 3236 @NonNull RoutingSessionInfo sessionInfo) { 3237 for (RouterRecord routerRecord : routerRecords) { 3238 routerRecord.notifySessionInfoChanged(sessionInfo); 3239 } 3240 } 3241 3242 /** 3243 * Notifies {@code manager} with all known routes. This only happens once after {@code 3244 * manager} is registered through {@link #registerManager(IMediaRouter2Manager, String) 3245 * registerManager()}. 3246 * 3247 * @param manager {@link IMediaRouter2Manager} to be notified. 3248 */ notifyInitialRoutesToManager(@onNull IMediaRouter2Manager manager)3249 private void notifyInitialRoutesToManager(@NonNull IMediaRouter2Manager manager) { 3250 if (mLastNotifiedRoutesToPrivilegedRouters.isEmpty()) { 3251 return; 3252 } 3253 try { 3254 manager.notifyRoutesUpdated( 3255 new ArrayList<>(mLastNotifiedRoutesToPrivilegedRouters.values())); 3256 } catch (RemoteException ex) { 3257 Slog.w(TAG, "Failed to notify all routes. Manager probably died.", ex); 3258 } 3259 } 3260 notifyRoutesUpdatedToManagers( @onNull List<IMediaRouter2Manager> managers, @NonNull List<MediaRoute2Info> routes)3261 private void notifyRoutesUpdatedToManagers( 3262 @NonNull List<IMediaRouter2Manager> managers, 3263 @NonNull List<MediaRoute2Info> routes) { 3264 for (IMediaRouter2Manager manager : managers) { 3265 try { 3266 manager.notifyRoutesUpdated(routes); 3267 } catch (RemoteException ex) { 3268 Slog.w(TAG, "Failed to notify routes changed. Manager probably died.", ex); 3269 } 3270 } 3271 } 3272 notifySessionCreatedToManagers(long managerRequestId, @NonNull RoutingSessionInfo session)3273 private void notifySessionCreatedToManagers(long managerRequestId, 3274 @NonNull RoutingSessionInfo session) { 3275 int requesterId = toRequesterId(managerRequestId); 3276 int originalRequestId = toOriginalRequestId(managerRequestId); 3277 3278 for (ManagerRecord manager : getManagerRecords()) { 3279 try { 3280 manager.mManager.notifySessionCreated( 3281 ((manager.mManagerId == requesterId) ? originalRequestId : 3282 MediaRouter2Manager.REQUEST_ID_NONE), session); 3283 } catch (RemoteException ex) { 3284 Slog.w(TAG, "notifySessionCreatedToManagers: " 3285 + "Failed to notify. Manager probably died.", ex); 3286 } 3287 } 3288 } 3289 notifySessionUpdatedToManagers( @onNull List<IMediaRouter2Manager> managers, @NonNull RoutingSessionInfo sessionInfo)3290 private void notifySessionUpdatedToManagers( 3291 @NonNull List<IMediaRouter2Manager> managers, 3292 @NonNull RoutingSessionInfo sessionInfo) { 3293 for (IMediaRouter2Manager manager : managers) { 3294 try { 3295 manager.notifySessionUpdated(sessionInfo); 3296 } catch (RemoteException ex) { 3297 Slog.w(TAG, "notifySessionUpdatedToManagers: " 3298 + "Failed to notify. Manager probably died.", ex); 3299 } 3300 } 3301 } 3302 notifySessionReleasedToManagers( @onNull List<IMediaRouter2Manager> managers, @NonNull RoutingSessionInfo sessionInfo)3303 private void notifySessionReleasedToManagers( 3304 @NonNull List<IMediaRouter2Manager> managers, 3305 @NonNull RoutingSessionInfo sessionInfo) { 3306 for (IMediaRouter2Manager manager : managers) { 3307 try { 3308 manager.notifySessionReleased(sessionInfo); 3309 } catch (RemoteException ex) { 3310 Slog.w(TAG, "notifySessionReleasedToManagers: " 3311 + "Failed to notify. Manager probably died.", ex); 3312 } 3313 } 3314 } 3315 notifyDiscoveryPreferenceChangedToManager(@onNull RouterRecord routerRecord, @NonNull IMediaRouter2Manager manager)3316 private void notifyDiscoveryPreferenceChangedToManager(@NonNull RouterRecord routerRecord, 3317 @NonNull IMediaRouter2Manager manager) { 3318 try { 3319 manager.notifyDiscoveryPreferenceChanged(routerRecord.mPackageName, 3320 routerRecord.mDiscoveryPreference); 3321 } catch (RemoteException ex) { 3322 Slog.w(TAG, "Failed to notify preferred features changed." 3323 + " Manager probably died.", ex); 3324 } 3325 } 3326 notifyDiscoveryPreferenceChangedToManagers(@onNull String routerPackageName, @Nullable RouteDiscoveryPreference discoveryPreference)3327 private void notifyDiscoveryPreferenceChangedToManagers(@NonNull String routerPackageName, 3328 @Nullable RouteDiscoveryPreference discoveryPreference) { 3329 MediaRouter2ServiceImpl service = mServiceRef.get(); 3330 if (service == null) { 3331 return; 3332 } 3333 List<IMediaRouter2Manager> managers = new ArrayList<>(); 3334 synchronized (service.mLock) { 3335 for (ManagerRecord managerRecord : mUserRecord.mManagerRecords) { 3336 managers.add(managerRecord.mManager); 3337 } 3338 } 3339 for (IMediaRouter2Manager manager : managers) { 3340 try { 3341 manager.notifyDiscoveryPreferenceChanged(routerPackageName, 3342 discoveryPreference); 3343 } catch (RemoteException ex) { 3344 Slog.w(TAG, "Failed to notify preferred features changed." 3345 + " Manager probably died.", ex); 3346 } 3347 } 3348 } 3349 notifyRouteListingPreferenceChangeToManagers( String routerPackageName, @Nullable RouteListingPreference routeListingPreference)3350 private void notifyRouteListingPreferenceChangeToManagers( 3351 String routerPackageName, @Nullable RouteListingPreference routeListingPreference) { 3352 MediaRouter2ServiceImpl service = mServiceRef.get(); 3353 if (service == null) { 3354 return; 3355 } 3356 List<IMediaRouter2Manager> managers = new ArrayList<>(); 3357 synchronized (service.mLock) { 3358 for (ManagerRecord managerRecord : mUserRecord.mManagerRecords) { 3359 managers.add(managerRecord.mManager); 3360 } 3361 } 3362 for (IMediaRouter2Manager manager : managers) { 3363 try { 3364 manager.notifyRouteListingPreferenceChange( 3365 routerPackageName, routeListingPreference); 3366 } catch (RemoteException ex) { 3367 Slog.w( 3368 TAG, 3369 "Failed to notify preferred features changed." 3370 + " Manager probably died.", 3371 ex); 3372 } 3373 } 3374 // TODO(b/238178508): In order to support privileged media router instances, we also 3375 // need to update routers other than the one making the update. 3376 } 3377 notifyRequestFailedToManager(@onNull IMediaRouter2Manager manager, int requestId, int reason)3378 private void notifyRequestFailedToManager(@NonNull IMediaRouter2Manager manager, 3379 int requestId, int reason) { 3380 try { 3381 manager.notifyRequestFailed(requestId, reason); 3382 } catch (RemoteException ex) { 3383 Slog.w(TAG, "Failed to notify manager of the request failure." 3384 + " Manager probably died.", ex); 3385 } 3386 } 3387 updateDiscoveryPreferenceOnHandler()3388 private void updateDiscoveryPreferenceOnHandler() { 3389 MediaRouter2ServiceImpl service = mServiceRef.get(); 3390 if (service == null) { 3391 return; 3392 } 3393 List<RouterRecord> activeRouterRecords; 3394 List<RouterRecord> allRouterRecords = getRouterRecords(); 3395 3396 boolean areManagersScanning = areManagersScanning(service, getManagerRecords()); 3397 3398 if (areManagersScanning) { 3399 activeRouterRecords = allRouterRecords; 3400 } else { 3401 activeRouterRecords = getIndividuallyActiveRouters(service, allRouterRecords); 3402 } 3403 3404 updateManagerScanningForProviders(areManagersScanning); 3405 3406 Set<String> activelyScanningPackages = new HashSet<>(); 3407 RouteDiscoveryPreference newPreference = 3408 buildCompositeDiscoveryPreference( 3409 activeRouterRecords, areManagersScanning, activelyScanningPackages); 3410 3411 Slog.i( 3412 TAG, 3413 TextUtils.formatSimple( 3414 "Updating composite discovery preference | preference: %s, active" 3415 + " routers: %s", 3416 newPreference, activelyScanningPackages)); 3417 3418 if (updateScanningOnUserRecord(service, activelyScanningPackages, newPreference)) { 3419 updateDiscoveryPreferenceForProviders(activelyScanningPackages); 3420 } 3421 } 3422 updateDiscoveryPreferenceForProviders(Set<String> activelyScanningPackages)3423 private void updateDiscoveryPreferenceForProviders(Set<String> activelyScanningPackages) { 3424 for (MediaRoute2Provider provider : mRouteProviders) { 3425 provider.updateDiscoveryPreference( 3426 activelyScanningPackages, mUserRecord.mCompositeDiscoveryPreference); 3427 } 3428 } 3429 updateScanningOnUserRecord( MediaRouter2ServiceImpl service, Set<String> activelyScanningPackages, RouteDiscoveryPreference newPreference)3430 private boolean updateScanningOnUserRecord( 3431 MediaRouter2ServiceImpl service, 3432 Set<String> activelyScanningPackages, 3433 RouteDiscoveryPreference newPreference) { 3434 synchronized (service.mLock) { 3435 if (newPreference.equals(mUserRecord.mCompositeDiscoveryPreference) 3436 && activelyScanningPackages.equals(mUserRecord.mActivelyScanningPackages)) { 3437 return false; 3438 } 3439 mUserRecord.mCompositeDiscoveryPreference = newPreference; 3440 mUserRecord.mActivelyScanningPackages = activelyScanningPackages; 3441 } 3442 return true; 3443 } 3444 3445 /** 3446 * Returns a composite {@link RouteDiscoveryPreference} that aggregates every router 3447 * record's individual discovery preference. 3448 * 3449 * <p>The {@link RouteDiscoveryPreference#shouldPerformActiveScan() active scan value} of 3450 * the composite discovery preference is true if one of the router records is actively 3451 * scanning or if {@code shouldForceActiveScan} is true. 3452 * 3453 * <p>The composite RouteDiscoveryPreference is used to query route providers once to obtain 3454 * all the routes of interest, which can be subsequently filtered for the individual 3455 * discovery preferences. 3456 */ 3457 @NonNull buildCompositeDiscoveryPreference( List<RouterRecord> activeRouterRecords, boolean shouldForceActiveScan, Set<String> activelyScanningPackages)3458 private static RouteDiscoveryPreference buildCompositeDiscoveryPreference( 3459 List<RouterRecord> activeRouterRecords, 3460 boolean shouldForceActiveScan, 3461 Set<String> activelyScanningPackages) { 3462 Set<String> preferredFeatures = new HashSet<>(); 3463 boolean activeScan = false; 3464 for (RouterRecord activeRouterRecord : activeRouterRecords) { 3465 RouteDiscoveryPreference preference = activeRouterRecord.mDiscoveryPreference; 3466 preferredFeatures.addAll(preference.getPreferredFeatures()); 3467 3468 boolean isRouterRecordActivelyScanning = 3469 Flags.enablePreventionOfManagerScansWhenNoAppsScan() 3470 ? (activeRouterRecord.isActivelyScanning() || shouldForceActiveScan) 3471 && !preference.getPreferredFeatures().isEmpty() 3472 : activeRouterRecord.isActivelyScanning(); 3473 3474 if (isRouterRecordActivelyScanning) { 3475 activeScan = true; 3476 activelyScanningPackages.add(activeRouterRecord.mPackageName); 3477 } 3478 } 3479 return new RouteDiscoveryPreference.Builder( 3480 List.copyOf(preferredFeatures), activeScan || shouldForceActiveScan) 3481 .build(); 3482 } 3483 updateManagerScanningForProviders(boolean isManagerScanning)3484 private void updateManagerScanningForProviders(boolean isManagerScanning) { 3485 for (MediaRoute2Provider provider : mRouteProviders) { 3486 if (provider instanceof MediaRoute2ProviderServiceProxy) { 3487 ((MediaRoute2ProviderServiceProxy) provider) 3488 .setManagerScanning(isManagerScanning); 3489 } 3490 } 3491 } 3492 3493 @NonNull getIndividuallyActiveRouters( MediaRouter2ServiceImpl service, List<RouterRecord> allRouterRecords)3494 private static List<RouterRecord> getIndividuallyActiveRouters( 3495 MediaRouter2ServiceImpl service, List<RouterRecord> allRouterRecords) { 3496 if (!service.mPowerManager.isInteractive() && !Flags.enableScreenOffScanning()) { 3497 return Collections.emptyList(); 3498 } 3499 3500 return allRouterRecords.stream() 3501 .filter( 3502 record -> 3503 isPackageImportanceSufficientForScanning( 3504 service, record.mPackageName) 3505 || record.mScanningState 3506 == SCANNING_STATE_SCANNING_FULL) 3507 .collect(Collectors.toList()); 3508 } 3509 areManagersScanning( MediaRouter2ServiceImpl service, List<ManagerRecord> managerRecords)3510 private static boolean areManagersScanning( 3511 MediaRouter2ServiceImpl service, List<ManagerRecord> managerRecords) { 3512 if (!service.mPowerManager.isInteractive() && !Flags.enableScreenOffScanning()) { 3513 return false; 3514 } 3515 3516 return managerRecords.stream().anyMatch(manager -> 3517 (manager.mScanningState == SCANNING_STATE_WHILE_INTERACTIVE 3518 && isPackageImportanceSufficientForScanning(service, 3519 manager.mOwnerPackageName)) 3520 || manager.mScanningState == SCANNING_STATE_SCANNING_FULL); 3521 } 3522 isPackageImportanceSufficientForScanning( MediaRouter2ServiceImpl service, String packageName)3523 private static boolean isPackageImportanceSufficientForScanning( 3524 MediaRouter2ServiceImpl service, String packageName) { 3525 return service.mActivityManager.getPackageImportance(packageName) 3526 <= REQUIRED_PACKAGE_IMPORTANCE_FOR_SCANNING; 3527 } 3528 findProvider(@ullable String providerId)3529 private MediaRoute2Provider findProvider(@Nullable String providerId) { 3530 for (MediaRoute2Provider provider : mRouteProviders) { 3531 if (TextUtils.equals(provider.getUniqueId(), providerId)) { 3532 return provider; 3533 } 3534 } 3535 return null; 3536 } 3537 } 3538 3539 static final class SessionCreationRequest { 3540 public final RouterRecord mRouterRecord; 3541 public final long mUniqueRequestId; 3542 public final long mManagerRequestId; 3543 public final RoutingSessionInfo mOldSession; 3544 public final MediaRoute2Info mRoute; 3545 SessionCreationRequest(@onNull RouterRecord routerRecord, long uniqueRequestId, long managerRequestId, @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route)3546 SessionCreationRequest(@NonNull RouterRecord routerRecord, long uniqueRequestId, 3547 long managerRequestId, @NonNull RoutingSessionInfo oldSession, 3548 @NonNull MediaRoute2Info route) { 3549 mRouterRecord = routerRecord; 3550 mUniqueRequestId = uniqueRequestId; 3551 mManagerRequestId = managerRequestId; 3552 mOldSession = oldSession; 3553 mRoute = route; 3554 } 3555 dump(@onNull PrintWriter pw, @NonNull String prefix)3556 public void dump(@NonNull PrintWriter pw, @NonNull String prefix) { 3557 pw.println(prefix + "SessionCreationRequest"); 3558 3559 String indent = prefix + " "; 3560 3561 pw.println(indent + "mUniqueRequestId=" + mUniqueRequestId); 3562 pw.println(indent + "mManagerRequestId=" + mManagerRequestId); 3563 mOldSession.dump(pw, indent); 3564 mRoute.dump(pw, prefix); 3565 } 3566 } 3567 } 3568