diff --git a/app/build.gradle b/app/build.gradle
index 7eda2a0..341e6f0 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -11,7 +11,6 @@ android {
targetSdkVersion 29
versionCode 1
versionName "1.0"
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
@@ -22,13 +21,10 @@ android {
}
dependencies {
- implementation fileTree(dir: 'libs', include: ['*.jar'])
- implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
- implementation 'androidx.appcompat:appcompat:1.1.0'
- implementation 'androidx.core:core-ktx:1.1.0'
- implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation project(":core")
- testImplementation 'junit:junit:4.12'
- androidTestImplementation 'androidx.test.ext:junit:1.1.1'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+ implementation project(":service")
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation "androidx.appcompat:appcompat:1.1.0"
+ implementation "androidx.core:core-ktx:1.1.0"
+ implementation "androidx.constraintlayout:constraintlayout:1.1.3"
}
diff --git a/app/src/androidTest/java/com/github/kr328/clash/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/github/kr328/clash/ExampleInstrumentedTest.kt
deleted file mode 100644
index ea1fd43..0000000
--- a/app/src/androidTest/java/com/github/kr328/clash/ExampleInstrumentedTest.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.github.kr328.clash
-
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.ext.junit.runners.AndroidJUnit4
-
-import org.junit.Test
-import org.junit.runner.RunWith
-
-import org.junit.Assert.*
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-@RunWith(AndroidJUnit4::class)
-class ExampleInstrumentedTest {
- @Test
- fun useAppContext() {
- // Context of the app under test.
- val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("com.github.kr328.clash", appContext.packageName)
- }
-}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2ace137..c4e513a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -19,19 +19,5 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/aidl/com/github/kr328/clash/IClashObserver.aidl b/app/src/main/aidl/com/github/kr328/clash/IClashObserver.aidl
deleted file mode 100644
index a18efbf..0000000
--- a/app/src/main/aidl/com/github/kr328/clash/IClashObserver.aidl
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.github.kr328.clash;
-
-import com.github.kr328.clash.core.ClashProcessStatus;
-
-interface IClashObserver {
- void onStatusChanged(in ClashProcessStatus status);
-}
diff --git a/app/src/main/aidl/com/github/kr328/clash/IClashService.aidl b/app/src/main/aidl/com/github/kr328/clash/IClashService.aidl
deleted file mode 100644
index 0323c2f..0000000
--- a/app/src/main/aidl/com/github/kr328/clash/IClashService.aidl
+++ /dev/null
@@ -1,13 +0,0 @@
-// IClashService.aidl
-package com.github.kr328.clash;
-
-import com.github.kr328.clash.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();
-}
diff --git a/app/src/main/aidl/com/github/kr328/clash/core/ClashProcessStatus.aidl b/app/src/main/aidl/com/github/kr328/clash/core/ClashProcessStatus.aidl
deleted file mode 100644
index 4d17b7c..0000000
--- a/app/src/main/aidl/com/github/kr328/clash/core/ClashProcessStatus.aidl
+++ /dev/null
@@ -1,3 +0,0 @@
-package com.github.kr328.clash.core;
-
-parcelable ClashProcessStatus;
\ No newline at end of file
diff --git a/app/src/main/java/com/github/kr328/clash/ClashService.kt b/app/src/main/java/com/github/kr328/clash/ClashService.kt
deleted file mode 100644
index 2dbea7e..0000000
--- a/app/src/main/java/com/github/kr328/clash/ClashService.kt
+++ /dev/null
@@ -1,100 +0,0 @@
-package com.github.kr328.clash
-
-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 (kv in observers) {
- runCatching {
- kv.value.onStatusChanged(status)
- }
- }
- }
- private val observers = mutableMapOf()
-
- 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()
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/github/kr328/clash/TunService.kt b/app/src/main/java/com/github/kr328/clash/TunService.kt
deleted file mode 100644
index 22c9a95..0000000
--- a/app/src/main/java/com/github/kr328/clash/TunService.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-package com.github.kr328.clash
-
-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)
-
- 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()
- }
-}
\ No newline at end of file
diff --git a/app/src/test/java/com/github/kr328/clash/ExampleUnitTest.kt b/app/src/test/java/com/github/kr328/clash/ExampleUnitTest.kt
deleted file mode 100644
index 077596f..0000000
--- a/app/src/test/java/com/github/kr328/clash/ExampleUnitTest.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.github.kr328.clash
-
-import org.junit.Test
-
-import org.junit.Assert.*
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-class ExampleUnitTest {
- @Test
- fun addition_isCorrect() {
- assertEquals(4, 2 + 2)
- }
-}
diff --git a/build.gradle b/build.gradle
index 88f44b3..d2b47a5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,10 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
- ext.kotlin_version = '1.3.60'
+ ext {
+ kotlin_version = '1.3.60'
+ room_version = '2.2.1'
+ }
repositories {
google()
jcenter()
diff --git a/core/src/androidTest/java/com/github/kr328/clash/core/ExampleInstrumentedTest.kt b/core/src/androidTest/java/com/github/kr328/clash/core/ExampleInstrumentedTest.kt
deleted file mode 100644
index a3f5f23..0000000
--- a/core/src/androidTest/java/com/github/kr328/clash/core/ExampleInstrumentedTest.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.github.kr328.clash.core
-
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.ext.junit.runners.AndroidJUnit4
-
-import org.junit.Test
-import org.junit.runner.RunWith
-
-import org.junit.Assert.*
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-@RunWith(AndroidJUnit4::class)
-class ExampleInstrumentedTest {
- @Test
- fun useAppContext() {
- // Context of the app under test.
- val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("com.github.kr328.clash.core.test", appContext.packageName)
- }
-}
diff --git a/core/src/test/java/com/github/kr328/clash/core/ExampleUnitTest.kt b/core/src/test/java/com/github/kr328/clash/core/ExampleUnitTest.kt
deleted file mode 100644
index a93771e..0000000
--- a/core/src/test/java/com/github/kr328/clash/core/ExampleUnitTest.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.github.kr328.clash.core
-
-import org.junit.Test
-
-import org.junit.Assert.*
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-class ExampleUnitTest {
- @Test
- fun addition_isCorrect() {
- assertEquals(4, 2 + 2)
- }
-}
diff --git a/service/.gitignore b/service/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/service/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/service/build.gradle b/service/build.gradle
new file mode 100644
index 0000000..d9a8cc6
--- /dev/null
+++ b/service/build.gradle
@@ -0,0 +1,37 @@
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-kapt'
+
+android {
+ compileSdkVersion 29
+ buildToolsVersion "29.0.2"
+
+
+ defaultConfig {
+ minSdkVersion 23
+ targetSdkVersion 29
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles 'consumer-rules.pro'
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+
+}
+
+dependencies {
+ kapt "androidx.room:room-compiler:$room_version"
+
+ implementation project(":core")
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation "androidx.core:core-ktx:1.1.0"
+ implementation "androidx.room:room-runtime:$room_version"
+}
diff --git a/service/consumer-rules.pro b/service/consumer-rules.pro
new file mode 100644
index 0000000..e69de29
diff --git a/service/proguard-rules.pro b/service/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/service/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/service/src/main/AndroidManifest.xml b/service/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..1fff614
--- /dev/null
+++ b/service/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/service/src/main/java/com/github/kr328/clash/service/VpnRequestActivity.kt b/service/src/main/java/com/github/kr328/clash/service/VpnRequestActivity.kt
new file mode 100644
index 0000000..358de3c
--- /dev/null
+++ b/service/src/main/java/com/github/kr328/clash/service/VpnRequestActivity.kt
@@ -0,0 +1,32 @@
+package com.github.kr328.clash.service
+
+import android.app.Activity
+import android.content.Intent
+import android.net.VpnService
+import android.os.Bundle
+
+class VpnRequestActivity : Activity() {
+ companion object {
+ const val REQUEST_CODE = 124
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val intent = VpnService.prepare(this)
+
+ if ( intent == null )
+ finish()
+ else
+ startActivityForResult(intent, REQUEST_CODE)
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ if ( requestCode == REQUEST_CODE && resultCode == RESULT_OK ) {
+ startService(Intent(this, TunService::class.java))
+ }
+ else {
+ super.onActivityResult(requestCode, resultCode, data)
+ }
+ }
+}
\ No newline at end of file
diff --git a/service/src/main/res/values/strings.xml b/service/src/main/res/values/strings.xml
new file mode 100644
index 0000000..30f183c
--- /dev/null
+++ b/service/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Service
+
diff --git a/settings.gradle b/settings.gradle
index 99b512a..38a9ed4 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,2 +1,2 @@
-include ':app', ':core'
+include ':app', ':core', ':service'
rootProject.name='Clash'