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