diff --git a/app/src/main/java/com/github/kr328/clash/AppCrashedActivity.kt b/app/src/main/java/com/github/kr328/clash/AppCrashedActivity.kt index 374c6fa9a517576d42aa10563ff54c115738f96a..0a0a2044392715ad01529a896a2cb9a06904b185 100644 --- a/app/src/main/java/com/github/kr328/clash/AppCrashedActivity.kt +++ b/app/src/main/java/com/github/kr328/clash/AppCrashedActivity.kt @@ -24,8 +24,6 @@ class AppCrashedActivity : BaseActivity<AppCrashedDesign>() { SystemLogcat.dumpCrash() } - Tracker.uploadLogcat(logs) - design.setAppLogs(logs) while (isActive) { diff --git a/app/src/main/java/com/github/kr328/clash/LogcatService.kt b/app/src/main/java/com/github/kr328/clash/LogcatService.kt index 11a757576a0f56eda0592aabdf305625c65f2c22..4484f86ad66e5457b6b47b0556df913ded90ea3e 100644 --- a/app/src/main/java/com/github/kr328/clash/LogcatService.kt +++ b/app/src/main/java/com/github/kr328/clash/LogcatService.kt @@ -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 { diff --git a/app/src/main/java/com/github/kr328/clash/log/SystemLogcat.kt b/app/src/main/java/com/github/kr328/clash/log/SystemLogcat.kt index bbead046d93bb02b26f98bb46148d44850cef666..848e057292591e5476e844aee425e8d22dd31612 100644 --- a/app/src/main/java/com/github/kr328/clash/log/SystemLogcat.kt +++ b/app/src/main/java/com/github/kr328/clash/log/SystemLogcat.kt @@ -8,7 +8,8 @@ object SystemLogcat { "Go", "DEBUG", "AndroidRuntime", - "ClashForAndroid" + "ClashForAndroid", + "LwIP", ) fun dumpCrash(): String { diff --git a/app/src/main/java/com/github/kr328/clash/remote/Remote.kt b/app/src/main/java/com/github/kr328/clash/remote/Remote.kt index 664ecc3a604b945dd85f7bc35dd273e577cbf06c..a80bc2ae97f415b67fe0e7cc92df452392c3979a 100644 --- a/app/src/main/java/com/github/kr328/clash/remote/Remote.kt +++ b/app/src/main/java/com/github/kr328/clash/remote/Remote.kt @@ -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() } } diff --git a/app/src/main/java/com/github/kr328/clash/remote/Service.kt b/app/src/main/java/com/github/kr328/clash/remote/Service.kt new file mode 100644 index 0000000000000000000000000000000000000000..079c0ae4a622c4c378afed8cefa7f53bb8aae41f --- /dev/null +++ b/app/src/main/java/com/github/kr328/clash/remote/Service.kt @@ -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) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/kr328/clash/remote/Services.kt b/app/src/main/java/com/github/kr328/clash/remote/Services.kt deleted file mode 100644 index 3d9d7f1062d7d52e07e5fa55d8ebd0f9d9ff15c3..0000000000000000000000000000000000000000 --- a/app/src/main/java/com/github/kr328/clash/remote/Services.kt +++ /dev/null @@ -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) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/github/kr328/clash/util/Remote.kt b/app/src/main/java/com/github/kr328/clash/util/Remote.kt index ffc9c8bc9bfc28fda01608cff3d4a9b8af9d0ea7..20120c41d8da09228f1e1ff4baa976d218ee0447 100644 --- a/app/src/main/java/com/github/kr328/clash/util/Remote.kt +++ b/app/src/main/java/com/github/kr328/clash/util/Remote.kt @@ -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) } } } diff --git a/service/src/main/AndroidManifest.xml b/service/src/main/AndroidManifest.xml index 60d3dbfd506f1729ae7aa2290f492f2e76ebed18..d869fb38d3163344c2b571fc5b06079ae952c470 100644 --- a/service/src/main/AndroidManifest.xml +++ b/service/src/main/AndroidManifest.xml @@ -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 diff --git a/service/src/main/java/com/github/kr328/clash/service/ClashManager.kt b/service/src/main/java/com/github/kr328/clash/service/ClashManager.kt index 33670558ef67fc9f80b201c9ef10be852e3751c7..aa92b4afe4db289dd93881029d4c9a914dc870ff 100644 --- a/service/src/main/java/com/github/kr328/clash/service/ClashManager.kt +++ b/service/src/main/java/com/github/kr328/clash/service/ClashManager.kt @@ -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) { diff --git a/service/src/main/java/com/github/kr328/clash/service/ProfileService.kt b/service/src/main/java/com/github/kr328/clash/service/ProfileManager.kt similarity index 81% rename from service/src/main/java/com/github/kr328/clash/service/ProfileService.kt rename to service/src/main/java/com/github/kr328/clash/service/ProfileManager.kt index 699bec8c41ae40f8264c2d01f15096f89ba6b4e2..47d66b04c071cd8f83293acda45beb024c53b95e 100644 --- a/service/src/main/java/com/github/kr328/clash/service/ProfileService.kt +++ b/service/src/main/java/com/github/kr328/clash/service/ProfileManager.kt @@ -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() - - Database.database //.init +class ProfileManager(private val context: Context) : IProfileManager, + CoroutineScope by CoroutineScope(Dispatchers.IO) { + private val store = ServiceStore(context) + init { launch { - ProfileReceiver.rescheduleAll(service) + Database.database //.init + + 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) } } } \ No newline at end of file diff --git a/service/src/main/java/com/github/kr328/clash/service/RemoteService.kt b/service/src/main/java/com/github/kr328/clash/service/RemoteService.kt new file mode 100644 index 0000000000000000000000000000000000000000..802110f3477039d52b1f88391b5eab228e5a2b9c --- /dev/null +++ b/service/src/main/java/com/github/kr328/clash/service/RemoteService.kt @@ -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!! + } +} \ No newline at end of file diff --git a/service/src/main/java/com/github/kr328/clash/service/remote/IRemoteService.kt b/service/src/main/java/com/github/kr328/clash/service/remote/IRemoteService.kt new file mode 100644 index 0000000000000000000000000000000000000000..b8c92456ca816e3304f7637e68f1e3cb3bc5dba4 --- /dev/null +++ b/service/src/main/java/com/github/kr328/clash/service/remote/IRemoteService.kt @@ -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 +} \ No newline at end of file