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