split ui / service

This commit is contained in:
Kr328 committed 2019-11-20 18:13:05 +08:00
1 parent cd1a8bf5e4
commit d332b6941a
20 files changed
+126 -304

No files matched your search

+5 -9
View File
@@ -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"
}
@@ -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)
}
}
-14
View File
@@ -19,19 +19,5 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".ClashService"
android:exported="false"
android:process=":clash" />
<service
android:name=".TunService"
android:exported="false"
android:process=":clash"
android:permission="android.permission.BIND_VPN_SERVICE">
<intent-filter>
<action android:name="android.net.VpnService" />
</intent-filter>
</service>
</application>
</manifest>
@@ -1,7 +0,0 @@
package com.github.kr328.clash;
import com.github.kr328.clash.core.ClashProcessStatus;
interface IClashObserver {
void onStatusChanged(in ClashProcessStatus status);
}
@@ -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();
}
@@ -1,3 +0,0 @@
package com.github.kr328.clash.core;
parcelable ClashProcessStatus;
@@ -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<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()
}
}
@@ -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()
}
}
@@ -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)
}
}
+4 -1
View File
@@ -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()
@@ -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)
}
}
@@ -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)
}
}
+1
View File
@@ -0,0 +1 @@
/build
+37
View File
@@ -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"
}
View File
Whitespace-only changes.
+21
View File
@@ -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
+22
View File
@@ -0,0 +1,22 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.kr328.clash.service">
<application>
<activity
android:name=".VpnRequestActivity"
android:exported="false"
android:theme="@android:style/Theme.NoDisplay" />
<service
android:name="ClashService"
android:exported="false"
android:process=":clash" />
<service
android:name="TunService"
android:exported="false"
android:process=":clash"
android:permission="android.permission.BIND_VPN_SERVICE">
<intent-filter>
<action android:name="android.net.VpnService" />
</intent-filter>
</service>
</application>
</manifest>
@@ -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)
}
}
}
+3
View File
@@ -0,0 +1,3 @@
<resources>
<string name="app_name">Service</string>
</resources>
+1 -1
View File
@@ -1,2 +1,2 @@
include ':app', ':core'
include ':app', ':core', ':service'
rootProject.name='Clash'