diff options
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; |