有个愿望:让小米平板2用上64位 Android9.0。因为64位 Android9.0 的 houdini 兼容性非常好,我曾经试过把 houdini9 植入 Android_x86-9.0 r2,然后安装到小米平板2中,发现APP运行很稳定,其它小米平板2安卓系统常见的APP闪退问题基本上都没有。只是因为显卡原因,Android_x86-9.0 r2 在小米平板2上只能运行在 nomodeset 模式下,视频靠CPU解码,导致系统卡顿很厉害。

目标很美好,但对于安卓适配新兵来说,实现这个目标还是要循序渐进。

我手头有能成功编译的 lineage13.0 for latte(小米平板2的代号是 latte)源码,lineage13.0 属于安卓6,为了缩小跨度,降低难度,我决定先从 lineage14.1 的小米平板2适配入手,因为 lineage14.1 属于安卓7,相比安卓6的变化比9要小一些。

虽然适配工作还没有完成,但过程中也有不少收获,总结一下。

一、下载源码

lineage14.1 的主源码可以按 LineageOS Wiki 上的说明下载,latte 的 vendor、device、kernel 则来自 Github( LineageOS Resources for Xiaomi Mi Pad 2 ),但配置 local_mainifest.xml 时要注意:不要使用 cm-14.1 分支的 vendor、device、kernel,它的BUG太多,基于它进行适配工作量巨大,并且会出现很多看不懂的问题。建议用 cm-13.0 分支,因为它已经在 lineage13.0 for latte上适配成功,本身不存在问题,所需要做的是对 lineage14.1 调试。

二、获取日志

最初编译出来的 lineage14.1 Rom 安装到小米平板2上多半无法成功运行,需要查看日志来分析原因,但系统如果没有运行到合适的阶段是无法通过 ADB 获取日志的,怎么办?群友 ygjsz 提供了一个方法。

在 init.rc 中加入以下代码:

# Take logs even when adb is not working.
# Thanks: markakash
# Add these lines to your init.rc file:service boot_lc_main /system/bin/logcat -f /cache/boot_lc_main.txtclass mainuser rootgroup root systemoneshotservice boot_dmesg /system/bin/sh -c "dmesg > /cache/boot_dmesg.txt"class mainuser rootgroup root systemoneshoton property:sys.boot_completed=1start boot_lc_mainstart boot_dmesg

这段代码在开机后会自动把日志记录到 cache 文件夹中。当系统启动失败后,长按电源键+音量增键,让平板进入 Recovery 模式,就能把日志拷贝出来分析。

三、处理 dlopen failed 错误

日志中常出现 dlopen failed 报错,形式为 cannot locate symbol "_ZNxxxx" referenced by "xxxxx.so",例如:

 dlopen failed: cannot locate symbol "_ZN7android13GraphicBufferC1Ejjij"  referenced by "/system/vendor/lib/hw/hwcomposer.gmin.so"

这里需要重点搞清 symbol "_ZN7androidxxxxxx" 是什么?简单说,它是系统在编译 so 时自动生成的某个函数的别名,这个函数在源码中本来有易理解的名称,但在编译时编译器给它生成了一个 symbol,这个 symbol 具有唯一性,生成规则与函数名称、参数个数和参数类型有关,系统运行时根据它就可以定位到具体的动态库中具体的函数。

解决这个报错的第一步是要定位这个函数,但由于不知道生成规则,所以无法直接解码 symbol 去定位函数源码,但我找到了一个方法。

报错中的 xxxxx.so 一般都位于 system/vendor/lib 中,因为用的是 lineage13.0 for latte 的 vendor, lineage14.1 中找不到的那个 symbol 在 lineage13.0 一定能找到,所以可以先到 lineage13.0 中找,确定函数位于哪个源码文件中,然后再看 lineage14.1 中这个文件为什么没有这个函数。

就以 dlopen failed: cannot locate symbol "_ZN7android13GraphicBufferC1Ejjij" referenced by "/system/vendor/lib/hw/hwcomposer.gmin.so" 举例。

先看 "_ZN7android13GraphicBufferC1Ejjij" 位于 lineage13.0的哪个动态链接库中:

