项目场景:

二期会议室需要替换成OD20的会议平板,为了方便安装,给了framework的同事一个会议室版本的无线投屏APK,作为系统应用打包进去了。

将无线投屏升级到现在调试的版本,启动后,底部通知栏就duang duang duang的弹,随之系统crash。

环境:OD20会议平板
角色:32位的会议室版本Apk,64位调试版Apk


原因分析:

前置知识:

  1. 不借助adb,不能直接在launcher里卸载系统应用,进入设置卸载覆盖安装的,可将app恢复成系统应用
  2. 系统应用如果短时间内反复crash,会导致系统崩溃
  3. 应用崩溃会重启的原因以及规避方案参考:Android 解决应用崩溃后重启的问题
  4. 项目中直接/间接引用到的第三方库,都会在 Android Studio 的 External Libraries 下找到
  5. 动态链接库要提供整套,不然会crash
  6. Service的标识:包(package)名}/{包名}.{服务(service)名称}

Step 1

开始分析,从报错堆栈:

03-26 17:01:33.352 28825 28825 D AndroidRuntime: Shutting down VM
03-26 17:01:33.353 28825 28825 E AndroidRuntime: FATAL EXCEPTION: main
03-26 17:01:33.353 28825 28825 E AndroidRuntime: Process: com.hikvision.cast, PID: 28825
03-26 17:01:33.353 28825 28825 E AndroidRuntime: java.lang.RuntimeException: Unable to get provider androidx.lifecycle.ProcessLifecycleOwnerInitializer:java.lang.ClassNotFoundException: Didn't find class "androidx.lifecycle.ProcessLifecycleOwnerInitializer" on path: DexPathList[[zip file "/system/app/i
nstcast/instcast.apk"],nativeLibraryDirectories=[/system/app/instcast/lib/arm, /system/app/instcast/instcast.apk!/lib/armeabi-v7a, /system/lib, /vendor/
lib]]
03-26 17:01:33.353 28825 28825 E AndroidRuntime:        at android.app.ActivityThread.installProvider(ActivityThread.java:6288)
03-26 17:01:33.353 28825 28825 E AndroidRuntime:        at android.app.ActivityThread.installContentProviders(ActivityThread.java:5851)
03-26 17:01:33.353 28825 28825 E AndroidRuntime:        at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5772)
03-26 17:01:33.353 28825 28825 E AndroidRuntime:        at android.app.ActivityThread.-wrap1(Unknown Source:0)
03-26 17:01:33.353 28825 28825 E AndroidRuntime:        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1661)
03-26 17:01:33.353 28825 28825 E AndroidRuntime:        at android.os.Handler.dispatchMessage(Handler.java:105)
03-26 17:01:33.353 28825 28825 E AndroidRuntime:        at android.os.Looper.loop(Looper.java:164)
03-26 17:01:33.353 28825 28825 E AndroidRuntime:        at android.app.ActivityThread.main(ActivityThread.java:6541)
03-26 17:01:33.353 28825 28825 E AndroidRuntime:        at java.lang.reflect.Method.invoke(Native Method)
03-26 17:01:33.353 28825 28825 E AndroidRuntime:        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
03-26 17:01:33.353 28825 28825 E AndroidRuntime:        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:775)
03-26 17:01:33.353 28825 28825 E AndroidRuntime: Caused by: java.lang.ClassNotFoundException: Didn't find class "androidx.lifecycle.ProcessLifecycleOwne
rInitializer" on path: DexPathList[[zip file "/system/app/instcast/instcast.apk"],nativeLibraryDirectories=[/system/app/instcast/lib/arm, /system/app/in
stcast/instcast.apk!/lib/armeabi-v7a, /system/lib, /vendor/lib]]
03-26 17:01:33.353 28825 28825 E AndroidRuntime:        at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:93)
03-26 17:01:33.353 28825 28825 E AndroidRuntime:        at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
03-26 17:01:33.353 28825 28825 E AndroidRuntime:        at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
03-26 17:01:33.353 28825 28825 E AndroidRuntime:        at android.app.ActivityThread.installProvider(ActivityThread.java:6273)
03-26 17:01:33.353 28825 28825 E AndroidRuntime:        ... 10 more
03-26 17:01:33.357  2414  5634 W ActivityManager: Process com.hikvision.cast has crashed too many times: killing!

结合缺陷表现来看,好的没谁了,就是它!

开始分析为什么找不到 androidx.lifecycle.ProcessLifecycleOwnerInitializer
混淆?分包?

垂死病中惊坐, androidx.lifecycle:lifecycle-process:2.2.0 我都没用到!

