0
点赞
收藏
分享

微信扫一扫

将小米平板2从低版本安卓适配到高版本的若干技巧与思考

老罗话编程 2022-02-05 阅读 180

有个愿望:让小米平板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 小米平板2,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,我感觉它是很不成熟的版本,在它基础上进行适配,工作量巨大,并且有很多看不懂的问题,建议用  cm-13.0 分支,因为它已经在 lineage13.0 for latte (小米平板2的代号是 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.txt
    class main
    user root
    group root system
    oneshot

service boot_dmesg /system/bin/sh -c "dmesg -w > /cache/boot_dmesg.txt"
    class main
    user root
    group root system
    oneshot

on property:sys.boot_completed=1
    start boot_lc_main
    start 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 有唯一性,根据它就可以定位到具体的源码文件中具体的函数。

由于不知道生成规则,所以无法直接解码 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" 位于哪个动态链接库中:

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",但有这个 symbol 并不表示这个函数在这个文件中,有可能这个文件只是调用了这个函数,那么哪个文件才是被调用者呢?

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

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
......

可以看到第3行就是那个 symbol,它前面有一串十六进制数 00007a20,它表明这个 symbol 在这个so文件中对应着实实在在的一个函数;如果这个 symbol 前面没有十六进制字符串,则表明这个 symbol 对应的函数不在这个so文件而是在别的文件中。通过这个方法就可以找到我们要找的那个so文件。在咱们的例子中,这个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/ 中。

对 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 中。

以上是在 lineage13.0 中找到的这个函数,打开 Lineage14.1 中同名 cpp,发现尽管其中也有 GraphicBuffer 函数,但所有的 GraphicBuffer 函数中没有参数为 (unsigned int, unsigned int, int, unsigned int) 的。参照 lineage13.0 中函数内容,在 lineage14.1 中添加该参数的 GraphicBuffer 函数。

添加过程中要注意:这个函数怎么写得具体情况具体分析,即要实现你要的功能,还不能破坏原有的函数。

添加完,编译后,将 lineage14.1 中新生成的 out/target/product/latte/system/lib/libui.so 通过 REC 模式拷贝到平板中,再开机获取日志,可以看到那条 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. 小米平板2适配高版本安卓这个目标,给我提供了强烈的学习动力。前面那些摸索出来的方法和工具,以及很多不方便记录下来的小而杂的收获,都是利益于这个动力的推动。可以肯定地说,没有这个目标我无法坚持自学这么多东西,这点倒是很符合费曼学习法。

2. 没有基础知识储备,适配工作是很困难的,我缺乏的知识包括:C++、JAVA、Android系统原理,等等。这些决定了急于求成是不行的,有强烈动机当然好,但一直急功近利地解决眼前问题,恨不得能马上就见成果,是不现实的,欲速则不达,欲速往往是在走弯路。成功一般是给有准备的人,有强烈愿望而无积累的人虽然有时也能得到,但那要么是因为运气,要么只是得到小成功。

3. 做一件辛苦的事,冲着最终目标去当然是应该的,但设计好过程中的子目标也很重要。尤其对于系统适配这个不知道是否能成功的目标,如果最终目标没有达成,过程中也没有收获,那真是白白浪费了时间。

4. 做 lineage14.1 for latte 对于我这个新手来说不是好的选择。当然这是我后来意识到的,刚开始时选择这个目标没有错,因为它对我最有吸引力,但遇到 “Zygote  : Preloading shared libraries...” 问题后,我认为还是应该暂时停下来,换一个目标。因为适配 lineage14.1 for latte 对于初学者不是好课题,它的 vendor 没有源码,遇到问题往往只能来回试探,效率很低,运气成分很大;Android7 相比 Android6 变化比预想的要大,导致 lineage13.0 的代码和日志对于 14.1 来说参考性不足;小米平板2是一款比较老的设备,给它做适配的人很少,导致网上可以找到的参考资料很少。

我打算换 Android-x86_64 9.0 试试,等知识积累足够多了,再回来继续 lineage for latte 的适配。

举报

相关推荐

0 条评论