You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Jetpack Compose应用Media3功能时出现SecurityException崩溃求助

问题分析与解决方案:Media3 SecurityException: Session rejected the connection request

核心排查与修复步骤

1. 检查Media Session安全策略配置

SecurityException最常见原因是Session的权限校验拒绝了连接请求,先确认MusicPlaybackService中MediaSession的安全策略:

  • 调试阶段可临时放开所有连接权限,验证是否因权限限制导致:
    val session = MediaSession.Builder(context, player)
        .setSessionSecurityPolicy(SessionSecurityPolicy.permitAll())
        .build()
    
  • 若需自定义权限控制,需在onGetSession方法中校验客户端信息(包名、签名),确保允许当前应用的Controller连接:
    override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? {
        // 仅允许本应用包名连接
        if (controllerInfo.packageName == packageName) {
            return session
        }
        return null
    }
    

2. 对齐Controller连接时机与Session生命周期

避免在Session未完全初始化时发起连接:

  • 通过ServiceConnection监听Service启动状态,在Service连接成功后再初始化Controller:
    val serviceIntent = Intent(context, MusicPlaybackService::class.java)
    context.bindService(serviceIntent, object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            val sessionToken = MediaSessionToken.fromBinder(service)
            MediaController.Builder(context, sessionToken).buildAsync()
        }
    
        override fun onServiceDisconnected(name: ComponentName?) {}
    }, Context.BIND_AUTO_CREATE)
    
  • 使用MediaController的setListener监听连接错误,避免崩溃:
    MediaController.Builder(context, sessionToken)
        .setListener(object : MediaController.Listener {
            override fun onSessionConnectError(error: SessionCommandException) {
                // 处理连接失败,如重试或提示用户
            }
        })
        .buildAsync()
    

3. 验证SessionToken的正确性

确保Controller获取的SessionToken与Service中创建的Session一致:

  • 在Service中通过session.sessionToken获取有效Token,避免传递过期或无效的Token;
  • 若跨进程连接,需确认客户端包名与Service声明的权限范围匹配,避免因包名不匹配被拒绝。

4. 排查多进程与权限隔离问题

如果应用使用多进程:

  • 检查AndroidManifest.xml中Service的android:process属性,确保Controller所在进程拥有访问MediaSession的权限;
  • 给Service进程添加必要权限(如android.permission.MEDIA_CONTENT_CONTROL),避免权限缺失导致连接被拒。

5. 开启详细日志定位根因

开启Media3调试日志,获取更具体的拒绝原因:
在AndroidManifest.xml中添加:

<meta-data
    android:name="com.google.android.exoplayer2.DEBUG"
    android:value="true" />

查看日志中伴随SecurityException的详细提示,通常会明确指出是权限不匹配、Token无效还是其他校验失败原因。

代码示例参考

修复后的MusicPlaybackService

class MusicPlaybackService : MediaSessionService() {
    private lateinit var player: ExoPlayer
    private lateinit var session: MediaSession

    override fun onCreate() {
        super.onCreate()
        player = ExoPlayer.Builder(this).build()
        session = MediaSession.Builder(this, player)
            .setSessionSecurityPolicy(SessionSecurityPolicy.permitAll())
            .setSessionActivity(PendingIntent.getActivity(this, 0, 
                Intent(this, MainActivity::class.java), PendingIntent.FLAG_IMMUTABLE))
            .build()
    }

    override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? {
        // 自定义校验逻辑,仅允许本应用连接
        return if (controllerInfo.packageName == packageName) session else null
    }

    override fun onDestroy() {
        player.release()
        session.release()
        super.onDestroy()
    }
}

修复后的MusicPlaybackController

class MusicPlaybackController(context: Context) : PlaybackController {
    private var mediaController: MediaController? = null

    init {
        val serviceIntent = Intent(context, MusicPlaybackService::class.java)
        context.bindService(serviceIntent, object : ServiceConnection {
            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                service?.let { binder ->
                    val sessionToken = MediaSessionToken.fromBinder(binder)
                    MediaController.Builder(context, sessionToken)
                        .setListener(object : MediaController.Listener {
                            override fun onSessionConnected(controller: MediaController, hints: MediaSession.ConnectionHints) {
                                mediaController = controller
                                // 初始化播放控制逻辑
                            }

                            override fun onSessionConnectError(error: SessionCommandException) {
                                Log.e("PlaybackController", "连接失败: ${error.message}")
                            }
                        })
                        .buildAsync()
                }
            }

            override fun onServiceDisconnected(name: ComponentName?) {}
        }, Context.BIND_AUTO_CREATE)
    }

    // 实现PlaybackController接口方法...
}

内容的提问来源于stack exchange,提问作者Aarav Bansal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 23:19:56