Compare commits

...
11 Commits
Author SHA1 Message Date
2dust 5436d67e47 up 2.2.5 2026-06-20 18:00:33 +08:00
2dust 1c3d247071 Revert "Use decoded server list when removing servers"
This reverts commit dcd060e350.
2026-06-20 17:58:20 +08:00
2dust d3e1e7c6fe Bug fix
https://github.com/2dust/v2rayNG/issues/5798
2026-06-20 16:35:29 +08:00
2dust a65c57ea0d Remove Tcping 2026-06-20 16:07:07 +08:00
2dust 8586c2550f feat: add TCPing pre-check to speed up real ping test 2026-06-20 12:32:26 +08:00
dependabot[bot] fa6c4807f3 Bump actions/checkout from 6 to 7 (#5801)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-20 10:27:41 +08:00
DHR60 6e65f46e3f kcp (#5793) 2026-06-17 20:36:24 +08:00
MrArrowww 98e7b8e1a1 Remove forced fragment packet override for TLS (#5780) 2026-06-14 20:04:28 +08:00
solokot 7dd42528db Update Russian translation (#5781) 2026-06-14 20:03:09 +08:00
Hossein Abaspanah e580b76e9e Update strings.xml (#5779) 2026-06-14 20:02:55 +08:00
Ilya KaznacheevandIlya Kaznacheev c4d02c00ce Use 'restore' icon instead of 'delete' for "Restart Service" notification (#5783)
Co-authored-by: Ilya Kaznacheev <ilya.kaznacheev@axiomjdk.ru>
2026-06-14 20:02:39 +08:00
21 changed files with 75 additions and 137 deletions

No files matched your search

+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
submodules: 'recursive'
fetch-depth: '0'
+1 -1
View File
@@ -11,6 +11,6 @@ jobs:
go:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Validate Fastlane Supply Metadata
uses: ashutoshgngwr/validate-fastlane-supply-metadata@v2.1.0
+2 -2
View File
@@ -12,8 +12,8 @@ android {
applicationId = "com.v2ray.ang"
minSdk = 24
targetSdk = 37
versionCode = 734
versionName = "2.2.4"
versionCode = 735
versionName = "2.2.5"
multiDexEnabled = true
val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';')
@@ -368,37 +368,40 @@ object CoreOutboundBuilder {
profileItem.kcpMtu?.let { kcpSetting.mtu = it }
profileItem.kcpTti?.let { kcpSetting.tti = it }
streamSettings.kcpSettings = kcpSetting
val udpMaskList = mutableListOf<OutboundBean.StreamSettingsBean.FinalMaskBean.MaskBean>()
val udpMaskList =
mutableListOf<OutboundBean.StreamSettingsBean.FinalMaskBean.MaskBean>()
if (!headerType.isNullOrEmpty() && headerType != "none") {
val kcpHeaderType = when {
headerType == "wechat-video" -> "header-wechat"
else -> "header-$headerType"
headerType == "wechat-video" -> "wechat"
else -> headerType
}
udpMaskList.add(
OutboundBean.StreamSettingsBean.FinalMaskBean.MaskBean(
type = kcpHeaderType,
settings = if (headerType == "dns" && !host.isNullOrEmpty()) {
type = "mkcp-legacy",
settings =
OutboundBean.StreamSettingsBean.FinalMaskBean.MaskBean.MaskSettingsBean(
domain = host
header = kcpHeaderType,
value = if (headerType == "dns" && !host.isNullOrEmpty()) {
host
} else {
null
}
)
} else {
null
}
)
)
}
if (seed.isNullOrEmpty()) {
udpMaskList.add(
OutboundBean.StreamSettingsBean.FinalMaskBean.MaskBean(
type = "mkcp-original"
type = "mkcp-legacy"
)
)
} else {
udpMaskList.add(
OutboundBean.StreamSettingsBean.FinalMaskBean.MaskBean(
type = "mkcp-aes128gcm",
type = "mkcp-legacy",
settings = OutboundBean.StreamSettingsBean.FinalMaskBean.MaskBean.MaskSettingsBean(
password = seed
value = seed
)
)
)
@@ -612,10 +615,6 @@ object CoreOutboundBuilder {
&& packets == "tlshello"
) {
packets = "1-3"
} else if (streamSettings.security == AppConfig.TLS
&& packets != "tlshello"
) {
packets = "tlshello"
}
val fragmentMask = OutboundBean.StreamSettingsBean.FinalMaskBean.MaskBean(
@@ -256,7 +256,6 @@ data class V2rayConfig(
var verifyPeerCertByName: String? = null,
var pinnedPeerCertSha256: String? = null,
// REALITY settings
val show: Boolean = false,
var publicKey: String? = null,
var shortId: String? = null,
var spiderX: String? = null,
@@ -296,7 +295,8 @@ data class V2rayConfig(
) {
data class MaskSettingsBean(
val password: String? = null,
val domain: String? = null,
val header: String? = null,
val value: String? = null,
// fragment
val packets: String? = null,
val length: String? = null,
@@ -286,9 +286,13 @@ object MmkvManager {
*/
fun removeAllServer(): Int {
val count = profileFullStorage.allKeys()?.count() ?: 0
mainStorage.clearAll()
profileFullStorage.clearAll()
serverAffStorage.clearAll()
serverRawStorage.clearAll()
decodeSubscriptions().forEach { sub ->
encodeServerList(mutableListOf(), sub.guid)
}
return count
}
@@ -105,7 +105,7 @@ object NotificationManager {
stopV2RayPendingIntent
)
.addAction(
R.drawable.ic_delete_24dp,
R.drawable.ic_restore_24dp,
service.getString(R.string.title_service_restart),
restartV2RayPendingIntent
)
@@ -6,8 +6,6 @@ import com.v2ray.ang.dto.UrlContentRequest
import com.v2ray.ang.util.HttpUtil
import com.v2ray.ang.util.JsonUtil
import com.v2ray.ang.util.LogUtil
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.isActive
import java.io.IOException
import java.net.InetSocketAddress
import java.net.Socket
@@ -15,29 +13,6 @@ import java.net.UnknownHostException
object SpeedtestManager {
private val tcpTestingSockets = ArrayList<Socket?>()
/**
* Measures the TCP connection time to a given URL and port.
*
* @param url The URL to connect to.
* @param port The port to connect to.
* @return The connection time in milliseconds, or -1 if the connection failed.
*/
suspend fun tcping(url: String, port: Int): Long {
var time = -1L
for (k in 0 until 2) {
val one = socketConnectTime(url, port)
if (!currentCoroutineContext().isActive) {
break
}
if (one != -1L && (time == -1L || one < time)) {
time = one
}
}
return time
}
/**
* Measures the time taken to establish a TCP connection to a given URL and port.
*
@@ -45,42 +20,34 @@ object SpeedtestManager {
* @param port The port to connect to.
* @return The connection time in milliseconds, or -1 if the connection failed.
*/
fun socketConnectTime(url: String, port: Int): Long {
fun socketConnectTime(url: String, port: Int, timeoutMs: Int = 1500): Long {
var socket: Socket? = null
val start = System.currentTimeMillis()
try {
val socket = Socket()
synchronized(this) {
tcpTestingSockets.add(socket)
}
val start = System.currentTimeMillis()
socket.connect(InetSocketAddress(url, port), 3000)
val time = System.currentTimeMillis() - start
synchronized(this) {
tcpTestingSockets.remove(socket)
}
socket.close()
return time
socket = Socket()
socket.connect(InetSocketAddress(url, port), timeoutMs)
return System.currentTimeMillis() - start
} catch (e: UnknownHostException) {
LogUtil.e(AppConfig.TAG, "Unknown host: $url", e)
} catch (e: IOException) {
LogUtil.e(AppConfig.TAG, "socketConnectTime IOException: $e")
LogUtil.e(AppConfig.TAG, "socketConnectTime IOException: ${e.message}")
} catch (e: Exception) {
LogUtil.e(AppConfig.TAG, "Failed to establish socket connection to $url:$port", e)
} finally {
socket?.let { s ->
try {
if (!s.isClosed) {
s.close()
}
} catch (closeEx: IOException) {
}
}
}
return -1
}
/**
* Closes all TCP sockets that are currently being tested.
*/
fun closeAllTcpSockets() {
synchronized(this) {
tcpTestingSockets.forEach {
it?.close()
}
tcpTestingSockets.clear()
}
}
fun getRemoteIPInfo(): String? {
val url = MmkvManager.decodeSettingsString(AppConfig.PREF_IP_API_URL)
.takeIf { !it.isNullOrBlank() } ?: AppConfig.IP_API_URL
@@ -4,7 +4,12 @@ import android.content.Context
import com.v2ray.ang.core.CoreConfigManager
import com.v2ray.ang.core.CoreNativeManager
import com.v2ray.ang.dto.RealPingEvent
import com.v2ray.ang.enums.EConfigType
import com.v2ray.ang.extension.isComplexType
import com.v2ray.ang.extension.isNotNullEmpty
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.handler.SettingsManager
import com.v2ray.ang.handler.SpeedtestManager
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
@@ -76,6 +81,21 @@ class RealPingWorkerService(
private fun startRealPing(guid: String): Long {
val retFailure = -1L
val config = MmkvManager.decodeServerConfig(guid) ?: return retFailure
if (!config.configType.isComplexType()
&& config.configType != EConfigType.HYSTERIA2
&& config.server.isNotNullEmpty()
&& config.serverPort?.toIntOrNull() != null
) {
val url = config.server.orEmpty()
val port = config.serverPort.orEmpty().toInt()
val tcpTime = SpeedtestManager.socketConnectTime(url, port, 1000)
if (tcpTime <= -1L) {
return retFailure
}
}
val configResult = CoreConfigManager.getV2rayConfig4Speedtest(context, guid)
if (!configResult.status) {
return retFailure
@@ -333,12 +333,6 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
true
}
R.id.ping_all -> {
toast(getString(R.string.connection_test_testing_count, mainViewModel.serversCache.count()))
mainViewModel.testAllTcping()
true
}
R.id.real_ping_all -> {
toast(getString(R.string.connection_test_testing_count, mainViewModel.serversCache.count()))
mainViewModel.testAllRealPing()
@@ -25,14 +25,10 @@ import com.v2ray.ang.extension.toastSuccess
import com.v2ray.ang.handler.AngConfigManager
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.handler.SettingsManager
import com.v2ray.ang.handler.SpeedtestManager
import com.v2ray.ang.util.LogUtil
import com.v2ray.ang.util.MessageUtil
import com.v2ray.ang.util.Utils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.Collections
@@ -46,7 +42,6 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
val isRunning by lazy { MutableLiveData<Boolean>() }
val updateListAction by lazy { MutableLiveData<Int>() }
val updateTestResultAction by lazy { MutableLiveData<String>() }
private val tcpingTestScope by lazy { CoroutineScope(Dispatchers.IO) }
/**
* Refer to the official documentation for [registerReceiver](https://developer.android.com/reference/androidx/core/content/ContextCompat#registerReceiver(android.content.Context,android.content.BroadcastReceiver,android.content.IntentFilter,int):
@@ -64,8 +59,6 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
*/
override fun onCleared() {
getApplication<AngApplication>().unregisterReceiver(mMsgReceiver)
tcpingTestScope.coroutineContext[Job]?.cancelChildren()
SpeedtestManager.closeAllTcpSockets()
LogUtil.i(AppConfig.TAG, "Main ViewModel is cleared")
super.onCleared()
}
@@ -178,32 +171,6 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
return ret
}
/**
* Tests the TCP ping for all servers.
*/
fun testAllTcping() {
tcpingTestScope.coroutineContext[Job]?.cancelChildren()
SpeedtestManager.closeAllTcpSockets()
MmkvManager.clearAllTestDelayResults(serversCache.map { it.guid }.toList())
val serversCopy = serversCache.toList()
for (item in serversCopy) {
item.profile.let { outbound ->
val serverAddress = outbound.server
val serverPort = outbound.serverPort
if (serverAddress != null && serverPort != null) {
tcpingTestScope.launch {
val testResult = SpeedtestManager.tcping(serverAddress, serverPort.toInt())
launch(Dispatchers.Main) {
MmkvManager.encodeServerTestDelayMillis(item.guid, testResult)
updateListAction.value = getPosition(item.guid)
}
}
}
}
}
}
/**
* Tests the real ping for all servers.
*/
@@ -341,11 +308,11 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
if (subscriptionId.isEmpty() && keywordFilter.isEmpty()) {
MmkvManager.removeAllServer()
} else {
val serverList = MmkvManager.decodeServerList(subscriptionId)
for (guid in serverList) {
MmkvManager.removeServer(guid)
val serversCopy = serversCache.toList()
for (item in serversCopy) {
MmkvManager.removeServer(item.guid)
}
serverList.count()
serversCache.toList().count()
}
return count
}
@@ -90,10 +90,6 @@
android:icon="@drawable/ic_share_24dp"
android:title="@string/title_export_all"
app:showAsAction="never" />
<item
android:id="@+id/ping_all"
android:title="@string/title_ping_all_server"
app:showAsAction="never" />
<item
android:id="@+id/real_ping_all"
android:title="@string/title_real_ping_all_server"
@@ -306,7 +306,6 @@
<string name="sub_setting_pre_profile_tip">The configuration remarks exists and is unique</string>
<string name="toast_invalid_update_interval">فاصل التحديث غير صالح. الحد الأدنى هو 15 دقيقة.</string>
<string name="title_sub_update">تحديث الاشتراك (أول خطوة)</string>
<string name="title_ping_all_server">Tcping لجميع الإعدادات</string>
<string name="title_real_ping_all_server"> اختبر جميع الإعدادات (3)</string>
<string name="title_user_asset_setting">Asset files</string>
<string name="title_sort_by_test_results">الفرز حسب نتائج الاختبار (5)</string>
@@ -306,7 +306,6 @@
<string name="sub_setting_pre_profile_tip">The configuration remarks exists and is unique</string>
<string name="toast_invalid_update_interval">আপডেটের বিরতি সঠিক নয়। সর্বনিম্ন ১৫ মিনিট।</string>
<string name="title_sub_update">সাবস্ক্রিপশন আপডেট</string>
<string name="title_ping_all_server">সব কনফিগারেশন TCPing</string>
<string name="title_real_ping_all_server">সব কনফিগারেশন প্রকৃত বিলম্ব</string>
<string name="title_user_asset_setting">Asset files</string>
<string name="title_sort_by_test_results">টেস্ট ফলাফল দ্বারা সাজানো</string>
@@ -119,9 +119,9 @@
<string name="server_lab_ech_config_list">نومگه کانفیگ Ech</string>
<string name="server_lab_verify_peer_cert_by_name">تئیڌ گوواهی هومتا و ری نوم</string>
<string name="server_lab_pinned_ca256">جا کلک گوواهی (SHA-256)</string>
<string name="pinned_ca256_action_fetch">Fetch and fill certificate fingerprint</string>
<string name="toast_fetch_cert_sha256_success">Certificate fingerprint fetched successfully</string>
<string name="toast_fetch_cert_sha256_failed">Failed to fetch certificate fingerprint</string>
<string name="pinned_ca256_action_fetch">گرؽڌن وو پور کردن جا کلک گوواهی</string>
<string name="toast_fetch_cert_sha256_success">جا کلک گوواهی وا مووفقیت گرؽڌه وابی</string>
<string name="toast_fetch_cert_sha256_failed">گرؽڌن جا کلک گوواهی مووفق نبی</string>
<!-- UserAssetActivity -->
<string name="toast_asset_copy_failed">لف گیری فایل ٱنجوم نوابی، ز ی برنومه دؽوۉداری فایل هیاری بگرین</string>
@@ -306,7 +306,6 @@
<string name="sub_setting_pre_profile_tip">موتمعن بۊ ک نوم موستعار هڌس وو جۊرس نی</string>
<string name="toast_invalid_update_interval">فاسله ورۊ کردن نا موئتبر هڌ. هدقل مقدار 15 دؽقه هڌ.</string>
<string name="title_sub_update">ورۊ کردن اشتراک بونکۊ سکویی</string>
<string name="title_ping_all_server">Tcping کانفیگا بونکۊ سکویی</string>
<string name="title_real_ping_all_server">تئخیر واقعی کانفیگا بونکۊ سکویی</string>
<string name="title_user_asset_setting">فایلا بونچک جوقرافیایی</string>
<string name="title_sort_by_test_results">ترتیب و ری نتیجه یل آزمایش</string>
@@ -303,7 +303,6 @@
<string name="sub_setting_pre_profile_tip">لطفاً مطمئن شوید که نام مستعار وجود دارد و منحصر به فرد است</string>
<string name="toast_invalid_update_interval">بازه بروزرسانی نامعتبر است. حداقل مقدار 15 دقیقه است.</string>
<string name="title_sub_update">به‌روزرسانی گروه فعلی اشتراک</string>
<string name="title_ping_all_server">TCPING کانفیگ های گروه فعلی</string>
<string name="title_real_ping_all_server">تاخیر واقعی کانفیگ های گروه فعلی</string>
<string name="title_user_asset_setting">فایل های منبع جغرافیایی</string>
<string name="title_sort_by_test_results">مرتب‌ سازی بر اساس نتایج آزمایش</string>
@@ -122,9 +122,9 @@
<string name="server_lab_browser_dialer">Использовать переадресацию браузера</string>
<string name="server_lab_browser_dialer_tip">Поддерживаются только исходящие соединения XHTTP (packet-up) и WS; настройки, связанные с TLS, могут быть проигнорированы или конфликтовать</string>
<string name="toast_allow_insecure_deprecated">ВНИМАНИЕ: пропуск проверки сертификата («Разрешать небезопасные соединения») будет отключён в августе 2026 года. Пожалуйста, используйте отпечатки сертификатов как можно скорее. Функция будет недоступна после истечения срока действия.</string>
<string name="pinned_ca256_action_fetch">Fetch and fill certificate fingerprint</string>
<string name="toast_fetch_cert_sha256_success">Certificate fingerprint fetched successfully</string>
<string name="toast_fetch_cert_sha256_failed">Failed to fetch certificate fingerprint</string>
<string name="pinned_ca256_action_fetch">Получить и заполнить отпечаток сертификата</string>
<string name="toast_fetch_cert_sha256_success">Отпечаток сертификата успешно получен</string>
<string name="toast_fetch_cert_sha256_failed">Отпечаток сертификата не получен</string>
<!-- UserAssetActivity -->
<string name="toast_asset_copy_failed">Невозможно скопировать файл, используйте файловый менеджер</string>
@@ -312,7 +312,6 @@
<string name="sub_setting_pre_profile_tip">Профиль должен быть уникальным</string>
<string name="toast_invalid_update_interval">Неправильный интервал обновления. Минимум 15 минут.</string>
<string name="title_sub_update">Обновить подписку</string>
<string name="title_ping_all_server">Проверить профили</string>
<string name="title_real_ping_all_server">Проверить задержку профилей</string>
<string name="title_user_asset_setting">Файлы ресурсов</string>
<string name="title_sort_by_test_results">Сортировать по результатам теста</string>
@@ -306,7 +306,6 @@
<string name="sub_setting_pre_profile_tip">The configuration remarks exists and is unique</string>
<string name="toast_invalid_update_interval">Khoảng thời gian cập nhật không hợp lệ. Giá trị tối thiểu là 15 phút.</string>
<string name="title_sub_update">Cập nhật các gói đăng ký</string>
<string name="title_ping_all_server">Ping tất cả máy chủ</string>
<string name="title_real_ping_all_server">Kiểm tra HTTP tất cả máy chủ</string>
<string name="title_user_asset_setting">Asset files</string>
<string name="title_sort_by_test_results">Sắp xếp lại theo lần kiểm tra cuối cùng</string>
@@ -307,7 +307,6 @@
<string name="sub_setting_pre_profile_tip">请确保配置别名存在并唯一</string>
<string name="toast_invalid_update_interval">更新间隔无效,最小值为 15 分钟。</string>
<string name="title_sub_update">更新订阅</string>
<string name="title_ping_all_server">测试配置 Tcping</string>
<string name="title_real_ping_all_server">测试配置真连接</string>
<string name="title_user_asset_setting">资源文件</string>
<string name="title_sort_by_test_results">按测试结果排序</string>
@@ -306,7 +306,6 @@
<string name="sub_setting_pre_profile_tip">请确保設定檔别名存在并唯一</string>
<string name="toast_invalid_update_interval">更新間隔無效,最小值為 15 分鐘。</string>
<string name="title_sub_update">更新訂閱</string>
<string name="title_ping_all_server">偵測設定 Tcping</string>
<string name="title_real_ping_all_server">偵測設定真延遲</string>
<string name="title_user_asset_setting">資源檔案</string>
<string name="title_sort_by_test_results">依偵測結果排序</string>
@@ -314,7 +314,6 @@
<string name="sub_setting_pre_profile_tip">The config remarks exist and are unique</string>
<string name="toast_invalid_update_interval">Invalid update interval. Minimum is 15 minutes.</string>
<string name="title_sub_update">Update subscription</string>
<string name="title_ping_all_server">Tcping config</string>
<string name="title_real_ping_all_server">Real delay config</string>
<string name="title_user_asset_setting">Asset files</string>
<string name="title_sort_by_test_results">Sorting by test results</string>