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 android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.BroadcastReceiver; 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.media.AudioManager; 27 import android.media.MediaRoute2Info; 28 import android.media.MediaRoute2ProviderInfo; 29 import android.media.MediaRoute2ProviderService; 30 import android.media.MediaRouter2Utils; 31 import android.media.RouteDiscoveryPreference; 32 import android.media.RoutingSessionInfo; 33 import android.os.Bundle; 34 import android.os.Handler; 35 import android.os.Looper; 36 import android.os.UserHandle; 37 import android.text.TextUtils; 38 import android.util.Log; 39 import android.util.Slog; 40 41 import com.android.internal.annotations.GuardedBy; 42 import com.android.media.flags.Flags; 43 44 import java.util.ArrayList; 45 import java.util.List; 46 import java.util.Objects; 47 import java.util.Set; 48 49 /** 50 * Provides routes for local playbacks such as phone speaker, wired headset, or Bluetooth speakers. 51 */ 52 // TODO: check thread safety. We may need to use lock to protect variables. 53 class SystemMediaRoute2Provider extends MediaRoute2Provider { 54 // Package-visible to use this tag for all system routing logic (done across multiple classes). 55 /* package */ static final String TAG = "MR2SystemProvider"; 56 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 57 58 private static final ComponentName COMPONENT_NAME = new ComponentName( 59 SystemMediaRoute2Provider.class.getPackage().getName(), 60 SystemMediaRoute2Provider.class.getName()); 61 62 static final String SYSTEM_SESSION_ID = "SYSTEM_SESSION"; 63 64 private final AudioManager mAudioManager; 65 protected final Handler mHandler; 66 private final Context mContext; 67 private final UserHandle mUser; 68 69 private final DeviceRouteController mDeviceRouteController; 70 private final BluetoothRouteController mBluetoothRouteController; 71 72 private String mSelectedRouteId; 73 // For apps without MODIFYING_AUDIO_ROUTING permission. 74 // This should be the currently selected route. 75 MediaRoute2Info mDefaultRoute; 76 77 @GuardedBy("mLock") 78 RoutingSessionInfo mSystemSessionInfo; 79 80 RoutingSessionInfo mDefaultSessionInfo; 81 82 private final AudioManagerBroadcastReceiver mAudioReceiver = 83 new AudioManagerBroadcastReceiver(); 84 85 private final Object mRequestLock = new Object(); 86 87 @GuardedBy("mRequestLock") 88 private volatile SessionCreationOrTransferRequest mPendingSessionCreationOrTransferRequest; 89 90 private final Object mTransferLock = new Object(); 91 92 @GuardedBy("mTransferLock") 93 @Nullable 94 private volatile SessionCreationOrTransferRequest mPendingTransferRequest; 95 create( Context context, UserHandle user, Looper looper)96 public static SystemMediaRoute2Provider create( 97 Context context, UserHandle user, Looper looper) { 98 var instance = new SystemMediaRoute2Provider(context, COMPONENT_NAME, user, looper); 99 instance.updateProviderState(); 100 instance.updateSessionInfosIfNeeded(); 101 return instance; 102 } 103 SystemMediaRoute2Provider( Context context, ComponentName componentName, UserHandle user, Looper looper)104 protected SystemMediaRoute2Provider( 105 Context context, ComponentName componentName, UserHandle user, Looper looper) { 106 super(componentName, /* isSystemRouteProvider= */ true); 107 mContext = context; 108 mUser = user; 109 mHandler = new Handler(looper); 110 111 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 112 113 mBluetoothRouteController = 114 BluetoothRouteController.createInstance( 115 context, 116 () -> { 117 publishProviderState(); 118 if (updateSessionInfosIfNeeded()) { 119 notifyGlobalSessionInfoUpdated(); 120 } 121 }); 122 123 mDeviceRouteController = 124 DeviceRouteController.createInstance( 125 context, 126 looper, 127 () -> 128 mHandler.post( 129 () -> { 130 publishProviderState(); 131 if (updateSessionInfosIfNeeded()) { 132 notifyGlobalSessionInfoUpdated(); 133 } 134 })); 135 } 136 start()137 public void start() { 138 IntentFilter intentFilter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION); 139 intentFilter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION); 140 mContext.registerReceiverAsUser(mAudioReceiver, mUser, 141 intentFilter, null, null); 142 mHandler.post( 143 () -> { 144 mDeviceRouteController.start(mUser); 145 mBluetoothRouteController.start(mUser); 146 }); 147 updateVolume(); 148 } 149 stop()150 public void stop() { 151 mContext.unregisterReceiver(mAudioReceiver); 152 mHandler.post( 153 () -> { 154 mBluetoothRouteController.stop(); 155 mDeviceRouteController.stop(); 156 notifyProviderState(); 157 }); 158 } 159 160 @Override setCallback(Callback callback)161 public void setCallback(Callback callback) { 162 super.setCallback(callback); 163 notifyProviderState(); 164 notifyGlobalSessionInfoUpdated(); 165 } 166 167 @Override requestCreateSession( long requestId, String packageName, String routeOriginalId, Bundle sessionHints, @RoutingSessionInfo.TransferReason int transferReason, @NonNull UserHandle transferInitiatorUserHandle, @NonNull String transferInitiatorPackageName)168 public void requestCreateSession( 169 long requestId, 170 String packageName, 171 String routeOriginalId, 172 Bundle sessionHints, 173 @RoutingSessionInfo.TransferReason int transferReason, 174 @NonNull UserHandle transferInitiatorUserHandle, 175 @NonNull String transferInitiatorPackageName) { 176 // Assume a router without MODIFY_AUDIO_ROUTING permission can't request with 177 // a route ID different from the default route ID. The service should've filtered. 178 if (TextUtils.equals(routeOriginalId, MediaRoute2Info.ROUTE_ID_DEFAULT)) { 179 mCallback.onSessionCreated(this, requestId, mDefaultSessionInfo); 180 return; 181 } 182 183 if (!Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) { 184 if (TextUtils.equals(routeOriginalId, mSelectedRouteId)) { 185 RoutingSessionInfo currentSessionInfo; 186 synchronized (mLock) { 187 currentSessionInfo = 188 Flags.enableMirroringInMediaRouter2() 189 ? mSystemSessionInfo 190 : mSessionInfos.get(0); 191 } 192 mCallback.onSessionCreated(this, requestId, currentSessionInfo); 193 return; 194 } 195 } 196 197 synchronized (mRequestLock) { 198 // Handle the previous request as a failure if exists. 199 if (mPendingSessionCreationOrTransferRequest != null) { 200 mCallback.onRequestFailed( 201 /* provider= */ this, 202 mPendingSessionCreationOrTransferRequest.mRequestId, 203 MediaRoute2ProviderService.REASON_UNKNOWN_ERROR); 204 } 205 mPendingSessionCreationOrTransferRequest = 206 new SessionCreationOrTransferRequest( 207 requestId, 208 routeOriginalId, 209 RoutingSessionInfo.TRANSFER_REASON_FALLBACK, 210 transferInitiatorUserHandle, 211 transferInitiatorPackageName); 212 } 213 214 // Only unprivileged routers call this method, therefore we use TRANSFER_REASON_APP. 215 transferToRoute( 216 requestId, 217 transferInitiatorUserHandle, 218 transferInitiatorPackageName, 219 SYSTEM_SESSION_ID, 220 routeOriginalId, 221 transferReason); 222 } 223 224 @Override releaseSession(long requestId, String sessionId)225 public void releaseSession(long requestId, String sessionId) { 226 // Do nothing 227 } 228 229 @Override updateDiscoveryPreference( Set<String> activelyScanningPackages, RouteDiscoveryPreference discoveryPreference)230 public void updateDiscoveryPreference( 231 Set<String> activelyScanningPackages, RouteDiscoveryPreference discoveryPreference) { 232 // Do nothing 233 } 234 235 @Override selectRoute(long requestId, String sessionId, String routeId)236 public void selectRoute(long requestId, String sessionId, String routeId) { 237 // Do nothing since we don't support multiple BT yet. 238 } 239 240 @Override deselectRoute(long requestId, String sessionId, String routeId)241 public void deselectRoute(long requestId, String sessionId, String routeId) { 242 // Do nothing since we don't support multiple BT yet. 243 } 244 245 @Override transferToRoute( long requestId, @NonNull UserHandle transferInitiatorUserHandle, @NonNull String transferInitiatorPackageName, String sessionOriginalId, String routeOriginalId, @RoutingSessionInfo.TransferReason int transferReason)246 public void transferToRoute( 247 long requestId, 248 @NonNull UserHandle transferInitiatorUserHandle, 249 @NonNull String transferInitiatorPackageName, 250 String sessionOriginalId, 251 String routeOriginalId, 252 @RoutingSessionInfo.TransferReason int transferReason) { 253 String selectedDeviceRouteId = mDeviceRouteController.getSelectedRoute().getId(); 254 if (TextUtils.equals(routeOriginalId, MediaRoute2Info.ROUTE_ID_DEFAULT)) { 255 if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) { 256 // Transfer to the default route (which is the selected route). We replace the id to 257 // be the selected route id so that the transfer reason gets updated. 258 routeOriginalId = selectedDeviceRouteId; 259 } else { 260 Log.w(TAG, "Ignoring transfer to " + MediaRoute2Info.ROUTE_ID_DEFAULT); 261 return; 262 } 263 } 264 265 if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) { 266 synchronized (mTransferLock) { 267 mPendingTransferRequest = 268 new SessionCreationOrTransferRequest( 269 requestId, 270 routeOriginalId, 271 transferReason, 272 transferInitiatorUserHandle, 273 transferInitiatorPackageName); 274 } 275 } 276 277 String finalRouteId = routeOriginalId; // Make a final copy to use it in the lambda. 278 boolean isAvailableDeviceRoute = 279 mDeviceRouteController.getAvailableRoutes().stream() 280 .anyMatch(it -> it.getId().equals(finalRouteId)); 281 boolean isSelectedDeviceRoute = TextUtils.equals(routeOriginalId, selectedDeviceRouteId); 282 283 if (isSelectedDeviceRoute || isAvailableDeviceRoute) { 284 // The requested route is managed by the device route controller. Note that the selected 285 // device route doesn't necessarily match mSelectedRouteId (which is the selected route 286 // of the routing session). If the selected device route is transferred to, we need to 287 // make the bluetooth routes inactive so that the device route becomes the selected 288 // route of the routing session. 289 mDeviceRouteController.transferTo(routeOriginalId); 290 mBluetoothRouteController.transferTo(null); 291 } else { 292 // The requested route is managed by the bluetooth route controller. 293 mDeviceRouteController.transferTo(null); 294 mBluetoothRouteController.transferTo(routeOriginalId); 295 } 296 297 if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses() 298 && updateSessionInfosIfNeeded()) { 299 notifyGlobalSessionInfoUpdated(); 300 } 301 } 302 303 @Override setRouteVolume(long requestId, String routeOriginalId, int volume)304 public void setRouteVolume(long requestId, String routeOriginalId, int volume) { 305 if (!TextUtils.equals(routeOriginalId, mSelectedRouteId)) { 306 return; 307 } 308 mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); 309 } 310 311 @Override setSessionVolume(long requestId, String sessionOriginalId, int volume)312 public void setSessionVolume(long requestId, String sessionOriginalId, int volume) { 313 // Do nothing since we don't support grouping volume yet. 314 } 315 316 @Override prepareReleaseSession(String sessionUniqueId)317 public void prepareReleaseSession(String sessionUniqueId) { 318 // Do nothing since the system session persists. 319 } 320 getDefaultRoute()321 public MediaRoute2Info getDefaultRoute() { 322 return mDefaultRoute; 323 } 324 getDefaultSessionInfo()325 public RoutingSessionInfo getDefaultSessionInfo() { 326 return mDefaultSessionInfo; 327 } 328 329 /** 330 * Returns the {@link RoutingSessionInfo} that corresponds to the package with the given name. 331 */ getSessionForPackage(String targetPackageName)332 public RoutingSessionInfo getSessionForPackage(String targetPackageName) { 333 synchronized (mLock) { 334 if (!mSessionInfos.isEmpty()) { 335 // Return a copy of the current system session with no modification, 336 // except setting the client package name. 337 return new RoutingSessionInfo.Builder(mSessionInfos.get(0)) 338 .setClientPackageName(targetPackageName) 339 .build(); 340 } else { 341 return null; 342 } 343 } 344 } 345 346 /** 347 * Builds a system {@link RoutingSessionInfo} with the selected route set to the currently 348 * selected <b>device</b> route (wired or built-in, but not bluetooth) and transferable routes 349 * set to the currently available (connected) bluetooth routes. 350 * 351 * <p>The session's client package name is set to the provided package name. 352 * 353 * <p>Returns {@code null} if there are no registered system sessions. 354 */ 355 @Nullable generateDeviceRouteSelectedSessionInfo(String packageName)356 public RoutingSessionInfo generateDeviceRouteSelectedSessionInfo(String packageName) { 357 synchronized (mLock) { 358 if (mSessionInfos.isEmpty()) { 359 return null; 360 } 361 362 MediaRoute2Info selectedDeviceRoute = mDeviceRouteController.getSelectedRoute(); 363 364 RoutingSessionInfo.Builder builder = 365 new RoutingSessionInfo.Builder(SYSTEM_SESSION_ID, packageName) 366 .setSystemSession(true); 367 builder.addSelectedRoute(selectedDeviceRoute.getId()); 368 for (MediaRoute2Info route : mBluetoothRouteController.getAllBluetoothRoutes()) { 369 builder.addTransferableRoute(route.getId()); 370 } 371 372 if (Flags.enableAudioPoliciesDeviceAndBluetoothController()) { 373 for (MediaRoute2Info route : mDeviceRouteController.getAvailableRoutes()) { 374 if (!TextUtils.equals(selectedDeviceRoute.getId(), route.getId())) { 375 builder.addTransferableRoute(route.getId()); 376 } 377 } 378 } 379 380 if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) { 381 var oldSessionInfo = 382 Flags.enableMirroringInMediaRouter2() 383 ? mSystemSessionInfo 384 : mSessionInfos.get(0); 385 builder.setTransferReason(oldSessionInfo.getTransferReason()) 386 .setTransferInitiator(oldSessionInfo.getTransferInitiatorUserHandle(), 387 oldSessionInfo.getTransferInitiatorPackageName()); 388 } 389 390 return builder.setProviderId(mUniqueId).build(); 391 } 392 } 393 394 /** 395 * Notifies the system provider of a {@link MediaRoute2ProviderServiceProxy} update. 396 * 397 * <p>To be overridden so as to generate system media routes for {@link 398 * MediaRoute2ProviderService} routes that {@link MediaRoute2Info#supportsSystemMediaRouting() 399 * support system media routing}). 400 * 401 * @param serviceProxy The proxy of the service that updated its state. 402 */ updateSystemMediaRoutesFromProxy(MediaRoute2ProviderServiceProxy serviceProxy)403 public void updateSystemMediaRoutesFromProxy(MediaRoute2ProviderServiceProxy serviceProxy) { 404 // Do nothing. This implementation doesn't support MR2ProviderService system media routes. 405 // The subclass overrides this method to implement app-managed system media routing (aka 406 // mirroring). 407 } 408 409 /** 410 * Called when the system provider state changes. 411 * 412 * <p>To be overridden by {@link SystemMediaRoute2Provider2}, so that app-provided system media 413 * routing routes are added before setting the provider state. 414 */ onSystemProviderRoutesChanged(MediaRoute2ProviderInfo providerInfo)415 public void onSystemProviderRoutesChanged(MediaRoute2ProviderInfo providerInfo) { 416 setProviderState(providerInfo); 417 } 418 updateProviderState()419 protected void updateProviderState() { 420 MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder(); 421 422 // We must have a device route in the provider info. 423 if (Flags.enableAudioPoliciesDeviceAndBluetoothController()) { 424 List<MediaRoute2Info> deviceRoutes = mDeviceRouteController.getAvailableRoutes(); 425 for (MediaRoute2Info route : deviceRoutes) { 426 builder.addRoute(route); 427 } 428 if (!Flags.enableMirroringInMediaRouter2()) { 429 setProviderState(builder.build()); 430 } 431 } else { 432 builder.addRoute(mDeviceRouteController.getSelectedRoute()); 433 } 434 435 for (MediaRoute2Info route : mBluetoothRouteController.getAllBluetoothRoutes()) { 436 builder.addRoute(route); 437 } 438 MediaRoute2ProviderInfo providerInfo = builder.build(); 439 onSystemProviderRoutesChanged(providerInfo); 440 if (DEBUG) { 441 Slog.d(TAG, "Updating system provider info : " + providerInfo); 442 } 443 } 444 445 /** 446 * Updates the mSessionInfo. Returns true if the session info is changed. 447 */ updateSessionInfosIfNeeded()448 boolean updateSessionInfosIfNeeded() { 449 synchronized (mLock) { 450 RoutingSessionInfo oldSessionInfo; 451 if (Flags.enableMirroringInMediaRouter2()) { 452 oldSessionInfo = mSystemSessionInfo; 453 } else { 454 oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(0); 455 } 456 457 RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder( 458 SYSTEM_SESSION_ID, "" /* clientPackageName */) 459 .setSystemSession(true); 460 461 MediaRoute2Info selectedDeviceRoute = mDeviceRouteController.getSelectedRoute(); 462 MediaRoute2Info selectedRoute = selectedDeviceRoute; 463 MediaRoute2Info selectedBtRoute = mBluetoothRouteController.getSelectedRoute(); 464 List<String> transferableRoutes = new ArrayList<>(); 465 466 if (selectedBtRoute != null) { 467 selectedRoute = selectedBtRoute; 468 transferableRoutes.add(selectedDeviceRoute.getId()); 469 } 470 mSelectedRouteId = selectedRoute.getId(); 471 mDefaultRoute = 472 new MediaRoute2Info.Builder(MediaRoute2Info.ROUTE_ID_DEFAULT, selectedRoute) 473 .setSystemRoute(true) 474 .setProviderId(mUniqueId) 475 .build(); 476 builder.addSelectedRoute(mSelectedRouteId); 477 if (Flags.enableAudioPoliciesDeviceAndBluetoothController()) { 478 for (MediaRoute2Info route : mDeviceRouteController.getAvailableRoutes()) { 479 String routeId = route.getId(); 480 if (!mSelectedRouteId.equals(routeId)) { 481 transferableRoutes.add(routeId); 482 } 483 } 484 } 485 for (MediaRoute2Info route : mBluetoothRouteController.getTransferableRoutes()) { 486 transferableRoutes.add(route.getId()); 487 } 488 489 for (String route : transferableRoutes) { 490 builder.addTransferableRoute(route); 491 } 492 493 if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) { 494 int transferReason = RoutingSessionInfo.TRANSFER_REASON_FALLBACK; 495 UserHandle transferInitiatorUserHandle = null; 496 String transferInitiatorPackageName = null; 497 498 if (oldSessionInfo != null 499 && containsSelectedRouteWithId(oldSessionInfo, selectedRoute.getId())) { 500 transferReason = oldSessionInfo.getTransferReason(); 501 transferInitiatorUserHandle = oldSessionInfo.getTransferInitiatorUserHandle(); 502 transferInitiatorPackageName = oldSessionInfo.getTransferInitiatorPackageName(); 503 } 504 505 synchronized (mTransferLock) { 506 if (mPendingTransferRequest != null) { 507 boolean isTransferringToTheSelectedRoute = 508 mPendingTransferRequest.isTargetRoute(selectedRoute); 509 boolean canBePotentiallyTransferred = 510 mPendingTransferRequest.isTargetRouteIdInRouteOriginalIdList( 511 transferableRoutes); 512 513 if (isTransferringToTheSelectedRoute) { 514 transferReason = mPendingTransferRequest.mTransferReason; 515 transferInitiatorUserHandle = 516 mPendingTransferRequest.mTransferInitiatorUserHandle; 517 transferInitiatorPackageName = 518 mPendingTransferRequest.mTransferInitiatorPackageName; 519 520 mPendingTransferRequest = null; 521 } else if (!canBePotentiallyTransferred) { 522 mPendingTransferRequest = null; 523 } 524 } 525 } 526 527 builder.setTransferReason(transferReason) 528 .setTransferInitiator( 529 transferInitiatorUserHandle, transferInitiatorPackageName); 530 } 531 532 RoutingSessionInfo newSessionInfo = builder.setProviderId(mUniqueId).build(); 533 534 synchronized (mRequestLock) { 535 reportPendingSessionRequestResultLockedIfNeeded(newSessionInfo); 536 } 537 538 if (Objects.equals(oldSessionInfo, newSessionInfo)) { 539 return false; 540 } else { 541 if (DEBUG) { 542 Slog.d(TAG, "Updating system routing session info : " + newSessionInfo); 543 } 544 mSystemSessionInfo = newSessionInfo; 545 onSystemSessionInfoUpdated(); 546 mDefaultSessionInfo = 547 new RoutingSessionInfo.Builder( 548 SYSTEM_SESSION_ID, "" /* clientPackageName */) 549 .setProviderId(mUniqueId) 550 .setSystemSession(true) 551 .addSelectedRoute(MediaRoute2Info.ROUTE_ID_DEFAULT) 552 .setTransferReason(newSessionInfo.getTransferReason()) 553 .setTransferInitiator( 554 newSessionInfo.getTransferInitiatorUserHandle(), 555 newSessionInfo.getTransferInitiatorPackageName()) 556 .build(); 557 return true; 558 } 559 } 560 } 561 562 @GuardedBy("mLock") onSystemSessionInfoUpdated()563 protected void onSystemSessionInfoUpdated() { 564 mSessionInfos.clear(); 565 mSessionInfos.add(mSystemSessionInfo); 566 } 567 568 @GuardedBy("mRequestLock") reportPendingSessionRequestResultLockedIfNeeded( RoutingSessionInfo newSessionInfo)569 private void reportPendingSessionRequestResultLockedIfNeeded( 570 RoutingSessionInfo newSessionInfo) { 571 if (mPendingSessionCreationOrTransferRequest == null) { 572 // No pending request, nothing to report. 573 return; 574 } 575 576 long pendingRequestId = mPendingSessionCreationOrTransferRequest.mRequestId; 577 if (mPendingSessionCreationOrTransferRequest.mTargetOriginalRouteId.equals( 578 mSelectedRouteId)) { 579 if (DEBUG) { 580 Slog.w( 581 TAG, 582 "Session creation success to route " 583 + mPendingSessionCreationOrTransferRequest.mTargetOriginalRouteId); 584 } 585 mPendingSessionCreationOrTransferRequest = null; 586 mCallback.onSessionCreated(this, pendingRequestId, newSessionInfo); 587 } else { 588 boolean isRequestedRouteConnectedBtRoute = isRequestedRouteConnectedBtRoute(); 589 if (!Flags.enableWaitingStateForSystemSessionCreationRequest() 590 || !isRequestedRouteConnectedBtRoute) { 591 if (DEBUG) { 592 Slog.w( 593 TAG, 594 "Session creation failed to route " 595 + mPendingSessionCreationOrTransferRequest 596 .mTargetOriginalRouteId); 597 } 598 mPendingSessionCreationOrTransferRequest = null; 599 mCallback.onRequestFailed( 600 this, pendingRequestId, MediaRoute2ProviderService.REASON_UNKNOWN_ERROR); 601 } else if (DEBUG) { 602 Slog.w( 603 TAG, 604 "Session creation waiting state to route " 605 + mPendingSessionCreationOrTransferRequest.mTargetOriginalRouteId); 606 } 607 } 608 } 609 610 @GuardedBy("mRequestLock") isRequestedRouteConnectedBtRoute()611 private boolean isRequestedRouteConnectedBtRoute() { 612 // Using AllRoutes instead of TransferableRoutes as BT Stack sends an intermediate update 613 // where two BT routes are active so the transferable routes list is empty. 614 // See b/307723189 for context 615 for (MediaRoute2Info btRoute : mBluetoothRouteController.getAllBluetoothRoutes()) { 616 if (TextUtils.equals( 617 btRoute.getId(), 618 mPendingSessionCreationOrTransferRequest.mTargetOriginalRouteId)) { 619 return true; 620 } 621 } 622 return false; 623 } 624 containsSelectedRouteWithId( @ullable RoutingSessionInfo sessionInfo, @NonNull String selectedRouteId)625 private boolean containsSelectedRouteWithId( 626 @Nullable RoutingSessionInfo sessionInfo, @NonNull String selectedRouteId) { 627 if (sessionInfo == null) { 628 return false; 629 } 630 631 List<String> selectedRoutes = sessionInfo.getSelectedRoutes(); 632 633 if (selectedRoutes.size() != 1) { 634 throw new IllegalStateException("Selected routes list should contain only 1 route id."); 635 } 636 637 String oldSelectedRouteId = MediaRouter2Utils.getOriginalId(selectedRoutes.get(0)); 638 return oldSelectedRouteId != null && oldSelectedRouteId.equals(selectedRouteId); 639 } 640 publishProviderState()641 void publishProviderState() { 642 updateProviderState(); 643 notifyProviderState(); 644 } 645 notifyGlobalSessionInfoUpdated()646 void notifyGlobalSessionInfoUpdated() { 647 if (mCallback == null) { 648 return; 649 } 650 651 RoutingSessionInfo sessionInfo; 652 synchronized (mLock) { 653 if (mSessionInfos.isEmpty()) { 654 return; 655 } 656 sessionInfo = mSessionInfos.get(0); 657 } 658 659 mCallback.onSessionUpdated( 660 this, sessionInfo, /* packageNamesWithRoutingSessionOverrides= */ Set.of()); 661 } 662 663 @Override getDebugString()664 protected String getDebugString() { 665 return TextUtils.formatSimple( 666 "%s - package: %s, selected route id: %s, bluetooth impl: %s", 667 getClass().getSimpleName(), 668 mComponentName.getPackageName(), 669 mSelectedRouteId, 670 mBluetoothRouteController.getClass().getSimpleName()); 671 } 672 updateVolume()673 void updateVolume() { 674 int devices = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC); 675 int volume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); 676 677 if (mDefaultRoute.getVolume() != volume) { 678 mDefaultRoute = new MediaRoute2Info.Builder(mDefaultRoute) 679 .setVolume(volume) 680 .build(); 681 } 682 683 if (mBluetoothRouteController.updateVolumeForDevices(devices, volume)) { 684 return; 685 } 686 687 mDeviceRouteController.updateVolume(volume); 688 689 publishProviderState(); 690 } 691 692 private class AudioManagerBroadcastReceiver extends BroadcastReceiver { 693 // This will be called in the main thread. 694 @Override onReceive(Context context, Intent intent)695 public void onReceive(Context context, Intent intent) { 696 if (!intent.getAction().equals(AudioManager.VOLUME_CHANGED_ACTION) 697 && !intent.getAction().equals(AudioManager.STREAM_DEVICES_CHANGED_ACTION)) { 698 return; 699 } 700 701 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); 702 if (streamType != AudioManager.STREAM_MUSIC) { 703 return; 704 } 705 706 if (Flags.enableMr2ServiceNonMainBgThread()) { 707 mHandler.post(SystemMediaRoute2Provider.this::updateVolume); 708 } else { 709 updateVolume(); 710 } 711 } 712 } 713 } 714