From ba25e055360ee895fdeba41051e4ecb2eb87e4f0 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 15 Apr 2026 15:46:06 +0800 Subject: [PATCH] Add process-based routing support Introduce process field for routing rules and wire it through UI, config parsing, and runtime resolution. Adds a PackageUidResolver utility to map package names to UIDs (with caching) and converts user-entered package names into UIDs when building v2ray routing rules. Updates V2rayConfig types, RoutingEditActivity layout/bindings/strings to accept process input, and adjusts getRouting to receive Context. Also adds extra logging and a defensive check in V2RayServiceManager when resolving connection owner UID. --- .../java/com/v2ray/ang/dto/RulesetItem.kt | 1 + .../java/com/v2ray/ang/dto/V2rayConfig.kt | 5 ++- .../v2ray/ang/handler/V2RayServiceManager.kt | 12 ++++- .../v2ray/ang/handler/V2rayConfigManager.kt | 19 +++++--- .../com/v2ray/ang/ui/RoutingEditActivity.kt | 2 + .../ang/ui/RoutingSettingRecyclerAdapter.kt | 2 +- .../com/v2ray/ang/util/PackageUidResolver.kt | 45 +++++++++++++++++++ .../main/res/layout/activity_routing_edit.xml | 20 +++++++++ .../app/src/main/res/values-ar/strings.xml | 2 +- .../app/src/main/res/values-bn/strings.xml | 2 +- .../src/main/res/values-bqi-rIR/strings.xml | 2 +- .../app/src/main/res/values-fa/strings.xml | 2 +- .../app/src/main/res/values-ru/strings.xml | 2 +- .../app/src/main/res/values-vi/strings.xml | 2 +- .../src/main/res/values-zh-rCN/strings.xml | 2 +- .../src/main/res/values-zh-rTW/strings.xml | 2 +- V2rayNG/app/src/main/res/values/strings.xml | 3 +- 17 files changed, 107 insertions(+), 18 deletions(-) create mode 100644 V2rayNG/app/src/main/java/com/v2ray/ang/util/PackageUidResolver.kt diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/RulesetItem.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/RulesetItem.kt index 9005f559..c02db29b 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/RulesetItem.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/RulesetItem.kt @@ -4,6 +4,7 @@ data class RulesetItem( var remarks: String? = "", var ip: List? = null, var domain: List? = null, + var process: List? = null, var outboundTag: String = "", var port: String? = null, var network: String? = null, diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt index 586985ec..55631e95 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt @@ -422,8 +422,9 @@ data class V2rayConfig( data class RulesBean( var type: String = "field", - var ip: ArrayList? = null, - var domain: ArrayList? = null, + var ip: List? = null, + var domain: List? = null, + var process: List? = null, var outboundTag: String? = null, var balancerTag: String? = null, var port: String? = null, diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2RayServiceManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2RayServiceManager.kt index bdadfc80..a6d2b762 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2RayServiceManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2RayServiceManager.kt @@ -19,6 +19,7 @@ import com.v2ray.ang.extension.toast import com.v2ray.ang.service.V2RayProxyOnlyService import com.v2ray.ang.service.V2RayVpnService import com.v2ray.ang.util.MessageUtil +import com.v2ray.ang.util.PackageUidResolver import com.v2ray.ang.util.Utils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -385,12 +386,21 @@ object V2RayServiceManager { else -> return -1L } + if (destIP.isBlank() || destPort == 0L) { + Log.d(AppConfig.TAG, "ProcessFinder: Find $network connection from $srcIP:$srcPort to :$destPort, (no dest)") + return -1L + } + return try { - cm.getConnectionOwnerUid( + val uid = cm.getConnectionOwnerUid( proto, InetSocketAddress(srcIP, srcPort.toInt()), InetSocketAddress(destIP, destPort.toInt()) ).toLong() + Log.d(AppConfig.TAG, "ProcessFinder: Find $network connection from $srcIP:$srcPort to $destIP:$destPort, uid=$uid") + //Log.d(AppConfig.TAG, "ProcessFinder: Find $network connection from $srcIP:$srcPort to $destIP:$destPort, uid=$uid,${PackageUidResolver.uidToPackageName(uid.toString())}") + + uid } catch (e: Exception) { -1L } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt index 4822eb4e..c3f03ec0 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt @@ -27,6 +27,7 @@ import com.v2ray.ang.fmt.VmessFmt import com.v2ray.ang.fmt.WireguardFmt import com.v2ray.ang.util.HttpUtil import com.v2ray.ang.util.JsonUtil +import com.v2ray.ang.util.PackageUidResolver import com.v2ray.ang.util.Utils object V2rayConfigManager { @@ -200,7 +201,7 @@ object V2rayConfigManager { getOutbounds(v2rayConfig, config) ?: return result getMoreOutbounds(v2rayConfig, config.subscriptionId) - getRouting(v2rayConfig) + getRouting(context, v2rayConfig) getFakeDns(v2rayConfig) @@ -257,7 +258,7 @@ object V2rayConfigManager { outboundsList.addAll(v2rayConfig.outbounds) v2rayConfig.outbounds = ArrayList(outboundsList) - getRouting(v2rayConfig) + getRouting(context, v2rayConfig) getFakeDns(v2rayConfig) @@ -452,7 +453,7 @@ object V2rayConfigManager { * @param v2rayConfig The V2ray configuration object to be modified * @return true if routing configuration was successful, false otherwise */ - private fun getRouting(v2rayConfig: V2rayConfig): Boolean { + private fun getRouting(context: Context, v2rayConfig: V2rayConfig): Boolean { try { v2rayConfig.routing.domainStrategy = @@ -461,7 +462,7 @@ object V2rayConfigManager { val rulesetItems = MmkvManager.decodeRoutingRulesets() rulesetItems?.forEach { key -> - getRoutingUserRule(key, v2rayConfig) + getRoutingUserRule(context, key, v2rayConfig) } } catch (e: Exception) { Log.e(AppConfig.TAG, "Failed to configure routing", e) @@ -476,7 +477,7 @@ object V2rayConfigManager { * @param item The ruleset item to add * @param v2rayConfig The V2ray configuration object to be modified */ - private fun getRoutingUserRule(item: RulesetItem?, v2rayConfig: V2rayConfig) { + private fun getRoutingUserRule(context: Context, item: RulesetItem?, v2rayConfig: V2rayConfig) { try { if (item == null || !item.enabled) { return @@ -497,6 +498,14 @@ object V2rayConfigManager { rule.ip = updatedIpList } + // Convert process package names to UIDs + rule.process?.let { processList -> + if (processList.isNotEmpty()) { + val uids = PackageUidResolver.packageNamesToUids(context, processList) + rule.process = uids.ifEmpty { null } + } + } + v2rayConfig.routing.rules.add(rule) } catch (e: Exception) { diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/RoutingEditActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/RoutingEditActivity.kt index 86cb495b..18057702 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/RoutingEditActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/RoutingEditActivity.kt @@ -42,6 +42,7 @@ class RoutingEditActivity : BaseActivity() { binding.chkLocked.isChecked = rulesetItem.locked == true binding.etDomain.text = Utils.getEditable(rulesetItem.domain?.joinToString(",")) binding.etIp.text = Utils.getEditable(rulesetItem.ip?.joinToString(",")) + binding.etProcess.text = Utils.getEditable(rulesetItem.process?.joinToString(",")) binding.etPort.text = Utils.getEditable(rulesetItem.port) binding.etProtocol.text = Utils.getEditable(rulesetItem.protocol?.joinToString(",")) binding.etNetwork.text = Utils.getEditable(rulesetItem.network) @@ -65,6 +66,7 @@ class RoutingEditActivity : BaseActivity() { locked = binding.chkLocked.isChecked domain = binding.etDomain.text.toString().nullIfBlank()?.split(",")?.map { it.trim() }?.filter { it.isNotEmpty() } ip = binding.etIp.text.toString().nullIfBlank()?.split(",")?.map { it.trim() }?.filter { it.isNotEmpty() } + process = binding.etProcess.text.toString().nullIfBlank()?.split(",")?.map { it.trim() }?.filter { it.isNotEmpty() } protocol = binding.etProtocol.text.toString().nullIfBlank()?.split(",")?.map { it.trim() }?.filter { it.isNotEmpty() } port = binding.etPort.text.toString().nullIfBlank() network = binding.etNetwork.text.toString().nullIfBlank() diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/RoutingSettingRecyclerAdapter.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/RoutingSettingRecyclerAdapter.kt index bdfe731c..3f5a68f1 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/RoutingSettingRecyclerAdapter.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/RoutingSettingRecyclerAdapter.kt @@ -25,7 +25,7 @@ class RoutingSettingRecyclerAdapter( val ruleset = rulesets[position] holder.itemRoutingSettingBinding.remarks.text = ruleset.remarks - holder.itemRoutingSettingBinding.domainIp.text = (ruleset.domain ?: ruleset.ip ?: ruleset.port)?.toString() + holder.itemRoutingSettingBinding.domainIp.text = (ruleset.domain ?: ruleset.ip ?: ruleset.process ?: ruleset.port)?.toString() holder.itemRoutingSettingBinding.outboundTag.text = ruleset.outboundTag holder.itemRoutingSettingBinding.chkEnable.isChecked = ruleset.enabled holder.itemRoutingSettingBinding.imgLocked.isVisible = ruleset.locked == true diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/PackageUidResolver.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/PackageUidResolver.kt new file mode 100644 index 00000000..19370376 --- /dev/null +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/PackageUidResolver.kt @@ -0,0 +1,45 @@ +package com.v2ray.ang.util + +import android.content.Context +import android.content.pm.PackageManager +import android.util.Log +import java.util.concurrent.ConcurrentHashMap + +object PackageUidResolver { + + private const val TAG = "PackageUidResolver" + + // In-process cache to avoid resolving the same package UID repeatedly. + private val packageUidCache = ConcurrentHashMap() + + val packageUidMap: Map + get() = packageUidCache + + fun packageNamesToUids(context: Context, packageNames: List): List { + return packageNames.mapNotNull { pkg -> + packageUidCache[pkg] ?: resolveUid(context, pkg)?.also { uid -> + packageUidCache[pkg] = uid + } + } + } + + fun uidsToPackageNames(uids: List): List { + if (uids.isEmpty()) return emptyList() + return packageUidCache.filterValues { it in uids }.keys.toList() + } + + fun uidToPackageName(uid: String): String? { + return packageUidCache.entries.firstOrNull { it.value == uid }?.key + } + + private fun resolveUid(context: Context, packageName: String): String? { + return try { + val uid = context.packageManager.getPackageUid(packageName, 0).toString() + Log.d(TAG, "Package: $packageName -> UID: $uid") + uid + } catch (_: PackageManager.NameNotFoundException) { + Log.w(TAG, "Package not found: $packageName") + null + } + } +} \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/layout/activity_routing_edit.xml b/V2rayNG/app/src/main/res/layout/activity_routing_edit.xml index d7b34e6f..65329d37 100644 --- a/V2rayNG/app/src/main/res/layout/activity_routing_edit.xml +++ b/V2rayNG/app/src/main/res/layout/activity_routing_edit.xml @@ -103,6 +103,26 @@ android:maxLines="10" /> + + + + + + + استراتيجية النطاق إعدادات التوجيه - مفصولة بفواصل (،)، تذكر الحفظ + افصل بعلامة الفاصلة (,) واختر أحد: domain أو ip أو process حفظ مسح Routing Rule Settings diff --git a/V2rayNG/app/src/main/res/values-bn/strings.xml b/V2rayNG/app/src/main/res/values-bn/strings.xml index 9bdb5425..c76b53ce 100644 --- a/V2rayNG/app/src/main/res/values-bn/strings.xml +++ b/V2rayNG/app/src/main/res/values-bn/strings.xml @@ -316,7 +316,7 @@ ডোমেইন কৌশল রাউটিং সেটিংস - কমা (,) দ্বারা আলাদা করুন, মনে রাখবেন সেভ করতে + কমা (,) দিয়ে আলাদা করুন। domain, ip বা process-এ একটি নির্বাচন করুন সেভ করুন মুছে ফেলুন Routing Rule Settings diff --git a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml index 71835ca1..36f9011f 100644 --- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml +++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml @@ -317,7 +317,7 @@ نشقه دامنه سامووا تور جوستن - وا کاما ز یک جوڌا ابۊن (،) پسند دامنه یا آی پی + وا کاما ز یک جوڌا ابۊن (،)؛ یکی را وارد کنید: domain، ip یا process زفت کردن روفتن سامووا قانۉݩ تور جوستن diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml index 76291c20..142bb9a4 100644 --- a/V2rayNG/app/src/main/res/values-fa/strings.xml +++ b/V2rayNG/app/src/main/res/values-fa/strings.xml @@ -314,7 +314,7 @@ استراتژی دامنه تنظیمات مسیریابی - با کاما (،) از هم جدا شوند، ذخیره کردن فراموش نشود + با کاما (,) جدا کنید. یکی از موارد domain، ip یا process را وارد کنید ذخیره حذف تنظیمات قانون مسیریابی diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml index 5549548d..7917c068 100644 --- a/V2rayNG/app/src/main/res/values-ru/strings.xml +++ b/V2rayNG/app/src/main/res/values-ru/strings.xml @@ -315,7 +315,7 @@ Доменная стратегия Маршрутизация - Введите требуемые домены/IP через запятую + Разделяйте запятой (,). Укажите только одно: domain, ip или process Сохранить Очистить Настройка правил маршрутизации diff --git a/V2rayNG/app/src/main/res/values-vi/strings.xml b/V2rayNG/app/src/main/res/values-vi/strings.xml index 9f927928..d47bd370 100644 --- a/V2rayNG/app/src/main/res/values-vi/strings.xml +++ b/V2rayNG/app/src/main/res/values-vi/strings.xml @@ -317,7 +317,7 @@ Chiến lược tên miền (DomainStrategy) Cài đặt định tuyến - Phân cách bằng dấu phẩy (,). Có thể tải xuống Rules mặc định để tham khảo ở menu ba chấm. + Phân cách bằng dấu phẩy (,). Chọn một trong: domain, ip hoặc process Lưu lại Xoá Routing Rule Settings diff --git a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml index 5db3fc0f..b54fbc81 100644 --- a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml @@ -315,7 +315,7 @@ 域名策略 路由设置 - 用逗号(,)隔开, domain 和 ip 二选一填写 + 用逗号(,)分隔,选择填写 domain、ip 或 process 中的一项 保存 清空 路由规则设置 diff --git a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml index 8d1a546b..57bd731c 100644 --- a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml @@ -315,7 +315,7 @@ 網域策略 轉送設定 - 以半形逗號「,」分隔,domain和ip二選一填寫 + 用逗號(,)分隔,選擇填寫 domain、ip 或 process 中的一項 儲存 清除 路由規則設定 diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml index 374d61ea..bde5e6cb 100644 --- a/V2rayNG/app/src/main/res/values/strings.xml +++ b/V2rayNG/app/src/main/res/values/strings.xml @@ -320,7 +320,7 @@ Domain strategy Routing Settings - Separated by commas(,), choose domain or ip + Separated by commas(,), \nchoose domain or ip or process Save Clear Routing Rule Settings @@ -333,6 +333,7 @@ Locked, keep this rule when import presets domain ip + process (Package Name, Only supports xray tun) port protocol [http,tls,bittorrent]