service event framework

This commit is contained in:
Kr328 committed 2019-11-25 17:01:20 +08:00
1 parent e14e91a2a4
commit 9023809c89
25 files changed
+317 -251

No files matched your search

+6
View File
@@ -41,3 +41,9 @@ keystore.properties
.cxx
*.hprof
# firebase
google-services.json
# Dolphin
.directory
+3
View File
@@ -48,4 +48,7 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0"
implementation "com.charleskorn.kaml:kaml:0.14.0"
implementation "com.google.android.material:material:1.2.0-alpha02"
implementation 'com.google.firebase:firebase-analytics:17.2.1'
}
apply plugin: 'com.google.gms.google-services'
@@ -0,0 +1,62 @@
package com.github.kr328.clash
import android.os.IBinder
import androidx.appcompat.app.AppCompatActivity
import com.github.kr328.clash.core.event.*
import com.github.kr328.clash.service.IClashEventObserver
abstract class BaseActivity : AppCompatActivity(), IClashEventObserver {
private val observerBinder = object: IClashEventObserver.Stub() {
override fun onLogEvent(event: LogEvent?) {
this@BaseActivity.onLogEvent(event)
}
override fun onProcessEvent(event: ProcessEvent?) {
this@BaseActivity.onProcessEvent(event)
}
override fun onProxyChangedEvent(event: ProxyChangedEvent?) {
this@BaseActivity.onProxyChangedEvent(event)
}
override fun onErrorEvent(event: ErrorEvent?) {
this@BaseActivity.onErrorEvent(event)
}
override fun onTrafficEvent(event: TrafficEvent?) {
this@BaseActivity.onTrafficEvent(event)
}
override fun onProfileChanged(event: ProfileChangedEvent?) {
this@BaseActivity.onProfileChanged(event)
}
}
override fun onLogEvent(event: LogEvent?) {
}
override fun onErrorEvent(event: ErrorEvent?) {
}
override fun onProfileChanged(event: ProfileChangedEvent?) {
}
override fun onProcessEvent(event: ProcessEvent?) {
}
override fun onProxyChangedEvent(event: ProxyChangedEvent?) {
}
override fun onTrafficEvent(event: TrafficEvent?) {
}
override fun asBinder(): IBinder {
return observerBinder
}
}
@@ -1,7 +1,5 @@
package com.github.kr328.clash
import androidx.appcompat.app.AppCompatActivity
class ClashStatusActivity : AppCompatActivity() {
class ClashStatusActivity : BaseActivity() {
}
@@ -15,7 +15,7 @@ import androidx.fragment.app.commit
import com.github.kr328.clash.view.FatItem
import kotlinx.android.synthetic.main.activity_new_profile.*
class CreateProfileActivity : AppCompatActivity() {
class CreateProfileActivity : BaseActivity() {
companion object {
val NEW_PROFILE_SOURCE = listOf(
AdapterData(R.drawable.ic_new_profile_file,
@@ -8,7 +8,6 @@ import android.view.View
import android.webkit.MimeTypeMap
import android.widget.EditText
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.charleskorn.kaml.Yaml
import com.charleskorn.kaml.YamlConfiguration
import com.github.kr328.clash.model.ClashProfile
@@ -18,13 +17,12 @@ import com.github.kr328.clash.utils.FileUtils
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.activity_import_file.*
import kotlinx.serialization.parse
import java.io.FileOutputStream
import java.lang.Exception
import java.lang.NullPointerException
import kotlin.concurrent.thread
class ImportFileActivity : AppCompatActivity() {
class ImportFileActivity : BaseActivity() {
companion object {
const val REQUEST_FILE_CODE = 185
}
@@ -137,7 +135,7 @@ class ImportFileActivity : AppCompatActivity() {
name = activity_import_file_name_text.text.toString(),
token = "file|" + activity_import_file_path_text.tag.toString(),
cache = cache.absolutePath,
selected = false,
active = false,
proxies = parsed.proxies.size,
proxyGroups = parsed.proxyGroups.size,
rules = parsed.rules.size
@@ -25,7 +25,7 @@ import kotlinx.android.synthetic.main.activity_main_clash_status.*
import kotlinx.android.synthetic.main.activity_main_profiles.*
import kotlinx.android.synthetic.main.activity_main_proxy_manage.*
class MainActivity : AppCompatActivity() {
class MainActivity : BaseActivity() {
companion object {
const val VPN_REQUEST_CODE = 233
}
@@ -12,7 +12,7 @@ import com.github.kr328.clash.service.data.ClashProfileEntity
import kotlinx.android.synthetic.main.activity_profiles.*
import kotlin.concurrent.thread
class ProfilesActivity : AppCompatActivity() {
class ProfilesActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_profiles)
@@ -1,13 +1,7 @@
package com.github.kr328.clash.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.CheckBox
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.widget.AppCompatRadioButton
import androidx.recyclerview.widget.RecyclerView
import com.github.kr328.clash.R
import com.github.kr328.clash.service.data.ClashProfileEntity
@@ -39,7 +33,7 @@ class ProfileAdapter(private val context: Context, private val listener: (Int) -
R.string.clash_profile_item_summary,
current.proxies, current.proxyGroups, current.rules
)
holder.view.isChecked = current.selected
holder.view.isChecked = current.active
holder.view.setOnClickListener {
listener(current.id)
}
+1
View File
@@ -14,6 +14,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:3.5.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
classpath 'com.google.gms:google-services:4.3.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
@@ -66,7 +66,8 @@ class Clash(
fun pollEvent(listener: (Event) -> Unit) {
runControl(COMMAND_POLL_EVENT) { _, input, _ ->
loop@while ( process.isRunning() ) {
loop@while ( process.getProcessStatus() == ProcessEvent.STARTED
&& !Thread.currentThread().isInterrupted ) {
when (val type = input.readInt()) {
Event.EVENT_CLOSE -> {
break@loop
@@ -75,7 +76,7 @@ class Clash(
listener(Json(JsonConfiguration.Stable).parse(LogEvent.serializer(), input.readString()))
}
Event.EVENT_PROXY_CHANGED -> {
listener(ProxyChangedEvent())
listener(Json(JsonConfiguration.Stable).parse(ProxyChangedEvent.serializer(), input.readString()))
}
Event.EVENT_TRAFFIC -> {
listener(Json(JsonConfiguration.Stable).parse(TrafficEvent.serializer(), input.readString()))
@@ -86,19 +87,9 @@ class Clash(
}
}
fun setEventEnabled(type: KClass<Event>, enabled: Boolean) {
fun setEventEnabled(type: Int, enabled: Boolean) {
runControl(COMMAND_SET_EVENT_ENABLED) { _, _, output ->
output.writeInt(
when ( type ) {
LogEvent::class ->
Event.EVENT_LOG
ProxyChangedEvent::class ->
Event.EVENT_PROXY_CHANGED
TrafficEvent::class ->
Event.EVENT_TRAFFIC
else -> throw IllegalArgumentException("Invalid event type $type")
}
)
output.writeInt(type)
output.writeInt(if ( enabled ) 1 else 0)
}
}
@@ -99,8 +99,11 @@ class ClashProcess(private val context: Context,
}
@Synchronized
fun isRunning(): Boolean {
return pid > 0
fun getProcessStatus(): ProcessEvent {
return if ( pid > 0 )
ProcessEvent.STARTED
else
ProcessEvent.STOPPED
}
fun stop() {
@@ -8,7 +8,7 @@ import kotlinx.serialization.Serializable
@Serializable
data class ErrorEvent(val type: Type, val message: String): Event, Parcelable {
enum class Type {
START_FAILURE
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
@@ -0,0 +1,27 @@
package com.github.kr328.clash.core.event
import android.os.Parcel
import android.os.Parcelable
import com.github.kr328.clash.core.serialization.Parcels
import kotlinx.serialization.Serializable
@Serializable
class ProfileChangedEvent : Event, Parcelable {
override fun writeToParcel(parcel: Parcel, flags: Int) {
Parcels.dump(serializer(), this, parcel)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<ProfileChangedEvent> {
override fun createFromParcel(parcel: Parcel): ProfileChangedEvent {
return Parcels.load(serializer(), parcel)
}
override fun newArray(size: Int): Array<ProfileChangedEvent?> {
return arrayOfNulls(size)
}
}
}
@@ -0,0 +1,41 @@
package com.github.kr328.clash.core.utils
import com.github.kr328.clash.core.Constants.TAG
object Log {
var handler: LogHandler = object: LogHandler {
override fun info(message: String, throwable: Throwable?) {
android.util.Log.i(TAG, message, throwable)
}
override fun warn(message: String, throwable: Throwable?) {
android.util.Log.w(TAG, message, throwable)
}
override fun error(message: String, throwable: Throwable?) {
android.util.Log.e(TAG, message, throwable)
}
override fun wtf(message: String, throwable: Throwable?) {
android.util.Log.wtf(TAG, message, throwable)
}
override fun debug(message: String, throwable: Throwable?) {
android.util.Log.d(TAG, message, throwable)
}
}
interface LogHandler {
fun info(message: String, throwable: Throwable?)
fun warn(message: String, throwable: Throwable?)
fun error(message: String, throwable: Throwable?)
fun wtf(message: String, throwable: Throwable?)
fun debug(message: String, throwable: Throwable?)
}
fun i(message: String, throwable: Throwable? = null) = handler.info(message, throwable)
fun w(message: String, throwable: Throwable? = null) = handler.warn(message, throwable)
fun e(message: String, throwable: Throwable? = null) = handler.error(message, throwable)
fun wtf(message: String, throwable: Throwable? = null) = handler.wtf(message, throwable)
fun debug(message: String, throwable: Throwable? = null) = handler.debug(message, throwable)
}
+1 -1
View File
@@ -39,4 +39,4 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "androidx.room:room-runtime:$room_version"
implementation 'androidx.core:core-ktx:1.2.0-rc01'
}
}
@@ -4,4 +4,5 @@ parcelable ProcessEvent;
parcelable LogEvent;
parcelable ProxyChangedEvent;
parcelable TrafficEvent;
parcelable ErrorEvent;
parcelable ErrorEvent;
parcelable ProfileChangedEvent;
@@ -8,4 +8,5 @@ interface IClashEventObserver {
void onLogEvent(in LogEvent event);
void onErrorEvent(in ErrorEvent event);
void onTrafficEvent(in TrafficEvent event);
void onProfileChanged(in ProfileChangedEvent event);
}
@@ -3,9 +3,6 @@ package com.github.kr328.clash.service;
import com.github.kr328.clash.service.IClashEventObserver;
interface IClashEventService {
void registerEventObserver(String id, in IClashEventObserver observer);
void registerEventObserver(String id, in IClashEventObserver observer, in int[] events);
void unregisterEventObserver(String id);
void acquireEvent(String id, int event);
void releaseEvent(String id, int event);
}
@@ -5,7 +5,13 @@ import com.github.kr328.clash.service.IClashEventService;
import com.github.kr328.clash.core.event.Event;
interface IClashService {
// Services
IClashEventService getEventService();
// Status
ProcessEvent getCurrentProcessStatus();
// Control
void startTunDevice(in ParcelFileDescriptor fd, int mtu);
void stopTunDevice();
void start();
@@ -16,7 +16,7 @@ class ClashEventService(private val master: Master) : IClashEventService.Stub()
private val EVENT_SET = setOf(Event.EVENT_LOG, Event.EVENT_TRAFFIC, Event.EVENT_PROXY_CHANGED)
}
private data class EventObserverRecord(val observer: IClashEventObserver, val acquiredEvent: MutableSet<Int>)
private data class EventObserverRecord(val observer: IClashEventObserver, val acquiredEvent: Set<Int>)
private val observers = mutableMapOf<String, EventObserverRecord>()
private lateinit var handler: Handler
@@ -42,7 +42,8 @@ class ClashEventService(private val master: Master) : IClashEventService.Stub()
fun preformLogEvent(event: LogEvent) {
handler.post {
observers.values.forEach {
it.observer.onLogEvent(event)
if ( it.acquiredEvent.contains(Event.EVENT_LOG) )
it.observer.onLogEvent(event)
}
}
}
@@ -50,7 +51,8 @@ class ClashEventService(private val master: Master) : IClashEventService.Stub()
fun preformProxyChangedEvent(event: ProxyChangedEvent) {
handler.post {
observers.values.forEach {
it.observer.onProxyChangedEvent(event)
if ( it.acquiredEvent.contains(Event.EVENT_PROXY_CHANGED) )
it.observer.onProxyChangedEvent(event)
}
}
}
@@ -58,7 +60,8 @@ class ClashEventService(private val master: Master) : IClashEventService.Stub()
fun preformTrafficEvent(event: TrafficEvent) {
handler.post {
observers.values.forEach {
it.observer.onTrafficEvent(event)
if ( it.acquiredEvent.contains(Event.EVENT_TRAFFIC) )
it.observer.onTrafficEvent(event)
}
}
}
@@ -71,23 +74,11 @@ class ClashEventService(private val master: Master) : IClashEventService.Stub()
}
}
override fun acquireEvent(id: String?, event: Int) {
fun preformProfileChangedEvent(event: ProfileChangedEvent) {
handler.post {
require(id != null)
observers[id]?.acquiredEvent?.add(event)
recastEventRequirement()
}
}
override fun releaseEvent(id: String?, event: Int) {
handler.post {
require(id != null)
observers[id]?.acquiredEvent?.remove(event)
recastEventRequirement()
observers.values.forEach {
it.observer.onProfileChanged(event)
}
}
}
@@ -101,15 +92,25 @@ class ClashEventService(private val master: Master) : IClashEventService.Stub()
}
}
override fun registerEventObserver(id: String?, observer: IClashEventObserver?) {
override fun registerEventObserver(
id: String?,
observer: IClashEventObserver?,
events: IntArray?
) {
handler.post {
require(id != null && observer != null)
require(id != null && observer != null && events != null)
observers[id] = EventObserverRecord(observer, mutableSetOf())
observers[id] = EventObserverRecord(observer, events.toSet())
observer.asBinder().linkToDeath({
unregisterEventObserver(id)
}, 0)
recastEventRequirement()
}
}
fun recastEventRequirement() {
private fun recastEventRequirement() {
handler.post {
val req = observers.values.flatMap {
it.acquiredEvent
@@ -1,122 +1,43 @@
package com.github.kr328.clash.service
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.ComponentName
import android.content.Intent
import android.os.*
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import android.os.Binder
import android.os.IBinder
import android.os.ParcelFileDescriptor
import com.github.kr328.clash.core.Clash
import com.github.kr328.clash.core.event.ProcessEvent
import com.github.kr328.clash.core.Constants
import com.github.kr328.clash.core.event.*
import com.github.kr328.clash.core.utils.Log
import com.github.kr328.clash.service.data.ClashDatabase
import com.github.kr328.clash.service.data.ClashProfileEntity
import java.io.File
import java.io.IOException
import java.lang.Exception
import java.lang.IllegalArgumentException
import java.util.concurrent.Executors
import kotlin.concurrent.thread
class ClashService : Service() {
companion object {
private const val TAG = "ClashForAndroid"
}
private val handler = Handler()
class ClashService : Service(), IClashEventObserver, ClashEventService.Master {
private val executor = Executors.newSingleThreadExecutor()
private var pollThread: Thread? = null
private val eventService = ClashEventService(this)
private lateinit var clash: Clash
private lateinit var currentProfile: LiveData<ClashProfileEntity?>
private var tunEnabled = false
private var status =
ProcessEvent(ProcessEvent.STATUS_STOPPED_INT)
set(value) {
field = value
for (observer in observers.values) {
runCatching {
observer.onStatusChanged(status)
}
}
}
private val observers = mutableMapOf<String, IClashObserver>()
private val defaultProfileObserver = object: Observer<ClashProfileEntity?> {
override fun onChanged(file: ClashProfileEntity?) {
executor.submit {
if (file == null){
clash.process.stop()
return@submit
}
Log.i(TAG, "Loading profile ${file.cache}")
try {
clash.loadProfile(File(file.cache))
} catch (e: IOException) {
clash.process.stop()
Log.w(TAG, "Load profile failure", e)
}
try {
Log.d(Constants.TAG, clash.queryProxies().toString())
}
catch (e: Exception) {
Log.e(Constants.TAG, "NMSL" ,e)
}
handler.post(this@ClashService::updateNotification)
}
}
}
private inner class ClashServiceImpl : IClashService.Stub() {
override fun registerObserver(
id: String?,
notifyCurrent: Boolean,
observer: IClashObserver?
) {
if (id == null || observer == null)
throw RemoteException()
observers[id] = observer
if (notifyCurrent)
observer.onStatusChanged(status)
observer.asBinder().linkToDeath({
observers.remove(id)
}, 0)
}
override fun unregisterObserver(id: String?) {
if (id == null)
throw RemoteException()
observers.remove(id)
}
private lateinit var database: ClashDatabase
private val clashService = object: IClashService.Stub() {
override fun stopTunDevice() {
clash.stopTunDevice()
handler.post(this@ClashService::updateNotification)
}
override fun start() {
try {
clash.process.start()
} catch (e: IOException) {
Log.e(TAG, "Start failure", e)
Log.e("Start failure", e)
throw RemoteException(e.message)
this@ClashService.eventService.preformErrorEvent(
ErrorEvent(ErrorEvent.Type.START_FAILURE, e.toString())
)
}
}
@@ -124,48 +45,50 @@ class ClashService : Service() {
clash.process.stop()
}
override fun getClashProcessStatus(): ProcessEvent {
return status
}
override fun startTunDevice(fd: ParcelFileDescriptor, mtu: Int) {
try {
clash.startTunDevice(fd.fileDescriptor, mtu)
fd.close()
tunEnabled = true
} catch (e: IOException) {
throw RemoteException(e.message)
}
Log.e("Start tun failure", e)
handler.post(this@ClashService::updateNotification)
this@ClashService.eventService.preformErrorEvent(
ErrorEvent(ErrorEvent.Type.START_FAILURE, e.toString())
)
}
}
override fun getEventService(): IClashEventService {
return this@ClashService.eventService
}
override fun getCurrentProcessStatus(): ProcessEvent {
return clash.process.getProcessStatus()
}
}
override fun requireEvent(event: Int) {
clash.setEventEnabled(event, true)
}
override fun releaseEvent(event: Int) {
clash.setEventEnabled(event, false)
}
override fun onCreate() {
super.onCreate()
currentProfile = ClashDatabase.getInstance(this)
.openClashProfileDao()
.observeSelectedProfile()
database = ClashDatabase.getInstance(this)
eventService.registerEventObserver(ClashService::class.java.simpleName,
this,
intArrayOf(Event.EVENT_TRAFFIC))
clash = Clash(
this,
filesDir.resolve("clash"),
cacheDir.resolve("clash_controller")
) {
this.status = it
handler.post {
when (it.status) {
ProcessEvent.STATUS_STARTED_INT ->
currentProfile.observeForever(defaultProfileObserver)
ProcessEvent.STATUS_STOPPED_INT ->
currentProfile.removeObserver(defaultProfileObserver)
}
}
handler.post(this::updateNotification)
}
cacheDir.resolve("clash_controller"),
eventService::preformProcessEvent)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
@@ -177,7 +100,7 @@ class ClashService : Service() {
}
override fun onBind(intent: Intent?): IBinder? {
return ClashServiceImpl()
return clashService
}
override fun onDestroy() {
@@ -187,43 +110,58 @@ class ClashService : Service() {
super.onDestroy()
}
private fun updateNotification() {
if ( status == ProcessEvent.STATUS_STARTED ) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManagerCompat.from(this)
.createNotificationChannel(NotificationChannel(CLASH_STATUS_NOTIFICATION_CHANNEL,
getString(R.string.clash_service_status_channel),
NotificationManager.IMPORTANCE_MIN))
override fun onProfileChanged(event: ProfileChangedEvent?) {
executor.submit {
val active = database.openClashProfileDao().queryActiveProfile()
if (active == null){
clash.process.stop()
return@submit
}
val notification = NotificationCompat.Builder(this, CLASH_STATUS_NOTIFICATION_CHANNEL)
.setSmallIcon(R.drawable.ic_notification_icon)
.setMode()
.setOngoing(true)
.setColorized(true)
.setColor(getColor(R.color.colorAccentService))
.setShowWhen(false)
.setContentTitle(getString(R.string.clash_service_running))
.setContentText(getString(R.string.clash_service_running_message, currentProfile.value?.name))
.setContentIntent(PendingIntent.getActivity(
this,
CLASH_STATUS_NOTIFICATION_ID,
Intent.makeMainActivity(ComponentName.createRelative(packageName, MAIN_ACTIVITY_NAME))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
PendingIntent.FLAG_UPDATE_CURRENT
))
.build()
Log.i("Loading profile ${active.cache}")
NotificationManagerCompat.from(this).notify(CLASH_STATUS_NOTIFICATION_ID, notification)
}
else {
NotificationManagerCompat.from(this).cancel(CLASH_STATUS_NOTIFICATION_ID)
try {
clash.loadProfile(File(active.cache))
} catch (e: IOException) {
clash.process.stop()
Log.w("Load profile failure", e)
}
}
}
private fun NotificationCompat.Builder.setMode(): NotificationCompat.Builder {
if ( tunEnabled )
setSubText(getString(R.string.clash_service_vpn_mode))
return this
override fun onProcessEvent(event: ProcessEvent?) {
when ( event!! ) {
ProcessEvent.STARTED -> {
pollThread = thread {
clash.pollEvent {
try {
when ( it ) {
is LogEvent ->
eventService.preformLogEvent(it)
is TrafficEvent ->
eventService.preformTrafficEvent(it)
is ProxyChangedEvent ->
eventService.preformProxyChangedEvent(it)
}
}
catch (e: Exception) {}
}
}
}
ProcessEvent.STOPPED -> {
pollThread?.interrupt()
pollThread = null
}
}
}
override fun onTrafficEvent(event: TrafficEvent?) {
}
override fun onProxyChangedEvent(event: ProxyChangedEvent?) {}
override fun onLogEvent(event: LogEvent?) {}
override fun onErrorEvent(event: ErrorEvent?) {}
override fun asBinder(): IBinder = throw IllegalArgumentException("asBinder Unsupported")
}
@@ -3,14 +3,15 @@ package com.github.kr328.clash.service
import android.app.Service
import android.content.*
import android.net.*
import android.os.Binder
import android.os.Build
import android.os.IBinder
import android.os.ParcelFileDescriptor
import android.util.Log
import com.github.kr328.clash.core.event.ProcessEvent
import com.github.kr328.clash.core.event.*
import com.github.kr328.clash.service.net.DefaultNetworkObserver
import java.lang.IllegalArgumentException
class TunService : VpnService() {
class TunService : VpnService(), IClashEventObserver {
companion object {
const val TAG = "ClashForAndroid"
@@ -42,29 +43,16 @@ class TunService : VpnService() {
service
) ?: throw NullPointerException()
this@TunService.clash = clash
clash.registerObserver("tun", false, object: IClashObserver.Stub() {
override fun onStatusChanged(event: ProcessEvent?) {
if ( event == null )
return
clash.eventService.registerEventObserver(TunService::class.java.simpleName,
this@TunService,
intArrayOf())
Log.d(TAG, "New clash status $event")
when ( event ) {
ProcessEvent.STATUS_STOPPED ->
stopSelf()
ProcessEvent.STATUS_STARTED ->
clash.startTunDevice(fileDescriptor, VPN_MTU)
}
}
})
if ( clash.clashProcessStatus == ProcessEvent.STATUS_STARTED )
if ( clash.currentProcessStatus == ProcessEvent.STARTED )
clash.startTunDevice(fileDescriptor, VPN_MTU)
else
clash.start()
this@TunService.clash = clash
}
}
@@ -82,7 +70,6 @@ class TunService : VpnService() {
.addAddress(PRIVATE_VLAN6_CLIENT, 126)
.addDefaultDns()
.addDisallowedApplication(packageName)
.addDns()
.addBypassPrivateRoute()
.setMtu(VPN_MTU)
.setBlocking(false)
@@ -117,11 +104,13 @@ class TunService : VpnService() {
defaultNetworkObserver.unregister()
}
private fun Builder.addDefaultDns(): Builder {
DEFAULT_DNS.forEach {
addDnsServer(it)
override fun onProcessEvent(event: ProcessEvent?) {
when ( event ) {
ProcessEvent.STOPPED ->
stopSelf()
ProcessEvent.STARTED ->
clash.startTunDevice(fileDescriptor, VPN_MTU)
}
return this
}
private fun Builder.setMeteredCompat(isMetered: Boolean): Builder {
@@ -134,7 +123,6 @@ class TunService : VpnService() {
// IPv4
resources.getStringArray(R.array.bypass_private_route).forEach {
val address = it.split("/")
Log.d(TAG, "$address")
addRoute(address[0], address[1].toInt())
}
@@ -144,10 +132,17 @@ class TunService : VpnService() {
return this
}
private fun Builder.addDns(): Builder {
private fun Builder.addDefaultDns(): Builder {
resources.getStringArray(R.array.default_dns).forEach {
addDnsServer(it)
}
return this
}
override fun onLogEvent(event: LogEvent?) {}
override fun onErrorEvent(event: ErrorEvent?) {}
override fun onProfileChanged(event: ProfileChangedEvent?) {}
override fun onProxyChangedEvent(event: ProxyChangedEvent?) {}
override fun onTrafficEvent(event: TrafficEvent?) {}
override fun asBinder(): IBinder = throw IllegalArgumentException("asBinder Unsupported")
}
@@ -8,13 +8,16 @@ import androidx.room.Query
@Dao
interface ClashProfileDao {
@Query("UPDATE profiles SET selected = CASE WHEN id = :id THEN 1 ELSE 0 END")
@Query("UPDATE profiles SET active = CASE WHEN id = :id THEN 1 ELSE 0 END")
fun setSelectedProfile(id: Int)
@Query("SELECT * FROM profiles WHERE selected = 1 LIMIT 1")
@Query("SELECT * FROM profiles WHERE active = 1 LIMIT 1")
fun observeSelectedProfile(): LiveData<ClashProfileEntity?>
@Query("SELECT name FROM profiles WHERE selected = 1 LIMIT 1")
@Query("SELECT * FROM profiles WHERE active = 1 LIMIT 1")
fun queryActiveProfile(): ClashProfileEntity?
@Query("SELECT name FROM profiles WHERE active = 1 LIMIT 1")
fun observeDefaultProfileName(): LiveData<String?>
@Query("SELECT * FROM profiles ORDER BY name")
@@ -8,7 +8,7 @@ data class ClashProfileEntity(
@ColumnInfo(name = "name") val name: String,
@ColumnInfo(name = "token") val token: String,
@ColumnInfo(name = "cache") val cache: String,
@ColumnInfo(name = "selected") val selected: Boolean,
@ColumnInfo(name = "active") val active: Boolean,
@ColumnInfo(name = "proxies") val proxies: Int,
@ColumnInfo(name = "proxy_groups") val proxyGroups: Int,
@ColumnInfo(name = "rules") val rules: Int,