butterfly@Amazon:~/lineage/13.0/out/target/product/latte/system/lib$ find -type f -name '*.so' | xargs grep "_ZN7android13GraphicBufferC1Ejjij"

出现以下结果:

匹配到二进制文件 ./libstagefright_wfd.so
匹配到二进制文件 ./libshim_camera.so
匹配到二进制文件 ./libgui.so
匹配到二进制文件 ./hw/camera.gmin.so
匹配到二进制文件 ./arm/libgui.so
匹配到二进制文件 ./arm/libui.so
匹配到二进制文件 ./arm/nb/libgui.so
匹配到二进制文件 ./arm/nb/libui.so
匹配到二进制文件 ./libui.so
匹配到二进制文件 ./libwebviewchromium_plat_support.so

说明这些so动态链接文件中都有 "_ZN7android13GraphicBufferC1Ejjij",但有并不表示这个函数在这个文件中,有可能这个文件只是调用了这个函数而已,那么哪个才是被调用的文件呢?

对这些文件逐个执行下面命令:

butterfly@Amazon:~/lineage/13.0$ ~/Android/Sdk/ndk/16.1.4479499/toolchains/x86_64-4.9/prebuilt/linux-x86_64/bin/x86_64-linux-android-gcc-nm -D ~/lineage/13.0/out/target/product/latte/system/lib/libui.so | grep GraphicBuffer | sort

命令中,“GraphicBuffer”是 symbol 中一段有字面含义的文字,它一般是那个函数名称中的一部分。

出现下面结果:

00007930 T _ZN7android13GraphicBufferC1Ev
00007930 T _ZN7android13GraphicBufferC2Ev
00007a20 T _ZN7android13GraphicBufferC1Ejjij
00007a20 T _ZN7android13GraphicBufferC2Ejjij
00007b30 T _ZN7android13GraphicBuffer8initSizeEjjij
00007c00 T _ZN7android13GraphicBufferC1EjjijjP13native_handleb
......

可以看到第三行就是那个 symbol,它前面有一串十六进制数 00007a20,这表明这个 symbol 在这个so文件中对应着有实实在在的函数;如果这个 symbol 前面没有十六进制字符串,则表明这个 symbol 出现在这个文件中只是因为这个文件调用了别的库中的这个函数。通过这个方法可以找到有这个函数的so文件,在这个例子中就是 system/lib/libui.so(其实有一个方法可以减轻工作量,不用对每个so文件都检查:用 Cutter 工具打开 hwcomposer.gmin.so,查看它的依赖库,仅对是其依赖库的so文件进行分析)。

上面的命令中,x86_64-linux-android-gcc-nm 的路径是计算机中它的安装位置,我的安装在 ~/Android/Sdk/ndk/16.1.4479499/toolchains/x86_64-4.9/prebuilt/linux-x86_64/bin/ 中。

下面查这个so文件的确切函数名称和参数。对 system/lib/libui.so 执行下面这条命令(与上一条很像,但多一个 “-C”):

butterfly@Amazon:~/lineage/13.0$ ~/Android/Sdk/ndk/16.1.4479499/toolchains/x86_64-4.9/prebuilt/linux-x86_64/bin/x86_64-linux-android-gcc-nm -C -D ~/lineage/13.0/out/target/product/latte/system/lib/libui.so | grep GraphicBuffer | sort

出现下面结果:

00007930 T android::GraphicBuffer::GraphicBuffer()
00007930 T android::GraphicBuffer::GraphicBuffer()
00007a20 T android::GraphicBuffer::GraphicBuffer(unsigned int, unsigned int, int, unsigned int)
00007a20 T android::GraphicBuffer::GraphicBuffer(unsigned int, unsigned int, int, unsigned int)
00007b30 T android::GraphicBuffer::initSize(unsigned int, unsigned int, int, unsigned int)
00007c00 T android::GraphicBuffer::GraphicBuffer(unsigned int, unsigned int, int, unsigned int, unsigned int, native_handle*, bool)
00007c00 T android::GraphicBuffer::GraphicBuffer(unsigned int, unsigned int, int, unsigned int, unsigned int, native_handle*, bool)
00007d00 T android::GraphicBuffer::GraphicBuffer(ANativeWindowBuffer*, bool)
00007d00 T android::GraphicBuffer::GraphicBuffer(ANativeWindowBuffer*, bool)
00007e10 T android::GraphicBuffer::~GraphicBuffer()
00007e10 T android::GraphicBuffer::~GraphicBuffer()
......

