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

Commit 5e2ebbd7 authored by Santiago Seifert's avatar Santiago Seifert
Browse files

Hide MediaSession details from MediaSessions clients

This will enable us to use MediaSessions for other streams that also
support volume adjustment (e.g. mirroring sessions), and not just media
sessions. We can later consider renaming the MediaSessions class to
something like VolumeSessions.

Bug: b/362507305
Test: Presubmit, build and verifying regular volume adjustment
Test: funcionality while casting.
Test: adb shell dumpsys activity service SystemUI | grep -A 100 "VolumeDialogControllerImpl state:"
Flag: EXEMPT Non-functional change
Change-Id: I51739d930d7cdb5b93c58394c699d4b6c3c8567c
parent 1f01fadb
Loading
Loading
Loading
Loading
+43 −21
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@ import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.PackageManager
import android.media.MediaMetadata
import android.media.MediaMetadata
import android.media.session.MediaController
import android.media.session.MediaController
import android.media.session.MediaController.PlaybackInfo
import android.media.session.MediaSession
import android.media.session.MediaSession
import android.media.session.MediaSessionManager
import android.media.session.MediaSessionManager
import android.media.session.PlaybackState
import android.media.session.PlaybackState
@@ -98,16 +99,22 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) {
    }
    }


    /** Set volume `level` to remote media `token` */
    /** Set volume `level` to remote media `token` */
    fun setVolume(token: MediaSession.Token, level: Int) {
    fun setVolume(sessionId: SessionId, volumeLevel: Int) {
        when (sessionId) {
            is SessionId.Media -> setMediaSessionVolume(sessionId.token, volumeLevel)
        }
    }

    private fun setMediaSessionVolume(token: MediaSession.Token, volumeLevel: Int) {
        val record = mRecords[token]
        val record = mRecords[token]
        if (record == null) {
        if (record == null) {
            Log.w(TAG, "setVolume: No record found for token $token")
            Log.w(TAG, "setVolume: No record found for token $token")
            return
            return
        }
        }
        if (D.BUG) {
        if (D.BUG) {
            Log.d(TAG, "Setting level to $level")
            Log.d(TAG, "Setting level to $volumeLevel")
        }
        }
        record.controller.setVolumeTo(level, 0)
        record.controller.setVolumeTo(volumeLevel, 0)
    }
    }


    private fun onRemoteVolumeChangedH(sessionToken: MediaSession.Token, flags: Int) {
    private fun onRemoteVolumeChangedH(sessionToken: MediaSession.Token, flags: Int) {
@@ -122,7 +129,7 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) {
            )
            )
        }
        }
        val token = controller.sessionToken
        val token = controller.sessionToken
        mCallbacks.onRemoteVolumeChanged(token, flags)
        mCallbacks.onRemoteVolumeChanged(SessionId.from(token), flags)
    }
    }


    private fun onUpdateRemoteSessionListH(sessionToken: MediaSession.Token?) {
    private fun onUpdateRemoteSessionListH(sessionToken: MediaSession.Token?) {
@@ -158,7 +165,7 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) {
                controller.registerCallback(record, mHandler)
                controller.registerCallback(record, mHandler)
            }
            }
            val record = mRecords[token]
            val record = mRecords[token]
            val remote = isRemote(playbackInfo)
            val remote = playbackInfo.isRemote()
            if (remote) {
            if (remote) {
                updateRemoteH(token, record!!.name, playbackInfo)
                updateRemoteH(token, record!!.name, playbackInfo)
                record.sentRemote = true
                record.sentRemote = true
@@ -172,7 +179,7 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) {
                Log.d(TAG, "Removing " + record.name + " sentRemote=" + record.sentRemote)
                Log.d(TAG, "Removing " + record.name + " sentRemote=" + record.sentRemote)
            }
            }
            if (record.sentRemote) {
            if (record.sentRemote) {
                mCallbacks.onRemoteRemoved(token)
                mCallbacks.onRemoteRemoved(SessionId.from(token))
                record.sentRemote = false
                record.sentRemote = false
            }
            }
        }
        }
