Fix ConnectivityService discovery on APEX-rewritten devices

This commit is contained in:
世界
2026-03-12 21:07:41 +08:00
parent c2f1fd8e67
commit 62c1b49c9e

View File

@@ -6,6 +6,7 @@ import android.net.Network
import android.net.NetworkInfo import android.net.NetworkInfo
import android.os.Build import android.os.Build
import android.os.IBinder import android.os.IBinder
import android.os.Parcel
import de.robv.android.xposed.XC_MethodHook import de.robv.android.xposed.XC_MethodHook
import de.robv.android.xposed.XposedHelpers import de.robv.android.xposed.XposedHelpers
import io.nekohasekai.sfa.xposed.HookErrorStore import io.nekohasekai.sfa.xposed.HookErrorStore
@@ -26,6 +27,7 @@ class ConnectivityServiceHookHelper(private val classLoader: ClassLoader) : XHoo
private val hooked = AtomicBoolean(false) private val hooked = AtomicBoolean(false)
private val initializerHooked = AtomicBoolean(false) private val initializerHooked = AtomicBoolean(false)
private var classLoadUnhook: XC_MethodHook.Unhook? = null private var classLoadUnhook: XC_MethodHook.Unhook? = null
private var onTransactUnhook: XC_MethodHook.Unhook? = null
private val serviceManagerHooked = AtomicBoolean(false) private val serviceManagerHooked = AtomicBoolean(false)
private var connectivityClassLoader: ClassLoader = classLoader private var connectivityClassLoader: ClassLoader = classLoader
private val skipLogKeys = ConcurrentHashMap<String, Boolean>() private val skipLogKeys = ConcurrentHashMap<String, Boolean>()
@@ -53,6 +55,7 @@ class ConnectivityServiceHookHelper(private val classLoader: ClassLoader) : XHoo
} }
hookConnectivityServiceInitializer() hookConnectivityServiceInitializer()
hookClassLoaderFallback() hookClassLoaderFallback()
hookOnTransactFallback()
tryHookFromServiceManager() tryHookFromServiceManager()
} }
@@ -148,12 +151,39 @@ class ConnectivityServiceHookHelper(private val classLoader: ClassLoader) : XHoo
} }
} }
HookErrorStore.i(SOURCE, "ConnectivityService class not found in known classloaders") HookErrorStore.i(SOURCE, "ConnectivityService class not found in known classloaders")
val initializerNames = listOf(
"com.android.server.ConnectivityServiceInitializer",
"com.android.server.ConnectivityServiceInitializerB",
)
for (name in initializerNames) {
for (loader in loaders) {
val initCls = try {
if (loader != null) Class.forName(name, false, loader) else Class.forName(name)
} catch (_: Throwable) {
null
} ?: continue
try {
val field = initCls.getDeclaredField("mConnectivity")
val fieldType = field.type
if (fieldType.name.endsWith(".ConnectivityService")) {
HookErrorStore.i(
SOURCE,
"ConnectivityService class found via $name.mConnectivity: ${fieldType.name}",
)
return fieldType
}
} catch (_: Throwable) {
}
}
}
return null return null
} }
private fun hookConnectivityServiceInitializer() { private fun hookConnectivityServiceInitializer() {
if (sdkInt < 31 || sdkInt >= 33) { if (sdkInt < 31) {
HookErrorStore.d(SOURCE, "Skip ConnectivityServiceInitializer: sdk=$sdkInt (only exists in API 31-32)") HookErrorStore.d(SOURCE, "Skip ConnectivityServiceInitializer: sdk=$sdkInt (requires API 31+)")
return return
} }
val candidates = listOf( val candidates = listOf(
@@ -238,20 +268,20 @@ class ConnectivityServiceHookHelper(private val classLoader: ClassLoader) : XHoo
classLoadUnhook = null classLoadUnhook = null
return return
} }
when (name) { when {
"com.android.server.ConnectivityService" -> { name == "com.android.server.ConnectivityService" ||
name.endsWith(".com.android.server.ConnectivityService") -> {
val cls = param.result as? Class<*> ?: return val cls = param.result as? Class<*> ?: return
HookErrorStore.i( HookErrorStore.i(
SOURCE, SOURCE,
"ConnectivityService loaded via ${param.thisObject.javaClass.name}", "ConnectivityService loaded via ${param.thisObject.javaClass.name}: $name",
) )
installHooks(cls, "loadClass") installHooks(cls, "loadClass")
classLoadUnhook?.unhook() classLoadUnhook?.unhook()
classLoadUnhook = null classLoadUnhook = null
} }
"com.android.server.ConnectivityServiceInitializer", name == "com.android.server.ConnectivityServiceInitializer" ||
"com.android.server.ConnectivityServiceInitializerB", name == "com.android.server.ConnectivityServiceInitializerB" -> {
-> {
if (sdkInt < 31) return if (sdkInt < 31) return
if (initializerHooked.get()) return if (initializerHooked.get()) return
val cls = param.result as? Class<*> ?: return val cls = param.result as? Class<*> ?: return
@@ -322,6 +352,41 @@ class ConnectivityServiceHookHelper(private val classLoader: ClassLoader) : XHoo
} }
} }
private fun hookOnTransactFallback() {
if (onTransactUnhook != null) return
try {
val stub = XposedHelpers.findClass("android.net.IConnectivityManager\$Stub", classLoader)
onTransactUnhook = XposedHelpers.findAndHookMethod(
stub,
"onTransact",
Int::class.javaPrimitiveType,
Parcel::class.java,
Parcel::class.java,
Int::class.javaPrimitiveType,
object : SafeMethodHook(SOURCE) {
override fun beforeHook(param: MethodHookParam) {
if (hooked.get()) {
onTransactUnhook?.unhook()
onTransactUnhook = null
return
}
val serviceClass = param.thisObject.javaClass
HookErrorStore.i(
SOURCE,
"ConnectivityService discovered via onTransact: ${serviceClass.name}",
)
installHooks(serviceClass, "onTransact")
onTransactUnhook?.unhook()
onTransactUnhook = null
}
},
)
HookErrorStore.i(SOURCE, "Hooked IConnectivityManager.Stub.onTransact for discovery")
} catch (e: Throwable) {
HookErrorStore.w(SOURCE, "Hook onTransact fallback failed: ${e.message}", e)
}
}
private fun hookConnectivityServiceInitializerClass(cls: Class<*>) { private fun hookConnectivityServiceInitializerClass(cls: Class<*>) {
if (sdkInt < 31) return if (sdkInt < 31) return
if (initializerHooked.get()) return if (initializerHooked.get()) return