Shadowsocks protocol added

This commit is contained in:
aman 2022-04-26 23:49:20 +05:30
parent 59b4bf5267
commit 29656fb9a6
22 changed files with 791 additions and 664 deletions

View file

@ -1 +0,0 @@
/build

View file

@ -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"

View file

@ -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')"
]
}
}

View file

@ -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')"
]
}
}

View file

@ -30,48 +30,45 @@
android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAI_zVxZthz2HDuz9toTvkYvL0L5GA-OjeUIfBeXg" />
<service
android:name="org.amnezia.vpn.shadowsocks.core.bg.VpnService"
android:directBootAware="true"
android:exported="false"
android:label="@string/app_name"
android:permission="android.permission.BIND_VPN_SERVICE"
android:process=":bg"
tools:targetApi="n">
<intent-filter>
<action android:name="android.net.VpnService" />
</intent-filter>
<meta-data
android:name="android.net.VpnService.SUPPORTS_ALWAYS_ON"
android:value="true" />
</service>
<!-- <service-->
<!-- android:name="org.amnezia.vpn.shadowsocks.core.bg.ShadowsocksVpnService"-->
<!-- android:directBootAware="true"-->
<!-- android:exported="false"-->
<!-- android:label="@string/app_name"-->
<!-- android:permission="android.permission.BIND_VPN_SERVICE"-->
<!-- android:process=":BG"-->
<!-- tools:targetApi="n">-->
<!-- <intent-filter>-->
<!-- <action android:name="android.net.VpnService" />-->
<!-- </intent-filter>-->
<!-- </service>-->
<service
android:name="org.amnezia.vpn.shadowsocks.core.bg.TransproxyService"
android:directBootAware="true"
android:exported="false"
android:process=":bg"
tools:targetApi="n" />
<!-- <service-->
<!-- android:name="org.amnezia.vpn.shadowsocks.core.bg.TransproxyService"-->
<!-- android:directBootAware="true"-->
<!-- android:exported="false"-->
<!-- android:process=":QtOnlyProcess"-->
<!-- tools:targetApi="n" />-->
<service
android:name="org.amnezia.vpn.shadowsocks.core.bg.ProxyService"
android:directBootAware="true"
android:exported="false"
android:process=":bg"
tools:targetApi="n" />
<!-- <service-->
<!-- android:name="org.amnezia.vpn.shadowsocks.core.bg.ProxyService"-->
<!-- android:directBootAware="true"-->
<!-- android:exported="false"-->
<!-- android:process=":QtOnlyProcess"-->
<!-- tools:targetApi="n" />-->
<activity
android:name="org.amnezia.vpn.shadowsocks.core.VpnRequestActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=""
android:theme="@style/Theme.AppCompat.Translucent" />
<!-- <activity-->
<!-- android:name="org.amnezia.vpn.shadowsocks.core.VpnRequestActivity"-->
<!-- android:excludeFromRecents="true"-->
<!-- android:launchMode="singleTask"-->
<!-- android:taskAffinity=""-->
<!-- android:theme="@style/Theme.AppCompat.Translucent" />-->
<receiver
android:name="org.amnezia.vpn.shadowsocks.core.BootReceiver"
android:directBootAware="true"
android:enabled="false"
android:process=":bg">
android:process=":QtOnlyProcess">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
@ -87,48 +84,48 @@
<service
android:name="androidx.work.impl.background.systemalarm.SystemAlarmService"
android:directBootAware="true"
android:process=":bg"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<service
android:name="androidx.work.impl.background.systemjob.SystemJobService"
android:directBootAware="true"
android:process=":bg"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<receiver
android:name="androidx.work.impl.utils.ForceStopRunnable$BroadcastReceiver"
android:directBootAware="true"
android:process=":bg"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<receiver
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryChargingProxy"
android:directBootAware="true"
android:process=":bg"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<receiver
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryNotLowProxy"
android:directBootAware="true"
android:process=":bg"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<receiver
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$StorageNotLowProxy"
android:directBootAware="true"
android:process=":bg"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<receiver
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$NetworkStateProxy"
android:directBootAware="true"
android:process=":bg"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<receiver
android:name="androidx.work.impl.background.systemalarm.RescheduleReceiver"
android:directBootAware="true"
android:process=":bg"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<receiver
android:name="androidx.work.impl.background.systemalarm.ConstraintProxyUpdateReceiver"
android:directBootAware="true"
android:process=":bg"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
</application>
</manifest>