找到 00007a20 那行,其对应的是 GraphicBuffer::GraphicBuffer(unsigned int, unsigned int, int, unsigned int),这就是那个函数的名称与参数。

然后查找函数源码。执行下面命令:

butterfly@Amazon:~/lineage/13.0$ find -type f -name '*.mk' | xargs grep "LOCAL_MODULE := libui"

出现:

./frameworks/native/libs/ui/Android.mk:LOCAL_MODULE := libui

由此可知 libui 的源码在 frameworks/native/libs/ui/ ,进到这个文件夹,运行:

butterfly@Amazon:~/lineage/13.0/frameworks/native/libs/ui$ find -type f -name '*.cpp' | xargs grep "GraphicBuffer::GraphicBuffer("

出现:

./GraphicBuffer.cpp:GraphicBuffer::GraphicBuffer()
./GraphicBuffer.cpp:GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight,
./GraphicBuffer.cpp:GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight,
./GraphicBuffer.cpp:GraphicBuffer::GraphicBuffer(ANativeWindowBuffer* buffer, bool keepOwnership)

至此,可以确定那个 symbol 对应的函数在 frameworks/native/libs/ui/GraphicBuffer.cpp 中。

打开 ~/lineage/13.0/frameworks/native/libs/ui/GraphicBuffer.cpp,可以找到 GraphicBuffer::GraphicBuffer(unsigned int, unsigned int, int, unsigned int)。打开 Lineage14.1 中同名 文件,发现尽管其中也有 GraphicBuffer::GraphicBuffer 函数,但所有的 GraphicBuffer 函数中没有参数为 (unsigned int, unsigned int, int, unsigned int) 的。

现在可以动手解决 dlopen failed 错误了:参照 lineage13.0 中函数内容,在 lineage14.1 中添加相同参数的 GraphicBuffer 函数。这个函数怎么写要具体情况具体分析,即要实现其应有的功能,又不能破坏原有的函数。

添加完,编译后, 让平板进入REC 模式,将 lineage14.1 中新生成的 out/target/product/latte/system/lib/libui.so 拷贝到平板中,再开机获取日志,可以看到那条 dlopen failed 报错不再出现了。

其它 dlopen failed 错误也可以参考这个方法解决。

但有一个类似报错折腾了我很长时间,需要用到不同的处理方法。这个报错形式是:Can't locate symbol "ufoInitPlatform" referenced by ......

这个问题折磨我很久才发现是因为系统中有两个 libskuwa.so,一个在 system/lib 中,一个在 vendor/lib 中,默认会调用 system/lib 下那个,而那个是没有 ufoInitPlatform 函数的。解决办法就是把 system/lib 下面那个 libskuwa.so 删除。

通过前面这些处理方法,我成功地实现了小米平板2的开机动画显示。

四、目前遇到的困难

适配工作还在艰难进行中,不断遇到新的问题。目前主要有三个问题不知道该怎么分析。

1.keymaster

日志中有下面报错重复出现:

3336  3336 I keystore: Found keymaster0 module Keymaster Intel MEI HAL, version 3
3336  3336 I SoftKeymaster: system/keymaster/soft_keymaster_device.cpp, Line 131: Creating device
3336  3336 D SoftKeymaster: system/keymaster/soft_keymaster_device.cpp, Line 132: Device address: 0xf6eb0000
3336  3336 I ITEEKeyMaster: intel_mei_km_open called
3336  3336 I ITEEKeyMaster: Connected to keymasterd successfully
3336  3336 I ITEEKeyMaster: get_supported_classes called
2904  3337 E ITEEKeyMasterD: Could not acquire wake lock ret = -2
3336  3336 E ITEEKeyMaster: Received error -2 on command 0 from FW
3336  3336 E ITEEKeyMaster: CMD_ID_GET_SUPPORTED_CLASSES failed
3336  3336 E keystore: Error opening keystore keymaster0 device.
3336  3336 E keystore: keystore keymaster could not be initialized; exiting

