Add Enable New TUN Feature option

When enabled, TUN will use hev-socks5-tunnel; otherwise, it will use badvpn-tun2socks
This commit is contained in:
2dust
2025-08-04 18:32:16 +08:00
parent b60423d1c0
commit 15de18b736
21 changed files with 176 additions and 25 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -62,6 +62,7 @@ object AppConfig {
const val PREF_IS_BOOTED = "pref_is_booted" const val PREF_IS_BOOTED = "pref_is_booted"
const val PREF_CHECK_UPDATE_PRE_RELEASE = "pref_check_update_pre_release" const val PREF_CHECK_UPDATE_PRE_RELEASE = "pref_check_update_pre_release"
const val PREF_GEO_FILES_SOURCES = "pref_geo_files_sources" const val PREF_GEO_FILES_SOURCES = "pref_geo_files_sources"
const val PREF_USE_HEV_TUNNEL = "pref_use_hev_tunnel"
/** Cache keys. */ /** Cache keys. */
const val CACHE_SUBSCRIPTION_ID = "cache_subscription_id" const val CACHE_SUBSCRIPTION_ID = "cache_subscription_id"

View File

@@ -0,0 +1,96 @@
package com.v2ray.ang.service
import android.content.Context
import android.os.ParcelFileDescriptor
import android.util.Log
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.VPN_MTU
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.handler.SettingsManager
import java.io.File
/**
* Manages the tun2socks process that handles VPN traffic
*/
class TProxyService(
private val context: Context,
private val vpnInterface: ParcelFileDescriptor,
private val isRunningProvider: () -> Boolean,
private val restartCallback: () -> Unit
) : Tun2SocksControl {
companion object {
@JvmStatic
@Suppress("FunctionName")
private external fun TProxyStartService(configPath: String, fd: Int)
@JvmStatic
@Suppress("FunctionName")
private external fun TProxyStopService()
@JvmStatic
@Suppress("FunctionName")
private external fun TProxyGetStats(): LongArray?
init {
System.loadLibrary("hev-socks5-tunnel")
}
}
/**
* Starts the tun2socks process with the appropriate parameters.
*/
override fun startTun2Socks() {
Log.i(AppConfig.TAG, "Starting HevSocks5Tunnel via JNI")
val configContent = buildConfig()
val configFile = File(context.filesDir, "hev-socks5-tunnel.yaml").apply {
writeText(configContent)
}
Log.i(AppConfig.TAG, "Config file created: ${configFile.absolutePath}")
Log.d(AppConfig.TAG, "Config content:\n$configContent")
try {
Log.i(AppConfig.TAG, "TProxyStartService...")
TProxyStartService(configFile.absolutePath, vpnInterface.fd)
} catch (e: Exception) {
Log.e(AppConfig.TAG, "HevSocks5Tunnel exception: ${e.message}")
}
}
private fun buildConfig(): String {
val socksPort = SettingsManager.getSocksPort()
val vpnConfig = SettingsManager.getCurrentVpnInterfaceAddressConfig()
return buildString {
appendLine("tunnel:")
appendLine(" mtu: $VPN_MTU")
appendLine(" ipv4: ${vpnConfig.ipv4Client}")
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6) == true) {
appendLine(" ipv6: '${vpnConfig.ipv6Client}'")
}
appendLine("socks5:")
appendLine(" port: ${socksPort}")
appendLine(" address: ${AppConfig.LOOPBACK}")
appendLine(" udp: 'udp'")
MmkvManager.decodeSettingsString(AppConfig.PREF_LOGLEVEL)?.let { logPref ->
if (logPref != "none") {
val logLevel = if (logPref == "warning") "warn" else logPref
appendLine("misc:")
appendLine(" log-level: $logLevel")
}
}
}
}
/**
* Stops the tun2socks process
*/
override fun stopTun2Socks() {
try {
Log.i(AppConfig.TAG, "TProxyStopService...")
TProxyStopService()
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to stop hev-socks5-tunnel", e)
}
}
}

View File

@@ -0,0 +1,19 @@
package com.v2ray.ang.service
/**
* Interface that defines the control operations for tun2socks implementations.
*
* This interface is implemented by different tunnel solutions like:
*/
interface Tun2SocksControl {
/**
* Starts the tun2socks process with the appropriate parameters.
* This initializes the VPN tunnel and connects it to the SOCKS proxy.
*/
fun startTun2Socks()
/**
* Stops the tun2socks process and cleans up resources.
*/
fun stopTun2Socks()
}

View File

