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

Commit f577dab2 authored by Santiago Seifert's avatar Santiago Seifert
Browse files

Make MediaSessions listen for system media session overrides

This enables the sys ui volume slider to show up for volume adjustment
events in mirroring sessions. We still need to change routing session
updates to only show the slider as a result of a hardware volume key
press.

Bug: b/396394220
Test: atest CtsMediaRouterTestCases
Flag: com.android.media.flags.enable_mirroring_in_media_router_2
Change-Id: I1fccb4cf3891a1c61c92ea3dad9e97f7b7fc27ce
parent 57747440
Loading
Loading
Loading
Loading
+143 −0
Original line number Diff line number Diff line
@@ -15,10 +15,17 @@
 */
package com.android.settingslib.volume

import android.Manifest
import android.annotation.RequiresPermission
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.media.AppId
import android.media.AudioManager
import android.media.MediaMetadata
import android.media.MediaRouter2
import android.media.RoutingSessionInfo
import android.media.session.MediaController
import android.media.session.MediaController.PlaybackInfo
import android.media.session.MediaSession
@@ -30,6 +37,7 @@ import android.os.HandlerExecutor
import android.os.Looper
import android.os.Message
import android.util.Log
import com.android.media.flags.Flags
import java.io.PrintWriter
import java.util.Objects

@@ -46,10 +54,21 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) {
        mContext.getSystemService(Context.MEDIA_SESSION_SERVICE) as MediaSessionManager
    private val mRecords: MutableMap<MediaSession.Token, MediaControllerRecord> = HashMap()
    private val mCallbacks: Callbacks = callbacks
    @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
    private val mSessionsListener =
        MediaSessionManager.OnActiveSessionsChangedListener { controllers ->
            onActiveSessionsUpdatedH(controllers!!)
        }
    @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
    private val mediaRouter2: MediaRouter2? =
        if (Flags.enableMirroringInMediaRouter2()) {
            MediaRouter2.getInstance(mContext, mContext.getPackageName())
        } else {
            null
        }

    private val proxyRouters = mutableMapOf<AppId, ProxyMediaRouter2Record>()
    private val systemSessionOverridesListener = this::onSystemSessionOverridesChanged

    private val mRemoteSessionCallback: MediaSessionManager.RemoteSessionCallback =
        object : MediaSessionManager.RemoteSessionCallback {
@@ -74,6 +93,16 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) {
        for ((i, r) in mRecords.values.withIndex()) {
            r.controller.dump(i + 1, writer)
        }
        if (Flags.enableMirroringInMediaRouter2()) {
            synchronized(proxyRouters) {
                writer.println("  mProxyRouters.size: ${proxyRouters.size}")
                for ((appId, record) in proxyRouters.entries) {
                    writer.println(
                        "    $appId -> ${record.mProxyMr2.systemController.routingSessionInfo}"
                    )
                }
            }
        }
    }

    /** init MediaSessions */
@@ -86,6 +115,14 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) {
        mInit = true
        postUpdateSessions()
        mMgr.registerRemoteSessionCallback(mHandlerExecutor, mRemoteSessionCallback)

        mediaRouter2?.let {
            mHandler.post { onSystemSessionOverridesChanged(it.systemSessionOverridesAppIds) }
            it.registerSystemSessionOverridesListener(
                mHandlerExecutor,
                systemSessionOverridesListener,
            )
        }
    }

    /** Destroy MediaSessions */
