Android内置 系统apk 导致某些系统应用概率性闪退问题

问题现象

定制系统时,需要内置一些第三方的apk。按照系统app的内置方法,增加share system uid获得系统权限。在使用的过程中大概率会出现某些系统应用(如:资源管理器,设置…)闪退。并伴随录音服务挂掉,秘钥链等问题(开始以为是独立的bug,后面分析都是内置系统apk引起的问题)。


原因分析:

1.查看logcat

下面是关键log:

12-28 03:12:44.469 6258 6258 E AndroidRuntime: Process: com.android.rk, PID: 6258
12-28 03:12:44.469 6258 6258 E AndroidRuntime: java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.android.rk/com.android.rk.RockExplorer}: java.lang.ClassNotFoundException: Didn’t find class “com.android.rk.RockExplorer” on path: DexPathList[[zip file “/system/framework/org.apache.http.legacy.boot.jar”, zip file “/system/app/RkExplorer/RkExplorer.apk”],nativeLibraryDirectories=[/system/app/RkExplorer/lib/arm64, /system/fake-libs, /system/app/RkExplorer/RkExplorer.apk!/lib/armeabi-v7a, /system/lib, /system/lib]]

以上日志可看出RkExplorer.apk找不到32位的库,所以会闪退。可以看下系统内置的RkExplorer是多少位?

adb shell dumpsys package p > dumpsys_p //查看系统apk信息

在dumpsys_p中找到RkExplorer的 primaryCpuAbi=arm64-v8a,可见RkExplorer是64位apk,没有编译32位库,所以找不到32位库就会闪退。为什么64位的RkExplorer apk会到32位的目录下去找所需要的库?

根本原因是我们内置app 使用了32位库,所有apk被判定为32位;作为系统app,使用了系统的systemuid,由于Android的机制规定一个进程只能是32位或者64位中的一种,所有使用systemuid的apk都因为内置了app使用32位的库而强制变成了32位;由于系统内置的64位apk没有对应的32位库,所以会闪退。

2.android加载so文件的机制

apk在安装的过程中,系统就会对apk进行解析根据里面so文件类型,确定这个apk是安装在32位还是64位虚拟机上,如果是32位虚拟机那么就不能使用64位so,如果是64位虚拟机也不能使用32位so。而64位设备可以提供32和64位两种虚拟机,根据apk选择开启哪一种,因此说64位设备兼容32的so库。

具体说就是apk在安装的时候,apk解包的时候,就已经确定要加载多少位的库,如果apk里面放有两种库,实际用的时候,也是只会加载一种库。

3.apk架构确定顺序
  1. 如果apk包中lib文件夹下有.so库,就根据这个.so库的架构模式,确定app的primaryCpuAbi的值;
  2. 对于system app, 如果没法通过第一步确定primaryCpuAbi的值,PKMS会根据/system/app/app_name(xxx)/lib和/system/app/app_name(xxx)/lib64这两个文件夹是否存在,来确定它的primaryCpuAbi的值;
  3. 对于还没有确定的app, 在最后还会将自己的primaryCpuAbi值与和他使用相同UID的package的值设成一样;
  4. 对于到这里还没有确认primaryCpuAbi的app,就会在启动进程时使用ro.product.cpu.abilist这个property的值的第一项作为它关联的ABI;

解决方案:

基于android加载so文件的机制,我们可以采用一下几种方法来解决该问题:

  1. apk不使用系统的uid,自己新增一个具有系统权限的uid(比较麻烦)
  2. 把我们的apk修改为64位的,将所有使用的32位so替换为64位(可能有些三方so不提供64位版本)
  3. 把所有32位库再封装一次,做成jar包,然后使用这些jar包。(需要自己封装)
  4. 将所有32位库服务抽离出来,单独运行一个(或多个)进程。(需要提前确定好app的架构,不然需要重构app)
  5. 在系统apk(例如:Settings)安装位置system/priv-app/xxx/目录下面建立一个lib/arm64文件夹.(利用apk架构确定顺序强制将系统apk变为64位
  6. 将系统apk编译为32位(可能影响性能)
    a. 在apk对应的android.mk中: LOCAL_DEX_PREOPT := nostripping
    b. device/rockchip/rk3399/BoardConfig.mk中:DEX_PREOPT_DEFAULT := nostripping

参考

https://www.jianshu.com/p/62b3c153ad68
https://blog.csdn.net/lemonpi/article/details/79620039

猜你喜欢

转载自blog.csdn.net/weixin_45639314/article/details/128712546