难道是LiveEventBus间接引用到了?但不应该啊,AS的 External Libraries 下也没找到(AS你病了吗?快醒醒)

罢了罢了,先解决问题,把这个库引用上,防混淆,打入主dex,笑嘻嘻的恢复出厂设置,install,open…
… … duang duang duang,系统crash

佛祖心中坐,卧槽心中留

Step 2

不能轻易放弃,几经折腾,猜测有可能是前面的crash导致的,最终表现在这个Class没找到?就跟调试时经常出现的JNI的某方法没实现问题一样。往上一瞟:

2021-03-29 18:12:30.487 3737-3737/? E/AndroidRuntime: FATAL EXCEPTION: mainProcess: com.hikvision.cast, PID: 3737java.lang.UnsatisfiedLinkError: dlopen failed: "/data/app/com.hikvision.cast-5Xy7PPXmWlo655vBQWul3Q==/lib/arm64/libmmkv.so" is 64-bit instead of 32-bitat java.lang.Runtime.loadLibrary0(Runtime.java:1016)at java.lang.System.loadLibrary(System.java:1657)at com.tencent.mmkv.MMKV.n(SourceFile:2)at com.tencent.mmkv.MMKV.l(SourceFile:3)at com.hikvision.cast.basic.utils.f.h(SourceFile:1)at com.hikvision.cast.App.onCreate(SourceFile:4)at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1118)at android.app.LoadedApk.makeApplication(LoadedApk.java:981)at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2725)at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)at android.app.ActivityThread.-wrap11(Unknown Source:0)at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)at android.os.Handler.dispatchMessage(Handler.java:105)at android.os.Looper.loop(Looper.java:164)at android.app.ActivityThread.main(ActivityThread.java:6541)at java.lang.reflect.Method.invoke(Native Method)at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:775)

被腾讯坑了?MMKV拿了个32位的动态链接库,放到armv8a目录下?
很快啊,打开Ubuntu虚拟机一看,是64位的没错。

而且冷静下来一看,log提示是说拿64位的代替32位了。

Step 3

难道是链接库的CPU架构支持导致?一顿重启、重装、恢复出厂设置、覆盖安装后,得出以下结论:

Step 4

基本有个定论了,32位的系统apk只能升级到32位的。

但是还有个小彩蛋,为什么有两个投屏码?而且杀死进程后,又出现系统崩溃的问题。
尝试解决:重启试试?果然重启大法好。那多半是framework的缓存和策略问题了。

再扩展思考下:
和 install 和 install -r 有关系吗?
—> 不加-r 都安装不上

adb: failed to install G:\WorkSpace\Apps\InstCast-Project-2.2-Dev\app-s\build\outputs\apk\meeting\release\cast_1.4.7_meeting_2021_03_30_v7_release.apk: Failure [INSTALL_FAILED_ALREADY_EXISTS: Attempt to re-install com.hikvision.cast without first uninstalling.]

Step 5

  1. 猜测是Service没有被替换的原因,使用老的分支(要被升的那个apk),将Service替换成新的com.hikvision.cast.service.InteractService,确实没覆盖上,但杀死新app后不会触发系统崩溃。
  2. 在新的分支,替换成display.interactive.cast.service.InteractService,投屏码显示****,并没有覆盖原来的Service

分析新老分支:
相同:package 都是 com.hikvision.cast
不同:旧分支的代码目录是display.interactive.cast,新分支代码目录是com.hikvision.cast(看dex里的结构,确实是用的代码目录)

但是!服务的标识,和代码的包名也是相关的。至此,应用层角度,能排查的和能试验的都完毕了,总结一下,去咨询一下Framework的专家们。

整体排查历程如下所示:


整理总结

出错现象:

  1. OD20设备,系统应用覆盖安装后崩溃,不断被系统恢复,然后系统crash

现象总结:

  1. 使用32位动态链接库的系统应用不能直接升级到64位,只能从32位升级到32位(32+64位的都放在包里也不行)
  2. 预置系统应用的Service不能被覆盖(有两个投屏码),杀新app的进程,会导致系统crash

可能能作为突破的点:

  1. 首次安装系统/恢复出厂设置会出现,restart后,恢复到预装(启动预装应用),再覆盖安装,启动不出错,杀进程也不出错
    (系统应用读取动态链接库的顺序?手动升级系统内置应用还是系统应用吗?为何会固步自封在被覆盖app支持的CPU架构上,有缓存还是没正确的解析新apk?)
  2. 如何才能正确的停止系统服务的Service
  3. 有没有类似手机厂商的,预装应用但不是系统应用的方案

