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'