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

@@ -5862,12 +5862,28 @@ class MainActivity : AppCompatActivity() {
/**
* MediaProjection静态持有者 - 增强版
* 专门针对Android 15权限丢失问题进行优化
*
* 🚨 核心修复:添加全局创建锁,确保同一时刻只有一个地方能创建 MediaProjection 实例。
* 重复调用 getMediaProjection(resultCode, resultData) 会创建新实例,
* 系统会自动 stop 旧实例并触发 onStop 回调,形成权限掉落死循环。
*/
object MediaProjectionHolder {
private var mediaProjection: MediaProjection? = null
private var permissionResultCode: Int? = null
private var permissionData: Intent? = null
// 🔒 全局创建锁:防止多个管理器并发调用 getMediaProjection() 创建新实例
private val creationLock = java.util.concurrent.locks.ReentrantLock()
// 🔒 创建中标记:防止 onStop 回调触发的恢复流程与正在进行的创建冲突
@Volatile
private var isCreatingProjection = false
// 🔒 上次创建时间:防止短时间内重复创建
@Volatile
private var lastCreationTime: Long = 0L
private const val MIN_CREATION_INTERVAL = 5000L // 最小创建间隔5秒
// Android 15权限保护增强
@Volatile
private var permissionCreationTime: Long = 0L
@@ -5984,6 +6000,87 @@ object MediaProjectionHolder {
)
}
/**
* 🔒 安全创建 MediaProjection 的唯一入口
*
* 所有需要创建 MediaProjection 的地方都必须通过此方法,
* 禁止直接调用 mediaProjectionManager.getMediaProjection()。
*
* 核心保护机制:
* 1. 优先返回已有对象,避免重复创建
* 2. 全局创建锁,防止并发创建
* 3. 最小创建间隔,防止短时间内重复创建触发 onStop 死循环
*/
fun safeGetOrCreateProjection(
context: android.content.Context,
resultCode: Int,
resultData: Intent
): MediaProjection? {
// 第一步:优先返回已有对象
val existing = mediaProjection
if (existing != null) {
Log.i("MediaProjectionHolder", "✅ safeCreate: 已有有效对象,直接复用")
return existing
}
// 第二步:检查创建间隔
val now = System.currentTimeMillis()
if (now - lastCreationTime < MIN_CREATION_INTERVAL) {
Log.w("MediaProjectionHolder", "⚠️ safeCreate: 距上次创建不足${MIN_CREATION_INTERVAL}ms跳过")
return null
}
// 第三步:加锁创建,防止并发
if (!creationLock.tryLock()) {
Log.w("MediaProjectionHolder", "⚠️ safeCreate: 其他线程正在创建,跳过")
return null
}
try {
// double-check锁内再次检查
val doubleCheck = mediaProjection
if (doubleCheck != null) {
Log.i("MediaProjectionHolder", "✅ safeCreate: double-check发现已有对象")
return doubleCheck
}
isCreatingProjection = true
Log.i("MediaProjectionHolder", "🔒 safeCreate: 开始创建新的MediaProjection")
val manager = context.getSystemService(
android.content.Context.MEDIA_PROJECTION_SERVICE
) as? android.media.projection.MediaProjectionManager
if (manager == null) {
Log.e("MediaProjectionHolder", "❌ safeCreate: MediaProjectionManager为null")
return null
}
val projection = manager.getMediaProjection(resultCode, resultData)
if (projection != null) {
mediaProjection = projection
lastCreationTime = now
permissionCreationTime = now
Log.i("MediaProjectionHolder", "✅ safeCreate: 创建成功 hash=${projection.hashCode()}")
} else {
Log.w("MediaProjectionHolder", "❌ safeCreate: getMediaProjection返回null")
}
return projection
} catch (e: Exception) {
Log.e("MediaProjectionHolder", "❌ safeCreate: 创建异常", e)
return null
} finally {
isCreatingProjection = false
creationLock.unlock()
}
}
/**
* 检查是否正在创建 MediaProjection供 onStop 回调判断是否应跳过恢复)
*/
fun isCreating(): Boolean = isCreatingProjection
// 新增:强制清理方法,仅在确实需要停止时使用
fun forceStopMediaProjection() {
Log.i("MediaProjectionHolder", "🛑 强制停止MediaProjection仅在用户主动停止时使用")