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

Commit 73c6cb34 authored by Santiago Seifert's avatar Santiago Seifert
Browse files

Implement system session lifecycle callbacks

This CL adds the concept of a global session (note that a default
session exists already), which is the session that's applicable to all
apps unless they have an overriding session (which is a session that
overrides the global session and affects one app specifically).

This means a new capability of the system provider to have one routing
session by default, and then apply specific routing sessions to specific
apps.

Bug: b/362507305
Test: atest CtsMediaBetterTogetherTestCases:android.media.bettertogether.cts.SystemMediaRoutingTest --rerun-until-failure
Flag: com.android.media.flags.enable_mirroring_in_media_router_2
Change-Id: I6c25f75feb200c86f67e569b07cc6cbef53ac8a3
parent c9617d35
Loading
Loading
Loading
Loading
+53 −13
Original line number Diff line number Diff line
@@ -226,6 +226,10 @@ public abstract class MediaRoute2ProviderService extends Service {
    @GuardedBy("mSessionLock")
    private final ArrayMap<String, MediaStreams> mOngoingMediaStreams = new ArrayMap<>();

    @GuardedBy("mSessionLock")
    private final ArrayMap<String, RoutingSessionInfo> mPendingSystemSessionReleases =
            new ArrayMap<>();

    public MediaRoute2ProviderService() {
        mHandler = new Handler(Looper.getMainLooper());
    }
@@ -419,7 +423,7 @@ public abstract class MediaRoute2ProviderService extends Service {
        }

        AudioFormat audioFormat = formats.mAudioFormat;
        var mediaStreamsBuilder = new MediaStreams.Builder();
        var mediaStreamsBuilder = new MediaStreams.Builder(sessionInfo);
        if (audioFormat != null) {
            populateAudioStream(audioFormat, uid, mediaStreamsBuilder);
        }
@@ -526,8 +530,14 @@ public abstract class MediaRoute2ProviderService extends Service {
        RoutingSessionInfo sessionInfo;
        synchronized (mSessionLock) {
            sessionInfo = mSessionInfos.remove(sessionId);
            maybeReleaseMediaStreams(sessionId);

            if (Flags.enableMirroringInMediaRouter2()) {
                if (sessionInfo == null) {
                    sessionInfo = maybeReleaseMediaStreams(sessionId);
                }
                if (sessionInfo == null) {
                    sessionInfo = mPendingSystemSessionReleases.remove(sessionId);
                }
            }
            if (sessionInfo == null) {
                Log.w(TAG, "notifySessionReleased: Ignoring unknown session info.");
                return;
@@ -544,20 +554,26 @@ public abstract class MediaRoute2ProviderService extends Service {
        }
    }

    /** Releases any system media routing resources associated with the given {@code sessionId}. */
    private boolean maybeReleaseMediaStreams(String sessionId) {
    /**
     * Releases any system media routing resources associated with the given {@code sessionId}.
     *
     * @return The {@link RoutingSessionInfo} that corresponds to the released media streams, or
     *     null if no streams were released.
     */
    @Nullable
    private RoutingSessionInfo maybeReleaseMediaStreams(String sessionId) {
        if (!Flags.enableMirroringInMediaRouter2()) {
            return false;
            return null;
        }
        synchronized (mSessionLock) {
            var streams = mOngoingMediaStreams.remove(sessionId);
            if (streams != null) {
                releaseAudioStream(streams.mAudioPolicy, streams.mAudioRecord);
                // TODO: b/380431086: Release the video stream once implemented.
                return true;
                return streams.mSessionInfo;
            }
        }
        return false;
        return null;
    }

    // We cannot reach the code that requires MODIFY_AUDIO_ROUTING without holding it.
@@ -1026,12 +1042,16 @@ public abstract class MediaRoute2ProviderService extends Service {
            if (!checkCallerIsSystem()) {
                return;
            }
            // We proactively release the system media routing once the system requests it, to
            // ensure it happens immediately.
            if (!maybeReleaseMediaStreams(sessionId)
                    && !checkSessionIdIsValid(sessionId, "releaseSession")) {
            synchronized (mSessionLock) {
                // We proactively release the system media routing session resources when the
                // system requests it, to ensure it happens immediately.
                RoutingSessionInfo releasedSession = maybeReleaseMediaStreams(sessionId);
                if (releasedSession != null) {
                    mPendingSystemSessionReleases.put(sessionId, releasedSession);
                } else if (!checkSessionIdIsValid(sessionId, "releaseSession")) {
                    return;
                }
            }

            addRequestId(requestId);
            mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onReleaseSession,
@@ -1054,9 +1074,19 @@ public abstract class MediaRoute2ProviderService extends Service {
        @Nullable private final AudioPolicy mAudioPolicy;
        @Nullable private final AudioRecord mAudioRecord;

        /**
         * Holds the last {@link RoutingSessionInfo} associated with these streams.
         *
         * @hide
         */
        @GuardedBy("MediaRoute2ProviderService.this.mSessionLock")
        @NonNull
        private RoutingSessionInfo mSessionInfo;

        // TODO: b/380431086: Add the video equivalent.

        private MediaStreams(Builder builder) {
            this.mSessionInfo = builder.mSessionInfo;
            this.mAudioPolicy = builder.mAudioPolicy;
            this.mAudioRecord = builder.mAudioRecord;
        }
@@ -1077,9 +1107,19 @@ public abstract class MediaRoute2ProviderService extends Service {
         */
        public static final class Builder {

            @NonNull private RoutingSessionInfo mSessionInfo;
            @Nullable private AudioPolicy mAudioPolicy;
            @Nullable private AudioRecord mAudioRecord;

            /**
             * Constructor.
             *
             * @param sessionInfo The {@link RoutingSessionInfo} associated with these streams.
             */
            Builder(@NonNull RoutingSessionInfo sessionInfo) {
                mSessionInfo = requireNonNull(sessionInfo);
            }

            /** Populates system media audio-related structures. */
            public Builder setAudioStream(
                    @NonNull AudioPolicy audioPolicy, @NonNull AudioRecord audioRecord) {
+23 −2
Original line number Diff line number Diff line
@@ -179,8 +179,29 @@ abstract class MediaRoute2Provider {
        void onProviderStateChanged(@Nullable MediaRoute2Provider provider);
        void onSessionCreated(@NonNull MediaRoute2Provider provider,
                long requestId, @Nullable RoutingSessionInfo sessionInfo);
        void onSessionUpdated(@NonNull MediaRoute2Provider provider,
                @NonNull RoutingSessionInfo sessionInfo);

        /**
         * Called when there's a session info change.
         *
         * <p>If the provided {@code sessionInfo} has a null {@link
         * RoutingSessionInfo#getClientPackageName()}, that means that it's applicable to all
         * packages. We call this type of routing session "global". This is typically used for
         * system provided {@link RoutingSessionInfo}. However, some applications may be exempted
         * from the global routing sessions, because their media is being routed using a session
         * different from the global routing session.
         *
         * @param provider The provider that owns the session that changed.
         * @param sessionInfo The new {@link RoutingSessionInfo}.
         * @param packageNamesWithRoutingSessionOverrides The names of packages that are not
         *     affected by global session changes. This set may only be non-empty when the {@code
         *     sessionInfo} is for the global session, and therefore has no {@link
         *     RoutingSessionInfo#getClientPackageName()}.
         */
        void onSessionUpdated(
                @NonNull MediaRoute2Provider provider,
                @NonNull RoutingSessionInfo sessionInfo,
                Set<String> packageNamesWithRoutingSessionOverrides);

        void onSessionReleased(@NonNull MediaRoute2Provider provider,
                @NonNull RoutingSessionInfo sessionInfo);

+20 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.media;

import static android.media.MediaRoute2ProviderService.REASON_REJECTED;
import static android.media.MediaRoute2ProviderService.REQUEST_ID_NONE;

import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -499,6 +500,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
        synchronized (mLock) {
            var systemMediaSessionCallback = mRequestIdToSystemSessionRequest.get(requestId);
            if (systemMediaSessionCallback != null) {
                mRequestIdToSystemSessionRequest.remove(requestId);
                mSystemSessionCallbacks.put(newSession.getOriginalId(), systemMediaSessionCallback);
                systemMediaSessionCallback.onSessionUpdate(newSession);
                return;
@@ -674,7 +676,11 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {

    private void dispatchSessionUpdated(RoutingSessionInfo session) {
        mHandler.sendMessage(
                obtainMessage(mCallback::onSessionUpdated, this, session));
                obtainMessage(
                        mCallback::onSessionUpdated,
                        this,
                        session,
                        /* packageNamesWithRoutingSessionOverrides= */ Set.of()));
    }

    private void dispatchSessionReleased(RoutingSessionInfo session) {
@@ -717,6 +723,19 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
                for (RoutingSessionInfo sessionInfo : mSessionInfos) {
                    mCallback.onSessionReleased(this, sessionInfo);
                }
                if (Flags.enableMirroringInMediaRouter2()) {
                    for (var callback : mSystemSessionCallbacks.values()) {
                        callback.onSessionReleased();
                    }
                    mSystemSessionCallbacks.clear();
                    int requestsSize = mRequestIdToSystemSessionRequest.size();
                    for (int i = 0; i < requestsSize; i++) {
                        var callback = mRequestIdToSystemSessionRequest.valueAt(i);
                        var requestId = mRequestIdToSystemSessionRequest.keyAt(i);
                        callback.onRequestFailed(requestId, REASON_REJECTED);
                    }
                    mSystemSessionCallbacks.clear();
                }
                mSessionInfos.clear();
                mReleasingSessions.clear();
                mRequestIdToSessionCreationRequest.clear();
+34 −6
Original line number Diff line number Diff line
@@ -2634,10 +2634,17 @@ class MediaRouter2ServiceImpl {
        }

        @Override
        public void onSessionUpdated(@NonNull MediaRoute2Provider provider,
                @NonNull RoutingSessionInfo sessionInfo) {
            sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionInfoChangedOnHandler,
                    this, provider, sessionInfo));
        public void onSessionUpdated(
                @NonNull MediaRoute2Provider provider,
                @NonNull RoutingSessionInfo sessionInfo,
                Set<String> packageNamesWithRoutingSessionOverrides) {
            sendMessage(
                    PooledLambda.obtainMessage(
                            UserHandler::onSessionInfoChangedOnHandler,
                            this,
                            provider,
                            sessionInfo,
                            packageNamesWithRoutingSessionOverrides));
        }

        @Override
@@ -3148,10 +3155,31 @@ class MediaRouter2ServiceImpl {
                    toOriginalRequestId(uniqueRequestId), sessionInfo);
        }

        private void onSessionInfoChangedOnHandler(@NonNull MediaRoute2Provider provider,
                @NonNull RoutingSessionInfo sessionInfo) {
        /**
         * Implementation of {@link MediaRoute2Provider.Callback#onSessionUpdated}.
         *
         * <p>Must run on the thread that corresponds to this {@link UserHandler}.
         */
        private void onSessionInfoChangedOnHandler(
                @NonNull MediaRoute2Provider provider,
                @NonNull RoutingSessionInfo sessionInfo,
                Set<String> packageNamesWithRoutingSessionOverrides) {
            List<ManagerRecord> managers = getManagerRecords();
            for (ManagerRecord manager : managers) {
                if (Flags.enableMirroringInMediaRouter2()) {
                    String targetPackageName = manager.mTargetPackageName;
                    boolean skipDueToOverride =
                            targetPackageName != null
                                    && packageNamesWithRoutingSessionOverrides.contains(
                                            targetPackageName);
                    boolean sessionIsForTargetPackage =
                            TextUtils.isEmpty(sessionInfo.getClientPackageName()) // is global.
                                    || TextUtils.equals(
                                            targetPackageName, sessionInfo.getClientPackageName());
                    if (skipDueToOverride || !sessionIsForTargetPackage) {
                        continue;
                    }
                }
                manager.notifySessionUpdated(sessionInfo);
            }

+8 −7
Original line number Diff line number Diff line
@@ -62,7 +62,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
    static final String SYSTEM_SESSION_ID = "SYSTEM_SESSION";

    private final AudioManager mAudioManager;
    private final Handler mHandler;
    protected final Handler mHandler;
    private final Context mContext;
    private final UserHandle mUser;

@@ -116,7 +116,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
                        () -> {
                            publishProviderState();
                            if (updateSessionInfosIfNeeded()) {
                                notifySessionInfoUpdated();
                                notifyGlobalSessionInfoUpdated();
                            }
                        });

@@ -129,7 +129,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
                                        () -> {
                                            publishProviderState();
                                            if (updateSessionInfosIfNeeded()) {
                                                notifySessionInfoUpdated();
                                                notifyGlobalSessionInfoUpdated();
                                            }
                                        }));
    }
@@ -161,7 +161,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
    public void setCallback(Callback callback) {
        super.setCallback(callback);
        notifyProviderState();
        notifySessionInfoUpdated();
        notifyGlobalSessionInfoUpdated();
    }

    @Override
@@ -296,7 +296,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {

        if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()
                && updateSessionInfosIfNeeded()) {
            notifySessionInfoUpdated();
            notifyGlobalSessionInfoUpdated();
        }
    }

@@ -643,7 +643,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
        notifyProviderState();
    }

    void notifySessionInfoUpdated() {
    void notifyGlobalSessionInfoUpdated() {
        if (mCallback == null) {
            return;
        }
@@ -656,7 +656,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
            sessionInfo = mSessionInfos.get(0);
        }

        mCallback.onSessionUpdated(this, sessionInfo);
        mCallback.onSessionUpdated(
                this, sessionInfo, /* packageNamesWithRoutingSessionOverrides= */ Set.of());
    }

    @Override
Loading