You can get song's metadata using MediaSessionManager.

Sample code:

package com.twobeone.aiagent.framework
import android.content.Context
import android.media.MediaMetadata
import android.media.session.MediaController
import android.media.session.MediaSessionManager
import android.media.session.PlaybackState
import android.util.Log
import com.example.aiagent.data.BT_PACKAGE_NAME
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

object MediaMetaDataManager {
val TAG = "MediaMetaDataManager"

private val _activeSessionPackages = MutableStateFlow(emptyList<String>())
val activeSessionPackages: StateFlow<List<String>> = _activeSessionPackages.asStateFlow()

private var _playbackState = MutableStateFlow(PlaybackState.STATE_NONE)
val playbackState: StateFlow<Int> = _playbackState.asStateFlow()

private val _mediaMetadata = MutableStateFlow(MediaMetadata.Builder()
.putString(MediaMetadata.METADATA_KEY_MEDIA_ID, "")
.putLong(MediaMetadata.METADATA_KEY_DURATION, 0)
.build()
)
val mediaMetadata: StateFlow<MediaMetadata> = _mediaMetadata.asStateFlow()

private var _activeSessionPackage = MutableStateFlow("")
val activeSessionPackage: StateFlow<String> = _activeSessionPackage.asStateFlow()

private var mPrimaryController: MediaController? = null

private val sessionListener: MediaSessionManager.OnActiveSessionsChangedListener =
MediaSessionManager.OnActiveSessionsChangedListener {
Log.d(TAG, "OnActiveSessionsChanged")
it?.let {
if (it.isNotEmpty() &&
mPrimaryController !== null &&
it[0] == mPrimaryController
) {
Log.d(TAG, "is same session")
return@OnActiveSessionsChangedListener
} else {
if (mPrimaryController != null) {
mPrimaryController?.unregisterCallback(callback)
}
checkSessionList(it)
}
} ?: let {
Log.d(TAG, "mediaSession is null")
}
}

private fun checkSessionList(list: List<MediaController>) {
if (list.isEmpty()) {
Log.d(TAG, "no active session, hide mini_player")
// sessionState.value = false
} else {
// sessionState.value = true
_activeSessionPackages.value = list.map { it.packageName }
list.forEach {
Log.d(TAG, "check controllerList ${it.packageName}")
}
mPrimaryController = list[0]
mPrimaryController?.registerCallback(callback)        //register MediaController.Callback to a active MediaController
_activeSessionPackage.value = mPrimaryController?.packageName.orEmpty()
Log.d(TAG, "packageName: ${_activeSessionPackage.value}")
mPrimaryController?.metadata?.let {
_mediaMetadata.value = it
if (_activeSessionPackage.value == BT_PACKAGE_NAME) {        //filter packages which you want
Log.d(
TAG,
"checkSessionList: " +
"mediaId = " +
"${it.getString(MediaMetadata.METADATA_KEY_MEDIA_ID)} \n" +
"title = ${it.getString(MediaMetadata.METADATA_KEY_TITLE)} \n" +
"artist = ${it.getString(MediaMetadata.METADATA_KEY_ARTIST)} \n" +
"album = ${it.getString(MediaMetadata.METADATA_KEY_ALBUM)} \n" +
"genre = ${it.getString(MediaMetadata.METADATA_KEY_GENRE)} \n" +
"albumArtUri = " +
"${it.getString(MediaMetadata.METADATA_KEY_ART_URI)} \n" +
"${it.description.title} " +
"${it.description.subtitle} " + // null or string
"${it.description.description} " + // null or string
"${it.description} " + // null or string
"${it.description.iconUri} ",
)
} else {
Log.d(
TAG,
"checkSessionList: " +
"mediaId = " +
"${it.getString(MediaMetadata.METADATA_KEY_MEDIA_ID)} \n" +
"displayTitle = " +
"${it.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE)} \n" +
"subTitle = " +
"${it.getString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE)} \n" +
"albumArt = " +
"${it.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART)} \n" +
"displayIcon = " +
"${it.getBitmap(MediaMetadata.METADATA_KEY_DISPLAY_ICON)} \n" +
"albumArtUri = " +
"${it.getString(MediaMetadata.METADATA_KEY_ART_URI)} \n" +
"${it.description.title} " +
"${it.description.subtitle} " +
"${it.description.iconBitmap} ",
)
}
} ?: let {
// TODO
}

mPrimaryController?.playbackState?.let {
_playbackState.value = it.state
}
}
}

private val callback: MediaController.Callback = object : MediaController.Callback() {
override fun onPlaybackStateChanged(state: PlaybackState?) {
state?.let {
if (_playbackState.value != it.state) {
Log.d(
TAG,
"MediaSessionManagerDataSource onPlaybackStateChanged : ${it.state}",
)
_playbackState.value = it.state
}
}
super.onPlaybackStateChanged(state)
}

override fun onMetadataChanged(metadata: MediaMetadata?) {
metadata?.let {  
Log.d(TAG, "MediaSessionManagerDataSource onMetadataChanged")
Log.d(
TAG,
"Changed Metadata: " +
"mediaId = " +
"${it.getString(MediaMetadata.METADATA_KEY_MEDIA_ID)} \n" +
"title = ${it.getString(MediaMetadata.METADATA_KEY_TITLE)} \n" +
"artist = ${it.getString(MediaMetadata.METADATA_KEY_ARTIST)} \n" +
"album = ${it.getString(MediaMetadata.METADATA_KEY_ALBUM)} \n" +
"genre = ${it.getString(MediaMetadata.METADATA_KEY_GENRE)} \n" +
"albumArtUri = " +
"${it.getString(MediaMetadata.METADATA_KEY_ART_URI)} \n" +
"${it.description.title} " +
"${it.description.subtitle} " + // null or string
"${it.description.description} " + // null or string
"${it.description} " + // null or string
"${it.description.iconUri} ",
)
_mediaMetadata.value = it
}
super.onMetadataChanged(metadata)
}
}

fun initialize(context: Context) {
Log.d(TAG, "initialize")

val mediaSessionManager =
context.getSystemService(Context.MEDIA_SESSION_SERVICE) as MediaSessionManager    
mediaSessionManager.addOnActiveSessionsChangedListener(sessionListener, null)    //register ActiveSessionsChangedListener
val controllerList = mediaSessionManager.getActiveSessions(null)        //initialize activeSessions
checkSessionList(controllerList)
Log.d(TAG, "active sessions: ${activeSessionPackages.value.joinToString { it }}")
}
}

MediaSession Manager has several active media sessions.

You can get main Controller which is currently active at 0 index.

Check the main Controller is which packages you want and register MediaController.Callback.

Then, you can get song's meta data whenever media session changes.