Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2a4670c2 authored by Santiago Seifert's avatar Santiago Seifert
Browse files

Populate transfer initiation data for remote routes

Bug: b/319651986
Test: atest CtsMediaBetterTogetherTestCases CtsMediaHostTestCases
Flag: com.android.media.flags.enable_built_in_speaker_route_suitability_statuses
Change-Id: I35ccce43184776b994cfac71e5aeb5325da05acd
parent e286dbe7
Loading
Loading
Loading
Loading
+18 −2
Original line number Diff line number Diff line
@@ -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);
        }
    }
}
+130 −4
Original line number Diff line number Diff line
@@ -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 {
+2 −1
Original line number Diff line number Diff line
@@ -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;