split ui / service

This commit is contained in:
Kr328 committed 2019-11-20 18:13:28 +08:00
1 parent d332b6941a
commit 792190b5f4
5 files changed
+206

No files matched your search

@@ -0,0 +1,3 @@
package com.github.kr328.clash.core;
parcelable ClashProcessStatus;
@@ -0,0 +1,7 @@
package com.github.kr328.clash.service;
import com.github.kr328.clash.core.ClashProcessStatus;
interface IClashObserver {
void onStatusChanged(in ClashProcessStatus status);
}
@@ -0,0 +1,12 @@
package com.github.kr328.clash.service;
import com.github.kr328.clash.service.IClashObserver;
interface IClashService {
void start();
void stop();
void registerObserver(String id, boolean notifyCurrent, in IClashObserver observer);
void unregisterObserver(String id);
void startTunDevice(in ParcelFileDescriptor fd, int mtu);
void stopTunDevice();
}
@@ -0,0 +1,100 @@
package com.github.kr328.clash.service
import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.os.ParcelFileDescriptor
import android.os.RemoteException
import com.github.kr328.clash.core.Clash
import com.github.kr328.clash.core.ClashProcessStatus
import java.io.IOException
class ClashService : Service() {
companion object {
private val TAG = "ClashForAndroid"
}
private lateinit var clash: Clash
private var status = ClashProcessStatus(ClashProcessStatus.STATUS_STOPPED)
set(value) {
field = value
for ( observer in observers.values ) {
runCatching {
observer.onStatusChanged(status)
}
}
}
private val observers = mutableMapOf<String, IClashObserver>()
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)
}
override fun stopTunDevice() {
clash.stop()
}
override fun start() {
try {
clash.start()
} catch (e: IOException) {
throw RemoteException(e.message)
}
}
override fun stop() {
clash.stop()
}
override fun startTunDevice(fd: ParcelFileDescriptor, mtu: Int) {
try {
clash.startTunDevice(fd.fileDescriptor, mtu)
} catch (e: IOException) {
throw RemoteException(e.message)
}
}
}
override fun onCreate() {
super.onCreate()
clash = Clash(
this,
filesDir.resolve("clash"),
cacheDir.resolve("clash_controller")
) {
this.status = it
}
}
override fun onBind(intent: Intent?): IBinder? {
return ClashServiceImpl()
}
override fun onDestroy() {
clash.stop()
super.onDestroy()
}
}
@@ -0,0 +1,84 @@
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.net.VpnService
import android.os.IBinder
import android.os.ParcelFileDescriptor
import com.github.kr328.clash.core.ClashProcessStatus
class TunService : VpnService() {
companion object {
// from https://github.com/shadowsocks/shadowsocks-android/blob/master/core/src/main/java/com/github/shadowsocks/bg/VpnService.kt
private const val VPN_MTU = 1500
private const val PRIVATE_VLAN4_CLIENT = "172.19.0.1"
private const val PRIVATE_VLAN4_ROUTER = "172.19.0.2"
private const val PRIVATE_VLAN6_CLIENT = "fdfe:dcba:9876::1"
private const val PRIVATE_VLAN6_ROUTER = "fdfe:dcba:9876::2"
}
private lateinit var fileDescriptor: ParcelFileDescriptor
private val connection = object: ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
stopSelf()
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val clash = IClashService.Stub.asInterface(
service
) ?: throw NullPointerException()
clash.start()
clash.registerObserver("tun", false, object: IClashObserver.Stub() {
override fun onStatusChanged(status: ClashProcessStatus?) {
if ( status == null )
return
when ( status.status ) {
ClashProcessStatus.STATUS_STOPPED ->
stopSelf()
ClashProcessStatus.STATUS_STARTED ->
clash.startTunDevice(fileDescriptor, VPN_MTU)
}
}
})
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
if ( prepare(this) != null ) {
startActivity(Intent(this, VpnRequestActivity::class.java))
stopSelf()
return Service.START_NOT_STICKY
}
fileDescriptor = Builder()
.addAddress(PRIVATE_VLAN4_CLIENT, 30)
.addDnsServer(PRIVATE_VLAN4_ROUTER)
.addAddress(PRIVATE_VLAN6_CLIENT, 126)
.addDnsServer(PRIVATE_VLAN6_ROUTER)
.addDisallowedApplication(packageName)
.addRoute("0.0.0.0", 0)
.addRoute("::", 0)
.setMtu(VPN_MTU)
.setBlocking(false)
.establish() ?: throw NullPointerException("Unable to establish VPN")
bindService(Intent(this, ClashService::class.java), connection, Context.BIND_AUTO_CREATE)
return Service.START_NOT_STICKY
}
override fun onDestroy() {
fileDescriptor.close()
unbindService(connection)
super.onDestroy()
}
}