@@ -213,8 +220,8 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) {
    private fun updateRemoteH(
    private fun updateRemoteH(
        token: MediaSession.Token,
        token: MediaSession.Token,
        name: String?,
        name: String?,
        pi: MediaController.PlaybackInfo,
        playbackInfo: PlaybackInfo,
    ) = mCallbacks.onRemoteUpdate(token, name, pi)
    ) = mCallbacks.onRemoteUpdate(SessionId.from(token), name, VolumeInfo.from(playbackInfo))


    private inner class MediaControllerRecord(val controller: MediaController) :
    private inner class MediaControllerRecord(val controller: MediaController) :
        MediaController.Callback() {
        MediaController.Callback() {
@@ -225,7 +232,7 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) {
            return method + " " + controller.packageName + " "
            return method + " " + controller.packageName + " "
        }
        }


        override fun onAudioInfoChanged(info: MediaController.PlaybackInfo) {
        override fun onAudioInfoChanged(info: PlaybackInfo) {
            if (D.BUG) {
            if (D.BUG) {
                Log.d(
                Log.d(
                    TAG,
                    TAG,
@@ -235,9 +242,9 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) {
                        sentRemote),
                        sentRemote),
                )
                )
            }
            }
            val remote = isRemote(info)
            val remote = info.isRemote()
            if (!remote && sentRemote) {
            if (!remote && sentRemote) {
                mCallbacks.onRemoteRemoved(controller.sessionToken)
                mCallbacks.onRemoteRemoved(SessionId.from(controller.sessionToken))
                sentRemote = false
                sentRemote = false
            } else if (remote) {
            } else if (remote) {
                updateRemoteH(controller.sessionToken, name, info)
                updateRemoteH(controller.sessionToken, name, info)
@@ -301,20 +308,36 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) {
        }
        }
    }
    }


    /** Opaque id for ongoing sessions that support volume adjustment. */
    sealed interface SessionId {

        companion object {
            fun from(token: MediaSession.Token) = Media(token)
        }

        data class Media(val token: MediaSession.Token) : SessionId
    }

    /** Holds session volume information. */
    data class VolumeInfo(val currentVolume: Int, val maxVolume: Int) {

        companion object {

            fun from(playbackInfo: PlaybackInfo) =
                VolumeInfo(playbackInfo.currentVolume, playbackInfo.maxVolume)
        }
    }

    /** Callback for remote media sessions */
    /** Callback for remote media sessions */
    interface Callbacks {
    interface Callbacks {
        /** Invoked when remote media session is updated */
        /** Invoked when remote media session is updated */
        fun onRemoteUpdate(
        fun onRemoteUpdate(token: SessionId?, name: String?, volumeInfo: VolumeInfo?)
            token: MediaSession.Token?,
            name: String?,
            pi: MediaController.PlaybackInfo?,
        )


        /** Invoked when remote media session is removed */
        /** Invoked when remote media session is removed */
        fun onRemoteRemoved(token: MediaSession.Token?)
        fun onRemoteRemoved(token: SessionId?)


        /** Invoked when remote volume is changed */
        /** Invoked when remote volume is changed */
        fun onRemoteVolumeChanged(token: MediaSession.Token?, flags: Int)
        fun onRemoteVolumeChanged(token: SessionId?, flags: Int)
    }
    }


    companion object {
    companion object {
@@ -325,12 +348,11 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) {
        const val UPDATE_REMOTE_SESSION_LIST: Int = 3
        const val UPDATE_REMOTE_SESSION_LIST: Int = 3


        private const val USE_SERVICE_LABEL = false
        private const val USE_SERVICE_LABEL = false

        private fun isRemote(pi: MediaController.PlaybackInfo?): Boolean =
            pi != null && pi.playbackType == MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE
    }
    }
}
}


private fun PlaybackInfo?.isRemote() = this?.playbackType == PlaybackInfo.PLAYBACK_TYPE_REMOTE

