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