• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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