想要得到的结果:

  1. 系统应用能从32位正确的更新到64位
  2. 系统应用的Service正确的覆盖

解决方案:

经过Framework同事的答疑解惑后,覆盖安装的问题应该是系统应用动态链接库的读取策略有一些问题,Service的问题应该是系统应用的服务被替换后没有清理干净导致。
—> 但可能不对,参考【拓展:2.Android 调用so库全过程】结合系统应用思考可得,64位机为了兼容32位系统,会跑一个兼容的32位的zygote来孵化应用进程,而系统应用主动拉起App导致ClassLoade根据CPU架构去apk里释放so的时候就出事了
<可以通过ClassLoader打印nativeLib路径>

采取如下方案:

  1. Crash:充分非系统应用测试后,非系统应用能较好的应对这些奇怪问题。分析无线投屏app的场景定位,后续采用集成到系统包,但不作为系统应用的方式
  2. Service:因为后期打入系统应用也不会再用这么老的版本,Service标识不变
  3. 继续跟进Framework从根本上解决该问题,完成闭环

现存的风险点:

  1. 二期会议室的十几台试点OD20存在升级风险
  2. 早期领导办公室的把投屏打包为系统应用的版本存在升级风险

拓展:
1.Android动态链接库查找顺序
2.Android 调用so库全过程


思考总结

  1. 分析日志视野要广一些
  2. 升级迭代版本不宜过大

根因定位

Service重启恢复
1.启动时,serviceDoneExecutingLocked调用onStartCommand的返回值设置ProcessRecord的stopIfKilled和deliveredStarts字段,构建重启数据
2.Service挂掉后,触发AMS的Binder讣告机制,handleAppDiedLocked,最终调用ActivityServices.killServicesLocked负责清理和重启需要的Service
3.限制:超过两次都失败,就不再重启Service,系统APP不受限制;像START_NOT_STICKY这种,登记会再次被取消
4.最近任务列表中移除,也会触发Service重启,仅仅多了onTaskRemoved回调,若不需要重启,设置stopWithTask
5.START_REDELIVER_INTENT重发所有Intent,且重启延时较大,START_STICK一般固定1s

RescueParty救援程序
1.触发 kill用户进程 和 恢复出厂设置
2.分为5个救援优先级,system_server 在5分钟内重启5次以上调整一次级别,永久性系统应用在30秒内崩溃5次以上调整一次级别
3.禁用场景:eng版本、userdebug版本、 persist.sys.disable_rescue为true

Android的so文件加载机制
1.通过primaryCpuAbi确定应用程序的abi,影响系统寻找so文件目录的流程(通过apk包里包含的so库的架构来决定app的primaryCpuAbi的值)
2.nativeLibraryDir -> 32(vendor/lib,再system/lib)/64(vendor/lib64,再system/lib64)
3.不同的cpu架构的so文件不能够混合使用
4.应用运行64位进程,无法使用32位abi的so文件,反之亦然
5.zygote根据primaryCpuAbi确定启动那个zygote,分为zygote32/zygote32_64/zygote64/zygote64_32

综上所述,导致问题的根本原因是(以32位系统应用升级为64位为例):
1.32位系统应用升级,非正常stopService,因设置了persistent,触发Binder讣告,AMS重启Service
2.32位进程被拉起,但通过apk获取的primaryCpuAbi是64位(即使有32/64位也是优先64位),读取64位的so文件目录,但进程是32位的又crash
3.短时间设置了persistent反复crash,触发RescueParty,并不断提高救援优先级,导致系统Recorvery

至于换Service的路径,出现两个投屏码是很明确的,因为标识的一部分就是路径,一个是被AMS拉起的旧App的Service,一个是新App启动的Service。

最终解决

知道了导致问题的根本原因,最终解决方案也就灵光乍现:
1.在无线投屏App还未形成一个稳定版本的时候,打成系统应用去掉persistent;但为了保活,附带一个保活App,20s周期检测无线投屏App是否运行中,挂掉的话拉起App,为了保证系统稳定性,重复拉起10次失败则退出保活。
2.无线投屏App加入保活机制,且加入4分钟连续crash5次,则终止保活的熔断机制,避免导致系统的Recorvery。

