diff --git a/core/schemas/com.github.shadowsocks.database.PrivateDatabase/29.json b/core/schemas/com.github.shadowsocks.database.PrivateDatabase/29.json
index 5a695600e7..8251c64540 100644
--- a/core/schemas/com.github.shadowsocks.database.PrivateDatabase/29.json
+++ b/core/schemas/com.github.shadowsocks.database.PrivateDatabase/29.json
@@ -2,11 +2,11 @@
"formatVersion": 1,
"database": {
"version": 29,
- "identityHash": "5b5c55a1277c63e14416316f9198ed43",
+ "identityHash": "edf35c5851b4cb55bb5dbbf278199450",
"entities": [
{
"tableName": "Profile",
- "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `host` TEXT NOT NULL, `remotePort` INTEGER NOT NULL, `password` TEXT NOT NULL, `method` TEXT NOT NULL, `route` TEXT NOT NULL, `remoteDns` TEXT NOT NULL, `proxyApps` INTEGER NOT NULL, `bypass` INTEGER NOT NULL, `udpdns` INTEGER NOT NULL, `ipv6` INTEGER NOT NULL, `metered` INTEGER NOT NULL, `individual` TEXT NOT NULL, `plugin` TEXT, `udpFallback` INTEGER, `subscription` INTEGER NOT NULL, `tx` INTEGER NOT NULL, `rx` INTEGER NOT NULL, `userOrder` INTEGER NOT NULL)",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `host` TEXT NOT NULL, `remotePort` INTEGER NOT NULL, `password` TEXT NOT NULL, `method` TEXT NOT NULL, `route` TEXT NOT NULL, `remoteDns` TEXT NOT NULL, `proxyApps` INTEGER NOT NULL, `bypass` INTEGER NOT NULL, `udpdns` INTEGER NOT NULL, `ipv6` INTEGER NOT NULL, `metered` INTEGER NOT NULL, `individual` TEXT NOT NULL, `plugin` TEXT, `udpFallback` INTEGER, `optimizeBuffers` INTEGER NOT NULL, `subscription` INTEGER NOT NULL, `tx` INTEGER NOT NULL, `rx` INTEGER NOT NULL, `userOrder` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
@@ -104,6 +104,12 @@
"affinity": "INTEGER",
"notNull": false
},
+ {
+ "fieldPath": "optimizeBuffers",
+ "columnName": "optimizeBuffers",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
{
"fieldPath": "subscription",
"columnName": "subscription",
@@ -130,10 +136,10 @@
}
],
"primaryKey": {
+ "autoGenerate": true,
"columnNames": [
"id"
- ],
- "autoGenerate": true
+ ]
},
"indices": [],
"foreignKeys": []
@@ -162,10 +168,10 @@
}
],
"primaryKey": {
+ "autoGenerate": false,
"columnNames": [
"key"
- ],
- "autoGenerate": false
+ ]
},
"indices": [],
"foreignKeys": []
@@ -174,7 +180,7 @@
"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, '5b5c55a1277c63e14416316f9198ed43')"
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'edf35c5851b4cb55bb5dbbf278199450')"
]
}
}
\ No newline at end of file
diff --git a/core/src/main/java/com/github/shadowsocks/bg/ProxyInstance.kt b/core/src/main/java/com/github/shadowsocks/bg/ProxyInstance.kt
index 05f834707b..41dcdaaea3 100644
--- a/core/src/main/java/com/github/shadowsocks/bg/ProxyInstance.kt
+++ b/core/src/main/java/com/github/shadowsocks/bg/ProxyInstance.kt
@@ -117,10 +117,22 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro
val cmd = arrayListOf(
File((service as Context).applicationInfo.nativeLibraryDir, Executable.SS_LOCAL).absolutePath,
"--stat-path", stat.absolutePath,
- "-c", configFile.absolutePath,
+ "-c", configFile.absolutePath
)
- if (service.isVpnService) cmd += "--vpn"
+ if (profile.optimizeBuffers) {
+ cmd += "--inbound-send-buffer-size=16384"
+ cmd += "--inbound-recv-buffer-size=16384"
+
+ if (profile.plugin?.isNotBlank() == true) {
+ cmd += "--outbound-send-buffer-size=16384"
+ cmd += "--outbound-recv-buffer-size=16384"
+ }
+ }
+
+ if (service.isVpnService) {
+ cmd += "--vpn"
+ }
if (route != Acl.ALL) {
cmd += "--acl"
diff --git a/core/src/main/java/com/github/shadowsocks/database/Profile.kt b/core/src/main/java/com/github/shadowsocks/database/Profile.kt
index bae60e1066..d1f95a1836 100644
--- a/core/src/main/java/com/github/shadowsocks/database/Profile.kt
+++ b/core/src/main/java/com/github/shadowsocks/database/Profile.kt
@@ -70,6 +70,7 @@ data class Profile(
var individual: String = "",
var plugin: String? = null,
var udpFallback: Long? = null,
+ var optimizeBuffers: Boolean = true,
// managed fields
var subscription: SubscriptionStatus = SubscriptionStatus.UserConfigured,
@@ -355,6 +356,7 @@ data class Profile(
DataStore.individual = individual
DataStore.plugin = plugin ?: ""
DataStore.udpFallback = udpFallback
+ DataStore.optimizeBuffers = optimizeBuffers
DataStore.privateStore.remove(Key.dirty)
}
@@ -378,5 +380,6 @@ data class Profile(
individual = DataStore.individual
plugin = DataStore.plugin
udpFallback = DataStore.udpFallback
+ optimizeBuffers = DataStore.optimizeBuffers
}
}
diff --git a/core/src/main/java/com/github/shadowsocks/preference/DataStore.kt b/core/src/main/java/com/github/shadowsocks/preference/DataStore.kt
index a523681f74..c6fd940c5c 100644
--- a/core/src/main/java/com/github/shadowsocks/preference/DataStore.kt
+++ b/core/src/main/java/com/github/shadowsocks/preference/DataStore.kt
@@ -104,6 +104,9 @@ object DataStore : OnPreferenceDataStoreChangeListener {
var udpFallback: Long?
get() = privateStore.getLong(Key.udpFallback)
set(value) = privateStore.putLong(Key.udpFallback, value)
+ var optimizeBuffers: Boolean
+ get() = privateStore.getBoolean(Key.optimizeBuffers) ?: true
+ set(value) = privateStore.putBoolean(Key.optimizeBuffers, value)
var dirty: Boolean
get() = privateStore.getBoolean(Key.dirty) ?: false
set(value) = privateStore.putBoolean(Key.dirty, value)
diff --git a/core/src/main/java/com/github/shadowsocks/utils/Constants.kt b/core/src/main/java/com/github/shadowsocks/utils/Constants.kt
index 52b6de20ec..b48f54bbd8 100644
--- a/core/src/main/java/com/github/shadowsocks/utils/Constants.kt
+++ b/core/src/main/java/com/github/shadowsocks/utils/Constants.kt
@@ -61,6 +61,7 @@ object Key {
const val plugin = "plugin"
const val pluginConfigure = "plugin.configure"
const val udpFallback = "udpFallback"
+ const val optimizeBuffers = "optimizeBuffers";
const val dirty = "profileDirty"
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index 04ed7d9d67..8fb5180a50 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -32,6 +32,8 @@
Remote Port
Password
Encrypt Method
+ Optimize TCP Buffers
+ Fix upload timeout (bufferbloat)
IPv6 Route
diff --git a/mobile/src/main/java/com/github/shadowsocks/ProfileConfigFragment.kt b/mobile/src/main/java/com/github/shadowsocks/ProfileConfigFragment.kt
index 8ca2d21c4d..f238097ec1 100644
--- a/mobile/src/main/java/com/github/shadowsocks/ProfileConfigFragment.kt
+++ b/mobile/src/main/java/com/github/shadowsocks/ProfileConfigFragment.kt
@@ -79,6 +79,7 @@ class ProfileConfigFragment : PreferenceFragmentCompat(),
private lateinit var pluginConfiguration: PluginConfiguration
private lateinit var receiver: BroadcastReceiver
private lateinit var udpFallback: Preference
+ private lateinit var optimizeBuffers: SwitchPreference
private fun makeDirt() {
DataStore.dirty = true
@@ -115,6 +116,8 @@ class ProfileConfigFragment : PreferenceFragmentCompat(),
pluginConfiguration = PluginConfiguration(DataStore.plugin)
initPlugins()
udpFallback = findPreference(Key.udpFallback)!!
+ optimizeBuffers = findPreference(Key.optimizeBuffers)!!
+ optimizeBuffers.isChecked = DataStore.optimizeBuffers
DataStore.privateStore.registerChangeListener(this)
val profile = ProfileManager.getProfile(profileId) ?: Profile()
diff --git a/mobile/src/main/res/xml/pref_profile.xml b/mobile/src/main/res/xml/pref_profile.xml
index 5db0dd19a3..64f2891d83 100644
--- a/mobile/src/main/res/xml/pref_profile.xml
+++ b/mobile/src/main/res/xml/pref_profile.xml
@@ -65,8 +65,11 @@
app:key="remoteDns"
app:icon="@drawable/ic_action_dns"
app:title="@string/remote_dns"
- app:useSimpleSummaryProvider="true"/>
-
+ app:useSimpleSummaryProvider="true" />
+