fix: 修复屏幕录制权限频繁掉落的根因问题
- MediaProjectionHolder添加全局创建锁safeGetOrCreateProjection() - 所有创建点统一通过安全入口,禁止直接调用getMediaProjection() - 重复创建新实例会导致系统stop旧实例触发onStop死循环 - Android15MediaProjectionManager/SmartMediaProjectionManager的onStop回调添加isCreating检查 - RemoteControlForegroundService备用方案改用安全创建入口 - AccessibilityRemoteService静默恢复改用安全创建入口 - 添加最小创建间隔5秒防止短时间内重复创建
This commit is contained in:
@@ -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(仅在用户主动停止时使用)")
|
||||
|
||||
@@ -72,95 +72,54 @@ class Android15MediaProjectionManager(
|
||||
@Volatile private var stableBroadcastSent = false
|
||||
|
||||
/**
|
||||
* Android 15 MediaProjection停止回调 - 优化版本
|
||||
* Android 15 MediaProjection停止回调 - 核心修复版本
|
||||
*
|
||||
* 核心修复:
|
||||
* 1. 增加权限稳定期检测,避免频繁触发恢复
|
||||
* 2. 改进保活检查识别逻辑
|
||||
* 3. 添加连续停止计数,防止无限循环
|
||||
* 4. 实现渐进式恢复策略
|
||||
* 🚨 根因修复:onStop 被触发的主要原因是其他组件调用了
|
||||
* getMediaProjection() 创建新实例,系统自动 stop 旧实例。
|
||||
*
|
||||
* 修复策略:
|
||||
* 1. 如果 Holder 中仍有有效对象,说明是旧实例被替换,静默处理
|
||||
* 2. 如果权限数据仍存在,只清理本地引用,不触发恢复
|
||||
* 3. 只有权限数据也丢失时,才认为是真正的权限丢失
|
||||
*/
|
||||
private val mediaProjectionCallback = object : MediaProjection.Callback() {
|
||||
override fun onStop() {
|
||||
val callbackTime = System.currentTimeMillis()
|
||||
val connectionTime = callbackTime - connectionStartTime
|
||||
val timeSincePermissionGranted = callbackTime - permissionGrantedTime
|
||||
val timeSinceLastStop = callbackTime - lastStopTime
|
||||
|
||||
// 更新停止统计
|
||||
consecutiveStopCount++
|
||||
lastStopTime = callbackTime
|
||||
Log.w(TAG, "🛑 Android 15 MediaProjection.onStop() - 连接时长: ${connectionTime}ms")
|
||||
|
||||
val stackTrace = Thread.currentThread().stackTrace.take(3).joinToString("\n") { " at ${it.className}.${it.methodName}(${it.fileName}:${it.lineNumber})" }
|
||||
|
||||
Log.w(TAG, """
|
||||
🛑 Android 15 MediaProjection.onStop() [第${consecutiveStopCount}次]
|
||||
📍 调用时间: $callbackTime
|
||||
⏰ 连接时长: ${connectionTime}ms (${connectionTime/1000.0}s)
|
||||
⏰ 权限年龄: ${timeSincePermissionGranted}ms (${timeSincePermissionGranted/1000.0}s)
|
||||
⏰ 距上次停止: ${timeSinceLastStop}ms
|
||||
📊 稳定期状态: isInStablePeriod=$isInStablePeriod, isPermissionStable=$isPermissionStable
|
||||
📊 恢复状态: isRecovering=$isRecovering, attempts=$recoveryAttempts
|
||||
📍 调用堆栈: $stackTrace
|
||||
""".trimIndent())
|
||||
|
||||
// ✅ 核心修复1:权限稳定期检测
|
||||
if (timeSincePermissionGranted < PERMISSION_STABLE_PERIOD && !isPermissionStable) {
|
||||
Log.i(TAG, "🛡️ 权限还在稳定期内(${timeSincePermissionGranted}ms < ${PERMISSION_STABLE_PERIOD}ms),这很可能是系统保活检查")
|
||||
handleKeepAliveCheck(connectionTime, timeSinceLastStop)
|
||||
// 🔒 如果 Holder 正在创建新实例,旧实例的 onStop 静默跳过
|
||||
if (MediaProjectionHolder.isCreating()) {
|
||||
Log.i(TAG, "🔒 Holder正在创建新实例,旧实例onStop静默跳过")
|
||||
mediaProjection = null
|
||||
return
|
||||
}
|
||||
|
||||
// ✅ 核心修复2:检测无限循环并强制停止
|
||||
if (consecutiveStopCount > MAX_CONSECUTIVE_STOPS && timeSinceLastStop < 30000) {
|
||||
Log.w(TAG, "⚠️ 检测到可能的无限循环:连续${consecutiveStopCount}次停止,强制标记为稳定")
|
||||
forceMarkAsStable()
|
||||
// ✅ 核心检查:Holder 中是否仍有有效对象
|
||||
val holderProjection = MediaProjectionHolder.getMediaProjection()
|
||||
val hasPermissionData = MediaProjectionHolder.getPermissionData() != null
|
||||
|
||||
if (holderProjection != null) {
|
||||
Log.i(TAG, "🛡️ Holder中仍有有效对象,旧实例被替换,静默处理")
|
||||
mediaProjection = null
|
||||
return
|
||||
}
|
||||
|
||||
// ✅ 核心修复3:如果权限已稳定,停止处理
|
||||
if (isPermissionStable || stopAllMonitoring) {
|
||||
Log.i(TAG, "🛡️ 权限已稳定或停止监听,跳过onStop处理")
|
||||
if (hasPermissionData) {
|
||||
Log.i(TAG, "🛡️ 权限数据仍存在,仅清理本地引用,不触发恢复")
|
||||
mediaProjection?.unregisterCallback(this)
|
||||
mediaProjection = null
|
||||
return
|
||||
}
|
||||
|
||||
// ✅ 核心修复4:恢复冷却期检测
|
||||
if (isRecovering || (callbackTime - lastRecoveryTime) < RECOVERY_COOLDOWN_PERIOD) {
|
||||
Log.w(TAG, "❄️ 恢复冷却期内或正在恢复中,跳过处理")
|
||||
logPermissionState("冷却期内跳过")
|
||||
return
|
||||
}
|
||||
|
||||
// Android 15特殊处理:判断停止原因
|
||||
val stopReason = determineStopReason(callbackTime, connectionTime, timeSinceLastStop)
|
||||
|
||||
// ✅ 根据停止原因决定处理策略
|
||||
when (stopReason) {
|
||||
"USER_STOPPED_VIA_STATUS_BAR" -> {
|
||||
Log.i(TAG, "🔴 用户主动停止,标记为稳定状态")
|
||||
handleUserStoppedSharing()
|
||||
return
|
||||
}
|
||||
"DEVICE_LOCKED" -> {
|
||||
Log.i(TAG, "🔒 设备锁屏停止,暂停但保留权限")
|
||||
handleDeviceLocked()
|
||||
return
|
||||
}
|
||||
"SYSTEM_KEEPALIVE_CHECK" -> {
|
||||
Log.i(TAG, "⚡ 系统保活检查,静默处理")
|
||||
handleKeepAliveCheck(connectionTime, timeSinceLastStop)
|
||||
return
|
||||
}
|
||||
"RAPID_CONSECUTIVE_STOPS" -> {
|
||||
Log.w(TAG, "🔄 连续快速停止,可能是权限冲突")
|
||||
handleRapidConsecutiveStops()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 如果到达这里,说明可能是真正的权限丢失
|
||||
Log.w(TAG, "❌ 疑似真正的权限丢失,启动渐进式恢复")
|
||||
handleSuspectedPermissionLoss(connectionTime)
|
||||
// 权限数据也丢失了,这是真正的权限丢失(用户主动停止)
|
||||
Log.w(TAG, "❌ 权限数据已丢失,判定为用户主动停止")
|
||||
mediaProjection?.unregisterCallback(this)
|
||||
mediaProjection = null
|
||||
isPermissionStable = true
|
||||
stopAllMonitoring = true
|
||||
onPermissionLost()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,32 +223,19 @@ class Android15MediaProjectionManager(
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 新增:处理疑似权限丢失
|
||||
* ✅ 新增:处理疑似权限丢失 - 简化版本
|
||||
*
|
||||
* 只清理本地引用,不触发任何恢复机制
|
||||
*/
|
||||
private fun handleSuspectedPermissionLoss(connectionTime: Long) {
|
||||
Log.w(TAG, "❓ 处理疑似权限丢失 - 连接时长: ${connectionTime}ms")
|
||||
|
||||
// 🛡️ 核心修复:检查是否为权限保活状态,避免在保活时启动权限恢复
|
||||
val hasPermissionData = MediaProjectionHolder.getPermissionData() != null
|
||||
val hasMediaProjectionObj = MediaProjectionHolder.getMediaProjection() != null
|
||||
|
||||
if (hasPermissionData && hasMediaProjectionObj) {
|
||||
Log.i(TAG, "🛡️ [权限保活] 检测到权限和对象都存在,这可能是保活检查,跳过疑似权限丢失处理避免弹窗")
|
||||
Log.i(TAG, "🛡️ [权限保活] 权限数据存在: $hasPermissionData, MediaProjection对象存在: $hasMediaProjectionObj")
|
||||
return
|
||||
}
|
||||
|
||||
// 渐进式处理:不立即清理权限数据
|
||||
// 清理本地引用
|
||||
mediaProjection?.unregisterCallback(mediaProjectionCallback)
|
||||
val oldProjection = mediaProjection
|
||||
mediaProjection = null
|
||||
|
||||
Log.d(TAG, "🧹 疑似丢失:清理本地MediaProjection ${oldProjection?.hashCode()}")
|
||||
|
||||
// 🚨 关键:暂时不清理MediaProjectionHolder数据,先尝试恢复
|
||||
|
||||
// 启动渐进式恢复
|
||||
startProgressiveRecovery()
|
||||
// 通知权限丢失
|
||||
onPermissionLost()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -377,213 +323,66 @@ class Android15MediaProjectionManager(
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 新增:渐进式恢复机制 - 替代激进的智能恢复
|
||||
* ✅ 新增:渐进式恢复机制 - 只从 Holder 获取已有对象
|
||||
*
|
||||
* 🚨 核心修复:禁止调用 getMediaProjection() 创建新实例
|
||||
*/
|
||||
private fun startProgressiveRecovery() {
|
||||
Log.i(TAG, "🔄 启动渐进式权限恢复")
|
||||
logPermissionState("渐进式恢复开始")
|
||||
Log.i(TAG, "🔄 启动渐进式权限恢复(仅复用已有对象)")
|
||||
|
||||
// 🛡️ 核心修复:检查是否为权限保活状态,避免在保活时启动恢复
|
||||
val hasPermissionData = MediaProjectionHolder.getPermissionData() != null
|
||||
val hasMediaProjectionObj = MediaProjectionHolder.getMediaProjection() != null
|
||||
|
||||
if (hasPermissionData && hasMediaProjectionObj) {
|
||||
Log.i(TAG, "🛡️ [权限保活] 检测到权限和对象都存在,这可能是保活检查,跳过渐进式恢复避免弹窗")
|
||||
Log.i(TAG, "🛡️ [权限保活] 权限数据存在: $hasPermissionData, MediaProjection对象存在: $hasMediaProjectionObj")
|
||||
return
|
||||
}
|
||||
|
||||
if (isRecovering) {
|
||||
Log.d(TAG, "🔄 恢复进程已在进行中,跳过")
|
||||
return
|
||||
}
|
||||
|
||||
if (recoveryAttempts >= MAX_RECOVERY_ATTEMPTS) {
|
||||
Log.w(TAG, "⚠️ 已达到最大恢复尝试次数($MAX_RECOVERY_ATTEMPTS),转入稳定模式")
|
||||
forceMarkAsStable()
|
||||
return
|
||||
}
|
||||
|
||||
// 检查冷却期
|
||||
val currentTime = System.currentTimeMillis()
|
||||
if ((currentTime - lastRecoveryTime) < RECOVERY_COOLDOWN_PERIOD) {
|
||||
Log.w(TAG, "❄️ 恢复冷却期内,跳过恢复")
|
||||
return
|
||||
}
|
||||
|
||||
isRecovering = true
|
||||
recoveryAttempts++
|
||||
lastRecoveryTime = currentTime
|
||||
|
||||
Log.i(TAG, "🔄 开始渐进式权限恢复 (尝试 $recoveryAttempts/$MAX_RECOVERY_ATTEMPTS)")
|
||||
|
||||
recoveryScope.launch {
|
||||
try {
|
||||
// 第一阶段:等待系统稳定
|
||||
Log.d(TAG, "📊 阶段1:等待系统稳定 (3秒)")
|
||||
delay(3000)
|
||||
|
||||
// 第二阶段:检查权限数据完整性
|
||||
Log.d(TAG, "📊 阶段2:检查权限数据完整性")
|
||||
val permissionData = MediaProjectionHolder.getPermissionData()
|
||||
if (permissionData == null) {
|
||||
Log.w(TAG, "❌ 权限数据丢失,无法恢复")
|
||||
handleRecoveryFailure("权限数据丢失")
|
||||
return@launch
|
||||
}
|
||||
|
||||
val (resultCode, resultData) = permissionData
|
||||
if (resultData == null) {
|
||||
Log.w(TAG, "❌ 权限Intent丢失,无法恢复")
|
||||
handleRecoveryFailure("权限Intent丢失")
|
||||
return@launch
|
||||
}
|
||||
|
||||
// 第三阶段:尝试静默恢复
|
||||
Log.d(TAG, "📊 阶段3:尝试静默恢复")
|
||||
if (attemptSilentRecovery()) {
|
||||
Log.i(TAG, "✅ 渐进式恢复成功")
|
||||
isRecovering = false
|
||||
consecutiveStopCount = 0 // 重置停止计数
|
||||
// 检查 Holder 中是否有有效对象
|
||||
val holderProjection = MediaProjectionHolder.getMediaProjection()
|
||||
if (holderProjection != null) {
|
||||
Log.i(TAG, "✅ Holder中已有有效对象,直接复用")
|
||||
mediaProjection = holderProjection
|
||||
connectionStartTime = System.currentTimeMillis()
|
||||
onPermissionRecovered()
|
||||
return@launch
|
||||
return
|
||||
}
|
||||
|
||||
// 第四阶段:延迟后再次尝试
|
||||
Log.d(TAG, "📊 阶段4:延迟后再次尝试")
|
||||
delay(5000)
|
||||
|
||||
if (attemptSilentRecovery()) {
|
||||
Log.i(TAG, "✅ 延迟恢复成功")
|
||||
isRecovering = false
|
||||
consecutiveStopCount = 0
|
||||
onPermissionRecovered()
|
||||
return@launch
|
||||
}
|
||||
|
||||
// 第五阶段:检查是否需要重新申请权限
|
||||
Log.d(TAG, "📊 阶段5:评估是否需要重新申请权限")
|
||||
if (recoveryAttempts < MAX_RECOVERY_ATTEMPTS) {
|
||||
Log.i(TAG, "🔄 静默恢复失败,考虑重新申请权限")
|
||||
handleRecoveryFailure("静默恢复失败")
|
||||
} else {
|
||||
Log.w(TAG, "⚠️ 达到最大恢复次数,转入稳定模式")
|
||||
forceMarkAsStable()
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "❌ 渐进式恢复异常", e)
|
||||
handleRecoveryFailure("恢复异常: ${e.message}")
|
||||
} finally {
|
||||
isRecovering = false
|
||||
}
|
||||
}
|
||||
// Holder 中无有效对象,通知权限丢失,等待外部重新授予
|
||||
Log.w(TAG, "❌ Holder中无有效对象,通知权限丢失")
|
||||
onPermissionLost()
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 新增:处理恢复失败
|
||||
* ✅ 新增:处理恢复失败 - 简化版本,不再触发重新申请
|
||||
*/
|
||||
private fun handleRecoveryFailure(reason: String) {
|
||||
Log.w(TAG, "❌ 权限恢复失败: $reason")
|
||||
|
||||
// 🛡️ 核心修复:检查是否为权限保活状态,避免在保活时触发权限重新申请
|
||||
val hasPermissionData = MediaProjectionHolder.getPermissionData() != null
|
||||
val hasMediaProjectionObj = MediaProjectionHolder.getMediaProjection() != null
|
||||
|
||||
if (hasPermissionData && hasMediaProjectionObj) {
|
||||
Log.i(TAG, "🛡️ [权限保活] 检测到权限和对象都存在,这可能是保活检查,跳过恢复失败处理避免弹窗")
|
||||
Log.i(TAG, "🛡️ [权限保活] 权限数据存在: $hasPermissionData, MediaProjection对象存在: $hasMediaProjectionObj")
|
||||
return
|
||||
}
|
||||
|
||||
// 如果恢复次数较少,等待更长时间后重试
|
||||
if (recoveryAttempts < MAX_RECOVERY_ATTEMPTS) {
|
||||
Log.i(TAG, "🕐 等待30秒后重试恢复")
|
||||
recoveryScope.launch {
|
||||
delay(30000) // 等待30秒
|
||||
if (!isPermissionStable && !stopAllMonitoring) {
|
||||
Log.i(TAG, "🔄 30秒后重试渐进式恢复")
|
||||
startProgressiveRecovery()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "⚠️ 多次恢复失败,考虑重新申请权限")
|
||||
triggerPermissionReRequest()
|
||||
}
|
||||
|
||||
// 通知权限丢失(但不立即清理)
|
||||
// 通知权限丢失,由外部决定是否重新申请
|
||||
onPermissionLost()
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 静默权限恢复(改进版本)
|
||||
*
|
||||
* 🚨 核心修复:优先检查 Holder 中是否已有有效对象,
|
||||
* 避免重复调用 getMediaProjection() 创建新实例导致旧实例被系统 stop,
|
||||
* 这是权限频繁掉落的根因。
|
||||
* 🚨 核心修复:禁止调用 getMediaProjection() 创建新实例!
|
||||
* 每次创建新实例,系统会自动 stop 旧实例,触发 onStop 回调,
|
||||
* 形成"权限丢失→恢复→再丢失"的死循环,这是权限频繁掉落的根因。
|
||||
*
|
||||
* 只从 Holder 获取已有对象,不重新创建。
|
||||
*/
|
||||
private suspend fun attemptSilentRecovery(): Boolean {
|
||||
return try {
|
||||
Log.i(TAG, "🤫 尝试静默权限恢复")
|
||||
logPermissionState("静默恢复开始")
|
||||
Log.i(TAG, "🤫 尝试静默权限恢复(仅复用已有对象)")
|
||||
|
||||
// 🛡️ 核心修复:检查是否为权限保活状态,避免在保活时进行恢复
|
||||
val hasPermissionData = MediaProjectionHolder.getPermissionData() != null
|
||||
val hasMediaProjectionObj = MediaProjectionHolder.getMediaProjection() != null
|
||||
|
||||
if (hasPermissionData && hasMediaProjectionObj) {
|
||||
Log.i(TAG, "🛡️ [权限保活] Holder中已有有效MediaProjection,直接复用")
|
||||
// 更新本地引用
|
||||
mediaProjection = MediaProjectionHolder.getMediaProjection()
|
||||
return true
|
||||
}
|
||||
|
||||
// Holder 中无有效对象,检查权限数据
|
||||
val permissionData = MediaProjectionHolder.getPermissionData()
|
||||
if (permissionData == null) {
|
||||
Log.w(TAG, "❌ 无权限数据,静默恢复失败")
|
||||
return false
|
||||
}
|
||||
|
||||
val (resultCode, resultData) = permissionData
|
||||
if (resultData == null) {
|
||||
Log.w(TAG, "❌ 权限Intent为空,静默恢复失败")
|
||||
return false
|
||||
}
|
||||
|
||||
Log.d(TAG, "🔑 使用现有权限数据进行静默恢复")
|
||||
|
||||
// 确保MediaProjectionManager已初始化
|
||||
if (mediaProjectionManager == null) {
|
||||
mediaProjectionManager = context.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
|
||||
}
|
||||
|
||||
val projection = mediaProjectionManager?.getMediaProjection(resultCode, resultData)
|
||||
|
||||
if (projection != null) {
|
||||
Log.d(TAG, "🏭 MediaProjection创建成功: ${projection.hashCode()}")
|
||||
|
||||
// 更新连接时间
|
||||
// 从 Holder 获取已有对象
|
||||
val existingProjection = MediaProjectionHolder.getMediaProjection()
|
||||
if (existingProjection != null) {
|
||||
Log.i(TAG, "✅ Holder中已有有效MediaProjection,直接复用")
|
||||
mediaProjection = existingProjection
|
||||
connectionStartTime = System.currentTimeMillis()
|
||||
|
||||
// 注册回调
|
||||
projection.registerCallback(mediaProjectionCallback, Handler(Looper.getMainLooper()))
|
||||
|
||||
// 更新引用
|
||||
mediaProjection = projection
|
||||
MediaProjectionHolder.setMediaProjection(projection)
|
||||
|
||||
logPermissionState("静默恢复成功")
|
||||
Log.i(TAG, "✅ 静默恢复成功")
|
||||
return true
|
||||
} else {
|
||||
Log.w(TAG, "❌ MediaProjection创建失败")
|
||||
return false
|
||||
}
|
||||
|
||||
// 🚨 Holder 中无有效对象,不再重新创建!
|
||||
// 重新创建会导致旧实例被 stop,形成死循环
|
||||
Log.w(TAG, "❌ Holder中无有效MediaProjection,静默恢复失败,等待权限重新授予")
|
||||
false
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "❌ 静默恢复异常", e)
|
||||
return false
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -672,39 +471,35 @@ class Android15MediaProjectionManager(
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 改进:智能恢复策略 - 避免频繁重新授权
|
||||
* ✅ 改进:智能恢复策略 - 只在确实需要时才重新申请权限
|
||||
*
|
||||
* 🚨 注意:此方法会启动 MainActivity 重新申请权限,
|
||||
* 只有在权限数据完全丢失时才应调用。
|
||||
*/
|
||||
private fun triggerPermissionReRequest() {
|
||||
try {
|
||||
// 🛡️ 核心修复:检查是否为权限保活状态,避免在保活时触发权限重新申请
|
||||
val hasPermissionData = MediaProjectionHolder.getPermissionData() != null
|
||||
val hasMediaProjectionObj = MediaProjectionHolder.getMediaProjection() != null
|
||||
// 防御性检查:如果 Holder 中仍有有效对象,不需要重新申请
|
||||
val holderProjection = MediaProjectionHolder.getMediaProjection()
|
||||
if (holderProjection != null) {
|
||||
Log.i(TAG, "🛡️ Holder中仍有有效对象,跳过权限重新申请")
|
||||
return
|
||||
}
|
||||
|
||||
if (hasPermissionData && hasMediaProjectionObj) {
|
||||
Log.i(TAG, "🛡️ [权限保活] 检测到权限和对象都存在,这可能是保活检查,跳过权限重新申请避免弹窗")
|
||||
Log.i(TAG, "🛡️ [权限保活] 权限数据存在: $hasPermissionData, MediaProjection对象存在: $hasMediaProjectionObj")
|
||||
val hasPermissionData = MediaProjectionHolder.getPermissionData() != null
|
||||
if (hasPermissionData) {
|
||||
Log.i(TAG, "🛡️ 权限数据仍存在,跳过权限重新申请")
|
||||
return
|
||||
}
|
||||
|
||||
val currentTime = System.currentTimeMillis()
|
||||
|
||||
// 检查恢复频率,避免频繁弹出授权对话框
|
||||
if (currentTime - lastRecoveryTime < MIN_RECOVERY_INTERVAL) {
|
||||
Log.w(TAG, "⚠️ 距离上次权限恢复时间过短,跳过重新授权")
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否已经尝试过太多次
|
||||
if (recoveryAttempts >= MAX_RECOVERY_ATTEMPTS) {
|
||||
Log.w(TAG, "⚠️ 恢复尝试次数过多,转入稳定模式")
|
||||
forceMarkAsStable()
|
||||
return
|
||||
}
|
||||
|
||||
lastRecoveryTime = currentTime
|
||||
Log.i(TAG, "🚀 触发MediaProjection权限重新申请")
|
||||
|
||||
// 发送权限重新申请广播
|
||||
val intent = Intent(context, com.hikoncont.MainActivity::class.java).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
putExtra("AUTO_REQUEST_PERMISSION", true)
|
||||
@@ -713,43 +508,35 @@ class Android15MediaProjectionManager(
|
||||
}
|
||||
context.startActivity(intent)
|
||||
|
||||
Log.i(TAG, "✅ 已启动MediaProjection权限重新申请")
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "❌ 触发权限重新申请失败", e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 新增:创建MediaProjection并注册回调
|
||||
* ✅ 创建MediaProjection并注册回调
|
||||
*
|
||||
* 🚨 注意:此方法只应在首次权限授予时调用一次!
|
||||
* 后续所有组件应从 Holder 获取已有对象,禁止重复创建。
|
||||
*/
|
||||
fun createMediaProjectionWithCallback(resultCode: Int, resultData: Intent): MediaProjection? {
|
||||
return try {
|
||||
Log.i(TAG, "🏭 创建MediaProjection并注册回调")
|
||||
Log.i(TAG, "🏭 创建MediaProjection并注册回调(通过安全创建入口)")
|
||||
|
||||
if (mediaProjectionManager == null) {
|
||||
mediaProjectionManager = context.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
|
||||
}
|
||||
|
||||
val projection = mediaProjectionManager?.getMediaProjection(resultCode, resultData)
|
||||
// 🚨 核心修复:通过 Holder 的安全创建入口统一创建
|
||||
val projection = MediaProjectionHolder.safeGetOrCreateProjection(
|
||||
context, resultCode, resultData
|
||||
)
|
||||
|
||||
if (projection != null) {
|
||||
// 设置连接开始时间
|
||||
connectionStartTime = System.currentTimeMillis()
|
||||
|
||||
// 注册回调
|
||||
projection.registerCallback(mediaProjectionCallback, Handler(Looper.getMainLooper()))
|
||||
|
||||
// 更新本地引用
|
||||
mediaProjection = projection
|
||||
|
||||
// 设置权限获取时间
|
||||
setPermissionGrantedTime()
|
||||
|
||||
Log.i(TAG, "✅ MediaProjection创建成功: ${projection.hashCode()}")
|
||||
return projection
|
||||
} else {
|
||||
Log.w(TAG, "❌ MediaProjection创建失败")
|
||||
Log.w(TAG, "❌ MediaProjection创建失败(安全创建入口返回null)")
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -2329,14 +2329,14 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
||||
/**
|
||||
* Android 15:重新生成MediaProjection以解决单次令牌限制
|
||||
*
|
||||
* 🚨 核心修复:优先从 Holder 获取已有对象,避免重复创建
|
||||
* 🚨 核心修复:只从 Holder 获取已有对象,禁止重复创建
|
||||
*/
|
||||
private fun regenerateMediaProjectionForAndroid15(): Boolean {
|
||||
return try {
|
||||
if (Build.VERSION.SDK_INT >= 35) {
|
||||
Log.i(TAG, "🔄 Android 15:尝试获取可用的MediaProjection")
|
||||
|
||||
// ✅ 优先从 Holder 获取已有对象
|
||||
// 只从 Holder 获取已有对象
|
||||
val existingProjection = MediaProjectionHolder.getMediaProjection()
|
||||
if (existingProjection != null) {
|
||||
Log.i(TAG, "✅ Holder中已有有效MediaProjection,直接复用")
|
||||
@@ -2344,29 +2344,12 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Holder 中无有效对象,从权限数据创建(仅一次)
|
||||
val permissionData = MediaProjectionHolder.getPermissionData()
|
||||
if (permissionData != null) {
|
||||
val (resultCode, resultData) = permissionData
|
||||
if (resultData != null) {
|
||||
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
|
||||
}
|
||||
Log.e(TAG, "❌ Android 15 MediaProjection重新生成失败")
|
||||
}
|
||||
}
|
||||
Log.e(TAG, "❌ Android 15重新生成失败:无有效权限数据")
|
||||
// 🚨 不再重新创建!避免死循环
|
||||
Log.w(TAG, "❌ Holder中无有效MediaProjection,等待权限重新授予")
|
||||
}
|
||||
false
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "❌ Android 15重新生成MediaProjection异常", e)
|
||||
Log.e(TAG, "❌ Android 15获取MediaProjection异常", e)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,11 +108,36 @@ class SmartMediaProjectionManager(
|
||||
|
||||
/**
|
||||
* 智能MediaProjection回调
|
||||
* 能够智能判断权限丢失的原因
|
||||
*
|
||||
* 🚨 核心修复:如果 Holder 中仍有有效对象,说明是旧实例被替换,
|
||||
* 不是真正的权限丢失,静默处理。
|
||||
*/
|
||||
private val smartCallback = object : MediaProjection.Callback() {
|
||||
override fun onStop() {
|
||||
Log.w(TAG, "🛑 MediaProjection权限丢失")
|
||||
Log.w(TAG, "🛑 SmartManager: MediaProjection.onStop()")
|
||||
|
||||
// 🔒 如果 Holder 正在创建新实例,旧实例的 onStop 静默跳过
|
||||
if (MediaProjectionHolder.isCreating()) {
|
||||
Log.i(TAG, "🔒 Holder正在创建新实例,旧实例onStop静默跳过")
|
||||
mediaProjection = null
|
||||
return
|
||||
}
|
||||
|
||||
// 🛡️ Holder 中仍有有效对象,说明是旧实例被替换
|
||||
val holderProjection = MediaProjectionHolder.getMediaProjection()
|
||||
if (holderProjection != null) {
|
||||
Log.i(TAG, "🛡️ Holder中仍有有效对象,旧实例被替换,静默处理")
|
||||
mediaProjection = null
|
||||
return
|
||||
}
|
||||
|
||||
// 权限数据仍存在,只清理本地引用,不触发恢复
|
||||
val hasPermissionData = MediaProjectionHolder.getPermissionData() != null
|
||||
if (hasPermissionData) {
|
||||
Log.i(TAG, "🛡️ 权限数据仍存在,仅清理本地引用")
|
||||
mediaProjection = null
|
||||
return
|
||||
}
|
||||
|
||||
val currentTime = System.currentTimeMillis()
|
||||
lastPermissionLostTime = currentTime
|
||||
@@ -264,8 +289,11 @@ class SmartMediaProjectionManager(
|
||||
/**
|
||||
* 尝试静默恢复
|
||||
*
|
||||
* 🚨 核心修复:优先检查 Holder 中是否已有有效对象,
|
||||
* 避免重复调用 getMediaProjection() 创建新实例导致旧实例被 stop
|
||||
* 🚨 核心修复:禁止调用 getMediaProjection() 创建新实例!
|
||||
* 每次创建新实例,系统会自动 stop 旧实例,触发 onStop 回调,
|
||||
* 形成"权限丢失→恢复→再丢失"的死循环。
|
||||
*
|
||||
* 只从 Holder 获取已有对象,不重新创建。
|
||||
*/
|
||||
private suspend fun attemptSilentRecovery(): Boolean {
|
||||
val currentAttempts = silentRecoveryAttempts.get()
|
||||
@@ -279,10 +307,9 @@ class SmartMediaProjectionManager(
|
||||
Log.i(TAG, "🤫 尝试静默恢复 (${silentRecoveryAttempts.get()}/$MAX_SILENT_RECOVERY_ATTEMPTS)")
|
||||
|
||||
try {
|
||||
// 延迟恢复,避免与系统操作冲突
|
||||
delay(SILENT_RECOVERY_DELAY)
|
||||
|
||||
// ✅ 优先检查 Holder 中是否已有有效的 MediaProjection 对象
|
||||
// 只从 Holder 获取已有对象
|
||||
val existingProjection = MediaProjectionHolder.getMediaProjection()
|
||||
if (existingProjection != null) {
|
||||
Log.i(TAG, "✅ Holder中已有有效MediaProjection,直接复用")
|
||||
@@ -292,22 +319,8 @@ class SmartMediaProjectionManager(
|
||||
return true
|
||||
}
|
||||
|
||||
// Holder 中无有效对象,尝试从权限数据重新创建(仅一次)
|
||||
val permissionData = MediaProjectionHolder.getPermissionData()
|
||||
if (permissionData != null) {
|
||||
val (resultCode, resultData) = permissionData
|
||||
if (resultData != null) {
|
||||
val newProjection = createMediaProjectionSafely(resultCode, resultData)
|
||||
if (newProjection != null) {
|
||||
Log.i(TAG, "✅ 静默恢复成功")
|
||||
notifyPermissionRecovered()
|
||||
resetRecoveryCounters()
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.w(TAG, "❌ 静默恢复失败,权限数据不可用")
|
||||
// 🚨 不再重新创建!避免死循环
|
||||
Log.w(TAG, "❌ Holder中无有效MediaProjection,等待权限重新授予")
|
||||
return false
|
||||
|
||||
} catch (e: Exception) {
|
||||
@@ -318,19 +331,23 @@ class SmartMediaProjectionManager(
|
||||
|
||||
/**
|
||||
* 安全地创建MediaProjection
|
||||
* 🚨 核心修复:通过 MediaProjectionHolder.safeGetOrCreateProjection() 统一创建,
|
||||
* 避免重复调用 getMediaProjection() 导致旧实例被 stop 的死循环
|
||||
*/
|
||||
private fun createMediaProjectionSafely(resultCode: Int, resultData: Intent): MediaProjection? {
|
||||
return try {
|
||||
val projection = mediaProjectionManager?.getMediaProjection(resultCode, resultData)
|
||||
val projection = MediaProjectionHolder.safeGetOrCreateProjection(
|
||||
context, resultCode, resultData
|
||||
)
|
||||
|
||||
if (projection != null) {
|
||||
// 注册智能回调
|
||||
projection.registerCallback(smartCallback, Handler(Looper.getMainLooper()))
|
||||
|
||||
mediaProjection = projection
|
||||
MediaProjectionHolder.setMediaProjection(projection)
|
||||
// safeGetOrCreateProjection 内部已设置到 Holder
|
||||
|
||||
Log.i(TAG, "✅ MediaProjection创建成功")
|
||||
Log.i(TAG, "✅ MediaProjection创建成功(通过安全创建入口)")
|
||||
}
|
||||
|
||||
projection
|
||||
|
||||
@@ -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,等待权限授予")
|
||||
}
|
||||
|
||||
// 标记权限完成
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user