clash service

This commit is contained in:
Kr328 committed 2019-11-20 12:10:33 +08:00
1 parent 4c84616797
commit b98d5a5ce0
14 files changed
+201 -167

No files matched your search

+40 -37
View File
@@ -1,38 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.github.kr328.clash">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<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>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.github.kr328.clash">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<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>
<provider
android:authorities="${applicationId}.data"
android:name=".DataProvider"
android:exported="false"/>
</application>
</manifest>
@@ -1,7 +1,7 @@
package com.github.kr328.clash;
import com.github.kr328.clash.model.ClashStatus;
import com.github.kr328.clash.core.ClashProcessStatus;
interface IClashObserver {
void onStatusChanged(in ClashStatus status);
void onStatusChanged(in ClashProcessStatus status);
}
@@ -8,6 +8,6 @@ interface IClashService {
void stop();
void registerObserver(String id, in IClashObserver observer);
void unregisterObserver(String id);
void loadProfile(in Uri path);
void startTunDevice(in ParcelFileDescriptor fd, int mtu);
void stopTunDevice();
}
@@ -0,0 +1,3 @@
package com.github.kr328.clash.core;
parcelable ClashProcessStatus;
@@ -1,3 +0,0 @@
package com.github.kr328.clash.model;
parcelable ClashStatus;
@@ -2,13 +2,11 @@ package com.github.kr328.clash
import android.app.Service
import android.content.Intent
import android.net.Uri
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.ClashProcess
import com.github.kr328.clash.model.ClashStatus
import com.github.kr328.clash.core.ClashProcessStatus
import java.io.IOException
class ClashService : Service() {
@@ -18,13 +16,13 @@ class ClashService : Service() {
private lateinit var clash: Clash
private var status = ClashProcess.Status.STOPPED
private var status = ClashProcessStatus(ClashProcessStatus.STATUS_STOPPED)
set(value) {
field = value
for (kv in observers) {
runCatching {
kv.value.onStatusChanged(ClashStatus(status))
kv.value.onStatusChanged(status)
}
}
}
@@ -37,7 +35,7 @@ class ClashService : Service() {
observers[id] = observer
observer.onStatusChanged(ClashStatus(status))
observer.onStatusChanged(status)
observer.asBinder().linkToDeath({
observers.remove(id)
@@ -51,6 +49,10 @@ class ClashService : Service() {
observers.remove(id)
}
override fun stopTunDevice() {
clash.stop()
}
override fun start() {
try {
clash.start()
@@ -63,14 +65,6 @@ class ClashService : Service() {
clash.stop()
}
override fun loadProfile(url: Uri?) {
try {
clash.loadProfile(url ?: throw RemoteException())
} catch (e: IOException) {
throw RemoteException(e.message)
}
}
override fun startTunDevice(fd: ParcelFileDescriptor, mtu: Int) {
try {
clash.startTunDevice(fd.fileDescriptor, mtu)
@@ -0,0 +1,44 @@
package com.github.kr328.clash
import android.content.ContentProvider
import android.content.ContentValues
import android.database.Cursor
import android.net.Uri
class DataProvider : ContentProvider() {
override fun insert(uri: Uri, values: ContentValues?): Uri? {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun query(
uri: Uri,
projection: Array<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
sortOrder: String?
): Cursor? {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onCreate(): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun update(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<out String>?
): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun getType(uri: Uri): String? {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
@@ -1,57 +1,26 @@
package com.github.kr328.clash
import android.app.Activity
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.net.VpnService
import android.os.Bundle
import android.os.IBinder
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
import kotlin.concurrent.thread
class MainActivity : AppCompatActivity() {
companion object {
private const val REQUEST_CODE = 233
private const val VPN_REQUEST = 234
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener {
startActivityForResult(Intent(Intent.ACTION_GET_CONTENT).setType("*/*"), REQUEST_CODE)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) {
Toast.makeText(this, intent?.dataString, Toast.LENGTH_LONG).show()
thread {
bindService(Intent(this, ClashService::class.java), object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
Toast.makeText(this@MainActivity, "Disconnected", Toast.LENGTH_LONG).show()
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
IClashService.Stub.asInterface(service)?.loadProfile(data?.data)
VpnService.prepare(this@MainActivity)?.apply {
startActivityForResult(this, VPN_REQUEST)
} ?: startService(Intent(this@MainActivity, TunService::class.java))
}
}, Context.BIND_AUTO_CREATE)
}
} else if (requestCode == VPN_REQUEST && resultCode == Activity.RESULT_OK) {
startService(Intent(this, TunService::class.java))
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
}
package com.github.kr328.clash
import android.app.Activity
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.net.VpnService
import android.os.Bundle
import android.os.IBinder
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
import kotlin.concurrent.thread
class MainActivity : AppCompatActivity() {
companion object {
private const val REQUEST_CODE = 233
private const val VPN_REQUEST = 234
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
@@ -9,9 +9,8 @@ import android.net.VpnService
import android.os.IBinder
import android.os.ParcelFileDescriptor
import com.github.kr328.clash.core.ClashProcess
import com.github.kr328.clash.model.ClashStatus
import com.github.kr328.clash.core.ClashStatus
import java.lang.NullPointerException
import java.lang.RuntimeException
class TunService : VpnService() {
companion object {
@@ -1,38 +0,0 @@
package com.github.kr328.clash.model
import android.os.Parcel
import android.os.Parcelable
import com.github.kr328.clash.core.ClashProcess
data class ClashStatus(val status: ClashProcess.Status) : Parcelable {
override fun writeToParcel(parcel: Parcel, flags: Int) {
when ( status ) {
ClashProcess.Status.STOPPED -> parcel.writeInt(0)
ClashProcess.Status.STARTED -> parcel.writeInt(1)
}
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<ClashStatus> {
override fun createFromParcel(parcel: Parcel): ClashStatus {
return ClashStatus(readFromParcel(parcel))
}
override fun newArray(size: Int): Array<ClashStatus?> {
return arrayOfNulls(size)
}
private fun readFromParcel(parcel: Parcel): ClashProcess.Status {
return when ( parcel.readInt() ) {
0 -> ClashProcess.Status.STOPPED
1 -> ClashProcess.Status.STARTED
else -> throw IllegalArgumentException()
}
}
}
}
@@ -4,16 +4,16 @@ import android.content.Context
import android.net.LocalSocket
import android.net.LocalSocketAddress
import android.net.Uri
import android.os.ParcelFileDescriptor
import android.util.Log
import org.json.JSONObject
import java.io.*
class Clash(private val context: Context,
clashDir: File,
private val controllerPath: File,
listener: (Status) -> Unit) :
ClashProcess(context, clashDir, controllerPath, listener) {
class Clash(
private val context: Context,
clashDir: File,
private val controllerPath: File,
listener: (ClashProcessStatus) -> Unit
) : ClashProcess(context, clashDir, controllerPath, listener) {
companion object {
const val COMMAND_PING = 0
const val COMMAND_TUN_START = 1
@@ -11,7 +11,7 @@ import kotlin.concurrent.thread
abstract class ClashProcess(private val context: Context,
private val clashDir: File,
private val controllerPath: File,
private val listener: (Status) -> Unit) {
private val listener: (ClashProcessStatus) -> Unit) {
companion object {
const val TAG = "ClashForAndroid"
@@ -23,10 +23,6 @@ abstract class ClashProcess(private val context: Context,
private val CONTROLLER_ERROR_PATTERN = Regex("\\[CONTROLLER] ERROR=\\{(.+)\\}")
}
enum class Status {
STARTED, STOPPED
}
private var pid: Int = 0
private var process: Process? = null
@@ -83,7 +79,7 @@ abstract class ClashProcess(private val context: Context,
process = p
pid = currentPid
listener(Status.STARTED)
listener(ClashProcessStatus(ClashProcessStatus.STATUS_STARTED))
Log.i(TAG, "Clash started pid = $pid")
@@ -99,7 +95,7 @@ abstract class ClashProcess(private val context: Context,
pid = -1
}
listener(Status.STOPPED)
listener(ClashProcessStatus(ClashProcessStatus.STATUS_STOPPED))
}
}
@@ -0,0 +1,32 @@
package com.github.kr328.clash.core
import android.os.Parcel
import android.os.Parcelable
data class ClashProcessStatus(val status: Int) : Parcelable {
companion object {
const val STATUS_STOPPED = 0
const val STATUS_STARTED = 1
@JvmField
val CREATOR = object: Parcelable.Creator<ClashProcessStatus> {
override fun createFromParcel(parcel: Parcel): ClashProcessStatus {
return ClashProcessStatus(parcel)
}
override fun newArray(size: Int): Array<ClashProcessStatus?> {
return arrayOfNulls(size)
}
}
}
constructor(parcel: Parcel) : this(parcel.readInt())
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(status)
}
override fun describeContents(): Int {
return 0
}
}
@@ -0,0 +1,35 @@
package com.github.kr328.clash.core
import android.os.Parcel
import android.os.Parcelable
data class ClashStatus(val status: Int) : Parcelable {
companion object {
const val STOPPED = 0
const val IDLE = 1
const val LOADING_PROFILE = 2
@JvmField
val CREATOR = object: Parcelable.Creator<ClashStatus> {
override fun createFromParcel(parcel: Parcel): ClashStatus {
return ClashStatus(parcel)
}
override fun newArray(size: Int): Array<ClashStatus?> {
return arrayOfNulls(size)
}
}
}
constructor(parcel: Parcel) : this(parcel.readInt())
override fun writeToParcel(dest: Parcel?, flags: Int) {
dest?.writeInt(status)
}
override fun describeContents(): Int {
return 0
}
}