From 15daef6ad255d23016a00162a687bf2efeba7dbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sun, 11 Jan 2026 18:05:41 +0800 Subject: [PATCH] Fix NetworkCapabilities field access for API compatibility - mUnderlyingNetworks: Added in Android 13 (not 12), use findFieldIfExists for Android 12 (APEX may upgrade), findField for Android 13+ - mOwnerUid: Added in Android 11, add SDK >= R check - mTransportInfo: Added in Android 10, add SDK >= Q check Fixes NullPointerException on devices running Android < 13 where setUnderlyingNetworks method doesn't exist. --- .../io/nekohasekai/sfa/xposed/VpnSanitizer.kt | 17 +++++++++++++++-- .../NetworkCapabilities+writeToParcel.kt | 10 ++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/io/nekohasekai/sfa/xposed/VpnSanitizer.kt b/app/src/main/java/io/nekohasekai/sfa/xposed/VpnSanitizer.kt index fa861d5..1aed0d5 100644 --- a/app/src/main/java/io/nekohasekai/sfa/xposed/VpnSanitizer.kt +++ b/app/src/main/java/io/nekohasekai/sfa/xposed/VpnSanitizer.kt @@ -3,6 +3,7 @@ package io.nekohasekai.sfa.xposed import android.net.LinkProperties import android.net.NetworkCapabilities import android.net.NetworkInfo +import android.os.Build import android.os.Parcel import android.os.Process import de.robv.android.xposed.XposedHelpers @@ -81,14 +82,26 @@ object VpnSanitizer { } private fun clearUnderlyingNetworks(caps: NetworkCapabilities) { - XposedHelpers.callMethod(caps, "setUnderlyingNetworks", null) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + val field = XposedHelpers.findField(NetworkCapabilities::class.java, "mUnderlyingNetworks") + field.set(caps, null) + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val field = XposedHelpers.findFieldIfExists(NetworkCapabilities::class.java, "mUnderlyingNetworks") + field?.set(caps, null) + } } private fun clearOwnerUid(caps: NetworkCapabilities) { - XposedHelpers.callMethod(caps, "setOwnerUid", Process.INVALID_UID) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + val field = XposedHelpers.findField(NetworkCapabilities::class.java, "mOwnerUid") + field.setInt(caps, Process.INVALID_UID) + } } private fun clearVpnTransportInfo(caps: NetworkCapabilities) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + return + } val field = XposedHelpers.findField(NetworkCapabilities::class.java, "mTransportInfo") val info = field.get(caps) ?: return if (info.javaClass.name.contains("VpnTransportInfo")) { diff --git a/app/src/main/java/io/nekohasekai/sfa/xposed/hooks/hidevpn/NetworkCapabilities+writeToParcel.kt b/app/src/main/java/io/nekohasekai/sfa/xposed/hooks/hidevpn/NetworkCapabilities+writeToParcel.kt index 1a95954..710e2c5 100644 --- a/app/src/main/java/io/nekohasekai/sfa/xposed/hooks/hidevpn/NetworkCapabilities+writeToParcel.kt +++ b/app/src/main/java/io/nekohasekai/sfa/xposed/hooks/hidevpn/NetworkCapabilities+writeToParcel.kt @@ -91,11 +91,13 @@ class HookNetworkCapabilitiesWriteToParcel : XHook { } private fun clearUnderlyingNetworks(caps: NetworkCapabilities) { - if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.S) { - return + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) { + val field = XposedHelpers.findField(NetworkCapabilities::class.java, "mUnderlyingNetworks") + field.set(caps, null) + } else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { + val field = XposedHelpers.findFieldIfExists(NetworkCapabilities::class.java, "mUnderlyingNetworks") + field?.set(caps, null) } - val field = XposedHelpers.findField(NetworkCapabilities::class.java, "mUnderlyingNetworks") - field.set(caps, null) } private fun clearOwnerUid(caps: NetworkCapabilities) {