diff --git a/.gitignore b/.gitignore
index 2cd26960..020b7ea5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,31 +1,8 @@
-classes
-bin
-gen
target
local.properties
-.classpath
-.project
-.settings
-tests/bin
-tests/gen
-tests/local.properties
-NUL
-gen_*
-*.class
-*.o
local.sbt
-.deps
/proguard-sbt.txt
#Intellij IDEA
-*.iml
-*.ipr
-*.iws
.idea/
-out/
.d
-
-#NDK
-src/main/obj
-src/main/libs
-jni/Application.mk
diff --git a/README.md b/README.md
index 14688f34..bec1e2c9 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,6 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala.
* JDK 1.8
* SBT 0.13.0+
-* Go 1.4+
* Android SDK
- Build Tools 25+
- Android Support Repository and Google Repository (see `build.sbt` for version)
@@ -53,7 +52,6 @@ Translators can go to [POEditor](https://poeditor.com/join/project/u5VHO9vhSf) t
shadowsocks-libev: GPLv3
pdnsd: GPLv3
libev: GPLv2
- kcptun: MIT
libsodium: ISC
diff --git a/mobile/src/main/assets/pages/about.html b/mobile/src/main/assets/pages/about.html
index eb991850..e8d232db 100644
--- a/mobile/src/main/assets/pages/about.html
+++ b/mobile/src/main/assets/pages/about.html
@@ -298,7 +298,6 @@ along with this program. If not, see http
shadowsocks-libev: GPLv3
pdnsd: GPLv3
libev: GPLv2
- kcptun: MIT
libsodium: ISC
diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml
index b14816a5..1aaf2586 100644
--- a/mobile/src/main/res/values/strings.xml
+++ b/mobile/src/main/res/values/strings.xml
@@ -135,12 +135,6 @@
Connected
Not connected
-
- Enable Shadowsocks over KCP
- KCP Port
- %d (port number of the remote KCPTUN server)
- KCP Parameters (CLI)
-
Custom rules
Selection…
diff --git a/mobile/src/main/res/xml/pref_profile.xml b/mobile/src/main/res/xml/pref_profile.xml
index 80816c63..6f9af431 100644
--- a/mobile/src/main/res/xml/pref_profile.xml
+++ b/mobile/src/main/res/xml/pref_profile.xml
@@ -71,26 +71,4 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/mobile/src/main/scala/com/github/shadowsocks/BaseService.scala b/mobile/src/main/scala/com/github/shadowsocks/BaseService.scala
index 3b532114..59af31ae 100644
--- a/mobile/src/main/scala/com/github/shadowsocks/BaseService.scala
+++ b/mobile/src/main/scala/com/github/shadowsocks/BaseService.scala
@@ -28,7 +28,6 @@ import java.util.{Timer, TimerTask}
import android.app.Service
import android.content.{BroadcastReceiver, Context, Intent, IntentFilter}
-import android.net.ConnectivityManager
import android.os.{Handler, IBinder, RemoteCallbackList}
import android.text.TextUtils
import android.util.Log
@@ -49,7 +48,6 @@ trait BaseService extends Service {
@volatile protected var profile: Profile = _
case class NameNotResolvedException() extends IOException
- case class KcpcliParseException(cause: Throwable) extends Exception(cause)
case class NullConnectionException() extends NullPointerException
var timer: Timer = _
@@ -67,19 +65,6 @@ trait BaseService extends Service {
}
var closeReceiverRegistered: Boolean = _
- var kcptunProcess: GuardedProcess = _
- private val networkReceiver: BroadcastReceiver = (context: Context, _: Intent) => {
- val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE).asInstanceOf[ConnectivityManager]
- val activeNetwork = cm.getActiveNetworkInfo
- val isConnected = activeNetwork != null && activeNetwork.isConnected
-
- if (isConnected && profile.kcp && kcptunProcess != null) {
- restartHanlder.removeCallbacks(null)
- restartHanlder.postDelayed(() => if (kcptunProcess != null) kcptunProcess.restart(), 2000)
- }
- }
- var networkReceiverRegistered: Boolean = _
-
val binder = new IShadowsocksService.Stub {
override def getState: Int = {
state
@@ -185,14 +170,6 @@ trait BaseService extends Service {
closeReceiverRegistered = true
}
- if (profile.kcp && !networkReceiverRegistered) {
- // register network change receiver
- val filter = new IntentFilter()
- filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
- registerReceiver(networkReceiver, filter)
- networkReceiverRegistered = true
- }
-
app.track(getClass.getSimpleName, "start")
changeState(State.CONNECTING)
@@ -202,8 +179,6 @@ trait BaseService extends Service {
Utils.ThrowableFuture(try connect() catch {
case _: NameNotResolvedException => stopRunner(stopService = true, getString(R.string.invalid_server))
- case exc: KcpcliParseException =>
- stopRunner(stopService = true, getString(R.string.service_failed) + ": " + exc.cause.getMessage)
case _: NullConnectionException => stopRunner(stopService = true, getString(R.string.reboot_required))
case exc: Throwable =>
stopRunner(stopService = true, getString(R.string.service_failed) + ": " + exc.getMessage)
@@ -218,10 +193,6 @@ trait BaseService extends Service {
unregisterReceiver(closeReceiver)
closeReceiverRegistered = false
}
- if (networkReceiverRegistered) {
- unregisterReceiver(networkReceiver)
- networkReceiverRegistered = false
- }
// Make sure update total traffic when stopping the runner
updateTrafficTotal(TrafficMonitor.txTotal, TrafficMonitor.rxTotal)
diff --git a/mobile/src/main/scala/com/github/shadowsocks/ProfileConfigFragment.scala b/mobile/src/main/scala/com/github/shadowsocks/ProfileConfigFragment.scala
index f558b81f..60b51b98 100644
--- a/mobile/src/main/scala/com/github/shadowsocks/ProfileConfigFragment.scala
+++ b/mobile/src/main/scala/com/github/shadowsocks/ProfileConfigFragment.scala
@@ -25,13 +25,11 @@ import android.content.{DialogInterface, Intent, SharedPreferences}
import android.os.{Build, Bundle, UserManager}
import android.support.v14.preference.SwitchPreference
import android.support.v7.app.AlertDialog
-import android.support.v7.preference.Preference
import android.support.v7.widget.Toolbar.OnMenuItemClickListener
import android.view.MenuItem
import be.mygod.preference.PreferenceFragment
import com.github.shadowsocks.ShadowsocksApplication.app
import com.github.shadowsocks.database.Profile
-import com.github.shadowsocks.preferences.KcpCliPreferenceDialogFragment
import com.github.shadowsocks.utils.{Action, Key, Utils}
class ProfileConfigFragment extends PreferenceFragment with OnMenuItemClickListener
@@ -75,11 +73,6 @@ class ProfileConfigFragment extends PreferenceFragment with OnMenuItemClickListe
isProxyApps.setChecked(app.settings.getBoolean(Key.proxyApps, false)) // fetch proxyApps updated by AppManager
}
- override def onDisplayPreferenceDialog(preference: Preference): Unit = preference.getKey match {
- case Key.kcpcli => displayPreferenceDialog(Key.kcpcli, new KcpCliPreferenceDialogFragment())
- case _ => super.onDisplayPreferenceDialog(preference)
- }
-
override def onMenuItemClick(item: MenuItem): Boolean = item.getItemId match {
case R.id.action_delete =>
new AlertDialog.Builder(getActivity)
diff --git a/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala b/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala
index 58ad4139..48a4a366 100644
--- a/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala
+++ b/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala
@@ -53,15 +53,10 @@ class ShadowsocksNatService extends BaseService {
var su: Shell.Interactive = _
def startShadowsocksDaemon() {
- val conf = if (profile.kcp) {
- ConfigUtils
- .SHADOWSOCKS.formatLocal(Locale.ENGLISH, "127.0.0.1", profile.localPort + 90, profile.localPort,
- profile.password, profile.method, 600)
- } else {
+ val conf =
ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, profile.host, profile.remotePort, profile.localPort,
profile.password, profile.method, 600)
- }
Utils.printToFile(new File(getApplicationInfo.dataDir + "/ss-local-nat.conf"))(p => {
p.println(conf)
})
@@ -85,27 +80,6 @@ class ShadowsocksNatService extends BaseService {
sslocalProcess = new GuardedProcess(cmd).start()
}
- def startKcptunDaemon() {
- if (profile.kcpcli == null) profile.kcpcli = ""
-
- val host = if (profile.host.contains(":")) {
- "[" + profile.host + "]"
- } else {
- profile.host
- }
-
- val cmd = ArrayBuffer[String](getApplicationInfo.nativeLibraryDir + "/libkcptun.so"
- , "-r", host + ":" + profile.kcpPort
- , "-l", "127.0.0.1:" + (profile.localPort + 90))
- try cmd ++= Utils.translateCommandline(profile.kcpcli) catch {
- case exc: Exception => throw KcpcliParseException(exc)
- }
-
- if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
-
- kcptunProcess = new GuardedProcess(cmd).start()
- }
-
def startDNSTunnel() {
if (profile.udpdns) {
val conf = ConfigUtils
@@ -130,15 +104,10 @@ class ShadowsocksNatService extends BaseService {
sstunnelProcess = new GuardedProcess(cmd).start()
} else {
- val conf = if (profile.kcp) {
- ConfigUtils
- .SHADOWSOCKS.formatLocal(Locale.ENGLISH, "127.0.0.1", profile.localPort + 90,
- profile.localPort + 63, profile.password, profile.method, 10)
- } else {
+ val conf =
ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, profile.host, profile.remotePort, profile.localPort + 63,
profile.password, profile.method, 10)
- }
Utils.printToFile(new File(getApplicationInfo.dataDir + "/ss-tunnel-nat.conf"))(p => {
p.println(conf)
})
@@ -205,7 +174,6 @@ class ShadowsocksNatService extends BaseService {
startShadowsocksDaemon()
if (!profile.udpdns) startDnsDaemon()
- if (profile.kcp) startKcptunDaemon()
setupIptables()
@@ -221,10 +189,6 @@ class ShadowsocksNatService extends BaseService {
}
def killProcesses() {
- if (kcptunProcess != null) {
- kcptunProcess.destroy()
- kcptunProcess = null
- }
if (sslocalProcess != null) {
sslocalProcess.destroy()
sslocalProcess = null
diff --git a/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala b/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala
index 9218aee5..46b334e9 100644
--- a/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala
+++ b/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala
@@ -91,10 +91,6 @@ class ShadowsocksVpnService extends VpnService with BaseService {
}
def killProcesses() {
- if (kcptunProcess != null) {
- kcptunProcess.destroy()
- kcptunProcess = null
- }
if (sslocalProcess != null) {
sslocalProcess.destroy()
sslocalProcess = null
@@ -157,76 +153,19 @@ class ShadowsocksVpnService extends VpnService with BaseService {
val fd = startVpn()
if (!sendFd(fd)) throw new Exception("sendFd failed")
- if (profile.kcp) {
- startKcptunDaemon()
- }
-
startShadowsocksDaemon()
- if (profile.udpdns && profile.kcp) {
- startShadowsocksUDPDaemon()
- }
-
if (!profile.udpdns) {
startDnsDaemon()
startDnsTunnel()
}
}
- def startKcptunDaemon() {
- if (profile.kcpcli == null) profile.kcpcli = ""
-
- val host = if (profile.host.contains(":")) {
- "[" + profile.host + "]"
- } else {
- profile.host
- }
-
- val cmd = ArrayBuffer(getApplicationInfo.nativeLibraryDir + "/libkcptun.so"
- , "-r", host + ":" + profile.kcpPort
- , "-l", "127.0.0.1:" + (profile.localPort + 90)
- , "--path", protectPath)
- try cmd ++= Utils.translateCommandline(profile.kcpcli) catch {
- case exc: Exception => throw KcpcliParseException(exc)
- }
-
- if (BuildConfig.DEBUG)
- Log.d(TAG, cmd.mkString(" "))
-
- kcptunProcess = new GuardedProcess(cmd).start()
- }
-
- def startShadowsocksUDPDaemon() {
- val conf = ConfigUtils
- .SHADOWSOCKS.formatLocal(Locale.ENGLISH, profile.host, profile.remotePort, profile.localPort,
- profile.password, profile.method, 600)
- Utils.printToFile(new File(getApplicationInfo.dataDir + "/ss-local-udp-vpn.conf"))(p => {
- p.println(conf)
- })
-
- val cmd = ArrayBuffer[String](getApplicationInfo.nativeLibraryDir + "/libss-local.so", "-V", "-U"
- , "-b", "127.0.0.1"
- , "-t", "600"
- , "-P", getApplicationInfo.dataDir
- , "-c", getApplicationInfo.dataDir + "/ss-local-udp-vpn.conf")
-
- if (profile.auth) cmd += "-A"
-
- if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
-
- sstunnelProcess = new GuardedProcess(cmd).start()
- }
-
def startShadowsocksDaemon() {
- val conf = if (profile.kcp) {
- ConfigUtils
- .SHADOWSOCKS.formatLocal(Locale.ENGLISH, "127.0.0.1", profile.localPort + 90, profile.localPort,
- profile.password, profile.method, 600)
- } else {
+ val conf =
ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, profile.host, profile.remotePort, profile.localPort,
profile.password, profile.method, 600)
- }
Utils.printToFile(new File(getApplicationInfo.dataDir + "/ss-local-vpn.conf"))(p => {
p.println(conf)
})
@@ -239,7 +178,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
if (profile.auth) cmd += "-A"
- if (profile.udpdns && !profile.kcp) cmd += "-u"
+ if (profile.udpdns) cmd += "-u"
if (profile.route != Acl.ALL) {
cmd += "--acl"
@@ -254,15 +193,10 @@ class ShadowsocksVpnService extends VpnService with BaseService {
}
def startDnsTunnel() {
- val conf = if (profile.kcp) {
- ConfigUtils
- .SHADOWSOCKS.formatLocal(Locale.ENGLISH, "127.0.0.1", profile.localPort + 90, profile.localPort + 63,
- profile.password, profile.method, 10)
- } else {
+ val conf =
ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, profile.host, profile.remotePort, profile.localPort + 63,
profile.password, profile.method, 10)
- }
Utils.printToFile(new File(getApplicationInfo.dataDir + "/ss-tunnel-vpn.conf"))(p => {
p.println(conf)
})
diff --git a/mobile/src/main/scala/com/github/shadowsocks/database/DBHelper.scala b/mobile/src/main/scala/com/github/shadowsocks/database/DBHelper.scala
index 9e4e55b5..ebbb3e2c 100644
--- a/mobile/src/main/scala/com/github/shadowsocks/database/DBHelper.scala
+++ b/mobile/src/main/scala/com/github/shadowsocks/database/DBHelper.scala
@@ -46,7 +46,7 @@ object DBHelper {
}
class DBHelper(val context: Context)
- extends OrmLiteSqliteOpenHelper(context, DBHelper.PROFILE, null, 21) {
+ extends OrmLiteSqliteOpenHelper(context, DBHelper.PROFILE, null, 22) {
import DBHelper._
lazy val profileDao: Dao[Profile, Int] = getDao(classOf[Profile])
@@ -115,24 +115,26 @@ class DBHelper(val context: Context)
profileDao.executeRawNoArgs("UPDATE `profile` SET route = 'bypass-lan-china' WHERE route = 'bypass-china'")
}
- if (oldVersion < 17) {
- profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN kcp SMALLINT;")
- profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN kcpcli VARCHAR DEFAULT " +
- "'--crypt none --mode normal --mtu 1200 --nocomp --dscp 46 --parityshard 0';")
- } else if (oldVersion < 20) {
- profileDao.executeRawNoArgs("UPDATE `profile` SET kcpcli = '--crypt none --mode normal --mtu 1200 --nocomp " +
- "--dscp 46 --parityshard 0' WHERE kcpcli IS NULL;")
- }
-
- if (oldVersion < 18) {
- profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN kcpPort INTEGER DEFAULT 8399;")
- } else if (oldVersion < 19) {
- profileDao.executeRawNoArgs("UPDATE `profile` SET kcpPort = 8399 WHERE kcpPort = 0;")
- }
-
if (oldVersion < 21) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN remoteDns VARCHAR DEFAULT '8.8.8.8';")
}
+
+ if (oldVersion < 17) {
+ profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN plugin VARCHAR;")
+ } else if (oldVersion < 22) {
+ // upgrade kcptun to SIP003 plugin
+ profileDao.executeRawNoArgs("BEGIN TRANSACTION;")
+ profileDao.executeRawNoArgs("ALTER TABLE `profile` RENAME TO `tmp`;")
+ TableUtils.createTable(connectionSource, classOf[Profile])
+ profileDao.executeRawNoArgs(
+ "INSERT INTO `profile`(id, name, host, localPort, remotePort, password, method, route, proxyApps, bypass," +
+ " udpdns, auth, ipv6, individual, tx, rx, date, userOrder, plugin) " +
+ "SELECT id, name, host, localPort, CASE WHEN kcp = 1 THEN kcpPort ELSE remotePort END, password, method, " +
+ "route, proxyApps, bypass, udpdns, auth, ipv6, individual, tx, rx, date, userOrder, " +
+ "CASE WHEN kcp = 1 THEN 'kcptun ' || kcpcli ELSE NULL END FROM `tmp`;")
+ profileDao.executeRawNoArgs("DROP TABLE `tmp`;")
+ profileDao.executeRawNoArgs("COMMIT;")
+ }
} catch {
case ex: Exception =>
app.track(ex)
diff --git a/mobile/src/main/scala/com/github/shadowsocks/database/Profile.scala b/mobile/src/main/scala/com/github/shadowsocks/database/Profile.scala
index dac6e203..19ffe4d5 100644
--- a/mobile/src/main/scala/com/github/shadowsocks/database/Profile.scala
+++ b/mobile/src/main/scala/com/github/shadowsocks/database/Profile.scala
@@ -90,13 +90,7 @@ class Profile {
var userOrder: Long = _
@DatabaseField
- var kcp: Boolean = _
-
- @DatabaseField
- var kcpPort: Int = 8399
-
- @DatabaseField
- var kcpcli: String = "--crypt none --mode normal --mtu 1200 --nocomp --dscp 46 --parityshard 0"
+ var plugin: String = _
def formattedAddress: String = (if (host.contains(":")) "[%s]:%d" else "%s:%d").format(host, remotePort)
def nameIsEmpty: Boolean = name == null || name.isEmpty
@@ -123,9 +117,7 @@ class Profile {
.putBoolean(Key.auth, auth)
.putBoolean(Key.ipv6, ipv6)
.putString(Key.individual, individual)
- .putBoolean(Key.kcp, kcp)
- .putInt(Key.kcpPort, kcpPort)
- .putString(Key.kcpcli, kcpcli)
+ .putString(Key.plugin, plugin)
.remove(Key.dirty)
def deserialize(pref: SharedPreferences) {
// It's assumed that default values are never used, so 0/false/null is always used even if that isn't the case
@@ -143,8 +135,6 @@ class Profile {
auth = pref.getBoolean(Key.auth, false)
ipv6 = pref.getBoolean(Key.ipv6, false)
individual = pref.getString(Key.individual, null)
- kcp = pref.getBoolean(Key.kcp, false)
- kcpPort = pref.getInt(Key.kcpPort, 0)
- kcpcli = pref.getString(Key.kcpcli, null)
+ plugin = pref.getString(Key.plugin, null)
}
}
diff --git a/mobile/src/main/scala/com/github/shadowsocks/preferences/KcpCliPreferenceDialogFragment.scala b/mobile/src/main/scala/com/github/shadowsocks/preferences/KcpCliPreferenceDialogFragment.scala
deleted file mode 100644
index 05319d9c..00000000
--- a/mobile/src/main/scala/com/github/shadowsocks/preferences/KcpCliPreferenceDialogFragment.scala
+++ /dev/null
@@ -1,54 +0,0 @@
-/*******************************************************************************/
-/* */
-/* 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 com.github.shadowsocks.preferences
-
-import android.app.AlertDialog
-import android.content.DialogInterface
-import eu.chainfire.libsuperuser.Shell
-import be.mygod.preference.EditTextPreferenceDialogFragment
-
-import scala.collection.JavaConverters._
-
-/**
- * @author Mygod
- */
-class KcpCliPreferenceDialogFragment extends EditTextPreferenceDialogFragment {
- override def onPrepareDialogBuilder(builder: AlertDialog.Builder) {
- super.onPrepareDialogBuilder(builder)
- builder.setNeutralButton("?", ((_, _) => new AlertDialog.Builder(builder.getContext)
- .setTitle("?")
- .setMessage(Shell.SH.run(builder.getContext.getApplicationInfo.nativeLibraryDir + "/libkcptun.so --help")
- .asScala
- .dropWhile(line => line != "GLOBAL OPTIONS:")
- .drop(1)
- .takeWhile(line => line.length() > 3)
- .filter(line =>
- !line.startsWith(" --localaddr ") &&
- !line.startsWith(" --remoteaddr ") &&
- !line.startsWith(" --path ") &&
- !line.startsWith(" --help,") &&
- !line.startsWith(" --version,"))
- .mkString("\n")
- .replaceAll(" {2,}", "\n")
- .substring(1)) // remove 1st \n
- .show()): DialogInterface.OnClickListener)
- }
-}
diff --git a/mobile/src/main/scala/com/github/shadowsocks/utils/Constants.scala b/mobile/src/main/scala/com/github/shadowsocks/utils/Constants.scala
index ea504bc0..2b2d1929 100644
--- a/mobile/src/main/scala/com/github/shadowsocks/utils/Constants.scala
+++ b/mobile/src/main/scala/com/github/shadowsocks/utils/Constants.scala
@@ -26,9 +26,8 @@ object Executable {
val SS_LOCAL = "ss-local"
val SS_TUNNEL = "ss-tunnel"
val TUN2SOCKS = "tun2socks"
- val KCPTUN = "kcptun"
- val EXECUTABLES = Array(SS_LOCAL, SS_TUNNEL, PDNSD, REDSOCKS, TUN2SOCKS, KCPTUN)
+ val EXECUTABLES = Array(SS_LOCAL, SS_TUNNEL, PDNSD, REDSOCKS, TUN2SOCKS)
}
object ConfigUtils {
@@ -153,9 +152,7 @@ object Key {
val localPort = "localPortNum"
val remoteDns = "remoteDns"
- val kcp = "kcp"
- val kcpPort = "kcpPort"
- val kcpcli = "kcpcli"
+ val plugin = "plugin"
val dirty = "profileDirty"