diff --git a/client/android/AndroidManifest.xml b/client/android/AndroidManifest.xml
index d0cfc058..78d575da 100644
--- a/client/android/AndroidManifest.xml
+++ b/client/android/AndroidManifest.xml
@@ -77,7 +77,10 @@
-
+
+
+
+
diff --git a/client/android/build.gradle b/client/android/build.gradle
index bc5322f7..001dc4cb 100644
--- a/client/android/build.gradle
+++ b/client/android/build.gradle
@@ -47,32 +47,10 @@ dependencies {
implementation "androidx.security:security-crypto:1.1.0-alpha03"
implementation "androidx.security:security-identity-credential:1.0.0-alpha02"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.2"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0"
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.0.10"
implementation project(path: ':shadowsocks')
- //ss
- // implementation 'androidx.lifecycle:lifecycle-common-java8:2.4.0'
- // implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
- // implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"
- // //implementation "androidx.core:core-ktx:1.2.0"
- // implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
- // implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.0"
- // implementation "androidx.lifecycle:lifecycle-livedata-core-ktx:2.4.0"
- // implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
- // implementation "androidx.room:room-runtime:2.2.5" // runtime
- // implementation "androidx.preference:preference:1.1.0"
- // implementation "androidx.work:work-runtime-ktx:2.3.4"
- // implementation "androidx.browser:browser:1.3.0-alpha01"
- // implementation "androidx.constraintlayout:constraintlayout:1.1.3"
- // implementation "com.google.android.material:material:1.2.0-alpha05"
- // implementation "com.google.code.gson:gson:2.8.5"
- // implementation "dnsjava:dnsjava:2.1.9"
- // implementation "org.connectbot.jsocks:jsocks:1.0.0"
- // implementation "com.afollestad.material-dialogs:core:2.6.0"
- // implementation 'com.takisoft.preferencex:preferencex:1.1.0'
- // implementation 'com.android.support:multidex:1.0.0'
- // api 'org.connectbot.jsocks:jsocks:1.0.0'
- // annotationProcessor "androidx.room:room-compiler:2.2.5"
- // annotationProcessor "androidx.lifecycle:lifecycle-compiler:2.4.0"
}
androidExtensions {
diff --git a/client/android/shadowsocks/.gitignore b/client/android/shadowsocks/.gitignore
deleted file mode 100644
index 796b96d1..00000000
--- a/client/android/shadowsocks/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/client/android/shadowsocks/build.gradle b/client/android/shadowsocks/build.gradle
index f3446189..d35daa11 100644
--- a/client/android/shadowsocks/build.gradle
+++ b/client/android/shadowsocks/build.gradle
@@ -3,6 +3,7 @@ allprojects {
repositories {
google()
jcenter()
+ mavenCentral()
}
}
@@ -40,14 +41,6 @@ android {
jvmTarget = '1.8'
}
}
-//publish {
-// userOrg = 'kyle' //bintray注册的用户名
-// groupId = 'com.kyle' //compile引用时的第1部分groupId
-// artifactId = 'shadowsocks' //compile引用时的第2部分项目名
-// publishVersion = '1.0.1' //compile引用时的第3部分版本号
-// desc = 'This is a shadowsocks library '
-// website = 'https://github.com/zhengKyles/shadowsocksDemo'
-//}
androidExtensions {
experimental = true
@@ -62,6 +55,8 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.30-M1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0"
+
implementation "androidx.core:core-ktx:1.2.0"
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
diff --git a/client/android/shadowsocks/schemas/org.amnezia.vpn.shadowsocks.core.database.PrivateDatabase/29.json b/client/android/shadowsocks/schemas/org.amnezia.vpn.shadowsocks.core.database.PrivateDatabase/29.json
deleted file mode 100644
index f9a4300e..00000000
--- a/client/android/shadowsocks/schemas/org.amnezia.vpn.shadowsocks.core.database.PrivateDatabase/29.json
+++ /dev/null
@@ -1,174 +0,0 @@
-{
- "formatVersion": 1,
- "database": {
- "version": 29,
- "identityHash": "b60ecca4d684ffe73173478bffd50a17",
- "entities": [
- {
- "tableName": "Profile",
- "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`bypass` INTEGER NOT NULL, `host` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `individual` TEXT NOT NULL, `ipv6` INTEGER NOT NULL, `metered` INTEGER NOT NULL, `method` TEXT NOT NULL, `name` TEXT, `password` TEXT NOT NULL, `plugin` TEXT, `proxyApps` INTEGER NOT NULL, `remoteDns` TEXT NOT NULL, `remotePort` INTEGER NOT NULL, `route` TEXT NOT NULL, `rx` INTEGER NOT NULL, `tx` INTEGER NOT NULL, `udpFallback` INTEGER, `udpdns` INTEGER NOT NULL, `userOrder` INTEGER NOT NULL)",
- "fields": [
- {
- "fieldPath": "bypass",
- "columnName": "bypass",
- "affinity": "INTEGER",
- "notNull": true
- },
- {
- "fieldPath": "host",
- "columnName": "host",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "id",
- "columnName": "id",
- "affinity": "INTEGER",
- "notNull": true
- },
- {
- "fieldPath": "individual",
- "columnName": "individual",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "ipv6",
- "columnName": "ipv6",
- "affinity": "INTEGER",
- "notNull": true
- },
- {
- "fieldPath": "metered",
- "columnName": "metered",
- "affinity": "INTEGER",
- "notNull": true
- },
- {
- "fieldPath": "method",
- "columnName": "method",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "name",
- "columnName": "name",
- "affinity": "TEXT",
- "notNull": false
- },
- {
- "fieldPath": "password",
- "columnName": "password",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "plugin",
- "columnName": "plugin",
- "affinity": "TEXT",
- "notNull": false
- },
- {
- "fieldPath": "proxyApps",
- "columnName": "proxyApps",
- "affinity": "INTEGER",
- "notNull": true
- },
- {
- "fieldPath": "remoteDns",
- "columnName": "remoteDns",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "remotePort",
- "columnName": "remotePort",
- "affinity": "INTEGER",
- "notNull": true
- },
- {
- "fieldPath": "route",
- "columnName": "route",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "rx",
- "columnName": "rx",
- "affinity": "INTEGER",
- "notNull": true
- },
- {
- "fieldPath": "tx",
- "columnName": "tx",
- "affinity": "INTEGER",
- "notNull": true
- },
- {
- "fieldPath": "udpFallback",
- "columnName": "udpFallback",
- "affinity": "INTEGER",
- "notNull": false
- },
- {
- "fieldPath": "udpdns",
- "columnName": "udpdns",
- "affinity": "INTEGER",
- "notNull": true
- },
- {
- "fieldPath": "userOrder",
- "columnName": "userOrder",
- "affinity": "INTEGER",
- "notNull": true
- }
- ],
- "primaryKey": {
- "columnNames": [
- "id"
- ],
- "autoGenerate": true
- },
- "indices": [],
- "foreignKeys": []
- },
- {
- "tableName": "KeyValuePair",
- "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `value` BLOB NOT NULL, `valueType` INTEGER NOT NULL, PRIMARY KEY(`key`))",
- "fields": [
- {
- "fieldPath": "key",
- "columnName": "key",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "value",
- "columnName": "value",
- "affinity": "BLOB",
- "notNull": true
- },
- {
- "fieldPath": "valueType",
- "columnName": "valueType",
- "affinity": "INTEGER",
- "notNull": true
- }
- ],
- "primaryKey": {
- "columnNames": [
- "key"
- ],
- "autoGenerate": false
- },
- "indices": [],
- "foreignKeys": []
- }
- ],
- "views": [],
- "setupQueries": [
- "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
- "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'b60ecca4d684ffe73173478bffd50a17')"
- ]
- }
-}
\ No newline at end of file
diff --git a/client/android/shadowsocks/schemas/org.amnezia.vpn.shadowsocks.core.database.PublicDatabase/4.json b/client/android/shadowsocks/schemas/org.amnezia.vpn.shadowsocks.core.database.PublicDatabase/4.json
deleted file mode 100644
index b15900b6..00000000
--- a/client/android/shadowsocks/schemas/org.amnezia.vpn.shadowsocks.core.database.PublicDatabase/4.json
+++ /dev/null
@@ -1,46 +0,0 @@
-{
- "formatVersion": 1,
- "database": {
- "version": 4,
- "identityHash": "f1aab1fb633378621635c344dbc8ac7b",
- "entities": [
- {
- "tableName": "KeyValuePair",
- "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `value` BLOB NOT NULL, `valueType` INTEGER NOT NULL, PRIMARY KEY(`key`))",
- "fields": [
- {
- "fieldPath": "key",
- "columnName": "key",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "value",
- "columnName": "value",
- "affinity": "BLOB",
- "notNull": true
- },
- {
- "fieldPath": "valueType",
- "columnName": "valueType",
- "affinity": "INTEGER",
- "notNull": true
- }
- ],
- "primaryKey": {
- "columnNames": [
- "key"
- ],
- "autoGenerate": false
- },
- "indices": [],
- "foreignKeys": []
- }
- ],
- "views": [],
- "setupQueries": [
- "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
- "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f1aab1fb633378621635c344dbc8ac7b')"
- ]
- }
-}
\ No newline at end of file
diff --git a/client/android/shadowsocks/src/main/AndroidManifest.xml b/client/android/shadowsocks/src/main/AndroidManifest.xml
index e98b9f4b..9769ab7a 100644
--- a/client/android/shadowsocks/src/main/AndroidManifest.xml
+++ b/client/android/shadowsocks/src/main/AndroidManifest.xml
@@ -30,48 +30,45 @@
android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAI_zVxZthz2HDuz9toTvkYvL0L5GA-OjeUIfBeXg" />
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
-
+
+
+
+
+
+
-
+
+
+
+
+
+
+ android:process=":QtOnlyProcess">
@@ -87,48 +84,48 @@
diff --git a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/VpnManager.kt b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/VpnManager.kt
index 044ba002..b9429544 100644
--- a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/VpnManager.kt
+++ b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/VpnManager.kt
@@ -14,12 +14,6 @@ import org.amnezia.vpn.shadowsocks.core.bg.BaseService
import org.amnezia.vpn.shadowsocks.core.preference.DataStore
import org.amnezia.vpn.shadowsocks.core.utils.Key
-/**
- * @author : kyle
- * e-mail : 1239878682@qq.com
- * @date : 2019/5/14 16:54
- * 看了我的代码,感动了吗?
- */
class VpnManager private constructor() {
var state = BaseService.State.Idle
@@ -80,31 +74,23 @@ class VpnManager private constructor() {
connect()
}
- /***
- * 开启或者关闭 自动判断
- */
- fun run(activity:Activity) {
+ fun run() {
when {
state.canStop -> Core.stopService()
- DataStore.serviceMode == Key.modeVpn -> {
- val intent = VpnService.prepare(activity)
- if (intent != null) activity.startActivityForResult(intent, REQUEST_CONNECT)
- else onActivityResult(REQUEST_CONNECT, Activity.RESULT_OK, null)
- }
+// DataStore.serviceMode == Key.modeVpn -> {
+// val intent = VpnService.prepare(activity)
+// if (intent != null) activity.startActivityForResult(intent, REQUEST_CONNECT)
+// else onActivityResult(REQUEST_CONNECT, Activity.RESULT_OK, null)
+// }
else -> Core.startService()
}
}
- /***
- * 设置状态监听
- */
+
fun setOnStatusChangeListener(listener: OnStatusChangeListener) {
this.listener = listener
}
- /***
- * application调用stop时调用
- */
fun onStop() {
connection.bandwidthTimeout = 0
}
@@ -112,31 +98,23 @@ class VpnManager private constructor() {
fun onStart() {
connection.bandwidthTimeout = 1000
}
- /***
- * activity调用onActivityResult时调用
- */
+
fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when {
requestCode != REQUEST_CONNECT -> {
}
resultCode == Activity.RESULT_OK -> Core.startService()
else -> {
- //无权限
+
}
}
}
- /***
- * 改变当前状态
- */
private fun changeState(state: BaseService.State) {
this.state = state
this.listener?.onStatusChanged(state)
}
- /***
- * 状态改变监听器
- */
interface OnStatusChangeListener {
fun onStatusChanged(state: BaseService.State)
@@ -144,24 +122,24 @@ class VpnManager private constructor() {
}
enum class Route(name: String) {
- //全部
+
ALL("all")
- //绕过局域网地址
+
,
BY_PASS_LAN("bypass-lan")
- //绕过中国大陆地址
+
,
BY_PASS_CHINA("bypass-china")
- //绕过局域网和中国大陆地址
+
,
BY_PASS_LAN_CHINA("bypass-lan-china")
- //GFW列表
+
,
GFW_LIST("gfwlist")
- //仅代理中国大陆地址
+
,
CHINA_LIST("china-list")
- //自定义规则
+
,
CUSTOM_RULES("custom-rules");
diff --git a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/acl/Acl.kt b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/acl/Acl.kt
index 2b0623b5..0bae1fac 100644
--- a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/acl/Acl.kt
+++ b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/acl/Acl.kt
@@ -1,23 +1,3 @@
-/*******************************************************************************
- * *
- * Copyright (C) 2017 by Max Lv *
- * Copyright (C) 2017 by Mygod Studio *
- * *
- * This program is free software: you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation, either version 3 of the License, or *
- * (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this program. If not, see . *
- * *
- *******************************************************************************/
-
package org.amnezia.vpn.shadowsocks.core.acl
import android.content.Context
diff --git a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/acl/AclSyncer.kt b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/acl/AclSyncer.kt
index f3214356..b42a1063 100644
--- a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/acl/AclSyncer.kt
+++ b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/acl/AclSyncer.kt
@@ -1,23 +1,3 @@
-/*******************************************************************************
- * *
- * Copyright (C) 2017 by Max Lv *
- * Copyright (C) 2017 by Mygod Studio *
- * *
- * This program is free software: you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation, either version 3 of the License, or *
- * (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this program. If not, see . *
- * *
- *******************************************************************************/
-
package org.amnezia.vpn.shadowsocks.core.acl
import android.content.Context
diff --git a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/aidl/ShadowsocksConnection.kt b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/aidl/ShadowsocksConnection.kt
index a411effc..bf728bcf 100644
--- a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/aidl/ShadowsocksConnection.kt
+++ b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/aidl/ShadowsocksConnection.kt
@@ -31,7 +31,7 @@ import android.os.RemoteException
import org.amnezia.vpn.shadowsocks.core.bg.BaseService
import org.amnezia.vpn.shadowsocks.core.bg.ProxyService
import org.amnezia.vpn.shadowsocks.core.bg.TransproxyService
-import org.amnezia.vpn.shadowsocks.core.bg.VpnService
+import org.amnezia.vpn.shadowsocks.core.bg.ShadowsocksVpnService
import org.amnezia.vpn.shadowsocks.core.preference.DataStore
import org.amnezia.vpn.shadowsocks.core.utils.Action
import org.amnezia.vpn.shadowsocks.core.utils.Key
@@ -45,7 +45,7 @@ class ShadowsocksConnection(private val handler: Handler = Handler(),
companion object {
val serviceClass get() = when (DataStore.serviceMode) {
Key.modeProxy -> ProxyService::class
- Key.modeVpn -> VpnService::class
+ Key.modeVpn -> ShadowsocksVpnService::class
Key.modeTransproxy -> TransproxyService::class
else -> throw UnknownError()
}.java
diff --git a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/bg/BaseService.kt b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/bg/BaseService.kt
index 2be043b3..8e44f37f 100644
--- a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/bg/BaseService.kt
+++ b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/bg/BaseService.kt
@@ -25,26 +25,25 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.*
+import android.util.Log
import androidx.core.content.getSystemService
-
+import kotlinx.coroutines.*
import org.amnezia.vpn.shadowsocks.core.Core
import org.amnezia.vpn.shadowsocks.core.Core.app
+import org.amnezia.vpn.shadowsocks.core.R
import org.amnezia.vpn.shadowsocks.core.aidl.IShadowsocksService
import org.amnezia.vpn.shadowsocks.core.aidl.IShadowsocksServiceCallback
import org.amnezia.vpn.shadowsocks.core.aidl.TrafficStats
-import org.amnezia.vpn.shadowsocks.core.R
import org.amnezia.vpn.shadowsocks.core.plugin.PluginManager
import org.amnezia.vpn.shadowsocks.core.utils.Action
import org.amnezia.vpn.shadowsocks.core.utils.broadcastReceiver
import org.amnezia.vpn.shadowsocks.core.utils.printLog
import org.amnezia.vpn.shadowsocks.core.utils.readableMessage
-import kotlinx.coroutines.*
import java.io.File
import java.net.BindException
import java.net.InetAddress
import java.net.URL
import java.net.UnknownHostException
-import java.util.*
/**
* This object uses WeakMap to simulate the effects of multi-inheritance.
@@ -64,13 +63,13 @@ object BaseService {
const val CONFIG_FILE = "shadowsocks.conf"
const val CONFIG_FILE_UDP = "shadowsocks-udp.conf"
- class Data internal constructor(private val service: Interface) {
+ class Data(private val service: Interface) {
var state = State.Stopped
var processes: GuardedProcessPool? = null
var proxy: ProxyInstance? = null
var udpFallback: ProxyInstance? = null
- var notification: ServiceNotification? = null
+// var notification: ServiceNotification? = null
val closeReceiver = broadcastReceiver { _, intent ->
when (intent.action) {
Action.RELOAD -> service.forceLoad()
@@ -96,7 +95,8 @@ object BaseService {
stopListeningForBandwidth(callback ?: return)
}
}
- private val bandwidthListeners = mutableMapOf() // the binder is the real identifier
+ private val bandwidthListeners =
+ mutableMapOf() // the binder is the real identifier
private val handler = Handler()
override fun getState(): Int = (data?.state ?: State.Idle).ordinal
@@ -119,14 +119,15 @@ object BaseService {
}
private fun registerTimeout() {
- handler.postDelayed(this::onTimeout, bandwidthListeners.values.min() ?: return)
+ handler.postDelayed(this::onTimeout, bandwidthListeners.values.minOrNull() ?: return)
}
+
private fun onTimeout() {
val proxies = listOfNotNull(data?.proxy, data?.udpFallback)
val stats = proxies
- .map { Pair(it.profile.id, it.trafficMonitor?.requestUpdate()) }
- .filter { it.second != null }
- .map { Triple(it.first, it.second!!.first, it.second!!.second) }
+ .map { Pair(it.profile.id, it.trafficMonitor?.requestUpdate()) }
+ .filter { it.second != null }
+ .map { Triple(it.first, it.second!!.first, it.second!!.second) }
if (stats.any { it.third } && data?.state == State.Connected && bandwidthListeners.isNotEmpty()) {
val sum = stats.fold(TrafficStats()) { a, b -> a + b.second }
broadcast { item ->
@@ -148,17 +149,21 @@ object BaseService {
val data = data
val proxy = data?.proxy ?: return
proxy.trafficMonitor?.out.also { stats ->
- cb.trafficUpdated(proxy.profile.id, if (stats == null) sum else {
- sum += stats
- stats
- })
+ cb.trafficUpdated(
+ proxy.profile.id, if (stats == null) sum else {
+ sum += stats
+ stats
+ }
+ )
}
data.udpFallback?.also { udpFallback ->
udpFallback.trafficMonitor?.out.also { stats ->
- cb.trafficUpdated(udpFallback.profile.id, if (stats == null) TrafficStats() else {
- sum += stats
- stats
- })
+ cb.trafficUpdated(
+ udpFallback.profile.id, if (stats == null) TrafficStats() else {
+ sum += stats
+ stats
+ }
+ )
}
}
cb.trafficUpdated(0, sum)
@@ -197,15 +202,17 @@ object BaseService {
interface Interface {
val data: Data
val tag: String
- fun createNotification(profileName: String): ServiceNotification
+// fun createNotification(profileName: String): ServiceNotification
- fun onBind(intent: Intent): IBinder? = if (intent.action == Action.SERVICE) data.binder else null
+ fun onBind(intent: Intent): IBinder? =
+ if (intent.action == Action.SERVICE) data.binder else null
fun forceLoad() {
val (profile, fallback) = Core.currentProfile
- ?: return stopRunner(false, (this as Context).getString(R.string.profile_empty))
+ ?: return stopRunner(false, (this as Context).getString(R.string.profile_empty))
if (profile.host.isEmpty() || profile.password.isEmpty() ||
- fallback != null && (fallback.host.isEmpty() || fallback.password.isEmpty())) {
+ fallback != null && (fallback.host.isEmpty() || fallback.password.isEmpty())
+ ) {
stopRunner(false, (this as Context).getString(R.string.proxy_empty))
return
}
@@ -221,17 +228,22 @@ object BaseService {
suspend fun startProcesses() {
val configRoot = (if (Build.VERSION.SDK_INT < 24 || app.getSystemService()
- ?.isUserUnlocked != false) app else Core.deviceStorage).noBackupFilesDir
+ ?.isUserUnlocked != false
+ ) app else Core.deviceStorage).noBackupFilesDir
val udpFallback = data.udpFallback
- data.proxy!!.start(this,
- File(Core.deviceStorage.noBackupFilesDir, "stat_main"),
- File(configRoot, CONFIG_FILE),
- if (udpFallback == null) "-u" else null)
+ data.proxy!!.start(
+ this,
+ File(Core.deviceStorage.noBackupFilesDir, "stat_main"),
+ File(configRoot, CONFIG_FILE),
+ if (udpFallback == null) "-u" else null
+ )
check(udpFallback?.pluginPath == null) { "UDP fallback cannot have plugins" }
- udpFallback?.start(this,
- File(Core.deviceStorage.noBackupFilesDir, "stat_udp"),
- File(configRoot, CONFIG_FILE_UDP),
- "-U")
+ udpFallback?.start(
+ this,
+ File(Core.deviceStorage.noBackupFilesDir, "stat_udp"),
+ File(configRoot, CONFIG_FILE_UDP),
+ "-U"
+ )
}
fun startRunner() {
@@ -264,8 +276,8 @@ object BaseService {
data.closeReceiverRegistered = false
}
- data.notification?.destroy()
- data.notification = null
+// data.notification?.destroy()
+// data.notification = null
val ids = listOfNotNull(data.proxy, data.udpFallback).map {
it.shutdown(this)
@@ -280,30 +292,36 @@ object BaseService {
data.changeState(State.Stopped, msg)
// stop the service if nothing has bound to it
- if (restart) startRunner() else stopSelf()
+ if (restart) {
+ startRunner()
+ } else {
+ Log.d("Aman", "Stop Self BaseService-------")
+// stopSelf()
+ }
}
}
- suspend fun preInit() { }
+ suspend fun preInit() {}
suspend fun resolver(host: String) = InetAddress.getAllByName(host)
suspend fun openConnection(url: URL) = url.openConnection()
fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val data = data
- if (data.state != State.Stopped) return Service.START_NOT_STICKY
+ if (data.state != State.Stopped) return Service.START_REDELIVER_INTENT
val profilePair = Core.currentProfile
this as Context
if (profilePair == null) {
// gracefully shutdown: https://stackoverflow.com/q/47337857/2245107
- data.notification = createNotification("")
+// data.notification = createNotification("")
stopRunner(false, getString(R.string.profile_empty))
- return Service.START_NOT_STICKY
+ return Service.START_REDELIVER_INTENT
}
val (profile, fallback) = profilePair
profile.name = profile.formattedName // save name for later queries
val proxy = ProxyInstance(profile)
data.proxy = proxy
- data.udpFallback = if (fallback == null) null else ProxyInstance(fallback, profile.route)
+ data.udpFallback =
+ if (fallback == null) null else ProxyInstance(fallback, profile.route)
if (!data.closeReceiverRegistered) {
registerReceiver(data.closeReceiver, IntentFilter().apply {
@@ -314,7 +332,7 @@ object BaseService {
data.closeReceiverRegistered = true
}
- data.notification = createNotification(profile.formattedName)
+// data.notification = createNotification(profile.formattedName)
data.changeState(State.Connecting)
data.connectingJob = GlobalScope.launch(Dispatchers.Main) {
@@ -340,16 +358,20 @@ object BaseService {
stopRunner(false, getString(R.string.invalid_server))
} catch (exc: Throwable) {
if (exc !is PluginManager.PluginNotFoundException &&
- exc !is BindException &&
- exc !is VpnService.NullConnectionException) {
+ exc !is BindException &&
+ exc !is ShadowsocksVpnService.NullConnectionException
+ ) {
printLog(exc)
}
- stopRunner(false, "${getString(R.string.service_failed)}: ${exc.readableMessage}")
+ stopRunner(
+ false,
+ "${getString(R.string.service_failed)}: ${exc.readableMessage}"
+ )
} finally {
data.connectingJob = null
}
}
- return Service.START_NOT_STICKY
+ return Service.START_REDELIVER_INTENT
}
}
}
diff --git a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/bg/ProxyService.kt b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/bg/ProxyService.kt
index ea07f30c..4f4372ef 100644
--- a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/bg/ProxyService.kt
+++ b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/bg/ProxyService.kt
@@ -29,8 +29,8 @@ import android.content.Intent
class ProxyService : Service(), BaseService.Interface {
override val data = BaseService.Data(this)
override val tag: String get() = "ShadowsocksProxyService"
- override fun createNotification(profileName: String): ServiceNotification =
- ServiceNotification(this, profileName, "service-proxy", true)
+// override fun createNotification(profileName: String): ServiceNotification =
+// ServiceNotification(this, profileName, "service-proxy", true)
override fun onBind(intent: Intent) = super.onBind(intent)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int =
diff --git a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/bg/ServiceNotification.kt b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/bg/ServiceNotification.kt
index 26df36ec..7a4ca1b3 100644
--- a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/bg/ServiceNotification.kt
+++ b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/bg/ServiceNotification.kt
@@ -1,135 +1,145 @@
-/*******************************************************************************
- * *
- * Copyright (C) 2017 by Max Lv *
- * Copyright (C) 2017 by Mygod Studio *
- * *
- * This program is free software: you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation, either version 3 of the License, or *
- * (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this program. If not, see . *
- * *
- *******************************************************************************/
-
-package org.amnezia.vpn.shadowsocks.core.bg
-
-import android.app.KeyguardManager
-import android.app.NotificationManager
-import android.app.PendingIntent
-import android.app.Service
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
-import android.os.Build
-import android.os.PowerManager
-import android.text.format.Formatter
-import androidx.core.app.NotificationCompat
-import androidx.core.content.ContextCompat
-import androidx.core.content.getSystemService
-import org.amnezia.vpn.shadowsocks.core.Core
-import org.amnezia.vpn.shadowsocks.core.aidl.IShadowsocksServiceCallback
-import org.amnezia.vpn.shadowsocks.core.aidl.TrafficStats
-import org.amnezia.vpn.shadowsocks.core.R
-import org.amnezia.vpn.shadowsocks.core.utils.Action
-import org.amnezia.vpn.shadowsocks.core.utils.broadcastReceiver
-
-/**
- * Android < 8 VPN: always invisible because of VPN notification/icon
- * Android < 8 other: only invisible in (possibly unsecure) lockscreen
- * Android 8+: always visible due to system limitations
- * (user can choose to hide the notification in secure lockscreen or anywhere)
- */
-class ServiceNotification(private val service: BaseService.Interface, profileName: String,
- channel: String, private val visible: Boolean = false) {
- private val keyGuard = (service as Context).getSystemService()!!
- private val nm by lazy { (service as Context).getSystemService()!! }
- private val callback: IShadowsocksServiceCallback by lazy {
- object : IShadowsocksServiceCallback.Stub() {
- override fun stateChanged(state: Int, profileName: String?, msg: String?) { } // ignore
- override fun trafficUpdated(profileId: Long, stats: TrafficStats) {
- if (profileId != 0L) return
- service as Context
- val txr = service.getString(R.string.speed, Formatter.formatFileSize(service, stats.txRate))
- val rxr = service.getString(R.string.speed, Formatter.formatFileSize(service, stats.rxRate))
- builder.setContentText("$txr↑\t$rxr↓")
- style.bigText(service.getString(R.string.stat_summary, txr, rxr,
- Formatter.formatFileSize(service, stats.txTotal),
- Formatter.formatFileSize(service, stats.rxTotal)))
- show()
- }
- override fun trafficPersisted(profileId: Long) { }
- }
- }
- private val lockReceiver = broadcastReceiver { _, intent -> update(intent.action) }
- private var callbackRegistered = false
-
- private val builder = NotificationCompat.Builder(service as Context, channel)
- .setWhen(0)
- .setColor(ContextCompat.getColor(service, R.color.material_primary_500))
- .setTicker(service.getString(R.string.forward_success))
- .setContentTitle(profileName)
- .setContentIntent(Core.configureIntent(service))
- .setSmallIcon(R.drawable.ic_service_active)
- private val style = NotificationCompat.BigTextStyle(builder).bigText("")
- private var isVisible = true
-
- init {
- service as Context
- if (Build.VERSION.SDK_INT < 24) builder.addAction(R.drawable.ic_navigation_close,
- service.getString(R.string.stop), PendingIntent.getBroadcast(service, 0, Intent(Action.CLOSE), 0))
- update(if (service.getSystemService()?.isInteractive != false)
- Intent.ACTION_SCREEN_ON else Intent.ACTION_SCREEN_OFF, true)
- service.registerReceiver(lockReceiver, IntentFilter().apply {
- addAction(Intent.ACTION_SCREEN_ON)
- addAction(Intent.ACTION_SCREEN_OFF)
- if (visible && Build.VERSION.SDK_INT < 26) addAction(Intent.ACTION_USER_PRESENT)
- })
- }
-
- private fun update(action: String?, forceShow: Boolean = false) {
- if (forceShow || service.data.state == BaseService.State.Connected) when (action) {
- Intent.ACTION_SCREEN_OFF -> {
- setVisible(false, forceShow)
- unregisterCallback() // unregister callback to save battery
- }
- Intent.ACTION_SCREEN_ON -> {
- setVisible(visible && !keyGuard.isKeyguardLocked, forceShow)
- service.data.binder.registerCallback(callback)
- service.data.binder.startListeningForBandwidth(callback, 1000)
- callbackRegistered = true
- }
- Intent.ACTION_USER_PRESENT -> setVisible(true, forceShow)
- }
- }
-
- private fun unregisterCallback() {
- if (callbackRegistered) {
- service.data.binder.unregisterCallback(callback)
- callbackRegistered = false
- }
- }
-
- private fun setVisible(visible: Boolean, forceShow: Boolean = false) {
- if (isVisible != visible) {
- isVisible = visible
- builder.priority = if (visible) NotificationCompat.PRIORITY_LOW else NotificationCompat.PRIORITY_MIN
- show()
- } else if (forceShow) show()
- }
-
- private fun show() = (service as Service).startForeground(1, builder.build())
-
- fun destroy() {
- (service as Service).unregisterReceiver(lockReceiver)
- unregisterCallback()
- service.stopForeground(true)
- nm.cancel(1)
- }
-}
+///*******************************************************************************
+// * *
+// * Copyright (C) 2017 by Max Lv *
+// * Copyright (C) 2017 by Mygod Studio *
+// * *
+// * This program is free software: you can redistribute it and/or modify *
+// * it under the terms of the GNU General Public License as published by *
+// * the Free Software Foundation, either version 3 of the License, or *
+// * (at your option) any later version. *
+// * *
+// * This program is distributed in the hope that it will be useful, *
+// * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+// * GNU General Public License for more details. *
+// * *
+// * You should have received a copy of the GNU General Public License *
+// * along with this program. If not, see . *
+// * *
+// *******************************************************************************/
+//
+//package org.amnezia.vpn.shadowsocks.core.bg
+//
+//import android.app.KeyguardManager
+//import android.app.NotificationManager
+//import android.app.PendingIntent
+//import android.app.Service
+//import android.content.Context
+//import android.content.Intent
+//import android.content.IntentFilter
+//import android.os.Build
+//import android.os.PowerManager
+//import android.text.format.Formatter
+//import androidx.core.app.NotificationCompat
+//import androidx.core.content.ContextCompat
+//import androidx.core.content.getSystemService
+//import org.amnezia.vpn.shadowsocks.core.Core
+//import org.amnezia.vpn.shadowsocks.core.aidl.IShadowsocksServiceCallback
+//import org.amnezia.vpn.shadowsocks.core.aidl.TrafficStats
+//import org.amnezia.vpn.shadowsocks.core.R
+//import org.amnezia.vpn.shadowsocks.core.utils.Action
+//import org.amnezia.vpn.shadowsocks.core.utils.broadcastReceiver
+//
+///**
+// * Android < 8 VPN: always invisible because of VPN notification/icon
+// * Android < 8 other: only invisible in (possibly unsecure) lockscreen
+// * Android 8+: always visible due to system limitations
+// * (user can choose to hide the notification in secure lockscreen or anywhere)
+// */
+//class ServiceNotification(private val service: BaseService.Interface, profileName: String,
+// channel: String, private val visible: Boolean = false) {
+// private val keyGuard = (service as Context).getSystemService()!!
+// private val nm by lazy { (service as Context).getSystemService()!! }
+// private val callback: IShadowsocksServiceCallback by lazy {
+// object : IShadowsocksServiceCallback.Stub() {
+// override fun stateChanged(state: Int, profileName: String?, msg: String?) {
+// when (state) {
+// BaseService.State.Connected.ordinal -> {
+// builder.setContentText("VPN Connected")
+// }
+// BaseService.State.Stopped.ordinal -> {
+// builder.setContentText("VPN Disconnected")
+// }
+// }
+// } // ignore
+// override fun trafficUpdated(profileId: Long, stats: TrafficStats) {
+//// if (profileId != 0L) return
+//// service as Context
+//// val txr = service.getString(R.string.speed, Formatter.formatFileSize(service, stats.txRate))
+//// val rxr = service.getString(R.string.speed, Formatter.formatFileSize(service, stats.rxRate))
+//// builder.setContentText("$txr↑\t$rxr↓")
+//// style.bigText(service.getString(R.string.stat_summary, txr, rxr,
+//// Formatter.formatFileSize(service, stats.txTotal),
+//// Formatter.formatFileSize(service, stats.rxTotal)))
+//// show()
+// }
+// override fun trafficPersisted(profileId: Long) { }
+// }
+// }
+//// private val lockReceiver = broadcastReceiver { _, intent -> update(intent.action) }
+// private var callbackRegistered = false
+//
+// private val builder = NotificationCompat.Builder(service as Context, channel)
+// .setWhen(0)
+// .setColor(ContextCompat.getColor(service, R.color.material_primary_500))
+// .setTicker(service.getString(R.string.forward_success))
+// .setContentTitle("AmneziaVPN -- testing")
+// .setContentIntent(Core.configureIntent(service))
+// .setSmallIcon(R.drawable.ic_amnezia_round)
+// private val style = NotificationCompat.BigTextStyle(builder).bigText("")
+// private var isVisible = true
+//
+// init {
+// service as Context
+//// if (Build.VERSION.SDK_INT < 24) builder.addAction(R.drawable.ic_navigation_close,
+//// service.getString(R.string.stop), PendingIntent.getBroadcast(service, 0, Intent(Action.CLOSE), 0))
+//// update(if (service.getSystemService()?.isInteractive != false)
+//// Intent.ACTION_SCREEN_ON else Intent.ACTION_SCREEN_OFF, true)
+//// service.registerReceiver(lockReceiver, IntentFilter().apply {
+//// addAction(Intent.ACTION_SCREEN_ON)
+//// addAction(Intent.ACTION_SCREEN_OFF)
+//// if (visible && Build.VERSION.SDK_INT < 26) addAction(Intent.ACTION_USER_PRESENT)
+//// })
+// }
+//
+//// private fun update(action: String?, forceShow: Boolean = false) {
+//// if (forceShow || service.data.state == BaseService.State.Connected) when (action) {
+//// Intent.ACTION_SCREEN_OFF -> {
+//// setVisible(false, forceShow)
+//// unregisterCallback() // unregister callback to save battery
+//// }
+//// Intent.ACTION_SCREEN_ON -> {
+//// setVisible(visible && !keyGuard.isKeyguardLocked, forceShow)
+//// service.data.binder.registerCallback(callback)
+//// service.data.binder.startListeningForBandwidth(callback, 1000)
+//// callbackRegistered = true
+//// }
+//// Intent.ACTION_USER_PRESENT -> setVisible(true, forceShow)
+//// }
+//// }
+//
+// private fun unregisterCallback() {
+// if (callbackRegistered) {
+// service.data.binder.unregisterCallback(callback)
+// callbackRegistered = false
+// }
+// }
+//
+// private fun setVisible(visible: Boolean, forceShow: Boolean = false) {
+// if (isVisible != visible) {
+// isVisible = visible
+// builder.priority = if (visible) NotificationCompat.PRIORITY_LOW else NotificationCompat.PRIORITY_MIN
+// show()
+// } else if (forceShow) show()
+// }
+//
+//
+// private fun show() = (service as Service).startForeground(1337, builder.build())
+//
+// fun destroy() {
+//// (service as Service).unregisterReceiver(lockReceiver)
+// unregisterCallback()
+//// service.stopForeground(true)
+// nm.cancel(1337)
+// }
+//}
diff --git a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/bg/VpnService.kt b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/bg/ShadowsocksVpnService.kt
similarity index 96%
rename from client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/bg/VpnService.kt
rename to client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/bg/ShadowsocksVpnService.kt
index dbe64ef6..403bb1a3 100644
--- a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/bg/VpnService.kt
+++ b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/bg/ShadowsocksVpnService.kt
@@ -30,6 +30,7 @@ import android.os.Build
import android.os.ParcelFileDescriptor
import android.system.ErrnoException
import android.system.Os
+import android.util.Log
import org.amnezia.vpn.shadowsocks.core.Core
import org.amnezia.vpn.shadowsocks.core.R
import org.amnezia.vpn.shadowsocks.core.VpnRequestActivity
@@ -51,7 +52,7 @@ import java.net.URL
import java.util.*
import android.net.VpnService as BaseVpnService
-class VpnService : BaseVpnService(), LocalDnsService.Interface {
+open class ShadowsocksVpnService : BaseVpnService(), LocalDnsService.Interface {
companion object {
private const val VPN_MTU = 1500
private const val PRIVATE_VLAN4_CLIENT = "172.19.0.1"
@@ -95,8 +96,10 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
override val data = BaseService.Data(this)
override val tag: String get() = "ShadowsocksVpnService"
- override fun createNotification(profileName: String): ServiceNotification =
- ServiceNotification(this, profileName, "service-vpn")
+
+ val NOTIFICATION_CHANNEL_ID = "com.amnezia.vpnNotification"
+// override fun createNotification(profileName: String): ServiceNotification =
+// ServiceNotification(this, profileName, NOTIFICATION_CHANNEL_ID)
private var conn: ParcelFileDescriptor? = null
private var worker: ProtectWorker? = null
@@ -117,7 +120,9 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
else -> super.onBind(intent)
}
- override fun onRevoke() = stopRunner()
+ override fun onRevoke() {
+ stopRunner()
+ }
override fun killProcesses(scope: CoroutineScope) {
super.killProcesses(scope)
@@ -136,7 +141,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
} else return super.onStartCommand(intent, flags, startId)
}
stopRunner()
- return Service.START_NOT_STICKY
+ return Service.START_STICKY
}
override suspend fun preInit() = DefaultNetworkListener.start(this) { underlyingNetwork = it }
diff --git a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/bg/TransproxyService.kt b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/bg/TransproxyService.kt
index 9d5e7bdf..1da2d3f2 100644
--- a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/bg/TransproxyService.kt
+++ b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/bg/TransproxyService.kt
@@ -29,15 +29,16 @@ import java.io.File
class TransproxyService : Service(), LocalDnsService.Interface {
override val data = BaseService.Data(this)
override val tag: String get() = "ShadowsocksTransproxyService"
- override fun createNotification(profileName: String): ServiceNotification =
- ServiceNotification(this, profileName, "service-transproxy", true)
+// override fun createNotification(profileName: String): ServiceNotification =
+// ServiceNotification(this, profileName, "service-transproxy", true)
override fun onBind(intent: Intent) = super.onBind(intent)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int =
- super.onStartCommand(intent, flags, startId)
+ super.onStartCommand(intent, flags, startId)
private fun startRedsocksDaemon() {
- File(Core.deviceStorage.noBackupFilesDir, "redsocks.conf").writeText("""base {
+ File(Core.deviceStorage.noBackupFilesDir, "redsocks.conf").writeText(
+ """base {
log_debug = off;
log_info = off;
log = stderr;
@@ -51,9 +52,15 @@ redsocks {
port = ${DataStore.portProxy};
type = socks5;
}
-""")
- data.processes!!.start(listOf(
- File(applicationInfo.nativeLibraryDir, Executable.REDSOCKS).absolutePath, "-c", "redsocks.conf"))
+"""
+ )
+ data.processes!!.start(
+ listOf(
+ File(applicationInfo.nativeLibraryDir, Executable.REDSOCKS).absolutePath,
+ "-c",
+ "redsocks.conf"
+ )
+ )
}
override suspend fun startProcesses() {
diff --git a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/utils/Constants.kt b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/utils/Constants.kt
index 1c3b700d..53203af4 100644
--- a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/utils/Constants.kt
+++ b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/utils/Constants.kt
@@ -75,9 +75,9 @@ object Key {
}
object Action {
- const val SERVICE = "com.kyle.shadowsocks.SERVICE"
- const val CLOSE = "com.kyle.shadowsocks.CLOSE"
- const val RELOAD = "com.kyle.shadowsocks.RELOAD"
+ const val SERVICE = "org.amnezia.vpn.shadowsocks.SERVICE"
+ const val CLOSE = "org.amnezia.vpn.shadowsocks.CLOSE"
+ const val RELOAD = "org.amnezia.vpn.shadowsocks.RELOAD"
- const val EXTRA_PROFILE_ID = "com.kyle.shadowsocks.EXTRA_PROFILE_ID"
+ const val EXTRA_PROFILE_ID = "org.amnezia.vpn.shadowsocks.EXTRA_PROFILE_ID"
}
diff --git a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/widget/NativePluginProvider.kt b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/widget/NativePluginProvider.kt
index 4d7a4423..3fd80cc7 100644
--- a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/widget/NativePluginProvider.kt
+++ b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/widget/NativePluginProvider.kt
@@ -38,10 +38,10 @@ import androidx.core.os.bundleOf
* ...
* <application>
* ...
- * <provider android:name="com.kyle.shadowsocks.$PLUGIN_ID.BinaryProvider"
- * android:authorities="com.kyle.shadowsocks.plugin.$PLUGIN_ID.BinaryProvider">
+ * <provider android:name="org.amnezia.vpn.shadowsocks.$PLUGIN_ID.BinaryProvider"
+ * android:authorities="org.amnezia.vpn.shadowsocks.plugin.$PLUGIN_ID.BinaryProvider">
* <intent-filter>
- * <category android:name="com.kyle.shadowsocks.plugin.ACTION_NATIVE_PLUGIN" />
+ * <category android:name="org.amnezia.vpn.shadowsocks.plugin.ACTION_NATIVE_PLUGIN" />
* </intent-filter>
* </provider>
* ...
diff --git a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/widget/PluginContract.kt b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/widget/PluginContract.kt
index fd980810..418e5086 100644
--- a/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/widget/PluginContract.kt
+++ b/client/android/shadowsocks/src/main/java/org/amnezia/vpn/shadowsocks/core/widget/PluginContract.kt
@@ -29,61 +29,61 @@ object PluginContract {
/**
* ContentProvider Action: Used for NativePluginProvider.
*
- * Constant Value: "com.kyle.shadowsocks.plugin.ACTION_NATIVE_PLUGIN"
+ * Constant Value: "org.amnezia.vpn.shadowsocks.plugin.ACTION_NATIVE_PLUGIN"
*/
- const val ACTION_NATIVE_PLUGIN = "com.kyle.shadowsocks.plugin.ACTION_NATIVE_PLUGIN"
+ const val ACTION_NATIVE_PLUGIN = "org.amnezia.vpn.shadowsocks.plugin.ACTION_NATIVE_PLUGIN"
/**
* Activity Action: Used for ConfigurationActivity.
*
- * Constant Value: "com.kyle.shadowsocks.plugin.ACTION_CONFIGURE"
+ * Constant Value: "org.amnezia.vpn.shadowsocks.plugin.ACTION_CONFIGURE"
*/
- const val ACTION_CONFIGURE = "com.kyle.shadowsocks.plugin.ACTION_CONFIGURE"
+ const val ACTION_CONFIGURE = "org.amnezia.vpn.shadowsocks.plugin.ACTION_CONFIGURE"
/**
* Activity Action: Used for HelpActivity or HelpCallback.
*
- * Constant Value: "com.kyle.shadowsocks.plugin.ACTION_HELP"
+ * Constant Value: "org.amnezia.vpn.shadowsocks.plugin.ACTION_HELP"
*/
- const val ACTION_HELP = "com.kyle.shadowsocks.plugin.ACTION_HELP"
+ const val ACTION_HELP = "org.amnezia.vpn.shadowsocks.plugin.ACTION_HELP"
/**
* The lookup key for a string that provides the plugin entry binary.
*
- * Example: "/data/data/com.kyle.shadowsocks.plugin.obfs_local/lib/libobfs-local.so"
+ * Example: "/data/data/org.amnezia.vpn.shadowsocks.plugin.obfs_local/lib/libobfs-local.so"
*
- * Constant Value: "com.kyle.shadowsocks.plugin.EXTRA_ENTRY"
+ * Constant Value: "org.amnezia.vpn.shadowsocks.plugin.EXTRA_ENTRY"
*/
- const val EXTRA_ENTRY = "com.kyle.shadowsocks.plugin.EXTRA_ENTRY"
+ const val EXTRA_ENTRY = "org.amnezia.vpn.shadowsocks.plugin.EXTRA_ENTRY"
/**
* The lookup key for a string that provides the options as a string.
*
* Example: "obfs=http;obfs-host=www.baidu.com"
*
- * Constant Value: "com.kyle.shadowsocks.plugin.EXTRA_OPTIONS"
+ * Constant Value: "org.amnezia.vpn.shadowsocks.plugin.EXTRA_OPTIONS"
*/
- const val EXTRA_OPTIONS = "com.kyle.shadowsocks.plugin.EXTRA_OPTIONS"
+ const val EXTRA_OPTIONS = "org.amnezia.vpn.shadowsocks.plugin.EXTRA_OPTIONS"
/**
* The lookup key for a CharSequence that provides user relevant help message.
*
* Example: "obfs=|tls> Enable obfuscating: HTTP or TLS (Experimental).
* obfs-host= Hostname for obfuscating (Experimental)."
*
- * Constant Value: "com.kyle.shadowsocks.plugin.EXTRA_HELP_MESSAGE"
+ * Constant Value: "org.amnezia.vpn.shadowsocks.plugin.EXTRA_HELP_MESSAGE"
*/
- const val EXTRA_HELP_MESSAGE = "com.kyle.shadowsocks.plugin.EXTRA_HELP_MESSAGE"
+ const val EXTRA_HELP_MESSAGE = "org.amnezia.vpn.shadowsocks.plugin.EXTRA_HELP_MESSAGE"
/**
* The metadata key to retrieve plugin id. Required for plugins.
*
- * Constant Value: "com.kyle.shadowsocks.plugin.id"
+ * Constant Value: "org.amnezia.vpn.shadowsocks.plugin.id"
*/
- const val METADATA_KEY_ID = "com.kyle.shadowsocks.plugin.id"
+ const val METADATA_KEY_ID = "org.amnezia.vpn.shadowsocks.plugin.id"
/**
* The metadata key to retrieve default configuration. Default value is empty.
*
- * Constant Value: "com.kyle.shadowsocks.plugin.default_config"
+ * Constant Value: "org.amnezia.vpn.shadowsocks.plugin.default_config"
*/
- const val METADATA_KEY_DEFAULT_CONFIG = "com.kyle.shadowsocks.plugin.default_config"
+ const val METADATA_KEY_DEFAULT_CONFIG = "org.amnezia.vpn.shadowsocks.plugin.default_config"
const val METHOD_GET_EXECUTABLE = "shadowsocks:getExecutable"
@@ -114,5 +114,5 @@ object PluginContract {
/**
* The authority for general plugin actions.
*/
- const val AUTHORITY = "com.kyle.shadowsocks"
+ const val AUTHORITY = "org.amnezia.vpn.shadowsocks"
}
diff --git a/client/android/shadowsocks/src/main/res/drawable/ic_amnezia_round.xml b/client/android/shadowsocks/src/main/res/drawable/ic_amnezia_round.xml
new file mode 100644
index 00000000..32df5ca4
--- /dev/null
+++ b/client/android/shadowsocks/src/main/res/drawable/ic_amnezia_round.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/client/android/shadowsocks/src/main/res/drawable/ic_service_active.xml b/client/android/shadowsocks/src/main/res/drawable/ic_service_active.xml
deleted file mode 100644
index 33062676..00000000
--- a/client/android/shadowsocks/src/main/res/drawable/ic_service_active.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
diff --git a/client/android/src/org/amnezia/vpn/VPNService.kt b/client/android/src/org/amnezia/vpn/VPNService.kt
index 27c604bc..f8ed3999 100644
--- a/client/android/src/org/amnezia/vpn/VPNService.kt
+++ b/client/android/src/org/amnezia/vpn/VPNService.kt
@@ -6,18 +6,137 @@ package org.amnezia.vpn
import android.content.Context
import android.content.Intent
-import android.os.Build
-import android.os.IBinder
+import android.content.pm.PackageManager
+import android.net.LocalSocket
+import android.net.LocalSocketAddress
+import android.net.Network
import android.net.ProxyInfo
-import android.os.ParcelFileDescriptor
+import android.os.*
+import android.system.ErrnoException
+import android.system.Os
import android.system.OsConstants
import com.wireguard.android.util.SharedLibraryLoader
import com.wireguard.config.*
import com.wireguard.crypto.Key
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import org.amnezia.vpn.shadowsocks.core.Core
+import org.amnezia.vpn.shadowsocks.core.R
+import org.amnezia.vpn.shadowsocks.core.VpnRequestActivity
+import org.amnezia.vpn.shadowsocks.core.acl.Acl
+import org.amnezia.vpn.shadowsocks.core.bg.*
+import org.amnezia.vpn.shadowsocks.core.database.Profile
+import org.amnezia.vpn.shadowsocks.core.database.ProfileManager
+import org.amnezia.vpn.shadowsocks.core.net.ConcurrentLocalSocketListener
+import org.amnezia.vpn.shadowsocks.core.net.DefaultNetworkListener
+import org.amnezia.vpn.shadowsocks.core.net.Subnet
+import org.amnezia.vpn.shadowsocks.core.preference.DataStore
+import org.amnezia.vpn.shadowsocks.core.utils.Key.modeVpn
+import org.amnezia.vpn.shadowsocks.core.utils.printLog
import org.json.JSONObject
+import java.io.Closeable
+import java.io.File
+import java.io.FileDescriptor
+import java.io.IOException
+import android.net.VpnService as BaseVpnService
+
+
+class VPNService : BaseVpnService(), LocalDnsService.Interface {
+
+ override val data = BaseService.Data(this)
+ override val tag: String get() = "VPNService"
+// override fun createNotification(profileName: String): ServiceNotification =
+// ServiceNotification(this, profileName, "service-vpn")
+
+ private var conn: ParcelFileDescriptor? = null
+ private var worker: ProtectWorker? = null
+ private var active = false
+ private var metered = false
+ private var underlyingNetwork: Network? = null
+ set(value) {
+ field = value
+ if (active && Build.VERSION.SDK_INT >= 22) setUnderlyingNetworks(underlyingNetworks)
+ }
+ private val underlyingNetworks
+ get() =
+ // clearing underlyingNetworks makes Android 9+ consider the network to be metered
+ if (Build.VERSION.SDK_INT >= 28 && metered) null else underlyingNetwork?.let {
+ arrayOf(
+ it
+ )
+ }
+
+ val handler = Handler(Looper.getMainLooper())
+ var runnable: Runnable = object : Runnable {
+ override fun run() {
+ if (mProtocol.equals("shadowsocks", true)) {
+ Log.e(tag, "run: -----------------: ${data.state}")
+ when (data.state) {
+ BaseService.State.Connected -> {
+ currentTunnelHandle = 1
+ isUp = true
+ }
+ BaseService.State.Stopped -> {
+ currentTunnelHandle = -1
+ isUp = false
+ }
+ else -> {
+
+ }
+ }
+ }
+ handler.postDelayed(this, 1000L) //wait 4 sec and run again
+ }
+ }
+
+ fun stopTest() {
+ handler.removeCallbacks(runnable)
+ }
+
+ fun startTest() {
+ handler.postDelayed(runnable, 0) //wait 0 ms and run
+ }
+
+ companion object {
+ 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"
+
+ /**
+ * https://android.googlesource.com/platform/prebuilts/runtime/+/94fec32/appcompat/hiddenapi-light-greylist.txt#9466
+ */
+ private val getInt = FileDescriptor::class.java.getDeclaredMethod("getInt$")
+
+ @JvmStatic
+ fun startService(c: Context) {
+ c.applicationContext.startService(
+ Intent(c.applicationContext, VPNService::class.java).apply {
+ putExtra("startOnly", true)
+ })
+ }
+
+ @JvmStatic
+ private external fun wgGetConfig(handle: Int): String?
+
+ @JvmStatic
+ private external fun wgGetSocketV4(handle: Int): Int
+
+ @JvmStatic
+ private external fun wgGetSocketV6(handle: Int): Int
+
+ @JvmStatic
+ private external fun wgTurnOff(handle: Int)
+
+ @JvmStatic
+ private external fun wgTurnOn(ifName: String, tunFd: Int, settings: String): Int
+
+ @JvmStatic
+ private external fun wgVersion(): String?
+ }
-class VPNService : android.net.VpnService() {
- private val tag = "VPNService"
private var mBinder: VPNServiceBinder = VPNServiceBinder(this)
private var mConfig: JSONObject? = null
private var mProtocol: String? = null
@@ -26,7 +145,11 @@ class VPNService : android.net.VpnService() {
private var mbuilder: Builder = Builder()
private var mOpenVPNThreadv3: OpenVPNThreadv3? = null
- private var currentTunnelHandle = -1
+ var currentTunnelHandle = -1
+
+ private var intent: Intent? = null
+ private var flags = 0
+ private var startId = 0
fun init() {
if (mAlreadyInitialised) {
@@ -41,8 +164,16 @@ class VPNService : android.net.VpnService() {
mAlreadyInitialised = true
}
+ override fun onCreate() {
+ super.onCreate()
+// Log.v(tag, "Aman: onCreate....................")
+// Log.v(tag, "Aman: onCreate....................")
+// Log.v(tag, "Aman: onCreate....................")
+// NotificationUtil.show(this) // Go foreground
+ }
+
override fun onUnbind(intent: Intent?): Boolean {
- Log.v(tag, "Got Unbind request")
+ Log.v(tag, "Aman: onUnbind....................")
if (!isUp) {
// If the Qt Client got closed while we were not connected
// we do not need to stay as a foreground service.
@@ -55,9 +186,21 @@ class VPNService : android.net.VpnService() {
* EntryPoint for the Service, gets Called when AndroidController.cpp
* calles bindService. Returns the [VPNServiceBinder] so QT can send Requests to it.
*/
- override fun onBind(intent: Intent?): IBinder? {
- Log.v(tag, "Got Bind request")
- init()
+ override fun onBind(intent: Intent): IBinder {
+ Log.v(tag, "Aman: onBind....................")
+ when (mProtocol) {
+ "shadowsocks" -> {
+ when (intent.action) {
+ SERVICE_INTERFACE -> super.onBind(intent)
+ else -> super.onBind(intent)
+ }
+ startTest()
+ }
+ else -> {
+ init()
+ }
+ }
+
return mBinder
}
@@ -67,11 +210,16 @@ class VPNService : android.net.VpnService() {
* or from Booting the device and having "connect on boot" enabled.
*/
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ Log.v(tag, "Aman: onStartCommand....................")
+ this.intent = intent
+ this.flags = flags
+ this.startId = startId
init()
intent?.let {
- if (intent.getBooleanExtra("startOnly", false)) {
+ if (!isUp && intent.getBooleanExtra("startOnly", false)) {
Log.i(tag, "Start only!")
- return super.onStartCommand(intent, flags, startId)
+ return START_REDELIVER_INTENT
+// return super.onStartCommand(intent, flags, startId)
}
}
// This start is from always-on
@@ -81,18 +229,39 @@ class VPNService : android.net.VpnService() {
val lastConfString = prefs.getString("lastConf", "")
if (lastConfString.isNullOrEmpty()) {
// We have nothing to connect to -> Exit
- Log.e(tag,"VPN service was triggered without defining a Server or having a tunnel")
- return super.onStartCommand(intent, flags, startId)
+ Log.e(tag, "VPN service was triggered without defining a Server or having a tunnel")
+ return super.onStartCommand(intent, flags, startId)
}
this.mConfig = JSONObject(lastConfString)
}
- return super.onStartCommand(intent, flags, startId)
+ mProtocol = mConfig!!.getString("protocol")
+ Log.e(tag, "mProtocol: $mProtocol")
+ if (mProtocol.equals("shadowsocks", true)) {
+ if (DataStore.serviceMode == modeVpn) {
+ if (prepare(this) != null) {
+ startActivity(
+ Intent(
+ this,
+ VpnRequestActivity::class.java
+ ).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ )
+ } else {
+ Log.e(tag, "Else part enter")
+// service?.startListeningForBandwidth(serviceCallback, 1000)
+ Log.e(tag, "test")
+ return super.onStartCommand(intent, flags, startId)
+ }
+ }
+ stopRunner()
+ }
+ return START_REDELIVER_INTENT
}
// Invoked when the application is revoked.
// At this moment, the VPN interface is already deactivated by the system.
override fun onRevoke() {
+ Log.v(tag, "Aman: onRevoke....................")
this.turnOff()
super.onRevoke()
}
@@ -145,6 +314,7 @@ class VPNService : android.net.VpnService() {
}
fun turnOn(json: JSONObject?): Int {
+ Log.v(tag, "Aman: turnOn....................")
if (!checkPermissions()) {
Log.e(tag, "turn on was called without no permissions present!")
isUp = false
@@ -152,23 +322,31 @@ class VPNService : android.net.VpnService() {
}
Log.i(tag, "Permission okay")
mConfig = json!!
- Log.i(tag, "Config: " + mConfig)
+ Log.i(tag, "Config: $mConfig")
mProtocol = mConfig!!.getString("protocol")
- Log.i(tag, "Protocol: " + mProtocol)
+ Log.i(tag, "Protocol: $mProtocol")
when (mProtocol) {
- "openvpn" -> startOpenVpn()
- "wireguard" -> startWireGuard()
- "shadowsocks" -> startShadowsocks()
+ "openvpn" -> {
+ startOpenVpn()
+ }
+ "wireguard" -> {
+ startWireGuard()
+ }
+ "shadowsocks" -> {
+ startShadowsocks()
+ startTest()
+ }
else -> {
Log.e(tag, "No protocol")
return 0
}
}
- NotificationUtil.show(this) // Go foreground
+ NotificationUtil.show(this)
return 1
}
fun establish(): ParcelFileDescriptor? {
+ Log.v(tag, "Aman: establish....................")
mbuilder.allowFamily(OsConstants.AF_INET)
mbuilder.allowFamily(OsConstants.AF_INET6)
@@ -208,7 +386,9 @@ class VPNService : android.net.VpnService() {
fun addHttpProxy(host: String, port: Int): Boolean {
val proxyInfo = ProxyInfo.buildDirectProxy(host, port)
Log.v(tag, "mbuilder.addHttpProxy($host, $port)")
- mbuilder.setHttpProxy(proxyInfo)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ mbuilder.setHttpProxy(proxyInfo)
+ }
return true
}
@@ -218,19 +398,22 @@ class VPNService : android.net.VpnService() {
}
fun turnOff() {
- Log.v(tag, "Try to disable tunnel")
+ Log.v(tag, "Aman: turnOff....................")
when (mProtocol) {
"wireguard" -> wgTurnOff(currentTunnelHandle)
"openvpn" -> ovpnTurnOff()
+ "shadowsocks" -> {
+ stopRunner(false)
+ stopTest()
+ }
else -> {
Log.e(tag, "No protocol")
}
}
currentTunnelHandle = -1
stopForeground(true)
-
isUp = false
- stopSelf();
+ stopSelf()
}
@@ -366,11 +549,89 @@ class VPNService : android.net.VpnService() {
private fun startShadowsocks() {
Log.e(tag, "startShadowsocks method enters")
- if(mConfig != null) {
- try {
+ if (mConfig != null) {
+ try {
+ Log.e(tag, "Config: $mConfig")
- } catch(e: Exception) {
- Log.e(tag, "Error in startShadowsocks: $e")
+ ProfileManager.clear()
+ val profile = Profile()
+// val iter: Iterator = mConfig!!.keys()
+// while (iter.hasNext()) {
+// val key = iter.next()
+// try {
+// val value: Any = mConfig!!.get(key)
+// Log.i(tag, "startShadowsocks: $key : $value")
+// } catch (e: JSONException) {
+// // Something went wrong!
+// }
+// }
+
+ val shadowsocksConfig = mConfig?.getJSONObject("shadowsocks_config_data")
+
+ if (shadowsocksConfig?.has("name") == true) {
+ profile.name = shadowsocksConfig.getString("name")
+ } else {
+ profile.name = "amnezia"
+ }
+ if (shadowsocksConfig?.has("method") == true) {
+ profile.method = shadowsocksConfig.getString("method").toString()
+ }
+ if (shadowsocksConfig?.has("server") == true) {
+ profile.host = shadowsocksConfig.getString("server").toString()
+ }
+ if (shadowsocksConfig?.has("password") == true) {
+ profile.password = shadowsocksConfig.getString("password").toString()
+ }
+ if (shadowsocksConfig?.has("server_port") == true) {
+ profile.remotePort = shadowsocksConfig.getInt("server_port")
+ }
+// if(mConfig?.has("local_port") == true) {
+// profile. = mConfig?.getInt("local_port")
+// }
+// profile.name = "amnezia"
+// profile.method = "chacha20-ietf-poly1305"
+// profile.host = "de01-ss.sshocean.net"
+// profile.password = "ZTZhN"
+// profile.remotePort = 8388
+
+ profile.proxyApps = false
+ profile.bypass = false
+ profile.metered = false
+ profile.dirty = false
+ profile.ipv6 = true
+
+ DataStore.profileId = ProfileManager.createProfile(profile).id
+ val switchProfile = Core.switchProfile(DataStore.profileId)
+ Log.i(tag, "startShadowsocks: SwitchProfile: $switchProfile")
+ intent?.putExtra("startOnly", false)
+ onStartCommand(
+ intent,
+ flags,
+ startId
+ )
+// startRunner()
+// VpnManager.getInstance().run()
+// VpnManager.getInstance()
+// .setOnStatusChangeListener(object : VpnManager.OnStatusChangeListener {
+// override fun onStatusChanged(state: BaseService.State) {
+// when (state) {
+// BaseService.State.Connected -> {
+// isUp = true
+// }
+// BaseService.State.Stopped -> {
+// isUp = false
+// }
+// else -> {}
+// }
+// }
+//
+// override fun onTrafficUpdated(profileId: Long, stats: TrafficStats) {
+//
+// }
+// })
+//// Core.startService()
+ } catch (e: Exception) {
+ Log.e(tag, "Error in startShadowsocks: $e")
}
} else {
Log.e(tag, "Invalid config file!!")
@@ -386,19 +647,20 @@ class VPNService : android.net.VpnService() {
private fun startWireGuard() {
val wireguard_conf = buildWireugardConfig(mConfig!!)
+ Log.i(tag, "startWireGuard: wireguard_conf : $wireguard_conf")
if (currentTunnelHandle != -1) {
Log.e(tag, "Tunnel already up")
// Turn the tunnel down because this might be a switch
wgTurnOff(currentTunnelHandle)
}
- val wgConfig: String = wireguard_conf!!.toWgUserspaceString()
+ val wgConfig: String = wireguard_conf.toWgUserspaceString()
val builder = Builder()
setupBuilder(wireguard_conf, builder)
- builder.setSession("avpn0")
+ builder.setSession("Amnezia")
builder.establish().use { tun ->
if (tun == null) return
Log.i(tag, "Go backend " + wgVersion())
- currentTunnelHandle = wgTurnOn("avpn0", tun.detachFd(), wgConfig)
+ currentTunnelHandle = wgTurnOn("Amnezia", tun.detachFd(), wgConfig)
}
if (currentTunnelHandle < 0) {
Log.e(tag, "Activation Error Code -> $currentTunnelHandle")
@@ -417,31 +679,163 @@ class VPNService : android.net.VpnService() {
.apply()
}
- companion object {
- @JvmStatic
- fun startService(c: Context) {
- c.applicationContext.startService(
- Intent(c.applicationContext, VPNService::class.java).apply {
- putExtra("startOnly", true)
- })
+ override suspend fun startProcesses() {
+ worker = ProtectWorker().apply { start() }
+ try {
+ Log.i(tag, "startProcesses: ------------------1")
+ super.startProcesses()
+ Log.i(tag, "startProcesses: ------------------2")
+ sendFd(startVpn())
+ Log.i(tag, "startProcesses: ------------------3")
+ } catch (e: Exception) {
+ e.printStackTrace()
}
-
- @JvmStatic
- private external fun wgGetConfig(handle: Int): String?
-
- @JvmStatic
- private external fun wgGetSocketV4(handle: Int): Int
-
- @JvmStatic
- private external fun wgGetSocketV6(handle: Int): Int
-
- @JvmStatic
- private external fun wgTurnOff(handle: Int)
-
- @JvmStatic
- private external fun wgTurnOn(ifName: String, tunFd: Int, settings: String): Int
-
- @JvmStatic
- private external fun wgVersion(): String?
}
+
+ override fun killProcesses(scope: CoroutineScope) {
+ super.killProcesses(scope)
+ active = false
+ scope.launch { DefaultNetworkListener.stop(this) }
+ worker?.shutdown(scope)
+ worker = null
+ conn?.close()
+ conn = null
+ }
+
+ private suspend fun startVpn(): FileDescriptor {
+ val profile = data.proxy!!.profile
+ Log.i(tag, "startVpn: -----------------------1")
+ val builder = Builder()
+ .setConfigureIntent(Core.configureIntent(this))
+ .setSession(profile.formattedName)
+ .setMtu(VPN_MTU)
+ .addAddress(PRIVATE_VLAN4_CLIENT, 30)
+ .addDnsServer(PRIVATE_VLAN4_ROUTER)
+ Log.i(tag, "startVpn: -----------------------2")
+ if (profile.ipv6) {
+ builder.addAddress(PRIVATE_VLAN6_CLIENT, 126)
+ builder.addRoute("::", 0)
+ }
+ Log.i(tag, "startVpn: -----------------------3")
+ val me = packageName
+ if (profile.proxyApps) {
+ profile.individual.split('\n')
+ .filter { it != me }
+ .forEach {
+ try {
+ if (profile.bypass) builder.addDisallowedApplication(it)
+ else builder.addAllowedApplication(it)
+ } catch (ex: PackageManager.NameNotFoundException) {
+ printLog(ex)
+ }
+ }
+ if (profile.bypass) {
+ builder.addDisallowedApplication(me)
+ }
+ } else {
+ builder.addDisallowedApplication(me)
+ }
+ Log.i(tag, "startVpn: -----------------------4")
+ when (profile.route) {
+ Acl.ALL, Acl.BYPASS_CHN, Acl.CUSTOM_RULES -> builder.addRoute("0.0.0.0", 0)
+ else -> {
+ resources.getStringArray(R.array.bypass_private_route).forEach {
+ val subnet = Subnet.fromString(it)!!
+ builder.addRoute(subnet.address.hostAddress, subnet.prefixSize)
+ }
+ builder.addRoute(PRIVATE_VLAN4_ROUTER, 32)
+ }
+ }
+ Log.i(tag, "startVpn: -----------------------5")
+ metered = profile.metered
+ active = true // possible race condition here?
+ Log.i(tag, "startVpn: -----------------------6")
+ builder.setUnderlyingNetworks(underlyingNetworks)
+ Log.i(tag, "startVpn: -----------------------7")
+ val conn = builder.establish() ?: throw NullConnectionException()
+ Log.i(tag, "startVpn: -----------------------8")
+ this.conn = conn
+ Log.i(tag, "startVpn: -----------------------9")
+ val cmd = arrayListOf(
+ File(applicationInfo.nativeLibraryDir, Executable.TUN2SOCKS).absolutePath,
+ "--netif-ipaddr", PRIVATE_VLAN4_ROUTER,
+ "--socks-server-addr", "${DataStore.listenAddress}:${DataStore.portProxy}",
+ "--tunmtu", VPN_MTU.toString(),
+ "--sock-path", "sock_path",
+ "--dnsgw", "127.0.0.1:${DataStore.portLocalDns}",
+ "--loglevel", "warning"
+ )
+ Log.i(tag, "startVpn: -----------------------10")
+ if (profile.ipv6) {
+ cmd += "--netif-ip6addr"
+ cmd += PRIVATE_VLAN6_ROUTER
+ }
+ Log.i(tag, "startVpn: -----------------------11")
+ cmd += "--enable-udprelay"
+ Log.i(tag, "startVpn: -----------------------12")
+ data.processes!!.start(cmd, onRestartCallback = {
+ try {
+ sendFd(conn.fileDescriptor)
+ } catch (e: ErrnoException) {
+ e.printStackTrace()
+ stopRunner(false, e.message)
+ }
+ })
+ Log.i(tag, "startVpn: -----------------------13")
+ return conn.fileDescriptor
+ }
+
+ private suspend fun sendFd(fd: FileDescriptor) {
+ var tries = 0
+ val path = File(Core.deviceStorage.noBackupFilesDir, "sock_path").absolutePath
+ while (true) try {
+ delay(50L shl tries)
+ LocalSocket().use { localSocket ->
+ localSocket.connect(
+ LocalSocketAddress(
+ path,
+ LocalSocketAddress.Namespace.FILESYSTEM
+ )
+ )
+ localSocket.setFileDescriptorsForSend(arrayOf(fd))
+ localSocket.outputStream.write(42)
+ }
+ return
+ } catch (e: IOException) {
+ if (tries > 5) throw e
+ tries += 1
+ }
+ }
+
+
+ private inner class ProtectWorker : ConcurrentLocalSocketListener(
+ "ShadowsocksVpnThread",
+ File(Core.deviceStorage.noBackupFilesDir, "protect_path")
+ ) {
+ override fun acceptInternal(socket: LocalSocket) {
+ socket.inputStream.read()
+ val fd = socket.ancillaryFileDescriptors!!.single()!!
+ CloseableFd(fd).use {
+ socket.outputStream.write(if (underlyingNetwork.let { network ->
+ if (network != null && Build.VERSION.SDK_INT >= 23) try {
+ network.bindSocket(fd)
+ true
+ } catch (e: IOException) {
+ // suppress ENONET (Machine is not on the network)
+ if ((e.cause as? ErrnoException)?.errno != 64) printLog(e)
+ false
+ } else protect(getInt.invoke(fd) as Int)
+ }) 0 else 1)
+ }
+ }
+ }
+
+ inner class NullConnectionException : NullPointerException() {
+ override fun getLocalizedMessage() = getString(R.string.reboot_required)
+ }
+
+ class CloseableFd(val fd: FileDescriptor) : Closeable {
+ override fun close() = Os.close(fd)
+ }
+
}