fix: 修复屏幕录制权限频繁掉落问题
- 根因:多处代码重复调用getMediaProjection()创建新实例,系统自动stop旧实例触发onStop回调,形成权限丢失恢复再丢失的死循环 - ScreenCaptureManager.ensureMediaProjection():禁止重复创建,只从Holder获取已有对象 - ScreenCaptureManager.captureWithMediaProjection():移除重复创建逻辑 - ScreenCaptureManager.triggerPermissionRecovery():优先从Holder和SmartManager获取已有对象 - ScreenCaptureManager.regenerateMediaProjectionForAndroid15():优先复用Holder中已有对象 - SmartMediaProjectionManager.attemptSilentRecovery():先检查Holder是否已有有效对象 - Android15MediaProjectionManager.attemptSilentRecovery():先检查Holder复用已有对象 - Android15MediaProjectionManager.determineStopReason():修复误判逻辑,Holder中仍有有效对象时识别为旧实例被替换而非用户主动停止 - AccessibilityRemoteService.attemptAndroid15SilentRecovery():优先复用Holder中已有对象 - AccessibilityRemoteService.handleMediaProjectionGranted():Android 11+优先从Holder获取 - RemoteControlForegroundService.handleStartMediaProjection():优先检查Holder避免重复创建
This commit is contained in:
@@ -562,36 +562,28 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 确保 MediaProjection 可用,尝试获取或恢复
|
||||
* 确保 MediaProjection 可用
|
||||
*
|
||||
* 🚨 核心修复:禁止重复调用 getMediaProjection(resultCode, resultData)
|
||||
* 每次调用都会创建新实例,系统会自动 stop 旧实例,触发 onStop 回调,
|
||||
* 形成"权限丢失→恢复→再丢失"的死循环,这是权限频繁掉落的根因。
|
||||
*
|
||||
* 只从 MediaProjectionHolder 获取已有对象,不重新创建。
|
||||
*/
|
||||
private fun ensureMediaProjection(): Boolean {
|
||||
if (mediaProjection != null) return true
|
||||
|
||||
// 从全局 Holder 获取已有的 MediaProjection 对象
|
||||
mediaProjection = MediaProjectionHolder.getMediaProjection()
|
||||
if (mediaProjection != null) return true
|
||||
|
||||
// 尝试从权限数据重新创建
|
||||
val permissionData = MediaProjectionHolder.getPermissionData()
|
||||
if (permissionData != null) {
|
||||
val (resultCode, resultData) = permissionData
|
||||
if (resultData != null) {
|
||||
try {
|
||||
val mediaProjectionManager = service.getSystemService(
|
||||
android.content.Context.MEDIA_PROJECTION_SERVICE
|
||||
) as android.media.projection.MediaProjectionManager
|
||||
mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, resultData)
|
||||
if (mediaProjection != null) {
|
||||
Log.i(TAG, "✅ 重新创建 MediaProjection 成功")
|
||||
MediaProjectionHolder.setMediaProjection(mediaProjection)
|
||||
return true
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "❌ 重新创建 MediaProjection 失败", e)
|
||||
}
|
||||
}
|
||||
if (mediaProjection != null) {
|
||||
Log.i(TAG, "✅ 从 MediaProjectionHolder 获取到已有 MediaProjection")
|
||||
return true
|
||||
}
|
||||
|
||||
Log.e(TAG, "❌ 无法获取 MediaProjection,权限可能未授予")
|
||||
// 🚨 不再重复调用 getMediaProjection() 创建新实例
|
||||
// 如果 Holder 中没有有效对象,说明权限确实未授予或已过期
|
||||
// 应由 MainActivity 的权限申请流程统一创建
|
||||
Log.w(TAG, "⚠️ MediaProjectionHolder 中无有效 MediaProjection,等待权限授予")
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -940,44 +932,21 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
||||
if (mediaProjection == null) {
|
||||
mediaProjection = MediaProjectionHolder.getMediaProjection()
|
||||
if (mediaProjection == null) {
|
||||
Log.w(TAG, "MediaProjection未初始化,检查权限数据")
|
||||
val permissionData = MediaProjectionHolder.getPermissionData()
|
||||
Log.w(TAG, "权限数据状态: ${if (permissionData != null) "存在" else "不存在"}")
|
||||
Log.w(TAG, "MediaProjection未初始化,Holder中无有效对象")
|
||||
|
||||
// 尝试重新创建MediaProjection
|
||||
if (permissionData != null) {
|
||||
val (resultCode, resultData) = permissionData
|
||||
if (resultData != null) {
|
||||
val mediaProjectionManager = service.getSystemService(android.content.Context.MEDIA_PROJECTION_SERVICE) as android.media.projection.MediaProjectionManager
|
||||
mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, resultData)
|
||||
if (mediaProjection != null) {
|
||||
Log.i(TAG, "重新创建MediaProjection成功")
|
||||
MediaProjectionHolder.setMediaProjection(mediaProjection)
|
||||
} else {
|
||||
Log.w(TAG, "重新创建MediaProjection失败")
|
||||
}
|
||||
}
|
||||
}
|
||||
// 🚨 核心修复:不再重复调用 getMediaProjection() 创建新实例
|
||||
// 重复创建会导致系统 stop 旧实例,触发 onStop 回调死循环
|
||||
// 权限应由 MainActivity 统一申请
|
||||
|
||||
if (mediaProjection == null) {
|
||||
// ✅ Android 15优化:先尝试静默恢复,再考虑权限申请
|
||||
if (android.os.Build.VERSION.SDK_INT >= 35) {
|
||||
Log.w(TAG, "⚠️ Android 15 MediaProjection为null,尝试静默恢复")
|
||||
if (attemptAndroid15SilentRecovery()) {
|
||||
// 静默恢复成功,继续执行
|
||||
Log.i(TAG, "✅ Android 15静默恢复成功,继续屏幕捕获")
|
||||
} else {
|
||||
// 静默恢复失败,可能确实需要重新申请权限
|
||||
Log.e(TAG, "❌ Android 15静默恢复失败,MediaProjection权限确实丢失")
|
||||
triggerPermissionRecovery()
|
||||
return null
|
||||
}
|
||||
} else {
|
||||
// 其他版本的处理逻辑
|
||||
Log.e(TAG, "❌ MediaProjection权限丢失,触发自动权限恢复")
|
||||
triggerPermissionRecovery()
|
||||
return null
|
||||
}
|
||||
// ✅ Android 15优化:直接报告权限不可用
|
||||
if (android.os.Build.VERSION.SDK_INT >= 35) {
|
||||
Log.w(TAG, "⚠️ Android 15 MediaProjection为null,等待权限重新授予")
|
||||
triggerPermissionRecovery()
|
||||
return null
|
||||
} else {
|
||||
Log.e(TAG, "❌ MediaProjection权限丢失,触发自动权限恢复")
|
||||
triggerPermissionRecovery()
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2143,46 +2112,38 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 触发智能权限恢复机制 - 使用智能权限管理器
|
||||
* ✅ 触发智能权限恢复机制
|
||||
*
|
||||
* 🚨 核心修复:优先从 Holder 获取已有对象,
|
||||
* 避免通过 SmartManager 重复创建新实例导致旧实例被 stop
|
||||
*/
|
||||
private fun triggerPermissionRecovery() {
|
||||
try {
|
||||
Log.i(TAG, "🧠 尝试使用智能权限管理器进行权限恢复")
|
||||
Log.i(TAG, "🧠 尝试权限恢复")
|
||||
|
||||
// 首先尝试智能权限管理器
|
||||
// ✅ 第一步:从 Holder 获取已有对象
|
||||
val holderProjection = com.hikoncont.MediaProjectionHolder.getMediaProjection()
|
||||
if (holderProjection != null) {
|
||||
Log.i(TAG, "✅ Holder中已有有效MediaProjection,直接复用")
|
||||
setMediaProjection(holderProjection)
|
||||
return
|
||||
}
|
||||
|
||||
// ✅ 第二步:从智能管理器获取
|
||||
val smartManager = com.hikoncont.manager.SmartMediaProjectionManager.getInstance(service)
|
||||
val currentProjection = smartManager.getCurrentMediaProjection()
|
||||
|
||||
if (currentProjection != null) {
|
||||
Log.i(TAG, "✅ 智能管理器找到有效的MediaProjection,直接使用")
|
||||
setMediaProjection(currentProjection)
|
||||
return
|
||||
}
|
||||
|
||||
// 智能管理器没有有效权限,检查是否可以静默恢复
|
||||
val permissionData = com.hikoncont.MediaProjectionHolder.getPermissionData()
|
||||
if (permissionData != null) {
|
||||
val (resultCode, resultData) = permissionData
|
||||
if (resultData != null) {
|
||||
Log.i(TAG, "🔧 尝试通过智能管理器静默恢复权限")
|
||||
val recovered = smartManager.setMediaProjection(resultCode, resultData)
|
||||
if (recovered) {
|
||||
Log.i(TAG, "✅ 智能权限恢复成功")
|
||||
val newProjection = smartManager.getCurrentMediaProjection()
|
||||
if (newProjection != null) {
|
||||
setMediaProjection(newProjection)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 智能恢复失败,回退到传统方式但更智能
|
||||
Log.w(TAG, "⚠️ 智能权限恢复失败,回退到传统恢复机制")
|
||||
// ✅ 第三步:都没有有效对象,回退到传统恢复(重新申请权限)
|
||||
Log.w(TAG, "⚠️ 无有效MediaProjection对象,回退到传统恢复机制")
|
||||
triggerTraditionalPermissionRecovery()
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "❌ 智能权限恢复失败,回退到传统方式", e)
|
||||
Log.e(TAG, "❌ 权限恢复失败,回退到传统方式", e)
|
||||
triggerTraditionalPermissionRecovery()
|
||||
}
|
||||
}
|
||||
@@ -2367,36 +2328,41 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
||||
|
||||
/**
|
||||
* Android 15:重新生成MediaProjection以解决单次令牌限制
|
||||
*
|
||||
* 🚨 核心修复:优先从 Holder 获取已有对象,避免重复创建
|
||||
*/
|
||||
private fun regenerateMediaProjectionForAndroid15(): Boolean {
|
||||
return try {
|
||||
if (Build.VERSION.SDK_INT >= 35) {
|
||||
Log.i(TAG, "🔄 Android 15:重新生成MediaProjection令牌")
|
||||
Log.i(TAG, "🔄 Android 15:尝试获取可用的MediaProjection")
|
||||
|
||||
// ✅ 优先从 Holder 获取已有对象
|
||||
val existingProjection = MediaProjectionHolder.getMediaProjection()
|
||||
if (existingProjection != null) {
|
||||
Log.i(TAG, "✅ Holder中已有有效MediaProjection,直接复用")
|
||||
mediaProjection = existingProjection
|
||||
return true
|
||||
}
|
||||
|
||||
// Holder 中无有效对象,从权限数据创建(仅一次)
|
||||
val permissionData = MediaProjectionHolder.getPermissionData()
|
||||
if (permissionData != null) {
|
||||
val (resultCode, resultData) = permissionData
|
||||
if (resultData != null) {
|
||||
// 通过Android 15管理器重新创建
|
||||
val accessibilityService = com.hikoncont.service.AccessibilityRemoteService.getInstance()
|
||||
val android15Manager = accessibilityService?.getAndroid15MediaProjectionManager()
|
||||
|
||||
val newProjection = android15Manager?.createMediaProjectionWithCallback(resultCode, resultData)
|
||||
if (newProjection != null) {
|
||||
// 更新本地引用
|
||||
mediaProjection = newProjection
|
||||
MediaProjectionHolder.setMediaProjection(newProjection)
|
||||
Log.i(TAG, "✅ Android 15 MediaProjection重新生成成功")
|
||||
return true
|
||||
} else {
|
||||
Log.e(TAG, "❌ Android 15 MediaProjection重新生成失败:创建返回null")
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "❌ Android 15重新生成失败:权限数据中Intent为null")
|
||||
Log.e(TAG, "❌ Android 15 MediaProjection重新生成失败")
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "❌ Android 15重新生成失败:无权限数据")
|
||||
}
|
||||
Log.e(TAG, "❌ Android 15重新生成失败:无有效权限数据")
|
||||
}
|
||||
false
|
||||
} catch (e: Exception) {
|
||||
|
||||
Reference in New Issue
Block a user