我能跟踪到报错来自 keystore.gmin.so,由于没有它的源码,无法进一步分析。

尝试用 keystore.default.so 替换 keystore.gmin.so,日志中报错消失,变成下面的内容,不确定是否解决。

3045  3045 I keystore: Found keymaster0 module Keymaster OpenSSL HAL, version 2
3045  3045 I SoftKeymaster: system/keymaster/soft_keymaster_device.cpp, Line 131: Creating device
3045  3045 D SoftKeymaster: system/keymaster/soft_keymaster_device.cpp, Line 132: Device address: 0xf6eb0000
3045  3045 I keystore: Keymaster0 module is software-only.  Using SoftKeymasterDevice instead.

2.sensorhubd

报错如下,不知道该如何分析。

3335  3335 F libc    : Fatal signal 11 (SIGSEGV), code 1, fault addr 0x0 in tid 3335 (sensorhubd)
3338  3338 E         : debuggerd: Unable to connect to activity manager (connect failed: Connection refused)
3338  3338 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
3338  3338 F DEBUG   : LineageOS Version: '14.1-20220120-UNOFFICIAL-latte'
3338  3338 F DEBUG   : Build fingerprint: 'Xiaomi/latte/latte:5.1/LMY47I/V8.2.2.0.LACCNDL:user/release-keys'
3338  3338 F DEBUG   : Revision: '0'
3338  3338 F DEBUG   : ABI: 'x86'
3338  3338 F DEBUG   : pid: 3335, tid: 3335, name: sensorhubd  >>> /system/bin/sensorhubd <<<
3338  3338 F DEBUG   : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
3338  3338 F DEBUG   :     eax 00000001  ebx f733fdcc  ecx 00000000  edx f7747371
3338  3338 F DEBUG   :     esi f6d1900c  edi f745f010
3338  3338 F DEBUG   :     xcs 00000023  xds 0000002b  xes 0000002b  xfs 00000003  xss 0000002b
3338  3338 F DEBUG   :     eip f6dd92ce  ebp ffb37a18  esp ffb37a18  flags 00010286
3338  3338 F DEBUG   :
3338  3338 F DEBUG   : backtrace:
3338  3338 F DEBUG   :     #00 pc 000062ce  /system/lib/libcutils.so (android_atomic_inc+14)
3338  3338 F DEBUG   :     #01 pc 00008f6d  /system/lib/libaware_client.so (_ZNK7android12SharedBuffer7acquireEv+29)
3338  3338 F DEBUG   :     #02 pc 00005470  /system/lib/libaware_client.so (_ZN7android7String8C1Ev+32)
3338  3338 F DEBUG   :     #03 pc 000074b1  /system/lib/libEGL.so
3338  3338 F DEBUG   :     #04 pc 0000b91b  /system/bin/linker (__dl__ZN6soinfo13call_functionEPKcPFvvE+139)
3338  3338 F DEBUG   :     #05 pc 0000b803  /system/bin/linker (__dl__ZN6soinfo10call_arrayEPKcPPFvvEjb+323)
3338  3338 F DEBUG   :     #06 pc 00009423  /system/bin/linker (__dl__ZN6soinfo17call_constructorsEv+371)
3338  3338 F DEBUG   :     #07 pc 0000937a  /system/bin/linker (__dl__ZN6soinfo17call_constructorsEv+202)
3338  3338 F DEBUG   :     #08 pc 0000937a  /system/bin/linker (__dl__ZN6soinfo17call_constructorsEv+202)
3338  3338 F DEBUG   :     #09 pc 0000937a  /system/bin/linker (__dl__ZN6soinfo17call_constructorsEv+202)
3338  3338 F DEBUG   :     #10 pc 0000937a  /system/bin/linker (__dl__ZN6soinfo17call_constructorsEv+202)
3338  3338 F DEBUG   :     #11 pc 0000937a  /system/bin/linker (__dl__ZN6soinfo17call_constructorsEv+202)
3338  3338 F DEBUG   :     #12 pc 00010ec2  /system/bin/linker (__dl__ZL29__linker_init_post_relocationR19KernelArgumentBlockj+7746)
3338  3338 F DEBUG   :     #13 pc 0000efb6  /system/bin/linker (__dl___linker_init+630)
579  3338  3338 F DEBUG   :     #14 pc 0001c4f3  /system/bin/linker (__dl__start+35)

