split ui / service
This commit is contained in:
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()
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user