@@ -6,6 +6,7 @@ import android.net.Network
66import android.net.NetworkInfo
77import android.os.Build
88import android.os.IBinder
9+ import android.os.Parcel
910import de.robv.android.xposed.XC_MethodHook
1011import de.robv.android.xposed.XposedHelpers
1112import io.nekohasekai.sfa.xposed.HookErrorStore
@@ -26,6 +27,7 @@ class ConnectivityServiceHookHelper(private val classLoader: ClassLoader) : XHoo
2627 private val hooked = AtomicBoolean (false )
2728 private val initializerHooked = AtomicBoolean (false )
2829 private var classLoadUnhook: XC_MethodHook .Unhook ? = null
30+ private var onTransactUnhook: XC_MethodHook .Unhook ? = null
2931 private val serviceManagerHooked = AtomicBoolean (false )
3032 private var connectivityClassLoader: ClassLoader = classLoader
3133 private val skipLogKeys = ConcurrentHashMap <String , Boolean >()
@@ -53,6 +55,7 @@ class ConnectivityServiceHookHelper(private val classLoader: ClassLoader) : XHoo
5355 }
5456 hookConnectivityServiceInitializer()
5557 hookClassLoaderFallback()
58+ hookOnTransactFallback()
5659 tryHookFromServiceManager()
5760 }
5861
@@ -148,12 +151,39 @@ class ConnectivityServiceHookHelper(private val classLoader: ClassLoader) : XHoo
148151 }
149152 }
150153 HookErrorStore .i(SOURCE , " ConnectivityService class not found in known classloaders" )
154+
155+ val initializerNames = listOf (
156+ " com.android.server.ConnectivityServiceInitializer" ,
157+ " com.android.server.ConnectivityServiceInitializerB" ,
158+ )
159+ for (name in initializerNames) {
160+ for (loader in loaders) {
161+ val initCls = try {
162+ if (loader != null ) Class .forName(name, false , loader) else Class .forName(name)
163+ } catch (_: Throwable ) {
164+ null
165+ } ? : continue
166+ try {
167+ val field = initCls.getDeclaredField(" mConnectivity" )
168+ val fieldType = field.type
169+ if (fieldType.name.endsWith(" .ConnectivityService" )) {
170+ HookErrorStore .i(
171+ SOURCE ,
172+ " ConnectivityService class found via $name .mConnectivity: ${fieldType.name} " ,
173+ )
174+ return fieldType
175+ }
176+ } catch (_: Throwable ) {
177+ }
178+ }
179+ }
180+
151181 return null
152182 }
153183
154184 private fun hookConnectivityServiceInitializer () {
155- if (sdkInt < 31 || sdkInt >= 33 ) {
156- HookErrorStore .d(SOURCE , " Skip ConnectivityServiceInitializer: sdk=$sdkInt (only exists in API 31-32 )" )
185+ if (sdkInt < 31 ) {
186+ HookErrorStore .d(SOURCE , " Skip ConnectivityServiceInitializer: sdk=$sdkInt (requires API 31+ )" )
157187 return
158188 }
159189 val candidates = listOf (
@@ -238,20 +268,20 @@ class ConnectivityServiceHookHelper(private val classLoader: ClassLoader) : XHoo
238268 classLoadUnhook = null
239269 return
240270 }
241- when (name) {
242- " com.android.server.ConnectivityService" -> {
271+ when {
272+ name == " com.android.server.ConnectivityService" ||
273+ name.endsWith(" .com.android.server.ConnectivityService" ) -> {
243274 val cls = param.result as ? Class <* > ? : return
244275 HookErrorStore .i(
245276 SOURCE ,
246- " ConnectivityService loaded via ${param.thisObject.javaClass.name} " ,
277+ " ConnectivityService loaded via ${param.thisObject.javaClass.name} : $name " ,
247278 )
248279 installHooks(cls, " loadClass" )
249280 classLoadUnhook?.unhook()
250281 classLoadUnhook = null
251282 }
252- " com.android.server.ConnectivityServiceInitializer" ,
253- " com.android.server.ConnectivityServiceInitializerB" ,
254- -> {
283+ name == " com.android.server.ConnectivityServiceInitializer" ||
284+ name == " com.android.server.ConnectivityServiceInitializerB" -> {
255285 if (sdkInt < 31 ) return
256286 if (initializerHooked.get()) return
257287 val cls = param.result as ? Class <* > ? : return
@@ -322,6 +352,41 @@ class ConnectivityServiceHookHelper(private val classLoader: ClassLoader) : XHoo
322352 }
323353 }
324354
355+ private fun hookOnTransactFallback () {
356+ if (onTransactUnhook != null ) return
357+ try {
358+ val stub = XposedHelpers .findClass(" android.net.IConnectivityManager\$ Stub" , classLoader)
359+ onTransactUnhook = XposedHelpers .findAndHookMethod(
360+ stub,
361+ " onTransact" ,
362+ Int ::class .javaPrimitiveType,
363+ Parcel ::class .java,
364+ Parcel ::class .java,
365+ Int ::class .javaPrimitiveType,
366+ object : SafeMethodHook (SOURCE ) {
367+ override fun beforeHook (param : MethodHookParam ) {
368+ if (hooked.get()) {
369+ onTransactUnhook?.unhook()
370+ onTransactUnhook = null
371+ return
372+ }
373+ val serviceClass = param.thisObject.javaClass
374+ HookErrorStore .i(
375+ SOURCE ,
376+ " ConnectivityService discovered via onTransact: ${serviceClass.name} " ,
377+ )
378+ installHooks(serviceClass, " onTransact" )
379+ onTransactUnhook?.unhook()
380+ onTransactUnhook = null
381+ }
382+ },
383+ )
384+ HookErrorStore .i(SOURCE , " Hooked IConnectivityManager.Stub.onTransact for discovery" )
385+ } catch (e: Throwable ) {
386+ HookErrorStore .w(SOURCE , " Hook onTransact fallback failed: ${e.message} " , e)
387+ }
388+ }
389+
325390 private fun hookConnectivityServiceInitializerClass (cls : Class <* >) {
326391 if (sdkInt < 31 ) return
327392 if (initializerHooked.get()) return
0 commit comments