Improve: merge ClashManager and ProfileService

This commit is contained in:
kr328 2021-05-25 20:28:40 +08:00
parent c48ce82640
commit a8f502ef4f
12 changed files with 161 additions and 148 deletions

View File

@ -24,8 +24,6 @@ class AppCrashedActivity : BaseActivity<AppCrashedDesign>() {
SystemLogcat.dumpCrash()
}
Tracker.uploadLogcat(logs)
design.setAppLogs(logs)
while (isActive) {

View File

@ -20,9 +20,9 @@ import com.github.kr328.clash.common.util.intent
import com.github.kr328.clash.core.model.LogMessage
import com.github.kr328.clash.log.LogcatCache
import com.github.kr328.clash.log.LogcatWriter
import com.github.kr328.clash.service.ClashManager
import com.github.kr328.clash.service.remote.IClashManager
import com.github.kr328.clash.service.RemoteService
import com.github.kr328.clash.service.remote.ILogObserver
import com.github.kr328.clash.service.remote.IRemoteService
import com.github.kr328.clash.service.remote.unwrap
import com.github.kr328.clash.util.logsDir
import kotlinx.coroutines.*
@ -52,7 +52,7 @@ class LogcatService : Service(), CoroutineScope by CoroutineScope(Dispatchers.De
showNotification()
bindService(ClashManager::class.intent, connection, Context.BIND_AUTO_CREATE)
bindService(RemoteService::class.intent, connection, Context.BIND_AUTO_CREATE)
}
override fun onDestroy() {
@ -88,7 +88,7 @@ class LogcatService : Service(), CoroutineScope by CoroutineScope(Dispatchers.De
return stopSelf()
launch(Dispatchers.IO) {
val service = binder.unwrap(IClashManager::class)
val service = binder.unwrap(IRemoteService::class).clash()
val channel = Channel<LogMessage>(CACHE_CAPACITY)
try {

View File

@ -8,7 +8,8 @@ object SystemLogcat {
"Go",
"DEBUG",
"AndroidRuntime",
"ClashForAndroid"
"ClashForAndroid",
"LwIP",
)
fun dumpCrash(): String {

View File

@ -15,7 +15,7 @@ import kotlinx.coroutines.launch
object Remote {
val broadcasts: Broadcasts = Broadcasts(Global.application)
val services: Services = Services(Global.application) {
val service: Service = Service(Global.application) {
ApplicationObserver.createdActivities.forEach { it.finish() }
val intent = AppCrashedActivity::class.intent
@ -56,10 +56,10 @@ object Remote {
while (true) {
if (visible.receive()) {
services.bind()
service.bind()
broadcasts.register()
} else {
services.unbind()
service.unbind()
broadcasts.unregister()
}
}

View File

@ -0,0 +1,64 @@
package com.github.kr328.clash.remote
import android.app.Application
import android.content.ComponentName
import android.content.Context
import android.content.ServiceConnection
import android.os.IBinder
import com.github.kr328.clash.Tracker
import com.github.kr328.clash.common.log.Log
import com.github.kr328.clash.common.util.intent
import com.github.kr328.clash.log.SystemLogcat
import com.github.kr328.clash.service.RemoteService
import com.github.kr328.clash.service.remote.IRemoteService
import com.github.kr328.clash.service.remote.unwrap
import com.github.kr328.clash.util.unbindServiceSilent
import java.util.concurrent.TimeUnit
class Service(private val context: Application, val crashed: () -> Unit) {
val remote = Resource<IRemoteService>()
private val connection = object : ServiceConnection {
private var lastCrashed: Long = -1
override fun onServiceConnected(name: ComponentName?, service: IBinder) {
remote.set(service.unwrap(IRemoteService::class))
}
override fun onServiceDisconnected(name: ComponentName?) {
remote.set(null)
Tracker.uploadLogcat(SystemLogcat.dumpCrash())
if (System.currentTimeMillis() - lastCrashed < TOGGLE_CRASHED_INTERVAL) {
unbind()
crashed()
}
lastCrashed = System.currentTimeMillis()
Log.w("RemoteManager crashed")
}
}
fun bind() {
try {
context.bindService(RemoteService::class.intent, connection, Context.BIND_AUTO_CREATE)
} catch (e: Exception) {
unbind()
crashed()
}
}
fun unbind() {
context.unbindServiceSilent(connection)
remote.set(null)
}
companion object {
private val TOGGLE_CRASHED_INTERVAL = TimeUnit.SECONDS.toMillis(10)
}
}

View File

@ -1,88 +0,0 @@
package com.github.kr328.clash.remote
import android.app.Application
import android.content.ComponentName
import android.content.Context
import android.content.ServiceConnection
import android.os.IBinder
import com.github.kr328.clash.common.log.Log
import com.github.kr328.clash.common.util.intent
import com.github.kr328.clash.service.ClashManager
import com.github.kr328.clash.service.ProfileService
import com.github.kr328.clash.service.remote.IClashManager
import com.github.kr328.clash.service.remote.IProfileManager
import com.github.kr328.clash.service.remote.unwrap
import com.github.kr328.clash.util.unbindServiceSilent
import java.util.concurrent.TimeUnit
class Services(private val context: Application, val crashed: () -> Unit) {
val clash = Resource<IClashManager>()
val profile = Resource<IProfileManager>()
private val clashConnection = object : ServiceConnection {
private var lastCrashed: Long = -1
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
clash.set(service?.unwrap(IClashManager::class))
}
override fun onServiceDisconnected(name: ComponentName?) {
clash.set(null)
if (System.currentTimeMillis() - lastCrashed < TOGGLE_CRASHED_INTERVAL) {
unbind()
crashed()
}
lastCrashed = System.currentTimeMillis()
Log.w("ClashManager crashed")
}
}
private val profileConnection = object : ServiceConnection {
private var lastCrashed: Long = -1
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
profile.set(service?.unwrap(IProfileManager::class))
}
override fun onServiceDisconnected(name: ComponentName?) {
profile.set(null)
if (System.currentTimeMillis() - lastCrashed < TOGGLE_CRASHED_INTERVAL) {
unbind()
crashed()
}
lastCrashed = System.currentTimeMillis()
Log.w("ProfileService crashed")
}
}
fun bind() {
try {
context.bindService(ClashManager::class.intent, clashConnection, Context.BIND_AUTO_CREATE)
context.bindService(ProfileService::class.intent, profileConnection, Context.BIND_AUTO_CREATE)
} catch (e: Exception) {
unbind()
crashed()
}
}
fun unbind() {
context.unbindServiceSilent(clashConnection)
context.unbindServiceSilent(profileConnection)
clash.set(null)
profile.set(null)
}
companion object {
private val TOGGLE_CRASHED_INTERVAL = TimeUnit.SECONDS.toMillis(10)
}
}

View File

@ -14,14 +14,15 @@ suspend fun <T> withClash(
block: suspend IClashManager.() -> T
): T {
while (true) {
val client = Remote.services.clash.get()
val remote = Remote.service.remote.get()
val client = remote.clash()
try {
return withContext(context) { client.block() }
} catch (e: DeadObjectException) {
Log.w("Remote services panic")
Remote.services.clash.reset(client)
Remote.service.remote.reset(remote)
}
}
}
@ -31,14 +32,15 @@ suspend fun <T> withProfile(
block: suspend IProfileManager.() -> T
): T {
while (true) {
val client = Remote.services.profile.get()
val remote = Remote.service.remote.get()
val client = remote.profile()
try {
return withContext(context) { client.block() }
} catch (e: DeadObjectException) {
Log.w("Remote services panic")
Remote.services.profile.reset(client)
Remote.service.remote.reset(remote)
}
}
}

View File

@ -25,11 +25,7 @@
</intent-filter>
</service>
<service
android:name=".ClashManager"
android:exported="false"
android:process=":background" />
<service
android:name=".ProfileService"
android:name=".RemoteService"
android:exported="false"
android:process=":background" />
<service

View File

@ -1,7 +1,6 @@
package com.github.kr328.clash.service
import android.content.Intent
import android.os.IBinder
import android.content.Context
import com.github.kr328.clash.common.log.Log
import com.github.kr328.clash.core.Clash
import com.github.kr328.clash.core.model.*
@ -9,22 +8,16 @@ import com.github.kr328.clash.service.data.Selection
import com.github.kr328.clash.service.data.SelectionDao
import com.github.kr328.clash.service.remote.IClashManager
import com.github.kr328.clash.service.remote.ILogObserver
import com.github.kr328.clash.service.remote.wrap
import com.github.kr328.clash.service.store.ServiceStore
import com.github.kr328.clash.service.util.sendOverrideChanged
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.ReceiveChannel
import java.util.*
class ClashManager : BaseService(), IClashManager {
private val store by lazy { ServiceStore(this) }
private val binder = this.wrap()
class ClashManager(private val context: Context) : IClashManager,
CoroutineScope by CoroutineScope(Dispatchers.IO) {
private val store = ServiceStore(context)
private var logReceiver: ReceiveChannel<LogMessage>? = null
override fun onBind(intent: Intent?): IBinder {
return binder
}
override fun queryTunnelState(): TunnelState {
return Clash.queryTunnelState()
}
@ -68,7 +61,7 @@ class ClashManager : BaseService(), IClashManager {
override fun patchOverride(slot: Clash.OverrideSlot, configuration: ConfigurationOverride) {
Clash.patchOverride(slot, configuration)
sendOverrideChanged()
context.sendOverrideChanged()
}
override fun clearOverride(slot: Clash.OverrideSlot) {

View File

@ -1,7 +1,6 @@
package com.github.kr328.clash.service
import android.content.Intent
import android.os.IBinder
import android.content.Context
import com.github.kr328.clash.service.data.Database
import com.github.kr328.clash.service.data.ImportedDao
import com.github.kr328.clash.service.data.Pending
@ -9,34 +8,27 @@ import com.github.kr328.clash.service.data.PendingDao
import com.github.kr328.clash.service.model.Profile
import com.github.kr328.clash.service.remote.IFetchObserver
import com.github.kr328.clash.service.remote.IProfileManager
import com.github.kr328.clash.service.remote.wrap
import com.github.kr328.clash.service.store.ServiceStore
import com.github.kr328.clash.service.util.directoryLastModified
import com.github.kr328.clash.service.util.generateProfileUUID
import com.github.kr328.clash.service.util.importedDir
import com.github.kr328.clash.service.util.pendingDir
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.FileNotFoundException
import java.util.*
class ProfileService : BaseService(), IProfileManager {
private val service = this
private val store by lazy { ServiceStore(this) }
private val binder = this.wrap()
override fun onBind(intent: Intent?): IBinder {
return binder
}
override fun onCreate() {
super.onCreate()
class ProfileManager(private val context: Context) : IProfileManager,
CoroutineScope by CoroutineScope(Dispatchers.IO) {
private val store = ServiceStore(context)
init {
launch {
Database.database //.init
launch {
ProfileReceiver.rescheduleAll(service)
ProfileReceiver.rescheduleAll(context)
}
}
@ -52,7 +44,7 @@ class ProfileService : BaseService(), IProfileManager {
PendingDao().insert(pending)
pendingDir.resolve(uuid.toString()).apply {
context.pendingDir.resolve(uuid.toString()).apply {
deleteRecursively()
mkdirs()
@ -119,21 +111,21 @@ class ProfileService : BaseService(), IProfileManager {
}
override suspend fun commit(uuid: UUID, callback: IFetchObserver?) {
ProfileProcessor.apply(service, uuid, callback)
ProfileProcessor.apply(context, uuid, callback)
scheduleUpdate(uuid, false)
}
override suspend fun release(uuid: UUID) {
ProfileProcessor.release(this, uuid)
ProfileProcessor.release(context, uuid)
}
override suspend fun delete(uuid: UUID) {
ImportedDao().queryByUUID(uuid)?.also {
ProfileReceiver.cancelNext(service, it)
ProfileReceiver.cancelNext(context, it)
}
ProfileProcessor.delete(service, uuid)
ProfileProcessor.delete(context, uuid)
}
override suspend fun queryByUUID(uuid: UUID): Profile? {
@ -159,7 +151,7 @@ class ProfileService : BaseService(), IProfileManager {
}
override suspend fun setActive(profile: Profile) {
ProfileProcessor.active(this, profile.uuid)
ProfileProcessor.active(context, profile.uuid)
}
private suspend fun resolveProfile(uuid: UUID): Profile? {
@ -186,14 +178,14 @@ class ProfileService : BaseService(), IProfileManager {
}
private fun resolveUpdatedAt(uuid: UUID): Long {
return pendingDir.resolve(uuid.toString()).directoryLastModified
?: importedDir.resolve(uuid.toString()).directoryLastModified
return context.pendingDir.resolve(uuid.toString()).directoryLastModified
?: context.importedDir.resolve(uuid.toString()).directoryLastModified
?: -1
}
private fun cloneImportedFiles(source: UUID, target: UUID = source) {
val s = importedDir.resolve(source.toString())
val t = pendingDir.resolve(target.toString())
val s = context.importedDir.resolve(source.toString())
val t = context.pendingDir.resolve(target.toString())
if (!s.exists())
throw FileNotFoundException("profile $source not found")
@ -207,9 +199,9 @@ class ProfileService : BaseService(), IProfileManager {
val imported = ImportedDao().queryByUUID(uuid) ?: return
if (startImmediately) {
ProfileReceiver.schedule(service, imported)
ProfileReceiver.schedule(context, imported)
} else {
ProfileReceiver.scheduleNext(service, imported)
ProfileReceiver.scheduleNext(context, imported)
}
}
}

View File

@ -0,0 +1,46 @@
package com.github.kr328.clash.service
import android.content.Intent
import android.os.IBinder
import com.github.kr328.clash.service.remote.IClashManager
import com.github.kr328.clash.service.remote.IRemoteService
import com.github.kr328.clash.service.remote.IProfileManager
import com.github.kr328.clash.service.remote.wrap
import com.github.kr328.clash.service.util.cancelAndJoinBlocking
class RemoteService : BaseService(), IRemoteService {
private val binder = this.wrap()
private var clash: ClashManager? = null
private var profile: ProfileManager? = null
private var clashBinder: IClashManager? = null
private var profileBinder: IProfileManager? = null
override fun onCreate() {
super.onCreate()
clash = ClashManager(this)
profile = ProfileManager(this)
clashBinder = clash?.wrap() as IClashManager?
profileBinder = profile?.wrap() as IProfileManager?
}
override fun onDestroy() {
super.onDestroy()
clash?.cancelAndJoinBlocking()
profile?.cancelAndJoinBlocking()
}
override fun onBind(intent: Intent?): IBinder {
return binder
}
override fun clash(): IClashManager {
return clashBinder!!
}
override fun profile(): IProfileManager {
return profileBinder!!
}
}

View File

@ -0,0 +1,9 @@
package com.github.kr328.clash.service.remote
import com.github.kr328.kaidl.BinderInterface
@BinderInterface
interface IRemoteService {
fun clash(): IClashManager
fun profile(): IProfileManager
}