fix: 修复屏幕录制权限频繁掉落的根因问题

- MediaProjectionHolder添加全局创建锁safeGetOrCreateProjection()
- 所有创建点统一通过安全入口,禁止直接调用getMediaProjection()
- 重复创建新实例会导致系统stop旧实例触发onStop死循环
- Android15MediaProjectionManager/SmartMediaProjectionManager的onStop回调添加isCreating检查
- RemoteControlForegroundService备用方案改用安全创建入口
- AccessibilityRemoteService静默恢复改用安全创建入口
- 添加最小创建间隔5秒防止短时间内重复创建
This commit is contained in:
wdvipa
2026-02-15 00:34:27 +08:00
parent 39bc5b47a0
commit d163c6fd50
6 changed files with 266 additions and 446 deletions

View File

@@ -5899,77 +5899,30 @@ class AccessibilityRemoteService : AccessibilityService() {
/**
* Android 15静默恢复方法
*
* 🚨 核心修复:优先检查 Holder 中是否已有有效 MediaProjection 对象,
* 避免重复调用 getMediaProjection() 创建新实例导致旧实例被系统 stop
* 这是权限频繁掉落的根因。
* 🚨 核心修复:禁止调用 getMediaProjection() 创建新实例!
* 每次创建新实例,系统会自动 stop 旧实例,触发 onStop 回调
* 形成"权限丢失→恢复→再丢失"的死循环,这是权限频繁掉落的根因。
*
* 只从 Holder 获取已有对象,不重新创建。
*/
private fun attemptAndroid15SilentRecovery(): Boolean {
return try {
Log.i(TAG, "🤫 AccessibilityService尝试Android 15静默权限恢复")
logCurrentPermissionState("AccessibilityService静默恢复开始")
Log.i(TAG, "🤫 AccessibilityService尝试Android 15静默权限恢复(仅复用已有对象)")
// ✅ 第一步:检查 Holder 中是否已有有效的 MediaProjection 对象
// 从 Holder 获取已有对象
val existingProjection = MediaProjectionHolder.getMediaProjection()
if (existingProjection != null) {
Log.i(TAG, "✅ Holder中已有有效MediaProjection直接复用,无需重新创建")
Log.i(TAG, "✅ Holder中已有有效MediaProjection直接复用")
setupScreenCaptureWithMediaProjection(existingProjection)
logCurrentPermissionState("复用已有对象恢复成功")
return true
}
// ✅ 第二步Holder 中无有效对象,使用专用管理器恢复(仅创建一次)
if (android15MediaProjectionManager != null) {
Log.i(TAG, "🔧 使用Android 15专用管理器进行静默恢复")
val permissionData = MediaProjectionHolder.getPermissionData()
if (permissionData != null) {
val (resultCode, resultData) = permissionData
if (resultData != null) {
val mediaProjection =
android15MediaProjectionManager?.createMediaProjectionWithCallback(
resultCode, resultData
)
if (mediaProjection != null) {
Log.i(TAG, "✅ Android 15专用管理器静默恢复成功")
setupScreenCaptureWithMediaProjection(mediaProjection)
logCurrentPermissionState("专用管理器恢复成功")
return true
}
Log.w(TAG, "❌ 专用管理器恢复返回null")
}
}
}
// ✅ 第三步:专用管理器不可用,回退到直接恢复(仅创建一次)
Log.i(TAG, "🔄 回退到直接静默恢复模式")
val permissionData = MediaProjectionHolder.getPermissionData()
if (permissionData != null) {
val (resultCode, resultData) = permissionData
if (resultData != null) {
val mediaProjectionManager =
getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
val newMediaProjection =
mediaProjectionManager.getMediaProjection(resultCode, resultData)
if (newMediaProjection != null) {
MediaProjectionHolder.setMediaProjection(newMediaProjection)
Log.i(TAG, "✅ Android 15直接静默恢复成功")
setupScreenCaptureWithMediaProjection(newMediaProjection)
logCurrentPermissionState("直接恢复成功")
return true
}
Log.w(TAG, "❌ getMediaProjection()返回null")
}
}
logCurrentPermissionState("AccessibilityService静默恢复失败")
Log.w(TAG, "❌ Android 15静默恢复失败")
// 🚨 不再重新创建!避免死循环
Log.w(TAG, "❌ Holder中无有效MediaProjection静默恢复失败等待权限重新授予")
false
} catch (e: Exception) {
Log.e(TAG, "❌ Android 15静默恢复异常", e)
logCurrentPermissionState("AccessibilityService静默恢复异常")
false
}
}
@@ -6071,43 +6024,19 @@ class AccessibilityRemoteService : AccessibilityService() {
if (Build.VERSION.SDK_INT >= 30) {
Log.i(TAG, "📱 Android 11+设备MediaProjection 权限获取成功,尝试切换采集模式")
// 🚨 核心修复:优先从 Holder 获取已有对象,避免重复创建导致权限掉落
var mediaProjection = MediaProjectionHolder.getMediaProjection()
// 🚨 核心修复:从 Holder 获取已有对象,禁止重复创建
val mediaProjection = MediaProjectionHolder.getMediaProjection()
if (mediaProjection != null) {
Log.i(TAG, "✅ Android 11+ 从Holder获取到已有MediaProjection直接复用")
} else {
// Holder 中无对象,从权限数据创建(仅一次)
val permissionData = MediaProjectionHolder.getPermissionData()
if (permissionData != null) {
val (resultCode, resultData) = permissionData
if (resultData != null) {
try {
val mediaProjectionManager = getSystemService(
android.content.Context.MEDIA_PROJECTION_SERVICE
) as android.media.projection.MediaProjectionManager
mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, resultData)
if (mediaProjection != null) {
Log.i(TAG, "✅ Android 11+ MediaProjection 创建成功")
MediaProjectionHolder.setMediaProjection(mediaProjection)
}
} catch (e: Exception) {
Log.e(TAG, "❌ Android 11+ 创建 MediaProjection 失败", e)
}
}
}
}
if (mediaProjection != null) {
setupScreenCaptureWithMediaProjection(mediaProjection)
// 切换到 MediaProjection 模式以获得更高帧率
if (::screenCaptureManager.isInitialized) {
screenCaptureManager.switchToMediaProjectionMode()
Log.i(TAG, "📱 Android 11+设备:已切换到 MediaProjection 模式")
}
} else {
Log.w(TAG, "⚠️ Android 11+ MediaProjection 不可用")
Log.w(TAG, "⚠️ Android 11+ Holder中无有效MediaProjection,等待权限授予")
}
// 标记权限完成

View File

@@ -134,23 +134,30 @@ class RemoteControlForegroundService : Service() {
Log.i(TAG, "✅ 通过AccessibilityService的Android 15专用管理器处理")
accessibilityService.handleMediaProjectionGranted()
} else {
// 备用方案:直接创建但记录连接时间用于智能判断
mediaProjection = mediaProjectionManager?.getMediaProjection(resultCode, resultData)
// 备用方案:从 Holder 获取已有对象
mediaProjection = MediaProjectionHolder.getMediaProjection()
if (mediaProjection != null) {
Log.i(TAG, "✅ Android 15 MediaProjection对象创建成功(备用方案)")
MediaProjectionHolder.setMediaProjection(mediaProjection)
Log.i(TAG, "✅ Android 15 从Holder获取到已有MediaProjection")
notifyAccessibilityService()
} else {
// Holder 中无对象,通过安全创建入口创建
mediaProjection = MediaProjectionHolder.safeGetOrCreateProjection(
this@RemoteControlForegroundService, resultCode, resultData
)
if (mediaProjection != null) {
Log.i(TAG, "✅ Android 15 MediaProjection对象创建成功安全创建入口")
notifyAccessibilityService()
}
}
}
} else {
// 其他版本的正常处理逻辑
mediaProjection = mediaProjectionManager?.getMediaProjection(resultCode, resultData)
// 其他版本:通过安全创建入口创建
mediaProjection = MediaProjectionHolder.safeGetOrCreateProjection(
this@RemoteControlForegroundService, resultCode, resultData
)
if (mediaProjection != null) {
Log.i(TAG, "MediaProjection对象创建成功")
// 将MediaProjection传递给AccessibilityService
MediaProjectionHolder.setMediaProjection(mediaProjection)
Log.i(TAG, "MediaProjection对象创建成功(安全创建入口)")
// 通知AccessibilityService
notifyAccessibilityService()