summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/media/MediaRoute2Provider.java20
-rw-r--r--services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java134
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider.java3
3 files changed, 150 insertions, 7 deletions
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 09605fefe80e..b0fa523da959 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -22,6 +22,7 @@ import android.content.ComponentName;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRouter2;
+import android.media.MediaRouter2Utils;
import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
import android.os.Bundle;
@@ -226,8 +227,23 @@ abstract class MediaRoute2Provider {
return route2Info != null && mTargetOriginalRouteId.equals(route2Info.getOriginalId());
}
- public boolean isTargetRouteIdInList(@NonNull List<String> routeOriginalIdList) {
- return routeOriginalIdList.stream().anyMatch(mTargetOriginalRouteId::equals);
+ /**
+ * Returns whether the given list of {@link MediaRoute2Info#getOriginalId() original ids}
+ * contains the {@link #mTargetOriginalRouteId target route id}.
+ */
+ public boolean isTargetRouteIdInRouteOriginalIdList(
+ @NonNull List<String> originalRouteIdList) {
+ return originalRouteIdList.stream().anyMatch(mTargetOriginalRouteId::equals);
+ }
+
+ /**
+ * Returns whether the given list of {@link MediaRoute2Info#getId() unique ids} contains the
+ * {@link #mTargetOriginalRouteId target route id}.
+ */
+ public boolean isTargetRouteIdInRouteUniqueIdList(@NonNull List<String> uniqueRouteIdList) {
+ return uniqueRouteIdList.stream()
+ .map(MediaRouter2Utils::getOriginalId)
+ .anyMatch(mTargetOriginalRouteId::equals);
}
}
}
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index 71cbcb91100f..d5e85dae1336 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -21,6 +21,7 @@ import static android.media.MediaRoute2ProviderService.REQUEST_ID_NONE;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -41,6 +42,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
+import android.util.LongSparseArray;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -49,7 +51,9 @@ import com.android.media.flags.Flags;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -77,7 +81,16 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
private boolean mLastDiscoveryPreferenceIncludesThisPackage = false;
@GuardedBy("mLock")
- final List<RoutingSessionInfo> mReleasingSessions = new ArrayList<>();
+ private final List<RoutingSessionInfo> mReleasingSessions = new ArrayList<>();
+
+ // We keep pending requests for transfers and sessions creation separately because transfers
+ // don't have an associated request id and session creations don't have a session id.
+ @GuardedBy("mLock")
+ private final LongSparseArray<SessionCreationOrTransferRequest>
+ mRequestIdToSessionCreationRequest;
+
+ @GuardedBy("mLock")
+ private final Map<String, SessionCreationOrTransferRequest> mSessionOriginalIdToTransferRequest;
MediaRoute2ProviderServiceProxy(
@NonNull Context context,
@@ -87,6 +100,8 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
int userId) {
super(componentName);
mContext = Objects.requireNonNull(context, "Context must not be null.");
+ mRequestIdToSessionCreationRequest = new LongSparseArray<>();
+ mSessionOriginalIdToTransferRequest = new HashMap<>();
mIsSelfScanOnlyProvider = isSelfScanOnlyProvider;
mUserId = userId;
mHandler = new Handler(looper);
@@ -109,6 +124,18 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
@NonNull UserHandle transferInitiatorUserHandle,
@NonNull String transferInitiatorPackageName) {
if (mConnectionReady) {
+ if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+ synchronized (mLock) {
+ mRequestIdToSessionCreationRequest.put(
+ requestId,
+ new SessionCreationOrTransferRequest(
+ requestId,
+ routeOriginalId,
+ transferReason,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName));
+ }
+ }
mActiveConnection.requestCreateSession(
requestId, packageName, routeOriginalId, sessionHints);
updateBinding();
@@ -118,6 +145,11 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
@Override
public void releaseSession(long requestId, String sessionId) {
if (mConnectionReady) {
+ if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+ synchronized (mLock) {
+ mSessionOriginalIdToTransferRequest.remove(sessionId);
+ }
+ }
mActiveConnection.releaseSession(requestId, sessionId);
updateBinding();
}
@@ -158,6 +190,18 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
String routeOriginalId,
@RoutingSessionInfo.TransferReason int transferReason) {
if (mConnectionReady) {
+ if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+ synchronized (mLock) {
+ mSessionOriginalIdToTransferRequest.put(
+ sessionOriginalId,
+ new SessionCreationOrTransferRequest(
+ requestId,
+ routeOriginalId,
+ transferReason,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName));
+ }
+ }
mActiveConnection.transferToRoute(requestId, sessionOriginalId, routeOriginalId);
}
}
@@ -384,6 +428,11 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
String newSessionId = newSession.getId();
synchronized (mLock) {
+ if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+ newSession =
+ createSessionWithPopulatedTransferInitiationDataLocked(
+ requestId, /* oldSessionInfo= */ null, newSession);
+ }
if (mSessionInfos.stream()
.anyMatch(session -> TextUtils.equals(session.getId(), newSessionId))
|| mReleasingSessions.stream()
@@ -397,6 +446,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
mCallback.onSessionCreated(this, requestId, newSession);
}
+ @GuardedBy("mLock")
private int findSessionByIdLocked(RoutingSessionInfo session) {
for (int i = 0; i < mSessionInfos.size(); i++) {
if (TextUtils.equals(mSessionInfos.get(i).getId(), session.getId())) {
@@ -417,7 +467,6 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
for (RoutingSessionInfo session : sessions) {
if (session == null) continue;
session = assignProviderIdForSession(session);
-
int sourceIndex = findSessionByIdLocked(session);
if (sourceIndex < 0) {
mSessionInfos.add(targetIndex++, session);
@@ -425,6 +474,12 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
} else if (sourceIndex < targetIndex) {
Slog.w(TAG, "Ignoring duplicate session ID: " + session.getId());
} else {
+ if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+ RoutingSessionInfo oldSessionInfo = mSessionInfos.get(sourceIndex);
+ session =
+ createSessionWithPopulatedTransferInitiationDataLocked(
+ REQUEST_ID_NONE, oldSessionInfo, session);
+ }
mSessionInfos.set(sourceIndex, session);
Collections.swap(mSessionInfos, sourceIndex, targetIndex++);
dispatchSessionUpdated(session);
@@ -432,11 +487,65 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
}
for (int i = mSessionInfos.size() - 1; i >= targetIndex; i--) {
RoutingSessionInfo releasedSession = mSessionInfos.remove(i);
+ mSessionOriginalIdToTransferRequest.remove(releasedSession.getId());
dispatchSessionReleased(releasedSession);
}
}
}
+ /**
+ * Returns a {@link RoutingSessionInfo} with transfer initiation data from the given {@code
+ * oldSessionInfo}, and any pending transfer or session creation requests.
+ */
+ @GuardedBy("mLock")
+ private RoutingSessionInfo createSessionWithPopulatedTransferInitiationDataLocked(
+ long requestId,
+ @Nullable RoutingSessionInfo oldSessionInfo,
+ @NonNull RoutingSessionInfo newSessionInfo) {
+ SessionCreationOrTransferRequest pendingRequest =
+ oldSessionInfo != null
+ ? mSessionOriginalIdToTransferRequest.get(newSessionInfo.getOriginalId())
+ : mRequestIdToSessionCreationRequest.get(requestId);
+ boolean pendingTargetRouteInSelectedRoutes =
+ pendingRequest != null
+ && pendingRequest.isTargetRouteIdInRouteUniqueIdList(
+ newSessionInfo.getSelectedRoutes());
+ boolean pendingTargetRouteInTransferableRoutes =
+ pendingRequest != null
+ && pendingRequest.isTargetRouteIdInRouteUniqueIdList(
+ newSessionInfo.getTransferableRoutes());
+
+ int transferReason;
+ UserHandle transferInitiatorUserHandle;
+ String transferInitiatorPackageName;
+ if (pendingTargetRouteInSelectedRoutes) { // The pending request has been satisfied.
+ transferReason = pendingRequest.mTransferReason;
+ transferInitiatorUserHandle = pendingRequest.mTransferInitiatorUserHandle;
+ transferInitiatorPackageName = pendingRequest.mTransferInitiatorPackageName;
+ } else if (oldSessionInfo != null) {
+ // No pending request, we copy the values from the old session object.
+ transferReason = oldSessionInfo.getTransferReason();
+ transferInitiatorUserHandle = oldSessionInfo.getTransferInitiatorUserHandle();
+ transferInitiatorPackageName = oldSessionInfo.getTransferInitiatorPackageName();
+ } else { // There's a new session with no associated creation request, we use defaults.
+ transferReason = RoutingSessionInfo.TRANSFER_REASON_FALLBACK;
+ transferInitiatorUserHandle = UserHandle.of(mUserId);
+ transferInitiatorPackageName = newSessionInfo.getClientPackageName();
+ }
+ if (pendingTargetRouteInSelectedRoutes || !pendingTargetRouteInTransferableRoutes) {
+ // The pending request has been satisfied, or the target route is no longer available.
+ if (oldSessionInfo != null) {
+ mSessionOriginalIdToTransferRequest.remove(newSessionInfo.getId());
+ } else if (pendingRequest != null) {
+ mRequestIdToSessionCreationRequest.remove(pendingRequest.mRequestId);
+ }
+ }
+ return new RoutingSessionInfo.Builder(newSessionInfo)
+ .setTransferInitiator(transferInitiatorUserHandle, transferInitiatorPackageName)
+ .setTransferReason(transferReason)
+ .build();
+ }
+
private void onSessionReleased(Connection connection, RoutingSessionInfo releasedSession) {
if (mActiveConnection != connection) {
return;
@@ -450,6 +559,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
boolean found = false;
synchronized (mLock) {
+ mSessionOriginalIdToTransferRequest.remove(releasedSession.getId());
for (RoutingSessionInfo session : mSessionInfos) {
if (TextUtils.equals(session.getId(), releasedSession.getId())) {
mSessionInfos.remove(session);
@@ -498,6 +608,11 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
}
private void onRequestFailed(Connection connection, long requestId, int reason) {
+ if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+ synchronized (mLock) {
+ mRequestIdToSessionCreationRequest.remove(requestId);
+ }
+ }
if (mActiveConnection != connection) {
return;
}
@@ -522,18 +637,29 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
}
mSessionInfos.clear();
mReleasingSessions.clear();
+ mRequestIdToSessionCreationRequest.clear();
+ mSessionOriginalIdToTransferRequest.clear();
}
}
}
@Override
protected String getDebugString() {
+ int pendingSessionCreationCount;
+ int pendingTransferCount;
+ synchronized (mLock) {
+ pendingSessionCreationCount = mRequestIdToSessionCreationRequest.size();
+ pendingTransferCount = mSessionOriginalIdToTransferRequest.size();
+ }
return TextUtils.formatSimple(
- "ProviderServiceProxy - package: %s, bound: %b, connection (active:%b, ready:%b)",
+ "ProviderServiceProxy - package: %s, bound: %b, connection (active:%b, ready:%b), "
+ + "pending (session creations: %d, transfers: %d)",
mComponentName.getPackageName(),
mBound,
mActiveConnection != null,
- mConnectionReady);
+ mConnectionReady,
+ pendingSessionCreationCount,
+ pendingTransferCount);
}
private final class Connection implements DeathRecipient {
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 6b409ee6f482..8c6273ce959f 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -443,7 +443,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
boolean isTransferringToTheSelectedRoute =
mPendingTransferRequest.isTargetRoute(selectedRoute);
boolean canBePotentiallyTransferred =
- mPendingTransferRequest.isTargetRouteIdInList(transferableRoutes);
+ mPendingTransferRequest.isTargetRouteIdInRouteOriginalIdList(
+ transferableRoutes);
if (isTransferringToTheSelectedRoute) {
transferReason = mPendingTransferRequest.mTransferReason;