@@ -93,6 +130,7 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) {
        if (D.BUG) {
            Log.d(TAG, "destroy")
        }
        mediaRouter2?.unregisterSystemSessionOverridesListener(systemSessionOverridesListener)
        mInit = false
        mMgr.removeOnActiveSessionsChangedListener(mSessionsListener)
        mMgr.unregisterRemoteSessionCallback(mRemoteSessionCallback)
@@ -102,9 +140,26 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) {
    fun setVolume(sessionId: SessionId, volumeLevel: Int) {
        when (sessionId) {
            is SessionId.Media -> setMediaSessionVolume(sessionId.token, volumeLevel)
            is SessionId.Routing ->
                setRoutingSessionVolume(sessionId.appWithSystemSessionOverride, volumeLevel)
        }
    }

    private fun setRoutingSessionVolume(appWithSystemSessionOverride: AppId, volumeLevel: Int) {
        val record = synchronized(proxyRouters) { proxyRouters[appWithSystemSessionOverride] }
        if (record == null) {
            Log.w(
                TAG,
                "setVolume: No routing session record found for $appWithSystemSessionOverride",
            )
            return
        }
        if (D.BUG) {
            Log.d(TAG, "Setting level to $volumeLevel")
        }
        record.mProxyMr2.systemController.volume = volumeLevel
    }

    private fun setMediaSessionVolume(token: MediaSession.Token, volumeLevel: Int) {
        val record = mRecords[token]
        if (record == null) {
@@ -117,6 +172,45 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) {
        record.controller.setVolumeTo(volumeLevel, 0)
    }

    @SuppressLint("MissingPermission")
    private fun onSystemSessionOverridesChanged(appsWithSessionOverrides: Set<AppId>) {
        val newRecords = mutableListOf<ProxyMediaRouter2Record>()
        var removedApps: Set<AppId>
        synchronized(proxyRouters) {
            appsWithSessionOverrides
                .filter { it !in proxyRouters }
                .forEach { appWithOverride ->
                    val proxyRouter =
                        MediaRouter2.getInstance(
                            mContext,
                            appWithOverride.mPackageName,
                            appWithOverride.mUserHandle,
                        )
                    val record = ProxyMediaRouter2Record(proxyRouter, appWithOverride)
                    proxyRouters.put(appWithOverride, record)
                    record.register()
                    newRecords.add(record)
                }

            removedApps = proxyRouters.keys - appsWithSessionOverrides
            removedApps.forEach { appToRemove ->
                proxyRouters.remove(appToRemove)?.let {
                    it.release()
                    if (D.BUG) {
                        Log.d(TAG, "Removing proxy record for ${it.mAppWithSystemSessionOverride}")
                    }
                }
            }
        }
        newRecords.forEach {
            updateRemoteH(
                it.mAppWithSystemSessionOverride,
                it.mProxyMr2.systemController.routingSessionInfo,
            )
        }
        removedApps.forEach { mCallbacks.onRemoteRemoved(SessionId.from(it)) }
    }

    private fun onRemoteVolumeChangedH(sessionToken: MediaSession.Token, flags: Int) {
        val controller = MediaController(mContext, sessionToken)
        if (D.BUG) {
@@ -223,6 +317,47 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) {
        playbackInfo: PlaybackInfo,
    ) = mCallbacks.onRemoteUpdate(SessionId.from(token), name, VolumeInfo.from(playbackInfo))

    private fun updateRemoteH(
        appWithSystemSessionOverride: AppId,
        routingSessionInfo: RoutingSessionInfo,
    ) =
        mCallbacks.onRemoteUpdate(
            SessionId.from(appWithSystemSessionOverride),
            routingSessionInfo.name.toString(),
            VolumeInfo.from(routingSessionInfo),
        )

    private inner class ProxyMediaRouter2Record(
        val mProxyMr2: MediaRouter2,
        val mAppWithSystemSessionOverride: AppId,
    ) : MediaRouter2.ControllerCallback() {
        fun register() {
            mProxyMr2.registerControllerCallback(mHandlerExecutor, this)
        }

        fun release() {
            mProxyMr2.unregisterControllerCallback(this)
        }

        override fun onControllerUpdated(
            controller: MediaRouter2.RoutingController,
            shouldShowVolumeUi: Boolean,
        ) {
            updateRemoteH(
                mAppWithSystemSessionOverride,
                mProxyMr2.systemController.routingSessionInfo,
            )
            mCallbacks.onRemoteVolumeChanged(
                SessionId.from(mAppWithSystemSessionOverride),
                if (shouldShowVolumeUi) {
                    AudioManager.FLAG_SHOW_UI
                } else {
                    0
                },
            )
        }
    }

    private inner class MediaControllerRecord(val controller: MediaController) :
        MediaController.Callback() {
        var sentRemote: Boolean = false
@@ -313,9 +448,13 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) {

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

            fun from(appWithSystemSessionOverride: AppId) = Routing(appWithSystemSessionOverride)
        }

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

        data class Routing(val appWithSystemSessionOverride: AppId) : SessionId
    }

    /** Holds session volume information. */
@@ -325,6 +464,10 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) {

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

            fun from(routingSessionInfo: RoutingSessionInfo): VolumeInfo {
                return VolumeInfo(routingSessionInfo.volume, routingSessionInfo.volumeMax)
            }
        }
    }