diff options
5 files changed, 217 insertions, 10 deletions
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java index 248efde4902c..bbb03e77c8c9 100644 --- a/media/java/android/media/MediaRoute2Info.java +++ b/media/java/android/media/MediaRoute2Info.java @@ -934,6 +934,15 @@ public final class MediaRoute2Info implements Parcelable { } /** + * 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 * {@link com.android.server.media.MediaRouterService}. diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java index d8c35358102d..f09be2c15ee0 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java @@ -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) { diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index e18ed410c045..58deffcbd4ba 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -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() @@ -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; diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index 8dfba39dcfd8..b93846bf9ee7 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -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 = @@ -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; @@ -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()); @@ -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(); @@ -373,7 +408,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { for (MediaRoute2Info route : deviceRoutes) { builder.addRoute(route); } - setProviderState(builder.build()); + if (!Flags.enableMirroringInMediaRouter2()) { + setProviderState(builder.build()); + } } else { builder.addRoute(mDeviceRouteController.getSelectedRoute()); } @@ -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); } @@ -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 */) @@ -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 */) @@ -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) { @@ -587,6 +634,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { RoutingSessionInfo sessionInfo; synchronized (mLock) { sessionInfo = mSessionInfos.get(0); + if (sessionInfo == null) { + return; + } } mCallback.onSessionUpdated(this, sessionInfo); diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java index 85b30ad8cadb..7dc30ab66fd2 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java @@ -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 @@ -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(), @@ -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)); + } + } } |