【Bug】一次Android系统应用32位升级到64位的踩坑记录相关推荐

  1. 网站服务器 64位,如何将win7系统从32位升级到64位_网站服务器运行维护,win7,32位,64位...

    win10如何重置此电脑_网站服务器运行维护 win10重置此电脑的方法是:1.点击[开始]按钮,进入设置:2.进入[更新和安全]选项,点击[恢复]:3.找到[重置此电脑],点击[开始]:4.选择[保 ...

  2. php int 32 64,php从32位升级到64位需要注意的几点

    php从32位升级到64位需要注意的几点 (2011-03-20 20:49:46) 标签: 杂谈 分类: php php升级到64位,代码要注意的问题: 1.整型问题. 32位操作系统,php用4个 ...

  3. 数据库32位升级到64位解决方案

    数据库32位升级到64位解决方案 一.背景介绍 突然发现本机64位操作系统下,装的是32位数据库,现想将32位库升级到64位数据库. 该操作同样适用于库损坏,只剩数据文件.控制文件.日志文件等文件的情 ...

  4. pcmark2 android测试,PCMark安卓版升级:支持64位ARM架构 存储测试为Storage 2.0

    作为一款跨平台的评测软件,PCMark的Android版本最近升级到了v3.0.4054.这个版本变化很大,官方支持64位ARM架构,跑分不能向下比较. ARM处理器长期支持64位,但支持的APP应用 ...

  5. 实战iOS应用从32位升级到64位

    苹果官方会在2015年2月1日不允许不支持arm64的应用的提交,这对我们这种开发移动应用产品的人来说是一把达摩克利斯之剑.我前面写过一篇文章<iOS上应用如何兼容32位系统和64位系统> ...

  6. oracle 10g 10.2.0.4 32位升级到64位

    32位升到64位有以下几种方法: 1.copy datafile后运行脚本升级 2.rman restore后运行脚本升级 3.通过exp/imp,expdp/impdp的transfer table ...

  7. php 32升64位,电脑怎么从32位升级到64位?

    笔者年轻时,在大学里读的是计算机科学与技术专业,关于电脑的位数问题,还是印象很深刻的.对于没有专业知识的人来讲,刚开始理解这个问题,可能会有一定的难度,笔者竭尽自己所能,力求把这个问题说明白,不周之处 ...

  8. win7升级php,win7系统如何从32位升级到64位

    下载win7 x64系统iso镜像到本地,右键使用WinRAR等工具解压出来. 将win7.gho文件和Onekey ghost工具放到同一目录,比如D盘,不要放C盘或桌面. 双击打开Onekey g ...

  9. 32位oracle10,Oracle10g32位升级到64位方法

    原环境windows 2003 Server 32位和Oracle10g 10.2.0 32位 IBM 3850两台+IBM DS3400磁盘阵列组成双机 升级之前很忐忑,因没有升级过,怕升级失败,因 ...

最新文章

  1. Tomcat - 都说Tomcat违背了双亲委派机制,到底对不对?
  2. JS 原型链图形详解
  3. 计算机视觉与深度学习 | Matlab实现单目视觉里程计基于SURF特征(代码类)
  4. windows安装ffmpeg
  5. 测试硬盘读写速度软件_Linux测试硬盘读写速度用什么命令
  6. 【Spring 5】响应式Web框架实战(上)
  7. Seaborn初学指南
  8. java.lang.NumberFormatException: For input string: “name”
  9. php-fpm with php-5.3.2 + APC
  10. Delphi全局热键的注册
  11. android关机背景,鍵盤消失后的Android白色背景
  12. python中的counter函数_Python的 counter内置函数,统计文本中的单词数量
  13. SysUtils.Trim、SysUtils.TrimLeft、SysUtils.TrimRight - 删除空格
  14. 美国转基因食品的生产现状
  15. McCabe环路复杂度计算方法
  16. oracle vm 强制关机,[已解决]Virtualbox安装archlinux过程中客户机和宿主机全部卡死...
  17. 用Git在阿里云下克隆时出现“Please make sure you have the correct access rights and the repository exists”错误
  18. UCN(User-Centric Networks,用户中心网络)
  19. ACW734. 能量石
  20. 「 MalabSimulink 」X0 returned by MATLAB S-function ‘NLSEF‘ in ‘ADRC_NN/S-Function1‘ must be a vector

热门文章

  1. “大服务器”拥有大智慧
  2. 疑难杂症篇(十八)--ROS系统中使用SLAM算法建图时出现地图漂移的几种原因
  3. 中级人工智能训练师认证题库
  4. 7、用css2和css3分别实现垂直居中和水平居中
  5. [附源码]Python计算机毕业设计OA自动化办公系统
  6. 第七讲(二):三层交换的原理及配置(不同vlan间通信)
  7. 如何让ipad 启动时默认横屏
  8. html语言教程 ppt,HTML语言基础入门课件.ppt
  9. Winphone开发之动态加载XAML
  10. H5页面拍照上传图片步骤