diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml
index a7e489d0..f9e3c0e9 100644
--- a/src/main/res/values-ru/strings.xml
+++ b/src/main/res/values-ru/strings.xml
@@ -53,6 +53,8 @@
Включите эту опцию для работы выбранных приложений в обход прокси
Авто Подключение
Запускать Shadowsocks при включении
+ Переключение требует наличия ROOT прав
+ Неподдерживаемая версия ядра: %s < 3.7.1
Проброс UDP
Пробрасывать UDP пакеты на удалённый сервер
diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml
index 2823c53d..cfbaa9bf 100644
--- a/src/main/res/values-zh-rCN/strings.xml
+++ b/src/main/res/values-zh-rCN/strings.xml
@@ -32,6 +32,8 @@
启用该选项,以使所选应用程序的流量不经过代理
自动连接
随系统启动后台服务
+ 切换需要 ROOT 权限
+ 不支持的内核版本:%s < 3.7.1
后台服务已开始运行。
无法连接远程服务器
服务器名无效
diff --git a/src/main/res/values-zh/strings.xml b/src/main/res/values-zh/strings.xml
index 2f15dab4..3d26d66a 100644
--- a/src/main/res/values-zh/strings.xml
+++ b/src/main/res/values-zh/strings.xml
@@ -33,6 +33,8 @@
啟用此選項,會略過已選擇的應用程式
自動連線
在裝置啟動時啟用 Shadowsocks
+ 切換需要 ROOT 權限
+ 不支持的內核版本:%s < 3.7.1
Shadowsocks 已啟動。
連線至遠端伺服器失敗
伺服器名稱無效
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index ac007a7d..71bce678 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -59,6 +59,8 @@
Enable this option to bypass selected apps
Auto Connect
Enable Shadowsocks on startup
+ Toggling requires ROOT permission
+ Unsupported kernel version: %s < 3.7.1
UDP Forwarding
Forward UDP packets to remote
diff --git a/src/main/res/xml/pref_all.xml b/src/main/res/xml/pref_all.xml
index 503ca76f..25cbd4a0 100644
--- a/src/main/res/xml/pref_all.xml
+++ b/src/main/res/xml/pref_all.xml
@@ -110,6 +110,12 @@
android:summary="@string/auto_connect_summary"
android:title="@string/auto_connect"/>
+
+
diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala
index 6af63b31..dd4cbe17 100644
--- a/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala
+++ b/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala
@@ -106,6 +106,8 @@ class ShadowsocksNatService extends BaseService {
if (profile.auth) cmd += "-A"
+ if (TcpFastOpen.sendEnabled) cmd += "--fast-open"
+
if (profile.route != Route.ALL) {
cmd += "--acl"
cmd += (getApplicationInfo.dataDir + "/acl.list")
@@ -149,6 +151,8 @@ class ShadowsocksNatService extends BaseService {
if (profile.auth) cmd += "-A"
+ if (TcpFastOpen.sendEnabled) cmd += "--fast-open"
+
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
sstunnelProcess = new GuardedProcess(cmd).start()
diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala
index 758b9558..8aec2664 100644
--- a/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala
+++ b/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala
@@ -12,7 +12,7 @@ import android.webkit.{WebView, WebViewClient}
import com.github.shadowsocks.ShadowsocksApplication.app
import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.preferences._
-import com.github.shadowsocks.utils.{Key, Utils}
+import com.github.shadowsocks.utils.{Key, TcpFastOpen, Utils}
object ShadowsocksSettings {
// Constants
@@ -154,6 +154,18 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan
}
switch.setChecked(BootReceiver.getEnabled(activity))
+ val tfo = findPreference("tcp_fastopen").asInstanceOf[SwitchPreference]
+ tfo.setChecked(TcpFastOpen.sendEnabled)
+ tfo.setOnPreferenceChangeListener((_, v) => {
+ val value = v.asInstanceOf[Boolean]
+ TcpFastOpen.enabled(value)
+ value == TcpFastOpen.sendEnabled
+ })
+ if (!TcpFastOpen.supported) {
+ tfo.setEnabled(false)
+ tfo.setSummary(getString(R.string.tcp_fastopen_summary_unsupported, java.lang.System.getProperty("os.version")))
+ }
+
findPreference("recovery").setOnPreferenceClickListener((preference: Preference) => {
app.track(TAG, "reset")
activity.recovery()
diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala
index ee053941..bb136335 100644
--- a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala
+++ b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala
@@ -236,6 +236,8 @@ class ShadowsocksVpnService extends VpnService with BaseService {
if (profile.auth) cmd += "-A"
+ if (TcpFastOpen.sendEnabled) cmd += "--fast-open"
+
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
sstunnelProcess = new GuardedProcess(cmd).start()
@@ -312,6 +314,8 @@ class ShadowsocksVpnService extends VpnService with BaseService {
if (profile.auth) cmd += "-A"
+ if (TcpFastOpen.sendEnabled) cmd += "--fast-open"
+
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
sstunnelProcess = new GuardedProcess(cmd).start()
diff --git a/src/main/scala/com/github/shadowsocks/utils/TcpFastOpen.scala b/src/main/scala/com/github/shadowsocks/utils/TcpFastOpen.scala
new file mode 100644
index 00000000..4dca873a
--- /dev/null
+++ b/src/main/scala/com/github/shadowsocks/utils/TcpFastOpen.scala
@@ -0,0 +1,36 @@
+package com.github.shadowsocks.utils
+
+import java.io.File
+
+import eu.chainfire.libsuperuser.Shell
+
+/**
+ * @author Mygod
+ */
+object TcpFastOpen {
+ /**
+ * Is kernel version >= 3.7.1.
+ */
+ lazy val supported = "^(\\d+)\\.(\\d+)\\.(\\d+)".r.findFirstMatchIn(System.getProperty("os.version")) match {
+ case Some(m) =>
+ val kernel = m.group(1).toInt
+ if (kernel < 3) false else if (kernel > 3) true else {
+ val major = m.group(2).toInt
+ if (major < 7) false else if (major > 7) true else m.group(3).toInt >= 1
+ }
+ case _ => false
+ }
+
+ def sendEnabled = {
+ val file = new File("/proc/sys/net/ipv4/tcp_fastopen")
+ file.canRead && (Utils.readAllLines(file).toInt & 1) > 0
+ }
+
+ def enabled(value: Boolean) = if (supported) {
+ val fastopen = "sysctl -w net.ipv4.tcp_fastopen=" + (if (value) 3 else 0)
+ Shell.SU.run(Array(
+ "mount -o remount,rw /system && " + fastopen + " && echo '#!/system/bin/sh\n" + fastopen +
+ "' > /etc/init.d/tcp_fastopen && chmod 755 /etc/init.d/tcp_fastopen",
+ "mount -o remount,ro /system"))
+ }
+}
diff --git a/src/main/scala/com/github/shadowsocks/utils/Utils.scala b/src/main/scala/com/github/shadowsocks/utils/Utils.scala
index bb7d78d9..374140b6 100644
--- a/src/main/scala/com/github/shadowsocks/utils/Utils.scala
+++ b/src/main/scala/com/github/shadowsocks/utils/Utils.scala
@@ -38,8 +38,10 @@
*/
package com.github.shadowsocks.utils
+import java.io.File
import java.net._
import java.security.MessageDigest
+import java.util.Scanner
import android.animation.{Animator, AnimatorListenerAdapter}
import android.content.pm.PackageManager
@@ -108,6 +110,13 @@ object Utils {
})
}
+ def readAllLines(f: File) = {
+ val scanner = new Scanner(f)
+ try {
+ scanner.useDelimiter("\\Z")
+ scanner.next()
+ } finally scanner.close()
+ }
def printToFile(f: java.io.File)(op: java.io.PrintWriter => Unit) {
val p = new java.io.PrintWriter(f)
try {