背景
老规矩,先交代背景。 现状是这样:
- wifi-ext-service.jar是我们开发的wifi的扩展接口的实现;
- wifi-service.jar与wifi-ext-service.jar均在编译时将代码混淆;
- wifi-ext-service.jar依赖wifi-service.jar;
- AOSP原生编译规则中,wifi-service.jar会静态依赖android.hardware.wifi-V1.0-java;
一切都相安无事,直到现在有个开发需求,需要wifi-ext-service.jar依赖android.hardware.wifi-V1.0-java并调用其中的IWifiChip/IWifi的某些函数;
问题 - 如何依赖
首先遇到的第一个两难问题:wifi-ext-service.jar在编译时到底要不要静态依赖android.hardware.wifi-V1.0-java?
选择1 非静态依赖android.hardware.wifi-V1.0-java
结果: 启动system_server时crash:
08-20 14:01:54.801 2911 2911 I system_server: Caused by: java.lang.ClassNotFoundException: Didn't find class "android.hardware.wifi.V1_0.IWifiChip$requestChipDebugInfoCallback" on path: DexPathList[[zip file "/system/framework/services.jar", zip file "/system/framework/ethernet-service.jar", zip file "/system/framework/wifi-service.jar", zip file "/system/framework/com.android.location.provider.jar", zip file "/system/framework/car-frameworks-service.jar", zip file "/system/framework/wifi-ext-service.jar"],nativeLibraryDirectories=[/system/lib64, /system/product/lib64, /system/lib64, /system/product/lib64]]
08-20 14:01:54.801 2911 2911 I system_server: at java.lang.Class dalvik.system.BaseDexClassLoader.findClass(java.lang.String) (BaseDexClassLoader.java:134)
08-20 14:01:54.801 2911 2911 I system_server: at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String, boolean) (ClassLoader.java:379)
08-20 14:01:54.801 2911 2911 I system_server: at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String) (ClassLoader.java:312)
08-20 14:01:54.801 2911 2911 I system_server: at void com.ext.server.wifi.q.<init>(android.content.Context) ((null):-1)
08-20 14:01:54.801 2911 2911 I system_server: at void com.ext.server.wifi.WifiExtService.<init>(android.content.Context) ((null):-1)
08-20 14:01:54.801 2911 2911 I system_server: at java.lang.Object java.lang.reflect.Constructor.newInstance0(java.lang.Object[]) (Constructor.java:-2)
08-20 14:01:54.801 2911 2911 I system_server: at java.lang.Object java.lang.reflect.Constructor.newInstance(java.lang.Object[]) (Constructor.java:343)
08-20 14:01:54.801 2911 2911 I system_server: at com.android.server.SystemService com.android.server.SystemServiceManager.startService(java.lang.Class) (SystemServiceManager.java:98)
08-20 14:01:54.801 2911 2911 I system_server: at com.android.server.SystemService com.android.server.SystemServiceManager.startService(java.lang.String) (SystemServiceManager.java:72)
08-20 14:01:54.801 2911 2911 I system_server: at void com.android.server.SystemServer.startOtherServices() (SystemServer.java:1169)
08-20 14:01:54.801 2911 2911 I system_server: at void com.android.server.SystemServer.run() (SystemServer.java:450)
08-20 14:01:54.801 2911 2911 I system_server: at void com.android.server.SystemServer.main(java.lang.String[]) (SystemServer.java:303)
08-20 14:01:54.801 2911 2911 I system_server: at java.lang.Object java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object[]) (Method.java:-2)
08-20 14:01:54.801 2911 2911 I system_server: at void com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run() (RuntimeInit.java:493)
08-20 14:01:54.801 2911 2911 I system_server: at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:838)
原因分析: 由于非静态依赖并不会在编译时将需要的类打包进wifi-ext-service.jar中,而是需要在运行时,通过SYSTEMSERVERCLASSPATH环境变量,找到可能提供这些类的jar包,并依次查找。 而在此当中,唯一静态引入这些类的wifi-service.jar又将其混淆了,因此按照包名、类名查找的话,完全没法找到;
这里估计会有人想到,是不是可以修改proguard,使其在混淆时将指定包名下的类排除除去就可以了?确实如此,但是仍然会有概率遇到选择2时的这个问题:
选择2 静态依赖android.hardware.wifi-V1.0-java
结果: 开启热点后system_server进程crash:
08-20 14:22:26.449 984 984 E AndroidRuntime: *** FATAL EXCEPTION IN SYSTEM PROCESS: main
08-20 14:22:26.449 984 984 E AndroidRuntime: java.lang.NoSuchMethodError: No static method p()Lb/a/a/a/b/a/a; in class Lb/a/a/a/b/a/a; or its super classes (declaration of 'b.a.a.a.b.a.a' appears in /system/framework/wifi-service.jar)
08-20 14:22:26.449 984 984 E AndroidRuntime: at com.ext.server.wifi.h.onReceive(Unknown Source:85)
08-20 14:22:26.449 984 984 E AndroidRuntime: at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0(LoadedApk.java:1391)
08-20 14:22:26.449 984 984 E AndroidRuntime: at android.app.-$$Lambda$LoadedApk$ReceiverDispatcher$Args$_BumDX2UKsnxLVrE6UJsJZkotuA.run(Unknown Source:2)
08-20 14:22:26.449 984 984 E AndroidRuntime: at android.os.Handler.handleCallback(Handler.java:873)
08-20 14:22:26.449 984 984 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:99)
08-20 14:22:26.449 984 984 E AndroidRuntime: at android.os.Looper.loop(Looper.java:193)
08-20 14:22:26.449 984 984 E AndroidRuntime: at com.android.server.SystemServer.run(SystemServer.java:473)
08-20 14:22:26.449 984 984 E AndroidRuntime: at com.android.server.SystemServer.main(SystemServer.java:303)
08-20 14:22:26.449 984 984 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
08-20 14:22:26.449 984 984 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
08-20 14:22:26.449 984 984 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:838)
原因分析: 看到这两行我就觉得大概率和混淆有关了:
08-20 14:22:26.449 984 984 E AndroidRuntime: java.lang.NoSuchMethodError: No static method p()Lb/a/a/a/b/a/a; in class Lb/a/a/a/b/a/a; or its super classes (declaration of 'b.a.a.a.b.a.a' appears in /system/framework/wifi-service.jar)
08-20 14:22:26.449 984 984 E AndroidRuntime: at com.ext.server.wifi.h.onReceive(Unknown Source:85)
- 首先,调用栈停留在wifi-ext-service.jar中的类,因此没有理由报wifi-service.jar中的类找不到方法;(wifi-ext-service主要是扩展wifi-service的接口,正常情况下不会反向持有wifi-service中的对象,何况wifi-service是混淆过的,要反向调用几乎不可能);
- 其次,通过反编译发现,wifi-service.jar与wifi-ext-service.jar中都存在b.a.a.a.b.a.a这个类,而前者的这个类没有p()方法,后者是有的,与报错信息显示一致;
- 为了确认这一推测,我通过对比反编译后的代码与源代码,确认反编译代码中提到的b.a.a.a.b.a.a类的p()方法实际为一个我自己创建的HAL接口的JAVA侧getService()方法调用;通过另一个静态JAVA库导入;
那么综合来看,结果比较明显了:由于wifi-service与wifi-ext-service各自在编译时并不知晓对方的存在(没有静态依赖),因此混淆时出现了类命名冲突的情况,导致在调用时跳转到了错误的类中,出现异常;
要解决这个问题,同样我们需要修改proguard文件,将具备区分度的包名保留;
解决 - 方案选择
在修改Proguard后,貌似上面两个选择都可以了:
- 首先,需要为wifi-service的混淆添加例外,将android.hardware.wifi-V1.0-java包含的类从混淆的目标中移除出来:
-keep class android.hardware.wifi.**{*;}
- 其次,为了防止a.a.a.a这种高概率会重复的类名冲突,需要将wifi-ext-service中大部分的包名区分出来:
-keeppackagenames vendor.ext.**
修改完后,问题得以解决;
后记 - 选择
最后,虽然使用同样的改法可以解决两个选择的问题,但是由于CTS测试项(详见https://blog.csdn.net/u014175785/article/details/115333461)的原因,以及我在调试过程中出现的交叉依赖导致ClassLoader加载报错等问题(这个问题在我写这篇文章时没有复现出来,因此没法展开介绍,后续如果再次遇到这类问题,届时再更新),我不建议采用静态依赖的方式导入;
|