diff options
author | 2024-12-10 17:50:18 +0000 | |
---|---|---|
committer | 2024-12-12 12:00:00 +0000 | |
commit | 42a3c29a7ec0776bd7c215ea90cc928d3b658ed9 (patch) | |
tree | c71c864d7e049f4f2c18a1bc8869e34876d6619a | |
parent | b0a6bcea15d93562ebd937e1f8d8171b425370a1 (diff) |
Add MR2ServiceProvider-provided system media routes
These are routes that support routing system media, and whose properties
are similar to system-provided routes (like Bluetooth, or Built-in
Speakers).
This change also adds them to the list of transferable routes of the
system routing session, but does not implement transfers in order to
simplify the review. Transfers are coming in a following CL.
Bug: b/362507305
Test: atest CtsMediaBetterTogetherTestCases CtsMediaHostTestCasts
Flag: com.android.media.flags.enable_mirroring_in_media_router_2
Change-Id: I5950914a8387ea579d22f1bdd2c526f34b6c0758
5 files changed, 217 insertions, 10 deletions
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java index 1e43db430291..56c214a64493 100644 --- a/media/java/android/media/MediaRoute2Info.java +++ b/media/java/android/media/MediaRoute2Info.java @@ -899,6 +899,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 bcfcfc2437a2..e1cad9a73f6a 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() @@ -2722,6 +2722,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)); + } + } } |