将 system/bin/sensorhubd 删除后报错消失,但问题肯定是没有解决。

3.Preloading shared libraries

前面两个报错凑乎着应付过去后,日志停在下面这行,也不知道该如何分析,需要恶补一些安卓系统启动方面的知识。

3054  3054 I Zygote  : Preloading shared libraries...

五、几点体会

不知道最终能否成功,但时不时总结一下很有必要。

  1. 小米平板2适配高版本安卓这个目标,给我提供了强烈的学习动机。前面那些摸索出来的方法和工具,以及很多不方便记录下来的小而杂的收获,都是得益于这个动机的推动。可以肯定地说,没有这个目标我无法坚持自学这么多东西,这点倒是很符合费曼学习法。
  2. 没有足够的知识储备,包括C++、JAVA、Android系统原理等,进行适配工作是很困难的。这些决定了不能急于求成,有强烈动机当然好,但一味急功近利地解决眼前问题,恨不得马上就见成果,是不现实的,欲速则不达,欲速往往是在走弯路。应该停下来补习补习相关知识。
  3. 做一件辛苦的事,冲着最终目标去当然是应该的,但设计好过程中的子目标也很重要。尤其对于系统适配这个不知道是否能成功的目标,如果最终目标没有达成,过程中也没有收获,那真是白白浪费了时间。对于这个项目,设置一些学习目标作为子目标是不错的选择。
  4. 做 lineage14.1 for latte 对于新手来说不是好的选择。刚开始时选择这个目标没有错,因为它对我最有吸引力,但遇到这么多不能克服的困难后,应该暂停,换一个目标。因为 lineage14.1 for latte 的 vendor 没有源码,遇到问题往往只能来回试探,效率很低,运气成分很大;Android7 相比 Android6 的变化比预想的要大,导致 lineage13.0 的代码和日志对于 14.1 来说参考性不足;小米平板2是一款比较老的设备,给它做适配的人很少,导致网上可以找到的参考资料很少。

我打算换个系统做学习样例,等知识积累足够多了,再回来继续做 lineage for latte 的适配。

希望高手大神们看到这篇文章,能给予指点,你们的一两句话也许对我就是拨云见日。拜托!

