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:
wdvipa
2026-02-15 00:22:47 +08:00
parent de9aa4430c
commit 39bc5b47a0
5 changed files with 161 additions and 175 deletions

View File

@@ -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) {