@@ -23,7 +23,7 @@ class Tun2SocksService(
private val vpnInterface: ParcelFileDescriptor, private val vpnInterface: ParcelFileDescriptor,
private val isRunningProvider: () -> Boolean, private val isRunningProvider: () -> Boolean,
private val restartCallback: () -> Unit private val restartCallback: () -> Unit
) { ) : Tun2SocksControl {
companion object { companion object {
private const val TUN2SOCKS = "libtun2socks.so" private const val TUN2SOCKS = "libtun2socks.so"
} }
@@ -33,7 +33,7 @@ class Tun2SocksService(
/** /**
* Starts the tun2socks process with the appropriate parameters. * Starts the tun2socks process with the appropriate parameters.
*/ */
fun startTun2Socks() { override fun startTun2Socks() {
Log.i(AppConfig.TAG, "Start run $TUN2SOCKS") Log.i(AppConfig.TAG, "Start run $TUN2SOCKS")
val socksPort = SettingsManager.getSocksPort() val socksPort = SettingsManager.getSocksPort()
val vpnConfig = SettingsManager.getCurrentVpnInterfaceAddressConfig() val vpnConfig = SettingsManager.getCurrentVpnInterfaceAddressConfig()
@@ -116,7 +116,7 @@ class Tun2SocksService(
/** /**
* Stops the tun2socks process * Stops the tun2socks process
*/ */
fun stopTun2Socks() { override fun stopTun2Socks() {
try { try {
Log.i(AppConfig.TAG, "$TUN2SOCKS destroy") Log.i(AppConfig.TAG, "$TUN2SOCKS destroy")
if (::process.isInitialized) { if (::process.isInitialized) {

View File

@@ -30,7 +30,7 @@ import java.lang.ref.SoftReference
class V2RayVpnService : VpnService(), ServiceControl { class V2RayVpnService : VpnService(), ServiceControl {
private lateinit var mInterface: ParcelFileDescriptor private lateinit var mInterface: ParcelFileDescriptor
private var isRunning = false private var isRunning = false
private var tun2SocksService: Tun2SocksService? = null private var tun2SocksService: Tun2SocksControl? = null
/**destroy /**destroy
* Unfortunately registerDefaultNetworkCallback is going to return our VPN interface: https://android.googlesource.com/platform/frameworks/base/+/dda156ab0c5d66ad82bdcf76cda07cbc0a9c8a2e * Unfortunately registerDefaultNetworkCallback is going to return our VPN interface: https://android.googlesource.com/platform/frameworks/base/+/dda156ab0c5d66ad82bdcf76cda07cbc0a9c8a2e
@@ -145,20 +145,20 @@ class V2RayVpnService : VpnService(), ServiceControl {
*/ */
private fun configureVpnService(): Boolean { private fun configureVpnService(): Boolean {
val builder = Builder() val builder = Builder()
// Configure network settings (addresses, routing and DNS) // Configure network settings (addresses, routing and DNS)
configureNetworkSettings(builder) configureNetworkSettings(builder)
// Configure app-specific settings (session name and per-app proxy) // Configure app-specific settings (session name and per-app proxy)
configurePerAppProxy(builder) configurePerAppProxy(builder)
// Close the old interface since the parameters have been changed // Close the old interface since the parameters have been changed
try { try {
mInterface.close() mInterface.close()
} catch (ignored: Exception) { } catch (ignored: Exception) {
// ignored // ignored
} }
// Configure platform-specific features // Configure platform-specific features
configurePlatformFeatures(builder) configurePlatformFeatures(builder)
@@ -173,21 +173,21 @@ class V2RayVpnService : VpnService(), ServiceControl {
} }
return false return false
} }
/** /**
* Configures the basic network settings for the VPN. * Configures the basic network settings for the VPN.
* This includes IP addresses, routing rules, and DNS servers. * This includes IP addresses, routing rules, and DNS servers.
* *
* @param builder The VPN Builder to configure * @param builder The VPN Builder to configure
*/ */
private fun configureNetworkSettings(builder: Builder) { private fun configureNetworkSettings(builder: Builder) {
val vpnConfig = SettingsManager.getCurrentVpnInterfaceAddressConfig() val vpnConfig = SettingsManager.getCurrentVpnInterfaceAddressConfig()
val bypassLan = SettingsManager.routingRulesetsBypassLan() val bypassLan = SettingsManager.routingRulesetsBypassLan()
// Configure IPv4 settings // Configure IPv4 settings
builder.setMtu(VPN_MTU) builder.setMtu(VPN_MTU)
builder.addAddress(vpnConfig.ipv4Client, 30) builder.addAddress(vpnConfig.ipv4Client, 30)
// Configure routing rules // Configure routing rules
if (bypassLan) { if (bypassLan) {
AppConfig.ROUTED_IP_LIST.forEach { AppConfig.ROUTED_IP_LIST.forEach {
@@ -197,7 +197,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
} else { } else {
builder.addRoute("0.0.0.0", 0) builder.addRoute("0.0.0.0", 0)
} }
// Configure IPv6 if enabled // Configure IPv6 if enabled
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6) == true) { if (MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6) == true) {
builder.addAddress(vpnConfig.ipv6Client, 126) builder.addAddress(vpnConfig.ipv6Client, 126)
@@ -208,7 +208,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
builder.addRoute("::", 0) builder.addRoute("::", 0)
} }
} }
// Configure DNS servers // Configure DNS servers
//if (MmkvManager.decodeSettingsBool(AppConfig.PREF_LOCAL_DNS_ENABLED) == true) { //if (MmkvManager.decodeSettingsBool(AppConfig.PREF_LOCAL_DNS_ENABLED) == true) {
// builder.addDnsServer(PRIVATE_VLAN4_ROUTER) // builder.addDnsServer(PRIVATE_VLAN4_ROUTER)
@@ -218,13 +218,13 @@ class V2RayVpnService : VpnService(), ServiceControl {
builder.addDnsServer(it) builder.addDnsServer(it)
} }
} }
builder.setSession(V2RayServiceManager.getRunningServerName()) builder.setSession(V2RayServiceManager.getRunningServerName())
} }
/** /**
* Configures platform-specific VPN features for different Android versions. * Configures platform-specific VPN features for different Android versions.
* *
* @param builder The VPN Builder to configure * @param builder The VPN Builder to configure
*/ */
private fun configurePlatformFeatures(builder: Builder) { private fun configurePlatformFeatures(builder: Builder) {
@@ -236,7 +236,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
Log.e(AppConfig.TAG, "Failed to request default network", e) Log.e(AppConfig.TAG, "Failed to request default network", e)
} }
} }
// Android Q (API 29) and above: Configure metering and HTTP proxy // Android Q (API 29) and above: Configure metering and HTTP proxy
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
builder.setMetered(false) builder.setMetered(false)
@@ -296,14 +296,23 @@ class V2RayVpnService : VpnService(), ServiceControl {
* Starts the tun2socks process with the appropriate parameters. * Starts the tun2socks process with the appropriate parameters.
*/ */
private fun runTun2socks() { private fun runTun2socks() {
tun2SocksService = Tun2SocksService( if (MmkvManager.decodeSettingsBool(AppConfig.PREF_USE_HEV_TUNNEL) == true) {
context = applicationContext, tun2SocksService = TProxyService(
vpnInterface = mInterface, context = applicationContext,
isRunningProvider = { isRunning }, vpnInterface = mInterface,
restartCallback = { runTun2socks() } isRunningProvider = { isRunning },
).also { restartCallback = { runTun2socks() }
it.startTun2Socks() )
} else {
tun2SocksService = Tun2SocksService(
context = applicationContext,
vpnInterface = mInterface,
isRunningProvider = { isRunning },
restartCallback = { runTun2socks() }
)
} }
tun2SocksService?.startTun2Socks()
} }
/** /**

View File

@@ -242,7 +242,8 @@ class SettingsActivity : BaseActivity() {
AppConfig.PREF_DOUBLE_COLUMN_DISPLAY, AppConfig.PREF_DOUBLE_COLUMN_DISPLAY,
AppConfig.PREF_PREFER_IPV6, AppConfig.PREF_PREFER_IPV6,
AppConfig.PREF_PROXY_SHARING, AppConfig.PREF_PROXY_SHARING,
AppConfig.PREF_ALLOW_INSECURE AppConfig.PREF_ALLOW_INSECURE,
AppConfig.PREF_USE_HEV_TUNNEL
).forEach { key -> ).forEach { key ->
findPreference<CheckBoxPreference>(key)?.isChecked = findPreference<CheckBoxPreference>(key)?.isChecked =
MmkvManager.decodeSettingsBool(key, false) MmkvManager.decodeSettingsBool(key, false)

View File

@@ -80,6 +80,7 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
AppConfig.SUBSCRIPTION_AUTO_UPDATE, AppConfig.SUBSCRIPTION_AUTO_UPDATE,
AppConfig.PREF_FRAGMENT_ENABLED, AppConfig.PREF_FRAGMENT_ENABLED,
AppConfig.PREF_MUX_ENABLED, AppConfig.PREF_MUX_ENABLED,
AppConfig.PREF_USE_HEV_TUNNEL
-> { -> {
MmkvManager.encodeSettings(key, sharedPreferences.getBoolean(key, false)) MmkvManager.encodeSettings(key, sharedPreferences.getBoolean(key, false))
} }

View File

@@ -247,6 +247,8 @@
<string name="title_language">اللغة</string> <string name="title_language">اللغة</string>
<string name="title_ui_settings">إعدادات واجهة المستخدم</string> <string name="title_ui_settings">إعدادات واجهة المستخدم</string>
<string name="title_pref_ui_mode_night">إعدادات وضع واجهة المستخدم ليلاً</string> <string name="title_pref_ui_mode_night">إعدادات وضع واجهة المستخدم ليلاً</string>
<string name="title_pref_use_hev_tunnel">Enable New TUN Feature</string>
<string name="summary_pref_use_hev_tunnel">When enabled, TUN will use hev-socks5-tunnel; otherwise, it will use badvpn-tun2socks.</string>
<string name="title_logcat">Logcat</string> <string name="title_logcat">Logcat</string>
<string name="logcat_copy">نسخ</string> <string name="logcat_copy">نسخ</string>

View File

@@ -248,6 +248,8 @@
<string name="title_language">ভাষা</string> <string name="title_language">ভাষা</string>
<string name="title_ui_settings">ইউআই সেটিংস</string> <string name="title_ui_settings">ইউআই সেটিংস</string>
<string name="title_pref_ui_mode_night">ইউআই মোড সেটিংস</string> <string name="title_pref_ui_mode_night">ইউআই মোড সেটিংস</string>
<string name="title_pref_use_hev_tunnel">Enable New TUN Feature</string>
<string name="summary_pref_use_hev_tunnel">When enabled, TUN will use hev-socks5-tunnel; otherwise, it will use badvpn-tun2socks.</string>
<string name="title_logcat">লগক্যাট</string> <string name="title_logcat">লগক্যাট</string>
<string name="logcat_copy">কপি করুন</string> <string name="logcat_copy">কপি করুন</string>

View File

@@ -248,6 +248,8 @@
<string name="title_language">زووݩ</string> <string name="title_language">زووݩ</string>
<string name="title_ui_settings">سامووا رابت منتوری</string> <string name="title_ui_settings">سامووا رابت منتوری</string>
<string name="title_pref_ui_mode_night">سامووا هالت رابت منتوری</string> <string name="title_pref_ui_mode_night">سامووا هالت رابت منتوری</string>
<string name="title_pref_use_hev_tunnel">Enable New TUN Feature</string>
<string name="summary_pref_use_hev_tunnel">When enabled, TUN will use hev-socks5-tunnel; otherwise, it will use badvpn-tun2socks.</string>
<string name="title_logcat">داسووا</string> <string name="title_logcat">داسووا</string>
<string name="logcat_copy">لف گیری</string> <string name="logcat_copy">لف گیری</string>

View File

@@ -245,6 +245,8 @@
<string name="title_language">زبان</string> <string name="title_language">زبان</string>
<string name="title_ui_settings">تنظیمات رابط کاربری</string> <string name="title_ui_settings">تنظیمات رابط کاربری</string>
<string name="title_pref_ui_mode_night">تنظیمات حالت رابط کاربری</string> <string name="title_pref_ui_mode_night">تنظیمات حالت رابط کاربری</string>
<string name="title_pref_use_hev_tunnel">Enable New TUN Feature</string>
<string name="summary_pref_use_hev_tunnel">When enabled, TUN will use hev-socks5-tunnel; otherwise, it will use badvpn-tun2socks.</string>
<string name="title_logcat">گزارشات</string> <string name="title_logcat">گزارشات</string>
<string name="logcat_copy">کپی</string> <string name="logcat_copy">کپی</string>

View File

@@ -247,6 +247,8 @@
<string name="title_language">Язык</string> <string name="title_language">Язык</string>
<string name="title_ui_settings">Настройки интерфейса</string> <string name="title_ui_settings">Настройки интерфейса</string>
<string name="title_pref_ui_mode_night">Тема интерфейса</string> <string name="title_pref_ui_mode_night">Тема интерфейса</string>
<string name="title_pref_use_hev_tunnel">Enable New TUN Feature</string>
<string name="summary_pref_use_hev_tunnel">When enabled, TUN will use hev-socks5-tunnel; otherwise, it will use badvpn-tun2socks.</string>
<string name="title_logcat">Журнал</string> <string name="title_logcat">Журнал</string>
<string name="logcat_copy">Копировать</string> <string name="logcat_copy">Копировать</string>

View File

@@ -248,6 +248,8 @@
<string name="title_language">Ngôn ngữ</string> <string name="title_language">Ngôn ngữ</string>
<string name="title_ui_settings">Cài đặt UI</string> <string name="title_ui_settings">Cài đặt UI</string>
<string name="title_pref_ui_mode_night">Cài đặt chế độ UI</string> <string name="title_pref_ui_mode_night">Cài đặt chế độ UI</string>
<string name="title_pref_use_hev_tunnel">Enable New TUN Feature</string>
<string name="summary_pref_use_hev_tunnel">When enabled, TUN will use hev-socks5-tunnel; otherwise, it will use badvpn-tun2socks.</string>
<string name="title_logcat">Logcat</string> <string name="title_logcat">Logcat</string>
<string name="logcat_copy">Sao chép</string> <string name="logcat_copy">Sao chép</string>

View File

@@ -245,6 +245,8 @@
<string name="title_language">语言</string> <string name="title_language">语言</string>
<string name="title_ui_settings">用户界面设置</string> <string name="title_ui_settings">用户界面设置</string>
<string name="title_pref_ui_mode_night">界面颜色设置</string> <string name="title_pref_ui_mode_night">界面颜色设置</string>
<string name="title_pref_use_hev_tunnel">启用新的 TUN 功能</string>
<string name="summary_pref_use_hev_tunnel">选择启用后 TUN 将使用 hev-socks5-tunnel 否则使用 badvpn-tun2socks</string>
<string name="title_logcat">Logcat</string> <string name="title_logcat">Logcat</string>
<string name="logcat_copy">复制</string> <string name="logcat_copy">复制</string>

View File

@@ -246,6 +246,8 @@
<string name="title_language">語言</string> <string name="title_language">語言</string>
<string name="title_ui_settings">介面顏色設定</string> <string name="title_ui_settings">介面顏色設定</string>
<string name="title_pref_ui_mode_night">介面顯示模式</string> <string name="title_pref_ui_mode_night">介面顯示模式</string>
<string name="title_pref_use_hev_tunnel">啟用新 TUN 功能</string>
<string name="summary_pref_use_hev_tunnel">選擇啟用後TUN 將使用 hev-socks5-tunnel否則使用 badvpn-tun2socks。</string>
<string name="title_logcat">Logcat</string> <string name="title_logcat">Logcat</string>
<string name="logcat_copy">複製</string> <string name="logcat_copy">複製</string>

View File

@@ -249,6 +249,8 @@
<string name="title_language">Language</string> <string name="title_language">Language</string>
<string name="title_ui_settings">UI settings</string> <string name="title_ui_settings">UI settings</string>
<string name="title_pref_ui_mode_night">UI mode settings</string> <string name="title_pref_ui_mode_night">UI mode settings</string>
<string name="title_pref_use_hev_tunnel">Enable New TUN Feature</string>
<string name="summary_pref_use_hev_tunnel">When enabled, TUN will use hev-socks5-tunnel; otherwise, it will use badvpn-tun2socks.</string>
<string name="title_logcat">Logcat</string> <string name="title_logcat">Logcat</string>
<string name="logcat_copy">Copy</string> <string name="logcat_copy">Copy</string>

View File

@@ -13,6 +13,7 @@
android:key="pref_route_only_enabled" android:key="pref_route_only_enabled"
android:summary="@string/summary_pref_route_only_enabled" android:summary="@string/summary_pref_route_only_enabled"
android:title="@string/title_pref_route_only_enabled" /> android:title="@string/title_pref_route_only_enabled" />
<CheckBoxPreference <CheckBoxPreference
android:key="pref_is_booted" android:key="pref_is_booted"
android:summary="@string/summary_pref_is_booted" android:summary="@string/summary_pref_is_booted"
@@ -254,6 +255,11 @@
android:key="pref_mode" android:key="pref_mode"
android:summary="%s" android:summary="%s"
android:title="@string/title_mode" /> android:title="@string/title_mode" />
<CheckBoxPreference
android:key="pref_use_hev_tunnel"
android:summary="@string/summary_pref_use_hev_tunnel"
android:title="@string/title_pref_use_hev_tunnel" />
</PreferenceCategory> </PreferenceCategory>