Loading media/java/android/media/MediaRoute2Info.java +9 −0 Original line number Diff line number Diff line Loading @@ -933,6 +933,15 @@ public final class MediaRoute2Info implements Parcelable { != 0; } /** * Returns whether this route supports {@link #FLAG_ROUTING_TYPE_REMOTE remote routing}. * * @hide */ public boolean supportsRemoteRouting() { return (mRoutingTypeFlags & MediaRoute2Info.FLAG_ROUTING_TYPE_REMOTE) != 0; } /** * Returns true if the route info has all of the required field. * A route is valid if and only if it is obtained from Loading services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java +3 −1 Original line number Diff line number Diff line Loading @@ -289,7 +289,9 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider { // doesn't have any registered discovery preference, we should still be able to route their // system media. boolean bindDueToSystemMediaRoutingSupport = mIsManagerScanning && mSupportsSystemMediaRouting; mLastDiscoveryPreference != null && mLastDiscoveryPreference.shouldPerformActiveScan() && mSupportsSystemMediaRouting; if (!getSessionInfos().isEmpty() || bindDueToManagerScan || bindDueToSystemMediaRoutingSupport) { Loading services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +10 −1 Original line number Diff line number Diff line Loading @@ -848,7 +848,7 @@ class MediaRouter2ServiceImpl { UserRecord userRecord = getOrCreateUserRecordLocked(userId); List<RoutingSessionInfo> sessionInfos; if (hasSystemRoutingPermissions) { if (setDeviceRouteSelected) { if (setDeviceRouteSelected && !Flags.enableMirroringInMediaRouter2()) { // Return a fake system session that shows the device route as selected and // available bluetooth routes as transferable. return userRecord.mHandler.getSystemProvider() Loading Loading @@ -2733,6 +2733,15 @@ class MediaRouter2ServiceImpl { newRoutes = Collections.emptySet(); } if (Flags.enableMirroringInMediaRouter2() && provider instanceof MediaRoute2ProviderServiceProxy proxyProvider) { // We notify the system provider of service updates, so that it can update the // system routing session by adding them as transferable routes. And we remove those // that don't support remote routing. mSystemProvider.updateSystemMediaRoutesFromProxy(proxyProvider); newRoutes.removeIf(it -> !it.supportsRemoteRouting()); } // Add new routes to the maps. ArrayList<MediaRoute2Info> addedRoutes = new ArrayList<>(); boolean hasAddedOrModifiedRoutes = false; Loading services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +58 −8 Original line number Diff line number Diff line Loading @@ -73,6 +73,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { // For apps without MODIFYING_AUDIO_ROUTING permission. // This should be the currently selected route. MediaRoute2Info mDefaultRoute; @GuardedBy("mLock") RoutingSessionInfo mSystemSessionInfo; RoutingSessionInfo mDefaultSessionInfo; private final AudioManagerBroadcastReceiver mAudioReceiver = Loading Loading @@ -180,7 +184,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { if (TextUtils.equals(routeOriginalId, mSelectedRouteId)) { RoutingSessionInfo currentSessionInfo; synchronized (mLock) { currentSessionInfo = mSessionInfos.get(0); currentSessionInfo = Flags.enableMirroringInMediaRouter2() ? mSystemSessionInfo : mSessionInfos.get(0); } mCallback.onSessionCreated(this, requestId, currentSessionInfo); return; Loading Loading @@ -354,7 +361,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) { RoutingSessionInfo oldSessionInfo = mSessionInfos.get(0); var oldSessionInfo = Flags.enableMirroringInMediaRouter2() ? mSystemSessionInfo : mSessionInfos.get(0); builder.setTransferReason(oldSessionInfo.getTransferReason()) .setTransferInitiator(oldSessionInfo.getTransferInitiatorUserHandle(), oldSessionInfo.getTransferInitiatorPackageName()); Loading @@ -364,6 +374,31 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } } /** * Notifies the system provider of a {@link MediaRoute2ProviderServiceProxy} update. * * <p>To be overridden so as to generate system media routes for {@link * MediaRoute2ProviderService} routes that {@link MediaRoute2Info#supportsSystemMediaRouting() * support system media routing}). * * @param serviceProxy The proxy of the service that updated its state. */ public void updateSystemMediaRoutesFromProxy(MediaRoute2ProviderServiceProxy serviceProxy) { // Do nothing. This implementation doesn't support MR2ProviderService system media routes. // The subclass overrides this method to implement app-managed system media routing (aka // mirroring). } /** * Called when the system provider state changes. * * <p>To be overridden by {@link SystemMediaRoute2Provider2}, so that app-provided system media * routing routes are added before setting the provider state. */ public void onSystemProviderRoutesChanged(MediaRoute2ProviderInfo providerInfo) { setProviderState(providerInfo); } protected void updateProviderState() { MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder(); Loading @@ -373,7 +408,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { for (MediaRoute2Info route : deviceRoutes) { builder.addRoute(route); } if (!Flags.enableMirroringInMediaRouter2()) { setProviderState(builder.build()); } } else { builder.addRoute(mDeviceRouteController.getSelectedRoute()); } Loading @@ -382,7 +419,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { builder.addRoute(route); } MediaRoute2ProviderInfo providerInfo = builder.build(); setProviderState(providerInfo); onSystemProviderRoutesChanged(providerInfo); if (DEBUG) { Slog.d(TAG, "Updating system provider info : " + providerInfo); } Loading @@ -393,8 +430,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { */ boolean updateSessionInfosIfNeeded() { synchronized (mLock) { RoutingSessionInfo oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get( 0); RoutingSessionInfo oldSessionInfo; if (Flags.enableMirroringInMediaRouter2()) { oldSessionInfo = mSystemSessionInfo; } else { oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(0); } RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder( SYSTEM_SESSION_ID, "" /* clientPackageName */) Loading Loading @@ -483,8 +524,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { if (DEBUG) { Slog.d(TAG, "Updating system routing session info : " + newSessionInfo); } mSessionInfos.clear(); mSessionInfos.add(newSessionInfo); mSystemSessionInfo = newSessionInfo; onSystemSessionInfoUpdated(); mDefaultSessionInfo = new RoutingSessionInfo.Builder( SYSTEM_SESSION_ID, "" /* clientPackageName */) Loading @@ -501,6 +542,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } } @GuardedBy("mLock") protected void onSystemSessionInfoUpdated() { mSessionInfos.clear(); mSessionInfos.add(mSystemSessionInfo); } @GuardedBy("mRequestLock") private void reportPendingSessionRequestResultLockedIfNeeded( RoutingSessionInfo newSessionInfo) { Loading Loading @@ -587,6 +634,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { RoutingSessionInfo sessionInfo; synchronized (mLock) { sessionInfo = mSessionInfos.get(0); if (sessionInfo == null) { return; } } mCallback.onSessionUpdated(this, sessionInfo); Loading services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java +137 −0 Original line number Diff line number Diff line Loading @@ -16,11 +16,26 @@ package com.android.server.media; import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO; import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderInfo; import android.media.MediaRoute2ProviderService; import android.media.RoutingSessionInfo; import android.os.Looper; import android.os.UserHandle; import android.util.ArraySet; import com.android.internal.annotations.GuardedBy; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.stream.Stream; /** * Extends {@link SystemMediaRoute2Provider} by adding system routes provided by {@link Loading @@ -30,6 +45,15 @@ import android.os.UserHandle; */ /* package */ class SystemMediaRoute2Provider2 extends SystemMediaRoute2Provider { private static final String ROUTE_ID_PREFIX_SYSTEM = "SYSTEM"; private static final String ROUTE_ID_SYSTEM_SEPARATOR = "."; @GuardedBy("mLock") private MediaRoute2ProviderInfo mLastSystemProviderInfo; @GuardedBy("mLock") private final Map<String, ProviderProxyRecord> mProxyRecords = new HashMap<>(); private static final ComponentName COMPONENT_NAME = new ComponentName( SystemMediaRoute2Provider2.class.getPackage().getName(), Loading @@ -46,4 +70,117 @@ import android.os.UserHandle; private SystemMediaRoute2Provider2(Context context, UserHandle user, Looper looper) { super(context, COMPONENT_NAME, user, looper); } @Override protected void onSystemSessionInfoUpdated() { updateSessionInfo(); } @Override public void updateSystemMediaRoutesFromProxy(MediaRoute2ProviderServiceProxy serviceProxy) { var proxyRecord = ProviderProxyRecord.createFor(serviceProxy); synchronized (mLock) { if (proxyRecord == null) { mProxyRecords.remove(serviceProxy.mUniqueId); } else { mProxyRecords.put(serviceProxy.mUniqueId, proxyRecord); } setProviderState(buildProviderInfo()); } updateSessionInfo(); notifyProviderState(); notifySessionInfoUpdated(); } @Override public void onSystemProviderRoutesChanged(MediaRoute2ProviderInfo providerInfo) { synchronized (mLock) { mLastSystemProviderInfo = providerInfo; setProviderState(buildProviderInfo()); } updateSessionInfo(); notifySessionInfoUpdated(); } /** * Updates the {@link #mSessionInfos} by expanding the {@link SystemMediaRoute2Provider} session * with information from the {@link MediaRoute2ProviderService provider services}. */ private void updateSessionInfo() { synchronized (mLock) { var systemSessionInfo = mSystemSessionInfo; if (systemSessionInfo == null) { // The system session info hasn't been initialized yet. Do nothing. return; } var builder = new RoutingSessionInfo.Builder(systemSessionInfo); mProxyRecords.values().stream() .flatMap(ProviderProxyRecord::getRoutesStream) .map(MediaRoute2Info::getId) .forEach(builder::addTransferableRoute); mSessionInfos.clear(); mSessionInfos.add(builder.build()); } } /** * Returns a new a provider info that includes all routes from the system provider {@link * SystemMediaRoute2Provider}, along with system routes from {@link MediaRoute2ProviderService * provider services}. */ @GuardedBy("mLock") private MediaRoute2ProviderInfo buildProviderInfo() { MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder(mLastSystemProviderInfo); mProxyRecords.values().stream() .flatMap(ProviderProxyRecord::getRoutesStream) .forEach(builder::addRoute); return builder.build(); } /** * Holds information about {@link MediaRoute2ProviderService provider services} registered in * the system. * * @param mProxy The corresponding {@link MediaRoute2ProviderServiceProxy}. * @param mSystemMediaRoutes The last snapshot of routes from the service that support system * media routing, as defined by {@link MediaRoute2Info#supportsSystemMediaRouting()}. */ private record ProviderProxyRecord( MediaRoute2ProviderServiceProxy mProxy, Collection<MediaRoute2Info> mSystemMediaRoutes) { /** Returns a stream representation of the {@link #mSystemMediaRoutes}. */ public Stream<MediaRoute2Info> getRoutesStream() { return mSystemMediaRoutes.stream(); } /** * Returns a new instance, or null if the given {@code serviceProxy} doesn't have an * associated {@link MediaRoute2ProviderInfo}. */ @Nullable public static ProviderProxyRecord createFor(MediaRoute2ProviderServiceProxy serviceProxy) { MediaRoute2ProviderInfo providerInfo = serviceProxy.getProviderInfo(); if (providerInfo == null) { return null; } ArraySet<MediaRoute2Info> routes = new ArraySet<>(); providerInfo.getRoutes().stream() .filter(MediaRoute2Info::supportsSystemMediaRouting) .forEach( route -> { String id = ROUTE_ID_PREFIX_SYSTEM + route.getProviderId() + ROUTE_ID_SYSTEM_SEPARATOR + route.getOriginalId(); routes.add( new MediaRoute2Info.Builder(id, route.getName()) .addFeature(FEATURE_LIVE_AUDIO) .build()); }); return new ProviderProxyRecord(serviceProxy, Collections.unmodifiableSet(routes)); } } } Loading
media/java/android/media/MediaRoute2Info.java +9 −0 Original line number Diff line number Diff line Loading @@ -933,6 +933,15 @@ public final class MediaRoute2Info implements Parcelable { != 0; } /** * Returns whether this route supports {@link #FLAG_ROUTING_TYPE_REMOTE remote routing}. * * @hide */ public boolean supportsRemoteRouting() { return (mRoutingTypeFlags & MediaRoute2Info.FLAG_ROUTING_TYPE_REMOTE) != 0; } /** * Returns true if the route info has all of the required field. * A route is valid if and only if it is obtained from Loading
services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java +3 −1 Original line number Diff line number Diff line Loading @@ -289,7 +289,9 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider { // doesn't have any registered discovery preference, we should still be able to route their // system media. boolean bindDueToSystemMediaRoutingSupport = mIsManagerScanning && mSupportsSystemMediaRouting; mLastDiscoveryPreference != null && mLastDiscoveryPreference.shouldPerformActiveScan() && mSupportsSystemMediaRouting; if (!getSessionInfos().isEmpty() || bindDueToManagerScan || bindDueToSystemMediaRoutingSupport) { Loading
services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +10 −1 Original line number Diff line number Diff line Loading @@ -848,7 +848,7 @@ class MediaRouter2ServiceImpl { UserRecord userRecord = getOrCreateUserRecordLocked(userId); List<RoutingSessionInfo> sessionInfos; if (hasSystemRoutingPermissions) { if (setDeviceRouteSelected) { if (setDeviceRouteSelected && !Flags.enableMirroringInMediaRouter2()) { // Return a fake system session that shows the device route as selected and // available bluetooth routes as transferable. return userRecord.mHandler.getSystemProvider() Loading Loading @@ -2733,6 +2733,15 @@ class MediaRouter2ServiceImpl { newRoutes = Collections.emptySet(); } if (Flags.enableMirroringInMediaRouter2() && provider instanceof MediaRoute2ProviderServiceProxy proxyProvider) { // We notify the system provider of service updates, so that it can update the // system routing session by adding them as transferable routes. And we remove those // that don't support remote routing. mSystemProvider.updateSystemMediaRoutesFromProxy(proxyProvider); newRoutes.removeIf(it -> !it.supportsRemoteRouting()); } // Add new routes to the maps. ArrayList<MediaRoute2Info> addedRoutes = new ArrayList<>(); boolean hasAddedOrModifiedRoutes = false; Loading
services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +58 −8 Original line number Diff line number Diff line Loading @@ -73,6 +73,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { // For apps without MODIFYING_AUDIO_ROUTING permission. // This should be the currently selected route. MediaRoute2Info mDefaultRoute; @GuardedBy("mLock") RoutingSessionInfo mSystemSessionInfo; RoutingSessionInfo mDefaultSessionInfo; private final AudioManagerBroadcastReceiver mAudioReceiver = Loading Loading @@ -180,7 +184,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { if (TextUtils.equals(routeOriginalId, mSelectedRouteId)) { RoutingSessionInfo currentSessionInfo; synchronized (mLock) { currentSessionInfo = mSessionInfos.get(0); currentSessionInfo = Flags.enableMirroringInMediaRouter2() ? mSystemSessionInfo : mSessionInfos.get(0); } mCallback.onSessionCreated(this, requestId, currentSessionInfo); return; Loading Loading @@ -354,7 +361,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) { RoutingSessionInfo oldSessionInfo = mSessionInfos.get(0); var oldSessionInfo = Flags.enableMirroringInMediaRouter2() ? mSystemSessionInfo : mSessionInfos.get(0); builder.setTransferReason(oldSessionInfo.getTransferReason()) .setTransferInitiator(oldSessionInfo.getTransferInitiatorUserHandle(), oldSessionInfo.getTransferInitiatorPackageName()); Loading @@ -364,6 +374,31 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } } /** * Notifies the system provider of a {@link MediaRoute2ProviderServiceProxy} update. * * <p>To be overridden so as to generate system media routes for {@link * MediaRoute2ProviderService} routes that {@link MediaRoute2Info#supportsSystemMediaRouting() * support system media routing}). * * @param serviceProxy The proxy of the service that updated its state. */ public void updateSystemMediaRoutesFromProxy(MediaRoute2ProviderServiceProxy serviceProxy) { // Do nothing. This implementation doesn't support MR2ProviderService system media routes. // The subclass overrides this method to implement app-managed system media routing (aka // mirroring). } /** * Called when the system provider state changes. * * <p>To be overridden by {@link SystemMediaRoute2Provider2}, so that app-provided system media * routing routes are added before setting the provider state. */ public void onSystemProviderRoutesChanged(MediaRoute2ProviderInfo providerInfo) { setProviderState(providerInfo); } protected void updateProviderState() { MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder(); Loading @@ -373,7 +408,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { for (MediaRoute2Info route : deviceRoutes) { builder.addRoute(route); } if (!Flags.enableMirroringInMediaRouter2()) { setProviderState(builder.build()); } } else { builder.addRoute(mDeviceRouteController.getSelectedRoute()); } Loading @@ -382,7 +419,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { builder.addRoute(route); } MediaRoute2ProviderInfo providerInfo = builder.build(); setProviderState(providerInfo); onSystemProviderRoutesChanged(providerInfo); if (DEBUG) { Slog.d(TAG, "Updating system provider info : " + providerInfo); } Loading @@ -393,8 +430,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { */ boolean updateSessionInfosIfNeeded() { synchronized (mLock) { RoutingSessionInfo oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get( 0); RoutingSessionInfo oldSessionInfo; if (Flags.enableMirroringInMediaRouter2()) { oldSessionInfo = mSystemSessionInfo; } else { oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(0); } RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder( SYSTEM_SESSION_ID, "" /* clientPackageName */) Loading Loading @@ -483,8 +524,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { if (DEBUG) { Slog.d(TAG, "Updating system routing session info : " + newSessionInfo); } mSessionInfos.clear(); mSessionInfos.add(newSessionInfo); mSystemSessionInfo = newSessionInfo; onSystemSessionInfoUpdated(); mDefaultSessionInfo = new RoutingSessionInfo.Builder( SYSTEM_SESSION_ID, "" /* clientPackageName */) Loading @@ -501,6 +542,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } } @GuardedBy("mLock") protected void onSystemSessionInfoUpdated() { mSessionInfos.clear(); mSessionInfos.add(mSystemSessionInfo); } @GuardedBy("mRequestLock") private void reportPendingSessionRequestResultLockedIfNeeded( RoutingSessionInfo newSessionInfo) { Loading Loading @@ -587,6 +634,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { RoutingSessionInfo sessionInfo; synchronized (mLock) { sessionInfo = mSessionInfos.get(0); if (sessionInfo == null) { return; } } mCallback.onSessionUpdated(this, sessionInfo); Loading
services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java +137 −0 Original line number Diff line number Diff line Loading @@ -16,11 +16,26 @@ package com.android.server.media; import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO; import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderInfo; import android.media.MediaRoute2ProviderService; import android.media.RoutingSessionInfo; import android.os.Looper; import android.os.UserHandle; import android.util.ArraySet; import com.android.internal.annotations.GuardedBy; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.stream.Stream; /** * Extends {@link SystemMediaRoute2Provider} by adding system routes provided by {@link Loading @@ -30,6 +45,15 @@ import android.os.UserHandle; */ /* package */ class SystemMediaRoute2Provider2 extends SystemMediaRoute2Provider { private static final String ROUTE_ID_PREFIX_SYSTEM = "SYSTEM"; private static final String ROUTE_ID_SYSTEM_SEPARATOR = "."; @GuardedBy("mLock") private MediaRoute2ProviderInfo mLastSystemProviderInfo; @GuardedBy("mLock") private final Map<String, ProviderProxyRecord> mProxyRecords = new HashMap<>(); private static final ComponentName COMPONENT_NAME = new ComponentName( SystemMediaRoute2Provider2.class.getPackage().getName(), Loading @@ -46,4 +70,117 @@ import android.os.UserHandle; private SystemMediaRoute2Provider2(Context context, UserHandle user, Looper looper) { super(context, COMPONENT_NAME, user, looper); } @Override protected void onSystemSessionInfoUpdated() { updateSessionInfo(); } @Override public void updateSystemMediaRoutesFromProxy(MediaRoute2ProviderServiceProxy serviceProxy) { var proxyRecord = ProviderProxyRecord.createFor(serviceProxy); synchronized (mLock) { if (proxyRecord == null) { mProxyRecords.remove(serviceProxy.mUniqueId); } else { mProxyRecords.put(serviceProxy.mUniqueId, proxyRecord); } setProviderState(buildProviderInfo()); } updateSessionInfo(); notifyProviderState(); notifySessionInfoUpdated(); } @Override public void onSystemProviderRoutesChanged(MediaRoute2ProviderInfo providerInfo) { synchronized (mLock) { mLastSystemProviderInfo = providerInfo; setProviderState(buildProviderInfo()); } updateSessionInfo(); notifySessionInfoUpdated(); } /** * Updates the {@link #mSessionInfos} by expanding the {@link SystemMediaRoute2Provider} session * with information from the {@link MediaRoute2ProviderService provider services}. */ private void updateSessionInfo() { synchronized (mLock) { var systemSessionInfo = mSystemSessionInfo; if (systemSessionInfo == null) { // The system session info hasn't been initialized yet. Do nothing. return; } var builder = new RoutingSessionInfo.Builder(systemSessionInfo); mProxyRecords.values().stream() .flatMap(ProviderProxyRecord::getRoutesStream) .map(MediaRoute2Info::getId) .forEach(builder::addTransferableRoute); mSessionInfos.clear(); mSessionInfos.add(builder.build()); } } /** * Returns a new a provider info that includes all routes from the system provider {@link * SystemMediaRoute2Provider}, along with system routes from {@link MediaRoute2ProviderService * provider services}. */ @GuardedBy("mLock") private MediaRoute2ProviderInfo buildProviderInfo() { MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder(mLastSystemProviderInfo); mProxyRecords.values().stream() .flatMap(ProviderProxyRecord::getRoutesStream) .forEach(builder::addRoute); return builder.build(); } /** * Holds information about {@link MediaRoute2ProviderService provider services} registered in * the system. * * @param mProxy The corresponding {@link MediaRoute2ProviderServiceProxy}. * @param mSystemMediaRoutes The last snapshot of routes from the service that support system * media routing, as defined by {@link MediaRoute2Info#supportsSystemMediaRouting()}. */ private record ProviderProxyRecord( MediaRoute2ProviderServiceProxy mProxy, Collection<MediaRoute2Info> mSystemMediaRoutes) { /** Returns a stream representation of the {@link #mSystemMediaRoutes}. */ public Stream<MediaRoute2Info> getRoutesStream() { return mSystemMediaRoutes.stream(); } /** * Returns a new instance, or null if the given {@code serviceProxy} doesn't have an * associated {@link MediaRoute2ProviderInfo}. */ @Nullable public static ProviderProxyRecord createFor(MediaRoute2ProviderServiceProxy serviceProxy) { MediaRoute2ProviderInfo providerInfo = serviceProxy.getProviderInfo(); if (providerInfo == null) { return null; } ArraySet<MediaRoute2Info> routes = new ArraySet<>(); providerInfo.getRoutes().stream() .filter(MediaRoute2Info::supportsSystemMediaRouting) .forEach( route -> { String id = ROUTE_ID_PREFIX_SYSTEM + route.getProviderId() + ROUTE_ID_SYSTEM_SEPARATOR + route.getOriginalId(); routes.add( new MediaRoute2Info.Builder(id, route.getName()) .addFeature(FEATURE_LIVE_AUDIO) .build()); }); return new ProviderProxyRecord(serviceProxy, Collections.unmodifiableSet(routes)); } } }