service refactored
This commit is contained in:
15 files changed
+115
-92
No files matched your search
@@ -39,12 +39,12 @@ class MainApplication : Application() {
|
||||
private fun ByteArray.toHexString(): String {
|
||||
return this.map {
|
||||
Integer.toHexString(it.toInt() and 0xff)
|
||||
}.map {
|
||||
}.joinToString(separator = "") {
|
||||
if (it.length < 2)
|
||||
"0$it"
|
||||
else
|
||||
it
|
||||
}.joinToString(separator = "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,40 +68,6 @@ class MainApplication : Application() {
|
||||
Crashlytics.setBool(CRASHLYTICS_SPLIT_APK_KEY, detectSplitArchive())
|
||||
Crashlytics.setString(CRASHLYTICS_BASE_SIZE_KEY, getBaseApkSize())
|
||||
Crashlytics.setUserIdentifier(userIdentifier)
|
||||
|
||||
Log.handler = object : Log.LogHandler {
|
||||
override fun info(message: String, throwable: Throwable?) {
|
||||
android.util.Log.i(Constants.TAG, message, throwable)
|
||||
}
|
||||
|
||||
override fun warn(message: String, throwable: Throwable?) {
|
||||
throwable?.also {
|
||||
Crashlytics.logException(it)
|
||||
}
|
||||
|
||||
android.util.Log.w(Constants.TAG, message, throwable)
|
||||
}
|
||||
|
||||
override fun error(message: String, throwable: Throwable?) {
|
||||
throwable?.also {
|
||||
Crashlytics.logException(it)
|
||||
}
|
||||
|
||||
android.util.Log.e(Constants.TAG, message, throwable)
|
||||
}
|
||||
|
||||
override fun wtf(message: String, throwable: Throwable?) {
|
||||
throwable?.also {
|
||||
Crashlytics.logException(it)
|
||||
}
|
||||
|
||||
android.util.Log.wtf(Constants.TAG, message, throwable)
|
||||
}
|
||||
|
||||
override fun debug(message: String, throwable: Throwable?) {
|
||||
android.util.Log.d(Constants.TAG, message, throwable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun detectFromPlay(): Boolean {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.github.kr328.clash.service
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import com.github.kr328.clash.core.Clash
|
||||
import com.github.kr328.clash.core.model.General
|
||||
@@ -9,7 +8,7 @@ import com.github.kr328.clash.core.model.ProxyGroup
|
||||
import com.github.kr328.clash.service.ipc.ParcelableCompletedFuture
|
||||
import com.github.kr328.clash.service.ipc.ParcelablePipe
|
||||
|
||||
class ClashManager(context: Context): IClashManager.Stub() {
|
||||
class ClashManager(context: Context) : IClashManager.Stub() {
|
||||
private val settings = context.getSharedPreferences("service", Context.MODE_PRIVATE)
|
||||
|
||||
override fun queryAllProxies(): Array<ProxyGroup> {
|
||||
@@ -27,7 +26,7 @@ class ClashManager(context: Context): IClashManager.Stub() {
|
||||
}
|
||||
|
||||
override fun openBandwidthEvent(): ParcelablePipe {
|
||||
return object: ParcelablePipe() {
|
||||
return object : ParcelablePipe() {
|
||||
val stream = Clash.openBandwidthEvent().apply {
|
||||
onEvent {
|
||||
send(it)
|
||||
@@ -45,7 +44,7 @@ class ClashManager(context: Context): IClashManager.Stub() {
|
||||
|
||||
return ParcelableCompletedFuture().apply {
|
||||
Clash.startHealthCheck(group).whenComplete { _: Unit?, u: Throwable? ->
|
||||
if ( u != null )
|
||||
if (u != null)
|
||||
this.completeExceptionally(u)
|
||||
else
|
||||
this.complete(null)
|
||||
@@ -54,7 +53,7 @@ class ClashManager(context: Context): IClashManager.Stub() {
|
||||
}
|
||||
|
||||
override fun openLogEvent(): ParcelablePipe {
|
||||
return object: ParcelablePipe() {
|
||||
return object : ParcelablePipe() {
|
||||
val stream = Clash.openLogEvent().apply {
|
||||
onEvent {
|
||||
send(it)
|
||||
|
||||
@@ -17,8 +17,10 @@ class ClashProfileManager(private val context: Context, private val database: Cl
|
||||
override fun updateProfile(id: Int): ParcelableCompletedFuture {
|
||||
val entity = database.openClashProfileDao().queryProfileById(id)
|
||||
|
||||
require(entity != null && (entity.type == ClashProfileEntity.Type.URL ||
|
||||
entity.type == ClashProfileEntity.Type.FILE))
|
||||
require(
|
||||
entity != null && (entity.type == ClashProfileEntity.Type.URL ||
|
||||
entity.type == ClashProfileEntity.Type.FILE)
|
||||
)
|
||||
|
||||
val result = ParcelableCompletedFuture()
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package com.github.kr328.clash.service
|
||||
|
||||
import android.app.Service
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.content.*
|
||||
import android.os.Binder
|
||||
import android.os.IBinder
|
||||
import com.github.kr328.clash.core.Clash
|
||||
import com.github.kr328.clash.service.data.ClashDatabase
|
||||
import com.github.kr328.clash.service.util.DefaultThreadPool
|
||||
import com.github.kr328.clash.service.util.sendBroadcastSelf
|
||||
|
||||
class ClashService : Service() {
|
||||
companion object {
|
||||
@@ -25,16 +25,18 @@ class ClashService : Service() {
|
||||
val tun = service.queryLocalInterface(TunService::class.java.name) as TunService
|
||||
|
||||
tun.startTun().whenComplete { _, u ->
|
||||
if (u != null) {
|
||||
stopReason = u.message
|
||||
stopSelf()
|
||||
return@whenComplete
|
||||
}
|
||||
if (u != null)
|
||||
return@whenComplete stopSelf(u.message ?: "Start tun failure")
|
||||
|
||||
notification.setVpn(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
private val profileObserver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
reloadProfile()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
@@ -49,7 +51,7 @@ class ClashService : Service() {
|
||||
|
||||
Clash.start()
|
||||
|
||||
sendBroadcast(Intent(Intents.INTENT_ACTION_CLASH_STARTED))
|
||||
sendBroadcastSelf(Intent(Intents.INTENT_ACTION_CLASH_STARTED))
|
||||
|
||||
val startVpn = intent?.getBooleanExtra(INTENT_EXTRA_START_TUN, true) ?: true
|
||||
|
||||
@@ -61,6 +63,10 @@ class ClashService : Service() {
|
||||
Context.BIND_AUTO_CREATE
|
||||
)
|
||||
|
||||
reloadProfile()
|
||||
|
||||
registerReceiver(profileObserver, IntentFilter(Intents.INTENT_ACTION_PROFILE_CHANGED))
|
||||
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
@@ -77,15 +83,33 @@ class ClashService : Service() {
|
||||
|
||||
notification.destroy()
|
||||
|
||||
sendBroadcast(
|
||||
sendBroadcastSelf(
|
||||
Intent(Intents.INTENT_ACTION_CLASH_STOPPED)
|
||||
.putExtra(Intents.INTENT_ACTION_CLASH_STOP_REASON, stopReason)
|
||||
)
|
||||
|
||||
unregisterReceiver(profileObserver)
|
||||
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun reloadProfile() {
|
||||
DefaultThreadPool.submit {
|
||||
val active = ClashDatabase.getInstance(this).openClashProfileDao().queryActiveProfile()
|
||||
?: return@submit stopSelf("Empty active profile")
|
||||
|
||||
Clash.loadProfile(
|
||||
filesDir.resolve(Constants.PROFILES_DIR).resolve(active.file),
|
||||
filesDir.resolve(Constants.CLASH_DIR).resolve(active.base)
|
||||
).whenComplete { _, u ->
|
||||
if (u != null)
|
||||
return@whenComplete stopSelf(u.message ?: "Load profile failure")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun stopSelf(reason: String) {
|
||||
stopReason = reason
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.github.kr328.clash.service
|
||||
|
||||
object Constants {
|
||||
const val CLASH_PROCESS_BROADCAST_ACTION = "com.github.kr328.clash.ClashService.ClashProcessEvent"
|
||||
const val CLASH_RELOAD_BROADCAST_ACTION = "com.github.kr328.clash.ClashService.ProfileReloadEvent"
|
||||
const val CLASH_PROCESS_BROADCAST_ACTION =
|
||||
"com.github.kr328.clash.ClashService.ClashProcessEvent"
|
||||
const val CLASH_RELOAD_BROADCAST_ACTION =
|
||||
"com.github.kr328.clash.ClashService.ProfileReloadEvent"
|
||||
|
||||
const val CLASH_DIR = "clash"
|
||||
const val PROFILES_DIR = "profiles"
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
package com.github.kr328.clash.service
|
||||
|
||||
object Intents {
|
||||
const val INTENT_ACTION_BIND_TUN_SERVICE = "${BuildConfig.LIBRARY_PACKAGE_NAME}.intent.action.bind.tun"
|
||||
const val INTENT_ACTION_CLASH_STARTED = "${BuildConfig.LIBRARY_PACKAGE_NAME}.intent.action.clash.started"
|
||||
const val INTENT_ACTION_CLASH_STOPPED = "${BuildConfig.LIBRARY_PACKAGE_NAME}.intent.action.clash.stopped"
|
||||
const val INTENT_ACTION_BIND_TUN_SERVICE =
|
||||
"${BuildConfig.LIBRARY_PACKAGE_NAME}.intent.action.bind.tun"
|
||||
const val INTENT_ACTION_CLASH_STARTED =
|
||||
"${BuildConfig.LIBRARY_PACKAGE_NAME}.intent.action.clash.started"
|
||||
const val INTENT_ACTION_CLASH_STOPPED =
|
||||
"${BuildConfig.LIBRARY_PACKAGE_NAME}.intent.action.clash.stopped"
|
||||
const val INTENT_ACTION_PROFILE_CHANGED =
|
||||
"${BuildConfig.LIBRARY_PACKAGE_NAME}.intent.action.profile.changed"
|
||||
|
||||
const val INTENT_ACTION_CLASH_STOP_REASON = "${BuildConfig.LIBRARY_PACKAGE_NAME}.intent.extra.clash.stop.reason"
|
||||
const val INTENT_ACTION_CLASH_STOP_REASON =
|
||||
"${BuildConfig.LIBRARY_PACKAGE_NAME}.intent.extra.clash.stop.reason"
|
||||
}
|
||||
@@ -20,11 +20,11 @@ class Settings(private val clashManager: IClashManager) {
|
||||
return clashManager.getSetting(key)
|
||||
}
|
||||
|
||||
fun <T>get(setting: Setting<T>): T {
|
||||
fun <T> get(setting: Setting<T>): T {
|
||||
return setting.parseValue(get(setting.key))
|
||||
}
|
||||
|
||||
fun <T>put(setting: Setting<T>, value: T) {
|
||||
fun <T> put(setting: Setting<T>, value: T) {
|
||||
put(setting.key, setting.valueToString(value))
|
||||
}
|
||||
|
||||
@@ -34,41 +34,46 @@ class Settings(private val clashManager: IClashManager) {
|
||||
fun valueToString(value: T): String
|
||||
}
|
||||
|
||||
class BooleanSetting(override val key: String, private val def: Boolean): Setting<Boolean> {
|
||||
class BooleanSetting(override val key: String, private val def: Boolean) : Setting<Boolean> {
|
||||
override fun parseValue(value: String?): Boolean {
|
||||
val v = value ?: return def
|
||||
return v.toBoolean()
|
||||
}
|
||||
|
||||
override fun valueToString(value: Boolean): String {
|
||||
return value.toString()
|
||||
}
|
||||
}
|
||||
|
||||
class IntSetting(override val key: String, private val def: Int): Setting<Int> {
|
||||
class IntSetting(override val key: String, private val def: Int) : Setting<Int> {
|
||||
override fun parseValue(value: String?): Int {
|
||||
val v = value ?: return def
|
||||
return v.toIntOrNull() ?: def
|
||||
}
|
||||
|
||||
override fun valueToString(value: Int): String {
|
||||
return value.toString()
|
||||
}
|
||||
}
|
||||
|
||||
class StringSetting(override val key: String, val def: String): Setting<String> {
|
||||
class StringSetting(override val key: String, val def: String) : Setting<String> {
|
||||
override fun parseValue(value: String?): String {
|
||||
return value ?: def
|
||||
}
|
||||
|
||||
override fun valueToString(value: String): String {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
class PackageListSetting(override val key: String, private val def: List<String>): Setting<List<String>> {
|
||||
class PackageListSetting(override val key: String, private val def: List<String>) :
|
||||
Setting<List<String>> {
|
||||
override fun parseValue(value: String?): List<String> {
|
||||
val v = value ?: return def
|
||||
|
||||
return v.split(":")
|
||||
}
|
||||
|
||||
override fun valueToString(value: List<String>): String {
|
||||
return value.joinToString(":")
|
||||
}
|
||||
|
||||
@@ -5,7 +5,11 @@ import androidx.room.Database
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
|
||||
@Database(version = 2, exportSchema = false, entities = [ClashProfileEntity::class, ClashProfileProxyEntity::class])
|
||||
@Database(
|
||||
version = 2,
|
||||
exportSchema = false,
|
||||
entities = [ClashProfileEntity::class, ClashProfileProxyEntity::class]
|
||||
)
|
||||
abstract class ClashDatabase : RoomDatabase() {
|
||||
abstract fun openClashProfileDao(): ClashProfileDao
|
||||
abstract fun openClashProfileProxyDao(): ClashProfileProxyDao
|
||||
|
||||
+16
-13
@@ -14,22 +14,25 @@ object ClashDatabaseMigrations {
|
||||
|
||||
database.execSQL("ALTER TABLE profiles RENAME TO _profiles")
|
||||
|
||||
database.execSQL("CREATE TABLE profiles(" +
|
||||
"name TEXT NOT NULL, " +
|
||||
"type INTEGER NOT NULL, " +
|
||||
"uri TEXT NOT NULL, " +
|
||||
"file TEXT NOT NULL, " +
|
||||
"base TEXT NOT NULL" +
|
||||
"active INTEGER NOT NULL, " +
|
||||
"last_update INTEGER NOT NULL, " +
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)")
|
||||
database.execSQL(
|
||||
"CREATE TABLE profiles(" +
|
||||
"name TEXT NOT NULL, " +
|
||||
"type INTEGER NOT NULL, " +
|
||||
"uri TEXT NOT NULL, " +
|
||||
"file TEXT NOT NULL, " +
|
||||
"base TEXT NOT NULL" +
|
||||
"active INTEGER NOT NULL, " +
|
||||
"last_update INTEGER NOT NULL, " +
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)"
|
||||
)
|
||||
|
||||
val cursor = database.query("SELECT name, token, file, active, last_update FROM _profile")
|
||||
val cursor =
|
||||
database.query("SELECT name, token, file, active, last_update FROM _profile")
|
||||
val random = SecureRandom()
|
||||
val bases = mutableSetOf<Long>()
|
||||
|
||||
cursor.moveToFirst()
|
||||
while ( !cursor.isAfterLast ) {
|
||||
while (!cursor.isAfterLast) {
|
||||
// old
|
||||
val name = cursor.getString(0)
|
||||
val token = cursor.getString(1)
|
||||
@@ -46,7 +49,7 @@ object ClashDatabaseMigrations {
|
||||
val uri = token.removePrefix("url|").removePrefix("file|")
|
||||
var base = random.nextLong().absoluteValue
|
||||
|
||||
while ( bases.contains(base) )
|
||||
while (bases.contains(base))
|
||||
base = random.nextLong().absoluteValue
|
||||
|
||||
database.insert("profiles",
|
||||
@@ -59,7 +62,7 @@ object ClashDatabaseMigrations {
|
||||
put("active", active)
|
||||
put("last_update", lastUpdate)
|
||||
put("base", base.toString())
|
||||
})
|
||||
})
|
||||
}
|
||||
cursor.close()
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ data class ClashProfileEntity(
|
||||
|
||||
@TypeConverter
|
||||
fun intToType(value: Int?): Type {
|
||||
return when ( value ) {
|
||||
return when (value) {
|
||||
Type.URL.id -> Type.URL
|
||||
Type.FILE.id -> Type.FILE
|
||||
Type.UNKNOWN.id -> Type.UNKNOWN
|
||||
|
||||
+7
-3
@@ -2,13 +2,17 @@ package com.github.kr328.clash.service.data
|
||||
|
||||
import androidx.room.*
|
||||
|
||||
@Entity(tableName = "profile_select_proxies",
|
||||
@Entity(
|
||||
tableName = "profile_select_proxies",
|
||||
indices = [Index("profile_id")],
|
||||
foreignKeys = [ForeignKey(entity = ClashProfileEntity::class,
|
||||
foreignKeys = [ForeignKey(
|
||||
entity = ClashProfileEntity::class,
|
||||
childColumns = ["profile_id"],
|
||||
parentColumns = ["id"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
onUpdate = ForeignKey.CASCADE)])
|
||||
onUpdate = ForeignKey.CASCADE
|
||||
)]
|
||||
)
|
||||
data class ClashProfileProxyEntity(
|
||||
@ColumnInfo(name = "profile_id") val profileId: Int,
|
||||
@ColumnInfo(name = "proxy") val proxy: String,
|
||||
|
||||
+5
-6
@@ -7,7 +7,7 @@ import java.util.concurrent.CompletableFuture
|
||||
|
||||
class ParcelableCompletedFuture private constructor(private val pipe: ParcelablePipe) :
|
||||
CompletableFuture<Parcelable?>(), Parcelable {
|
||||
constructor(): this(ParcelablePipe())
|
||||
constructor() : this(ParcelablePipe())
|
||||
constructor(parcel: Parcel) : this(
|
||||
parcel.readParcelable<ParcelablePipe>(
|
||||
ParcelableCompletedFuture::class.java.classLoader
|
||||
@@ -16,17 +16,16 @@ class ParcelableCompletedFuture private constructor(private val pipe: Parcelable
|
||||
pipe.onReceive {
|
||||
val result = (it ?: throw NullPointerException()) as ParcelableResult
|
||||
|
||||
if ( result.exception != null ) {
|
||||
if (result.exception != null) {
|
||||
completeExceptionally(RemoteException(result.exception))
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
complete(result.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun complete(value: Parcelable?): Boolean {
|
||||
if ( super.complete(value) ) {
|
||||
if (super.complete(value)) {
|
||||
pipe.send(ParcelableResult(value, null))
|
||||
return true
|
||||
}
|
||||
@@ -34,7 +33,7 @@ class ParcelableCompletedFuture private constructor(private val pipe: Parcelable
|
||||
}
|
||||
|
||||
override fun completeExceptionally(ex: Throwable?): Boolean {
|
||||
if ( super.completeExceptionally(ex) ) {
|
||||
if (super.completeExceptionally(ex)) {
|
||||
pipe.send(ParcelableResult(null, ex?.message ?: "Unknown"))
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.github.kr328.clash.service.ipc
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
data class ParcelableResult(val data: Parcelable?, val exception: String?): Parcelable {
|
||||
data class ParcelableResult(val data: Parcelable?, val exception: String?) : Parcelable {
|
||||
constructor(parcel: Parcel) : this(
|
||||
parcel.readParcelable(ParcelableResult::class.java.classLoader),
|
||||
parcel.readString()
|
||||
|
||||
@@ -50,7 +50,8 @@ class DefaultNetworkObserver(val context: Context, val listener: (Network?) -> U
|
||||
return try {
|
||||
connectivity.allNetworks
|
||||
.flatMap { network ->
|
||||
connectivity.getNetworkCapabilities(network)?.let { listOf(it to network) } ?: emptyList()
|
||||
connectivity.getNetworkCapabilities(network)?.let { listOf(it to network) }
|
||||
?: emptyList()
|
||||
}
|
||||
.asSequence()
|
||||
.filterNot {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.github.kr328.clash.service.util
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
||||
fun Context.sendBroadcastSelf(intent: Intent) {
|
||||
this.sendBroadcast(intent.setPackage(this.packageName))
|
||||
}
|
||||
Reference in new issue
Block a user