package com.hikoncont.manager import android.content.Context import android.content.Intent import android.hardware.display.DisplayManager import android.media.projection.MediaProjection import android.media.projection.MediaProjectionManager import android.os.Build import android.os.Handler import android.os.Looper import android.util.Log import androidx.annotation.RequiresApi import com.hikoncont.MediaProjectionHolder import kotlinx.coroutines.* /** * Android 15专用的MediaProjection管理器 * * Android 15引入了更严格的MediaProjection管理: * 1. 状态栏芯片允许用户随时停止屏幕共享 * 2. 锁屏时自动停止MediaProjection * 3. MediaProjection.Callback.onStop()在权限被系统停止时调用 * 4. 每个权限会话只能调用一次createVirtualDisplay() * 5. Intent只能使用一次,不能重复传递给getMediaProjection() * 6. 每次都需要用户同意 */ @RequiresApi(35) // Android 15 = API 35 class Android15MediaProjectionManager( private val context: Context, private val onPermissionLost: () -> Unit, private val onPermissionRecovered: () -> Unit ) { companion object { private const val TAG = "Android15MediaProjection" private const val MAX_RECOVERY_ATTEMPTS = 3 private const val RECOVERY_DELAY_MS = 2000L private const val MIN_RECOVERY_INTERVAL = 60000L // 最小恢复间隔:60秒 // ✅ 新增:权限稳定期配置 private const val PERMISSION_STABLE_PERIOD = 45000L // 45秒稳定期 private const val KEEPALIVE_CHECK_THRESHOLD = 10000L // 10秒内视为保活检查 private const val RECOVERY_COOLDOWN_PERIOD = 120000L // 2分钟恢复冷却期 private const val MAX_CONSECUTIVE_STOPS = 3 // 最大连续停止次数 } private var mediaProjection: MediaProjection? = null private var mediaProjectionManager: MediaProjectionManager? = null private var recoveryAttempts = 0 private var isRecovering = false private var lastRecoveryTime = 0L // 上次权限恢复时间 private val recoveryScope = CoroutineScope(Dispatchers.Main + SupervisorJob()) // ✅ Android 15新增:Session管理 private var currentSessionId: String? = null private var sessionUsed = false private var lastIntentData: Intent? = null private var lastResultCode: Int? = null // 连接开始时间记录 private var connectionStartTime = 0L // ✅ 权限稳定性管理 - 核心修复 private var isPermissionStable = false private var stopAllMonitoring = false private var permissionGrantedTime = 0L // 权限获取时间 private var consecutiveStopCount = 0 // 连续停止计数 private var lastStopTime = 0L // 上次停止时间 private var isInStablePeriod = false // 是否在稳定期内 // 防重复发送权限稳定广播的标记 @Volatile private var stableBroadcastSent = false /** * Android 15 MediaProjection停止回调 - 优化版本 * * 核心修复: * 1. 增加权限稳定期检测,避免频繁触发恢复 * 2. 改进保活检查识别逻辑 * 3. 添加连续停止计数,防止无限循环 * 4. 实现渐进式恢复策略 */ 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 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) return } // ✅ 核心修复2:检测无限循环并强制停止 if (consecutiveStopCount > MAX_CONSECUTIVE_STOPS && timeSinceLastStop < 30000) { Log.w(TAG, "⚠️ 检测到可能的无限循环:连续${consecutiveStopCount}次停止,强制标记为稳定") forceMarkAsStable() return } // ✅ 核心修复3:如果权限已稳定,停止处理 if (isPermissionStable || stopAllMonitoring) { Log.i(TAG, "🛡️ 权限已稳定或停止监听,跳过onStop处理") 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) } } /** * ✅ 新增:处理系统保活检查 */ private fun handleKeepAliveCheck(connectionTime: Long, timeSinceLastStop: Long) { Log.i(TAG, "🛡️ 处理系统保活检查 - 连接时长: ${connectionTime}ms, 距上次: ${timeSinceLastStop}ms") // 只清理本地MediaProjection引用,不触发任何恢复机制 mediaProjection?.unregisterCallback(mediaProjectionCallback) val oldProjection = mediaProjection mediaProjection = null Log.d(TAG, "🧹 保活检查:仅清理本地引用 ${oldProjection?.hashCode()} -> null") // 🚨 关键:不清理MediaProjectionHolder中的数据 // 🚨 关键:不启动任何恢复机制 // 🚨 关键:不调用onPermissionLost() logPermissionState("保活检查处理完成") // 🛡️ 核心修复:完全跳过恢复检查,避免触发权限弹窗 Log.i(TAG, "🛡️ [权限保活] 保活检查处理完成,完全跳过恢复机制避免权限弹窗") // 不再进行任何恢复检查或操作 // 注释掉原来的恢复检查代码,避免触发权限弹窗 /* // 短暂延迟后检查是否需要静默恢复 recoveryScope.launch { delay(2000) // 等待2秒,让系统完成保活检查 // 只有在真的需要时才恢复 val hasPermissionData = MediaProjectionHolder.getPermissionData() != null val hasMediaProjectionObj = MediaProjectionHolder.getMediaProjection() != null if (hasPermissionData && !hasMediaProjectionObj && mediaProjection == null) { Log.d(TAG, "🔄 保活检查后静默恢复MediaProjection对象") attemptSilentRecovery() } else { Log.d(TAG, "✅ 保活检查后无需恢复,状态正常") } } */ } /** * ✅ 新增:处理连续快速停止 */ private fun handleRapidConsecutiveStops() { Log.w(TAG, "🔄 处理连续快速停止,可能存在权限冲突") // 强制进入冷却期 lastRecoveryTime = System.currentTimeMillis() // 清理本地状态 mediaProjection?.unregisterCallback(mediaProjectionCallback) mediaProjection = null Log.i(TAG, "❄️ 进入强制冷却期,2分钟内不进行任何恢复尝试") // 延迟检查权限状态 recoveryScope.launch { delay(5000) // 等待5秒 val permissionData = MediaProjectionHolder.getPermissionData() if (permissionData != null) { Log.i(TAG, "🛡️ 权限数据仍然存在,可能只是临时冲突") // 不立即恢复,等待冷却期结束 } else { Log.w(TAG, "❌ 权限数据丢失,可能是真正的权限问题") // 也不立即恢复,避免无限循环 } } } /** * ✅ 新增:强制标记为稳定状态 */ private fun forceMarkAsStable() { Log.w(TAG, "🛡️ 强制标记权限为稳定状态,停止所有监听和恢复") isPermissionStable = true stopAllMonitoring = true isInStablePeriod = true // 停止所有恢复协程 try { recoveryScope.cancel() } catch (e: Exception) { Log.w(TAG, "停止恢复协程失败", e) } // 清理回调 mediaProjection?.unregisterCallback(mediaProjectionCallback) // 重置计数器 consecutiveStopCount = 0 Log.i(TAG, "✅ 已强制标记为稳定,所有恢复机制已停止") } /** * ✅ 新增:处理疑似权限丢失 */ 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() } /** * ✅ 改进:判断MediaProjection停止原因 - 更精确的判断逻辑 */ private fun determineStopReason(stopTime: Long, connectionDuration: Long, timeSinceLastStop: Long): String { val hasPermissionData = MediaProjectionHolder.getPermissionData() != null return when { // 连续快速停止,可能是权限冲突 consecutiveStopCount >= 3 && timeSinceLastStop < 5000 -> { Log.w(TAG, "🔄 检测到连续快速停止(${consecutiveStopCount}次),间隔${timeSinceLastStop}ms") "RAPID_CONSECUTIVE_STOPS" } // 系统保活检查 - 更严格的判断条件 connectionDuration < KEEPALIVE_CHECK_THRESHOLD && hasPermissionData && timeSinceLastStop > 1000 -> { Log.i(TAG, "⚡ 系统保活检查:连接时长${connectionDuration}ms < ${KEEPALIVE_CHECK_THRESHOLD}ms") "SYSTEM_KEEPALIVE_CHECK" } // 用户主动停止(状态栏点击) connectionDuration > 30000 && hasPermissionData -> { Log.i(TAG, "🔴 用户主动停止:连接时长${connectionDuration}ms > 30s") "USER_STOPPED_VIA_STATUS_BAR" } // 锁屏自动终止 connectionDuration > 5000 && connectionDuration < 60000 && hasPermissionData -> { Log.i(TAG, "🔒 可能的锁屏停止:连接时长${connectionDuration}ms在5-60秒之间") "DEVICE_LOCKED" } // 权限数据丢失 !hasPermissionData -> { Log.w(TAG, "🚫 权限数据丢失") "PERMISSION_LOST" } else -> { Log.w(TAG, "❓ 未知停止原因:连接${connectionDuration}ms,权限存在=$hasPermissionData") "UNKNOWN" } } } /** * 权限状态详细日志 */ private fun logPermissionState(context: String) { val permissionData = MediaProjectionHolder.getPermissionData() val mediaProjectionObj = MediaProjectionHolder.getMediaProjection() val permissionStats = MediaProjectionHolder.getPermissionStats() val localProjection = mediaProjection Log.d(TAG, """ 📊📊📊 权限状态检查 [$context] 📊📊📊 🔧 本地Manager状态: - isPermissionStable: $isPermissionStable - stopAllMonitoring: $stopAllMonitoring - isRecovering: $isRecovering - recoveryAttempts: $recoveryAttempts - 本地MediaProjection: ${localProjection?.hashCode()} 🗂️ MediaProjectionHolder状态: - 权限数据存在: ${permissionData != null} - MediaProjection对象: ${mediaProjectionObj?.hashCode()} - 数据有效性: ${MediaProjectionHolder.isPermissionDataValid()} - 权限统计: $permissionStats ⏰ 时间信息: - connectionStartTime: $connectionStartTime - 当前时间: ${System.currentTimeMillis()} - 连接时长: ${System.currentTimeMillis() - connectionStartTime}ms """.trimIndent()) } /** * ✅ 新增:渐进式恢复机制 - 替代激进的智能恢复 */ private fun startProgressiveRecovery() { Log.i(TAG, "🔄 启动渐进式权限恢复") logPermissionState("渐进式恢复开始") // 🛡️ 核心修复:检查是否为权限保活状态,避免在保活时启动恢复 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 // 重置停止计数 onPermissionRecovered() return@launch } // 第四阶段:延迟后再次尝试 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 } } } /** * ✅ 新增:处理恢复失败 */ 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() } /** * ✅ 静默权限恢复(改进版本) */ private suspend fun attemptSilentRecovery(): Boolean { return try { Log.i(TAG, "🤫 尝试静默权限恢复") logPermissionState("静默恢复开始") // 🛡️ 核心修复:检查是否为权限保活状态,避免在保活时进行恢复 val hasPermissionData = MediaProjectionHolder.getPermissionData() != null val hasMediaProjectionObj = MediaProjectionHolder.getMediaProjection() != null if (hasPermissionData && hasMediaProjectionObj) { Log.i(TAG, "🛡️ [权限保活] 检测到权限和对象都存在,这可能是保活检查,跳过恢复避免弹窗") Log.i(TAG, "🛡️ [权限保活] 权限数据存在: $hasPermissionData, MediaProjection对象存在: $hasMediaProjectionObj") return true // 返回true表示状态正常,无需恢复 } 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()}") // 更新连接时间 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 } } catch (e: Exception) { Log.e(TAG, "❌ 静默恢复异常", e) return false } } /** * ✅ 改进:用户主动停止处理 */ private fun handleUserStoppedSharing() { Log.i(TAG, "🔴 处理用户主动停止屏幕共享") // 标记为稳定状态,停止所有恢复 isPermissionStable = true stopAllMonitoring = true // 清理MediaProjection对象 mediaProjection?.unregisterCallback(mediaProjectionCallback) mediaProjection = null // 停止恢复协程 try { recoveryScope.cancel() } catch (e: Exception) { Log.w(TAG, "停止恢复协程失败", e) } // 通知屏幕捕获管理器停止 onPermissionLost() Log.i(TAG, "✅ 用户主动停止处理完成,已标记为稳定状态") } /** * ✅ 改进:设备锁屏处理 */ private fun handleDeviceLocked() { Log.i(TAG, "🔒 处理设备锁屏导致的屏幕共享停止") // 清理MediaProjection对象但保留权限数据 mediaProjection?.unregisterCallback(mediaProjectionCallback) mediaProjection = null // 暂停屏幕捕获但不标记为稳定(解锁后可恢复) onPermissionLost() Log.i(TAG, "✅ 设备锁屏处理完成,权限数据保留待解锁恢复") // 可以考虑监听解锁广播来自动恢复 // 这里暂不实现,等待用户主动操作 } /** * ✅ 新增:设置权限获取时间 */ fun setPermissionGrantedTime() { permissionGrantedTime = System.currentTimeMillis() isInStablePeriod = true consecutiveStopCount = 0 lastStopTime = 0L Log.i(TAG, "🛡️ 权限获取时间已设置: $permissionGrantedTime,进入稳定期") // 在稳定期结束后自动标记为稳定 recoveryScope.launch { delay(PERMISSION_STABLE_PERIOD) if (!isPermissionStable && !stopAllMonitoring) { Log.i(TAG, "✅ 权限稳定期结束,标记为稳定状态") isPermissionStable = true isInStablePeriod = false } } } /** * ✅ 新增:重置权限状态(用于重新开始权限申请) */ fun resetPermissionState() { Log.i(TAG, "🔄 重置Android 15权限状态") isPermissionStable = false stopAllMonitoring = false isInStablePeriod = false recoveryAttempts = 0 isRecovering = false consecutiveStopCount = 0 lastStopTime = 0L permissionGrantedTime = 0L lastRecoveryTime = 0L } /** * ✅ 改进:智能恢复策略 - 避免频繁重新授权 */ private fun triggerPermissionReRequest() { try { // 🛡️ 核心修复:检查是否为权限保活状态,避免在保活时触发权限重新申请 val hasPermissionData = MediaProjectionHolder.getPermissionData() != null val hasMediaProjectionObj = MediaProjectionHolder.getMediaProjection() != null if (hasPermissionData && hasMediaProjectionObj) { Log.i(TAG, "🛡️ [权限保活] 检测到权限和对象都存在,这可能是保活检查,跳过权限重新申请避免弹窗") Log.i(TAG, "🛡️ [权限保活] 权限数据存在: $hasPermissionData, MediaProjection对象存在: $hasMediaProjectionObj") 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) putExtra("ANDROID_15_RECOVERY", true) putExtra("PERMISSION_LOST_RECOVERY", true) } context.startActivity(intent) Log.i(TAG, "✅ 已启动MediaProjection权限重新申请") } catch (e: Exception) { Log.e(TAG, "❌ 触发权限重新申请失败", e) } } /** * ✅ 新增:创建MediaProjection并注册回调 */ fun createMediaProjectionWithCallback(resultCode: Int, resultData: Intent): MediaProjection? { return try { Log.i(TAG, "🏭 创建MediaProjection并注册回调") if (mediaProjectionManager == null) { mediaProjectionManager = context.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager } val projection = mediaProjectionManager?.getMediaProjection(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创建失败") return null } } catch (e: Exception) { Log.e(TAG, "❌ 创建MediaProjection异常", e) return null } } /** * 初始化管理器 */ fun initialize() { Log.i(TAG, "🔧 初始化Android 15 MediaProjection管理器") if (mediaProjectionManager == null) { mediaProjectionManager = context.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager } Log.i(TAG, "✅ Android 15 MediaProjection管理器初始化完成") } /** * 标记权限为稳定状态并停止所有监听 */ fun markPermissionStable() { Log.i(TAG, "🛡️ 手动标记Android 15权限为稳定状态") isPermissionStable = true stopAllMonitoring = true isInStablePeriod = false // 取消回调监听 mediaProjection?.unregisterCallback(mediaProjectionCallback) // 取消恢复协程 try { recoveryScope.cancel() } catch (e: Exception) { Log.w(TAG, "取消恢复协程失败", e) } Log.i(TAG, "✅ Android 15权限监听已停止") } /** * 获取当前MediaProjection对象 */ fun getMediaProjection(): MediaProjection? { return mediaProjection } /** * 检查MediaProjection是否有效 */ fun isMediaProjectionValid(): Boolean { return mediaProjection != null && !isRecovering } /** * 检查权限是否稳定 */ fun isPermissionStable(): Boolean { return isPermissionStable } /** * Android 15新增:标记session为已使用(在createVirtualDisplay时调用) */ fun markSessionUsed() { sessionUsed = true Log.i(TAG, "🔒 Android 15 session已标记为使用: $currentSessionId") } /** * Android 15新增:检查session是否已被使用 */ fun isSessionUsed(): Boolean { return sessionUsed } /** * 手动停止MediaProjection(用户主动停止) */ fun stopMediaProjection() { try { Log.i(TAG, "🛑 用户主动停止MediaProjection") // 标记为稳定状态 isPermissionStable = true stopAllMonitoring = true mediaProjection?.let { projection -> projection.unregisterCallback(mediaProjectionCallback) projection.stop() } mediaProjection = null // 停止恢复协程 try { recoveryScope.cancel() } catch (e: Exception) { Log.w(TAG, "停止恢复协程失败", e) } Log.i(TAG, "✅ MediaProjection已手动停止") } catch (e: Exception) { Log.e(TAG, "❌ 手动停止MediaProjection失败", e) } } /** * 销毁管理器 */ fun destroy() { try { Log.i(TAG, "🧹 销毁Android 15 MediaProjection管理器") // 停止所有协程 recoveryScope.cancel() // 清理MediaProjection mediaProjection?.unregisterCallback(mediaProjectionCallback) mediaProjection?.stop() mediaProjection = null mediaProjectionManager = null Log.i(TAG, "✅ Android 15 MediaProjection管理器已销毁") } catch (e: Exception) { Log.e(TAG, "❌ 销毁管理器失败", e) } } }