将小米平板2从低版本安卓适配到高版本的若干收获与思考相关推荐

  1. 小米平板2刷哪个系统更流畅_终于给小米平板1刷上第三方安卓7.1系统了。一个字很流畅,性能几乎比安卓4.4.4底包的miui9多一万多分。 小米平板2安卓效率...

    小米平板2安卓系统值得买吗 ZOL首页 产品报价 论坛ZOL问答 手机论坛 笔记本论坛 摄影论坛 硬件论坛 游戏论坛 软件论坛 GPS论坛 技术论坛 办公论坛 手机客户端 数码爱好者必备神器 中关村在 ...

  2. 低版本cad如何打开高版本图纸?不用升级软件也可以搞定

    有朋友提问:低版本cad如何打开高版本图纸?遇上这种情况的朋友,一般都会陷入两难境地.想要安装一个最新版的cad软件吧,既费时又麻烦,不这么解决又打不开图纸,真的很烦恼. 其实你只要转换一下思维方式, ...

  3. Anaconda由低版本python 升级到高版本

    Anaconda 由低版本python 升级到高版本 升级python 3.7 至 python 3.9 $conda create -n Py39 python=3.9 $conda activat ...

  4. 低版本mysql数据导入高版本_将高版本mysql数据库的数据导入低版本mysql中

    前言 最近做了个网站,准备放到虚拟主机上的时候,发现本地数据库是mysql5.6,服务器上的mysql是5.0的.于是尝试导出数据,结果,导入的数据不是出错,就是各种乱码.折腾了好久之后,终于找到了解 ...

  5. 低版本3dmax怎么打开高版本文件?3种简单的方法拿走不谢

    低版本3dmax怎么打开高版本文件?除了下载相应版本3dmax版本进行转化,还可以使用网页工具.免费转化插件进行转化,简单快捷,一起来看看使用方法. 一.渲云网页版高版本3dmax文件转低版本 一.点 ...

  6. 数领科技|学会用低版本solidworks软件打开高版本sw文件

    对各位使用solidworks进行设计的工程师而言,在工作中我们常常会遇到这样的情况,当我们将自己做好的SOLIDWORKS模型发给别人时,却因为对方的SOLIDWORKS版本较低,而打不开模型文件. ...

  7. CAD高版本窗体阵列LISP_CAD高版本窗体阵列LISP_AutoCAD高版本怎么把阵列对话框调出来?...

    AutoCAD低版本阵列ARRAY命令的参数设置用是对话框形式,到了高版本使用功能区界面后,不再使用对话框,而是会在功能区弹出一个"阵列"选项卡,如下图所示. 新版阵列还有一个不同 ...

  8. CAD高版本窗体阵列LISP_AutoCAD高版本怎么把阵列对话框调出来?

    AutoCAD低版本阵列ARRAY命令的参数设置用是对话框形式,到了高版本使用功能区界面后,不再使用对话框,而是会在功能区弹出一个"阵列"选项卡,如下图所示. 新版阵列还有一个不同 ...

  9. linux 下如何升级CMAKE?(安装指定版本cmake)(高版本cmake)(不删除之前的,可以用软连接)

    文章目录 原 20210929 但是卸载不知道怎么卸载,尴尬了.... 20220209 貌似原来cmake不需要卸载,直接安装新的然后做个软连接就好了 原 先把当前版本全卸载 # sudo apt ...

最新文章

  1. 卷积神经网络的复杂度分析
  2. catia曲面扫掠命令详解_Catia曲面基础与工程实践 3.2 扫掠曲面
  3. 关于easyui的layout的region的resize的问题(自适应浏览器)
  4. torchvision.transforms包的使用
  5. java只有高跟很高_女明星们为了合影不输,练就踩高跷本领,鞋跟一个比一个高!...
  6. 好程序员分享大势所趋 HTML5成Web开发者最关心的技术
  7. native react 折线图_react native中使用echarts
  8. linux 二级域名设置
  9. python123输出hello world_Python基础:输入与输出
  10. dataframe 空值替换为0_Python数据结构大结局:DataFrame
  11. tomcat启动报错,找不到相应的 queue,从而引发内存泄漏
  12. 微信怎么at所有人_变速箱报废、发动机故障、车门下沉,全新马自达3到底怎么了?...
  13. 商业需求分析(BRD)模板
  14. PR期刊投稿要求(整体要求、Title page、Highlight)
  15. 《跨界杂谈》(信息)安全
  16. 如何用计算机算工资,个人工资计算器避税
  17. 淘客基地教你如何推广淘客小程序
  18. [完全图解].NET Croe 使用JWT验证签名
  19. 正交匹配追踪算法OMP(Orthogonal Matching Pursuit)
  20. 校园、同城二手交易信息小程序 毕业设计毕业论文 开题报告和效果图参考(基于微信小程序毕业设计题目选题课题)

热门文章

  1. [HashMap]——遍历HashMap
  2. 陈宁计算机辅助设计说课稿,《丑小鸭》说课稿
  3. 【装机必看】主板该怎么选
  4. Glide系列-Webp如何被解析使用
  5. 通过深度学习从胸部X射线检测肺炎
  6. 用PS把一张图片的边缘做成渐变的效果
  7. 利用canvas绘制的拓扑动画效果
  8. While True和Try except
  9. MMSegmentation 训练测试全流程
  10. HTML怎么去除描边,css中文字如何描边?