package com.yingmao.clash.levellog import android.app.PendingIntent import android.app.Service import android.content.ComponentName import android.content.Intent import android.content.ServiceConnection import android.os.Binder import android.os.Build import android.os.IBinder import android.os.IInterface import android.util.Log import androidx.annotation.RequiresApi import androidx.core.app.NotificationChannelCompat import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import com.cncat.vpn.common.compat.getColorCompat import com.cncat.vpn.common.compat.pendingIntentFlags import com.cncat.vpn.common.compat.startForegroundCompat import com.cncat.vpn.common.util.intent import com.cncat.vpn.core.model.LogMessage import com.cncat.vpn.service.RemoteService import com.cncat.vpn.service.remote.ILogObserver import com.cncat.vpn.service.remote.IRemoteService import com.cncat.vpn.service.remote.unwrap import com.tencent.mmkv.MMKV import com.yingmao.clash.R import com.yingmao.clash.act.SettingActivity import com.yingmao.clash.app.MainApplication import com.yingmao.clash.conf.BZYConstants import com.yingmao.clash.util.Utils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.cancel import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.IOException class LogcatService : Service(), CoroutineScope by CoroutineScope(Dispatchers.Default), IInterface { private val connection = object : ServiceConnection { override fun onServiceDisconnected(name: ComponentName?) { Log.i("LogcatServiceClose","onServiceDisconnected::${name}") stopSelf() } @RequiresApi(Build.VERSION_CODES.KITKAT) override fun onServiceConnected(name: ComponentName?, service: IBinder?) { Log.i("LogcatServiceConnect","onServiceConnected::${name}") startObserver(service ?: return stopSelf()) } } override fun onCreate() { super.onCreate() running = true createNotificationChannel() showNotification() bindService(RemoteService::class.intent, connection, BIND_AUTO_CREATE) } override fun onDestroy() { cancel() unbindService(connection) stopForeground(true) running = false super.onDestroy() } override fun onBind(intent: Intent?): IBinder { return this.asBinder() } override fun asBinder(): IBinder { return object : Binder() { override fun queryLocalInterface(descriptor: String): IInterface { return this@LogcatService } } } @RequiresApi(Build.VERSION_CODES.KITKAT) private fun startObserver(binder: IBinder) { if (!binder.isBinderAlive) return stopSelf() launch(Dispatchers.IO) { val service = binder.unwrap(IRemoteService::class).clash() val channel = Channel(CACHE_CAPACITY) try { MMKV.defaultMMKV().encode(BZYConstants.Companion.BehaviorLogSwitch, true) val selectedName = MMKV.defaultMMKV().decodeString(BZYConstants.Companion.NODE_SELECTED_NAME_KEY, "") val equipmentModel = Utils.getEquipmentModel() AppLog.i("LogcatService", "selectedName===>${selectedName}") AppLog.i("LogcatService", "用户是否过期===>${checkExpire()}") AppLog.i("LogcatService", "机型数据===>${equipmentModel}") AppLog.i("LogcatService", "OAID===>${MainApplication.Companion.OAID}") val observer = object : ILogObserver { override fun newItem(log: LogMessage) { channel.trySend(log) } } service.setLogObserver(observer) while (isActive) { val msg = channel.receive() AppLog.i("LogcatServiceMsg", "core msg===>${msg}") } } catch (e: IOException) { Log.e("Write log file: $e", e.toString()) } finally { withContext(NonCancellable) { if (binder.isBinderAlive) { service.setLogObserver(null) } stopSelf() } } } } private fun createNotificationChannel() { NotificationManagerCompat.from(this) .createNotificationChannel( NotificationChannelCompat.Builder( CHANNEL_ID, NotificationManagerCompat.IMPORTANCE_DEFAULT ).setName(getString(R.string.clash_logcat)).build() ) } private fun showNotification() { val notification = NotificationCompat .Builder(this, CHANNEL_ID) .setSmallIcon(R.mipmap.oneclickconnection) .setColor(getColorCompat(R.color.color_clash)) .setContentTitle(getString(R.string.clash_logcat)) .setContentText("采集中") .setContentIntent( PendingIntent.getActivity( this, R.id.nf_logcat_status, SettingActivity::class.intent .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP), pendingIntentFlags(PendingIntent.FLAG_UPDATE_CURRENT) ) ) .build() startForegroundCompat(R.id.nf_logcat_status, notification) } companion object { private const val CHANNEL_ID = "clash_logcat_channel_id" private const val CACHE_CAPACITY = 129 var running: Boolean = false } private fun checkExpire(): Boolean { val validPeriod = MMKV.defaultMMKV().decodeLong(BZYConstants.Companion.MMKV_KEY_VPN_VALID_PERIOD) if (validPeriod > 0) { val nowTime = System.currentTimeMillis(); com.cncat.vpn.common.log.Log.d("validPeriod==>$validPeriod, nowTime==>$nowTime") if (validPeriod >= nowTime) { return false } } return true } }