summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Santiago Seifert <aquilescanta@google.com> 2024-12-10 17:50:18 +0000
committer Santiago Seifert <aquilescanta@google.com> 2024-12-12 12:00:00 +0000
commit42a3c29a7ec0776bd7c215ea90cc928d3b658ed9 (patch)
treec71c864d7e049f4f2c18a1bc8869e34876d6619a
parentb0a6bcea15d93562ebd937e1f8d8171b425370a1 (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
-rw-r--r--media/java/android/media/MediaRoute2Info.java9
-rw-r--r--services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java4
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java11
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider.java66
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java137
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));
+ }
+ }
}