private fun MediaController.dump(n: Int, writer: PrintWriter) {
private fun MediaController.dump(n: Int, writer: PrintWriter) {
    writer.println("  Controller $n: $packageName")
    writer.println("  Controller $n: $packageName")


+5 −2
Original line number Original line Diff line number Diff line
@@ -47,6 +47,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.filters.SmallTest;


import com.android.keyguard.TestScopeProvider;
import com.android.keyguard.TestScopeProvider;
import com.android.settingslib.volume.MediaSessions;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestCaseExtKt;
import com.android.systemui.SysuiTestCaseExtKt;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -268,13 +269,15 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
    @Test
    @Test
    public void testOnRemoteVolumeChanged_newStream_noNullPointer() {
    public void testOnRemoteVolumeChanged_newStream_noNullPointer() {
        MediaSession.Token token = new MediaSession.Token(Process.myUid(), null);
        MediaSession.Token token = new MediaSession.Token(Process.myUid(), null);
        mVolumeController.mMediaSessionsCallbacksW.onRemoteVolumeChanged(token, 0);
        var sessionId = MediaSessions.SessionId.Companion.from(token);
        mVolumeController.mMediaSessionsCallbacksW.onRemoteVolumeChanged(sessionId, 0);
    }
    }


    @Test
    @Test
    public void testOnRemoteRemove_newStream_noNullPointer() {
    public void testOnRemoteRemove_newStream_noNullPointer() {
        MediaSession.Token token = new MediaSession.Token(Process.myUid(), null);
        MediaSession.Token token = new MediaSession.Token(Process.myUid(), null);
        mVolumeController.mMediaSessionsCallbacksW.onRemoteRemoved(token);
        var sessionId = MediaSessions.SessionId.Companion.from(token);
        mVolumeController.mMediaSessionsCallbacksW.onRemoteRemoved(sessionId);
    }
    }


    @Test
    @Test
+17 −16
Original line number Original line Diff line number Diff line
@@ -37,8 +37,6 @@ import android.media.IAudioService;
import android.media.IVolumeController;
import android.media.IVolumeController;
import android.media.MediaRouter2Manager;
import android.media.MediaRouter2Manager;
import android.media.VolumePolicy;
import android.media.VolumePolicy;
import android.media.session.MediaController.PlaybackInfo;
import android.media.session.MediaSession.Token;
import android.net.Uri;
import android.net.Uri;
import android.os.Handler;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.HandlerExecutor;
@@ -61,6 +59,7 @@ import androidx.lifecycle.Observer;


import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.GuardedBy;
import com.android.settingslib.volume.MediaSessions;
import com.android.settingslib.volume.MediaSessions;
import com.android.settingslib.volume.MediaSessions.SessionId;
import com.android.systemui.Dumpable;
import com.android.systemui.Dumpable;
import com.android.systemui.Flags;
import com.android.systemui.Flags;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -1402,12 +1401,13 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
    }
    }


    protected final class MediaSessionsCallbacks implements MediaSessions.Callbacks {
    protected final class MediaSessionsCallbacks implements MediaSessions.Callbacks {
        private final HashMap<Token, Integer> mRemoteStreams = new HashMap<>();
        private final HashMap<SessionId, Integer> mRemoteStreams = new HashMap<>();


        private int mNextStream = DYNAMIC_STREAM_REMOTE_START_INDEX;
        private int mNextStream = DYNAMIC_STREAM_REMOTE_START_INDEX;


        @Override
        @Override
        public void onRemoteUpdate(Token token, String name, PlaybackInfo pi) {
        public void onRemoteUpdate(
                    SessionId token, String name, MediaSessions.VolumeInfo volumeInfo) {
                addStream(token, "onRemoteUpdate");
                addStream(token, "onRemoteUpdate");


                int stream = 0;
                int stream = 0;
@@ -1415,14 +1415,15 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
                    stream = mRemoteStreams.get(token);
                    stream = mRemoteStreams.get(token);
                }
                }
                Slog.d(TAG,
                Slog.d(TAG,
                        "onRemoteUpdate: stream: " + stream + " volume: " + pi.getCurrentVolume());
                        "onRemoteUpdate: stream: "
                                + stream + " volume: " + volumeInfo.getCurrentVolume());
                boolean changed = mState.states.indexOfKey(stream) < 0;
                boolean changed = mState.states.indexOfKey(stream) < 0;
                final StreamState ss = streamStateW(stream);
                final StreamState ss = streamStateW(stream);
                ss.dynamic = true;
                ss.dynamic = true;
                ss.levelMin = 0;
                ss.levelMin = 0;
                ss.levelMax = pi.getMaxVolume();
                ss.levelMax = volumeInfo.getMaxVolume();
                if (ss.level != pi.getCurrentVolume()) {
                if (ss.level != volumeInfo.getCurrentVolume()) {
                    ss.level = pi.getCurrentVolume();
                    ss.level = volumeInfo.getCurrentVolume();
                    changed = true;
                    changed = true;
                }
                }
                if (!Objects.equals(ss.remoteLabel, name)) {
                if (!Objects.equals(ss.remoteLabel, name)) {
@@ -1437,11 +1438,11 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
        }
        }


        @Override
        @Override
        public void onRemoteVolumeChanged(Token token, int flags) {
        public void onRemoteVolumeChanged(SessionId sessionId, int flags) {
                addStream(token, "onRemoteVolumeChanged");
                addStream(sessionId, "onRemoteVolumeChanged");
                int stream = 0;
                int stream = 0;
                synchronized (mRemoteStreams) {
                synchronized (mRemoteStreams) {
                    stream = mRemoteStreams.get(token);
                    stream = mRemoteStreams.get(sessionId);
                }
                }
                final boolean showUI = shouldShowUI(flags);
                final boolean showUI = shouldShowUI(flags);
                Slog.d(TAG, "onRemoteVolumeChanged: stream: " + stream + " showui? " + showUI);
                Slog.d(TAG, "onRemoteVolumeChanged: stream: " + stream + " showui? " + showUI);
@@ -1459,7 +1460,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
        }
        }


        @Override
        @Override
        public void onRemoteRemoved(Token token) {
        public void onRemoteRemoved(SessionId token) {
            int stream;
            int stream;
            synchronized (mRemoteStreams) {
            synchronized (mRemoteStreams) {
                if (!mRemoteStreams.containsKey(token)) {
                if (!mRemoteStreams.containsKey(token)) {
@@ -1480,7 +1481,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
        }
        }


        public void setStreamVolume(int stream, int level) {
        public void setStreamVolume(int stream, int level) {
            final Token token = findToken(stream);
            final SessionId token = findToken(stream);
            if (token == null) {
            if (token == null) {
                Log.w(TAG, "setStreamVolume: No token found for stream: " + stream);
                Log.w(TAG, "setStreamVolume: No token found for stream: " + stream);
                return;
                return;
@@ -1488,9 +1489,9 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
            mMediaSessions.setVolume(token, level);
            mMediaSessions.setVolume(token, level);
        }
        }


        private Token findToken(int stream) {
        private SessionId findToken(int stream) {
            synchronized (mRemoteStreams) {
            synchronized (mRemoteStreams) {
                for (Map.Entry<Token, Integer> entry : mRemoteStreams.entrySet()) {
                for (Map.Entry<SessionId, Integer> entry : mRemoteStreams.entrySet()) {
                    if (entry.getValue().equals(stream)) {
                    if (entry.getValue().equals(stream)) {
                        return entry.getKey();
                        return entry.getKey();
                    }
                    }
@@ -1499,7 +1500,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
            return null;
            return null;
        }
        }


        private void addStream(Token token, String triggeringMethod) {
        private void addStream(SessionId token, String triggeringMethod) {
            synchronized (mRemoteStreams) {
            synchronized (mRemoteStreams) {
                if (!mRemoteStreams.containsKey(token)) {
                if (!mRemoteStreams.containsKey(token)) {
                    mRemoteStreams.put(token, mNextStream);
                    mRemoteStreams.put(token, mNextStream);