View file

@ -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");

View file

@ -1,23 +1,3 @@
/*******************************************************************************
* *
* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
package org.amnezia.vpn.shadowsocks.core.acl
import android.content.Context

View file

@ -1,23 +1,3 @@
/*******************************************************************************
* *
* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
package org.amnezia.vpn.shadowsocks.core.acl
import android.content.Context

View file

@ -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

View file

@ -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<IBinder, Long>() // the binder is the real identifier
private val bandwidthListeners =
mutableMapOf<IBinder, Long>() // 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<UserManager>()
?.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
}
}
}

View file

@ -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 =

View file

@ -1,135 +1,145 @@
/*******************************************************************************
* *
* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
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<KeyguardManager>()!!
private val nm by lazy { (service as Context).getSystemService<NotificationManager>()!! }
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<PowerManager>()?.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 <max.c.lv@gmail.com> *
// * Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
// * *
// * 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 <http://www.gnu.org/licenses/>. *
// * *
// *******************************************************************************/
//
//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<KeyguardManager>()!!
// private val nm by lazy { (service as Context).getSystemService<NotificationManager>()!! }
// 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<PowerManager>()?.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)
// }
//}

View file

@ -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<LocalDnsService.Interface>.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<LocalDnsService.Interface>.onStartCommand(intent, flags, startId)
}
stopRunner()
return Service.START_NOT_STICKY
return Service.START_STICKY
}
override suspend fun preInit() = DefaultNetworkListener.start(this) { underlyingNetwork = it }

View file

@ -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<LocalDnsService.Interface>.onStartCommand(intent, flags, startId)
super<LocalDnsService.Interface>.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() {

View file

@ -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"
}

View file

@ -38,10 +38,10 @@ import androidx.core.os.bundleOf
* ...
* &lt;application&gt;
* ...
* &lt;provider android:name="com.kyle.shadowsocks.$PLUGIN_ID.BinaryProvider"
* android:authorities="com.kyle.shadowsocks.plugin.$PLUGIN_ID.BinaryProvider"&gt;
* &lt;provider android:name="org.amnezia.vpn.shadowsocks.$PLUGIN_ID.BinaryProvider"
* android:authorities="org.amnezia.vpn.shadowsocks.plugin.$PLUGIN_ID.BinaryProvider"&gt;
* &lt;intent-filter&gt;
* &lt;category android:name="com.kyle.shadowsocks.plugin.ACTION_NATIVE_PLUGIN" /&gt;
* &lt;category android:name="org.amnezia.vpn.shadowsocks.plugin.ACTION_NATIVE_PLUGIN" /&gt;
* &lt;/intent-filter&gt;
* &lt;/provider&gt;
* ...

View file

@ -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=<http></http>|tls> Enable obfuscating: HTTP or TLS (Experimental).
* obfs-host=<host_name> Hostname for obfuscating (Experimental)."
*
* Constant Value: "com.kyle.shadowsocks.plugin.EXTRA_HELP_MESSAGE"
* Constant Value: "org.amnezia.vpn.shadowsocks.plugin.EXTRA_HELP_MESSAGE"
</host_name> */
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"
}

File diff suppressed because one or more lines are too long

View file

@ -1,11 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:name="path"
android:fillColor="#fff"
android:pathData="M 21.25 2.28 L 17.55 18.55 L 9.26 15.89 L 16.58 7.16 L 6.83 15.37 L 0 12.8 L 21.25 2.28 ZM 9.45 17.56 L 12.09 18.41 L 9.46 22 L 9.45 17.56 Z" />
</vector>