为新设备编写Recovery device tree
一些自序
这篇教程中可能有些不对的地方,请各位指正,我也刚接触 Android 设备测开发两年,而且只有在节假日的时候有空一个人瞎捣鼓,为了薅 mjw 羊毛,故写这篇教程,而且我个人更推荐 Aosp Recovery 刷写第三方 ROM,TWRP 可能多多少少有些问题。Root 之类的不需要 TWRP 也可以,可以找到原机 boot 使用 Magisk 进行修补,也可以使用 KernelSU。另外我的语文功底不是很好,写不出辞藻,只能是北方地道的大白话,见谅哈 🤣///
准备工作
1.一颗勇敢的、不怕困难的强大心脏。两颗心好勇敢 – Falling in love
2.电脑配置:
硬件 | 需求 |
---|---|
CPU | 四核或更多 |
内存 | 8G 或更多 |
硬盘 | 至少 80G 空余空间 |
系统 | WSL 或实体 Linux,建议 Debian 系 |
3.足够的空闲时间。
4.Vscode 和 🤏Linux 知识储备
5.一台完好无损的新设备。
6.设备原厂的 boot/recovery/vendor_boot 镜像。(推荐 recovery 分区,因为里面包含一些启动必须的.rc(run command),如果没有该分区,寻找包含官方 recovery 内容的分区。值得注意的是部分设备 recovery 合并到了 boot 中,或是存在于 vendor_boot)
7.查看自己的设备出厂 Android 版本并确定用途,是用于官方 ROM 还是第三方类原生。出厂版本为 9 及以上的可以使用 twrp-11 及以上分支,9 以下使用 twrp-9 分支。
配置环境
安装依赖
1 | sudo apt update |
必要时可以阅读Git 和 Repo
配置 Git 和 Repo
安装完依赖后,在终端执行。
1 | git config --global user.name "your username" |
同步 TWRP 源码
在自己方便的地方,建立一个目录。
1 | mkdir twrp |
根据自己在准备工作中确定的分支同步相应的源码。
TWRP-9
1 | repo init --depth=1 -u https://github.com/minimal-manifest-twrp/platform_manifest_twrp_omni.git -b twrp-9.0 |
TWRP-11
1 | repo init --depth=1 -u https://github.com/minimal-manifest-twrp/platform_manifest_twrp_aosp.git -b twrp-11 |
TWRP-12.1
1 | repo init --depth=1 -u https://github.com/minimal-manifest-twrp/platform_manifest_twrp_aosp.git -b twrp-12.1 |
编写设备树
最基本的开机部分我通过 Redmi K50 做示例,并选取 TWRP-12.1 分支。
解密部分会单独成一个章节并分为高通和联发科。
获取资料
小米设备一般可以在Xiaomirom中的线刷包获取 boot/recovery/vendor_boot 镜像,其它设备请自行寻找。
安装工具和依赖
将准备工作中准备的 boot/recovery/vendor_boot 镜像解包,可以用任何工具,我这里使用Android_boot_image_editor按照自述文件进行依赖安装操作:
Mac(Intel)
1 | brew install lz4 xz dtc openjdk@17 |
Linux:
1 | sudo apt update |
Common:
1 | git clone https://github.com/cfig/Android_boot_image_editor |
解包
请注意”/“表示或的意思,请不要误以为是一个东西。
如果不确定分区寻找的是否正确,请尝试将可以的分区进行解包,并查看其
ramdisk
内是否包含recovery
字样的文件
将你准备好的boot
/recovery
/vendor_boot
复制到该目录,你可以使用 GUI 或者 CLI。如:
1 | cp <出厂boot/recovery/vendor_boot的绝对路径> boot/recovery/vendor_boot.img |
我这里以 Redmi K50 为例
1 | 10:12:13.188 [main] INFO cfig.bootimg.v3.VendorBoot - |
解包成功后终端会打印出以上信息,其中包含vendor_boot
目录结构,我们只需要ramdisk
里的部分内容就好了。
1 | ls build/unzip_boot/root.1 # 请根据自己的分区和设备判断ramdisk的命名,一般都直接是ramdisk |
不出意外的话会输出以下内容
1 | xiaolegun@xiaoleGundeMac-Pro ~/github/dump/bootedit master ? ls build/unzip_boot/root.1 |
如果包含 init.recovery*的字样,恭喜你成功找到了正确的分区。将这个目录复制到一个自己方便的地方。
至此资料已经大致齐全了,解密的资料会在解密章节来讲解如何获取。
初始化目录结构
在 twrp 源码根目录输入
1 | mkdir -p device/vendor/codename |
vendor
指的是供应商,如 xiaomi、oneplus、huawei、vivo、realme。codename
指的是设备代号,小米设备可以在Xiaomirom查询,其它设备自行查询亦或是在开发者选项勾选 USB 调试通过 adb 尝试获取。
1 | adb shell getprop ro.product.manufacturer # 供应商 |
我的 Redmi K50 会打印以下信息
1 | xiaolegun@xiaoleGundeMac-Pro ~/github/dump/bootedit master ? adb shell getprop ro.product.manufacturer |
我的 IQOO NEO5 Lite 会打印以下信息
1 | xiaolegun@xiaoleGundeMac-Pro ~/github/tmp/device/xiaomi/rubens adb shell getprop ro.product.manufacturer |
一般只要不是山寨机,小作坊出来的机子,用这两条命令应该是可以正确获取的。
初始化构建时必要的文件
无论是 AOSP device tree 亦或是 TWRP device tree 都基本类似于以下结构。
1 | / |
我们把这样的结构称为骨架树(skeleton tree)。
接下来我们按照以上结构为自己的设备写一份 Recovery device tree。
在终端使用touch
命令创建必要的文件。
1 | touch Android.mk AndroidProducts.mk BoardConfig.mk device.mk twrp_rubens.mk |
不出意外的话会打印出以下信息
1 | xiaolegun@xiaoleGundeMac-Pro ~/github/tmp/device/xiaomi/rubens touch Android.mk AndroidProducts.mk BoardConfig.mk device.mk twrp_rubens.mk |
可以下一步了~
初始 Android.mk 文件
打开 Vscode 并打开设备树目录,你足够强也可以使用 nano、vim 编辑。
选中 Android.mk 并按照自己的资料复制并更改以下内容
1 | LOCAL_PATH := $(call my-dir) |
codename
是需要你修改的部分
我修改后是这样的
1 | LOCAL_PATH := $(call my-dir) |
初始 AndroidProducts.mk 文件
选中 AndroidProducts.mk 并按照自己的资料复制并更改以下内容
1 | PRODUCT_MAKEFILES := \ |
我修改后是这样的
1 | PRODUCT_MAKEFILES := \ |
初始 BoardConfig.mk 文件
将之前准备的
ramdisk
文件夹用新窗口打开
1.找到目录下的 prop.default 或含 prop 字样的文件。以下注释类似 ro.*.*的就是从 prop 中获取的
2.解包 boot/recovery/vendor_boot 的 json
例如 build/unzip_boot/boot.json
选中 BoardConfig.mk 并按照自己的资料复制并更改以下内容
1 | DEVICE_PATH := device/vendor/codename |
无注释版
1 | DEVICE_PATH := device/vendor/codename |
这块写了好久我也不确定对不对,请各位指正,尤其是 Kernel 部分。
初始 device.mk 文件
- 准备好 prop.default
选中 AndroidProducts.mk 并按照自己的资料复制并更改以下内容
1 | DEVICE_PATH := device/xiaomi/rubens |
- A/B 无缝更新部分高通和联发科不一样,联发科设备可以照抄,注意 system/bin/mtk_plpath_utils 就好。高通参考venus twrp tree,并且高通的bootctrl和gpt-utils可以从 CLO 拿。
无注释版
1 | PRODUCT_SHIPPING_API_LEVEL := 31 |
初始 twrp_rubens.mk 文件
选中 AndroidProducts.mk 并按照自己的资料复制并更改以下内容
1 | # Inherit from common AOSP config |
无注释版
1 | # Inherit from common AOSP config |
初始 recovery 目录
拿出我们好久之前准备的 ramdisk 叭
通过tree
命令我们可以查看 ramdisk 的树状图,可以很清晰的查看包含的文件
1 | ├── acct |
因为避免教程时效性,我们尽可能选取 prebuilt 的办法,如 boot 和 mtk_plpath_utils 都采用预编译。
里面有很多文件是没用的,整理一下,对我们有用的只有这些
1 | . |
将它们按照以下操作复制
- first_stage_ramdisk –> recovery/root/first_stage_ramdisk
- init.recovery.mt6895.rc –> recovery/root/init.recovery.mt6895.rc
- lib –> recovery/root/lib
- system –> recovery/root/system (security 和 hw 文件夹里的内容作为备用,不要复制进去)
得到以下目录结构
1 | . |
打开 init.recovery.mt6895.rc 添加
1 | on boot |
例如
1 | on post-fs |
接下来可以开始编译咯~~
编译
编译前请检查注释是否全部删除
twrp 源码根目录执行
1 | . build/envsetup.sh |
不出意外的话一路绿灯,如果有报错的话请发邮箱至 1592501605@qq.com 咨询我
产物在 out/target/product/codename/vendor_boot.img\boot.img\recovery.img
手机重启到 fastboot 模式,使用 fastboot flash 刷写,例如
1 | fastboot flash vendor_boot /Users/xiaolegun/Downloads/vendor_boot.img |
则输出
1 | ERROR: could not clear input pipe; result e0005000, ignoring... |
就可以重启按电源加音量上尝试了,我自己是一次点亮,但有 bug。
修 BUG
修复 USB
我看到 twrp 控制台有很多报错,第一步肯定是先获取日志,使用logcat
命令获取,在此之前先使用adb devices
检测一下设备 usb 是否工作正常。
1 | xiaolegun@xiaoleGundeMac-Pro ~ adb devices ✔ 10208 10:05:31 |
可以看到adb
没有检测到设备,原因一般是 usb 的配置不对,但 usb 是好的。这时可以尝试关闭 mtp,再开启,如果电脑有反应,那么就可以继续下一步了。
在设备树中的 recovery/root,创建 init.recovery.usb.rc 的文件
1 | touch recovery/root/init.recovery.usb.rc |
并导入以下内容
1 | on property:sys.usb.config=mtp,adb |
这是我从我维护的 MI 6X 的 device tree 拿过来的,可以在联发科设备上正常工作。这段 shell 的大致意思就是在 usb 配置节点写入 mtp 和 adb 同时工作的字符串,这样 adb 就能顺利工作了。
此外打开原厂 init.rc 修正一下 usb 设备的 id
1 | MIUI ADD: START |
将/sys/class/android_usb/android0/idVendor
和write /sys/class/android_usb/android0/idProduct
后面的 id 分别替换到/config/usb_gadget/g1/idVendor
和/config/usb_gadget/g1/idProduct
处理完就是这样的。
1 | on property:sys.usb.config=mtp,adb |
接下来开机之后 adb 和 mtp 就能同时工作了。
修复 Resetprop
接下来就可以修复其它 bug 了,我看到控制台有一堆红色的报错:
1 | E:Unknown File System:'/mi_ext' |
这三个报错中第二个最好修,第一个次之,第三个与解密有关,放在另一个大章节讲。
第二个报错只需要在 BoardConfig.mk 中添加
1 | # Tool |
不用编译测试直接秒杀
修复挂载
1 | E:Unknown File System:'/mi_ext' |
我们打开 recovery/root/system/etc 中的 recovery.fstab,发现里面有很多空格,我们先将它格式化一下,有实力的可以把后面的 flag 也对齐整理好。
我比较没实力,简单格式化了一下,是这样的
1 | # 1 "vendor/mediatek/proprietary/hardware/fstab/mt6895/fstab.in.mt6895" |
这里面有很多无用分区,我们在 twrp 中操作不到,但是我也是第一次适配 MTK 设备,所以不“瞎说”误人子弟了,先把 bug 修好。
像 xiaomi 这样常见的挂载错误,我自己一般都是尝试把/mnt/vendor 使用 vscode 全部删除,让我们尝试一下。并且要把这一行删除,这行在我看来是重复定义(?而且也没指定分区格式。
1 | /mnt/vendor/mi_ext /mi_ext none ro,bind wait,nofail |
整理完之后是这样的
1 | # 1 "vendor/mediatek/proprietary/hardware/fstab/mt6895/fstab.in.mt6895" |
让我们编译试一下,值得注意的是,twrp 每次更改最好删除 out 重新编译,不要使用如make installclean
等命令,可能会开机黑屏或更改不生效。
经过一分钟,编译好了,刷入,开机,启动!可以看见挂载成功被修好。
修复屏幕显示
可以看到我们现在屏幕的顶部的时间被摄像头挖孔挡住了。我们使用两个偏移 Flag 来修复它。
在 BoardConfig.mk 中,加入以下 flag
1 | TW_Y_OFFSET := 106 #导航栏向上 |
修复振动
仅供参考,研究半天发现只有解密功能正常才能修振动,不然会冻屏!!!
不一定正确,也不一定普遍,仅供指路
老设备一般都是正常的,不需要额外处理,新设备使用 aidl hal 需要我们自己处理一下让 service 正常工作。
挂载 vendor,使用 twrp 的文件管理,在 vendor/etc/vintf/manifest 找到含有 vibrator 字样的文件。打开之后检查是否类似这样的文段
1 | <manifest version="1.0" type="device"> |
那恭喜你,找对了,我的文件名是vendor.xiaomi.hardware.vibratorfeature.service.xml
将它使用adb pull
放到电脑上自己方便的位置备用。
1 | xiaolegun@xiaoleGundeMac-Pro ~/github/$/mjw adb pull /vendor/etc/vintf/manifest/vendor.xiaomi.hardware.vibratorfeature.service.xml . |
不看扩展名,到 vendor/bin/hw/找到同名的 service 同样利用adb pull
放电脑备用。比如我的叫vendor.xiaomi.hardware.vibratorfeature.service
然后到 vendor/etc/init/找到同名.rc 文件,拉到电脑上,在里面加一条。并在定义 service 的最下面加上seclabel u:r:recovery:s0
可以被 recovery 使用。
1 | on boot |
例如
1 | on post-fs-data |
此外这种新设备在内核里还有振动模块需要加载,请到/vendor/modules/1.1/中寻找,例如我这台设备是haptic.ko
,用 adb pull 拉取到电脑上,然后
- haptic.ko –> recovery/root/vendor/modules/1.1
至此所有必要的资料就准备好了。可能开机后仍然不工作,需要抓取 log 补缺失的 library,当然这是后话了。
开干!
在 BoardConfig.mk 中加入以下 flag
1 | TW_SUPPORT_INPUT_AIDL_HAPTICS := true |
将备用文件按照以下操作复制
vendor.xiaomi.hardware.vibratorfeature.service.xml –> recovery/root/vendor/etc/vintf/manifest
vendor.xiaomi.hardware.vibratorfeature.service –> recovery/root/vendor/bin/hw
vendor.xiaomi.hardware.vibratorfeature.service.rc –> recovery/root/vendor/etc/init
之后编译,刷入,开机,喜提冻屏……
问题不大,使用adb logcat
抓取日志
1 | adb logcat > log.log |
打开 log,搜索F linker
、linker
、library
、beginning of crash
这几个关键词,我搜索第一个就出来了。
1 | F linker : CANNOT LINK EXECUTABLE "/vendor/bin/hw/vendor.xiaomi.hardware.vibratorfeature.service": library "android.hardware.vibrator-V1-ndk_platform.so" not found: needed by main executable |
这段报错的意思就是vendor.xiaomi.hardware.vibratorfeature.service
找不到android.hardware.vibrator-V1-ndk_platform.so
这个库,所以无法正常工作。
打开 BoardConfig.mk 加入以下 flag
1 | # Library |
再次编译尝试
开机之后依然冻屏,继续抓 log,查找
1 | F linker : CANNOT LINK EXECUTABLE "/vendor/bin/hw/vendor.xiaomi.hardware.vibratorfeature.service": library "vendor.hardware.vibratorfeature.IVibratorExt-V1-ndk_platform.so" not found: needed by main executable |
vendor.hardware.vibratorfeature
字样和我们之前导入的文件长得差不多,一般都是供应商专有文件,所以去设备提取,路径在 vendor/lib64 里,虽然设备冻屏了,但我们可以用adb pull
直接拉取到电脑。就像这样。
1 | xiaolegun@xiaoleGundeMac-Pro ~/github/$/mjw adb shell mount /vendor |
将这个文件按照以下提示操作
- vendor.hardware.vibratorfeature.IVibratorExt-V1-ndk_platform.so –> recovery/root/vendor/lib64
继续编译测试
还是冻屏,继续抓 log
1 | F linker : CANNOT LINK EXECUTABLE "/vendor/bin/hw/vendor.xiaomi.hardware.vibratorfeature.service": library "libtinyalsa.so" not found: needed by main executable |
这个 library twrp 的源码中应该没有,去设备拿一个 prebuilt 吧
1 | xiaolegun@xiaoleGundeMac-Pro ~/github/$/mjw adb shell mount /system_root |
将这个文件按照以下提示操作
- libtinyalsa.so –> recovery/root/system/lib64
编译测试
我艹了,还是冻屏,这次搜那几个关键词都搜不到了,搜vibrator
看看这鬼东西报什么
1 | E vendor.xiaomi.hardware.vibratorfeature.service: fail to load lib : /vendor/lib64/libaachaptics.so |
这次都直接给路径了,直接拿。
1 | xiaolegun@xiaoleGundeMac-Pro ~/github/$/mjw adb shell mount /vendor |
将这个文件按照以下提示操作
- libaachaptics.so –> recovery/root/vendor/lib64
至此应该没什么问题了,但是有个 flag 貌似要操作 data 内的东西,未解密会导致冻屏,所以等修完解密后再来修振动。
修复 CPU 温度
使用adb shell
+ grep
命令在手机节点里搜索 type 关键词。
1 | xiaolegun@xiaoleGundeMac-Pro ~ adb shell 'grep "cp_master" /sys/class/thermal/*/type' |
我这里选用cp_master
节点来读取温度。我的节点路径为/sys/class/thermal/thermal_zone54
在后面添加 temp 来读取温度,你可以使用cat
来检测节点是否可以正常读取温度
1 | adb shell 'cat /sys/class/thermal/thermal_zone54/temp' |
如果返回如
1 | 28000 |
这种类似的数值,那多半是没有问题的。
我们在 BoardConfig.mk 中加入
1 | TW_CUSTOM_CPU_TEMP_PATH := "/sys/class/thermal/thermal_zone54/temp" |
至此 cpu 温度就修复好了。
后序
这是我玩 Android 两年内第一次写关于 Android 的文章,内容和措辞可能不是很准确,再加上开学焦虑,租房因租金和房源不顺利,写的很乱,请各位指正。
解密等有空补,先补作业。
有实力的巨佬们给点饭恰(
爱发电:xiaoleGun或博客微信无手续费
参考资料
为新设备编写Recovery device tree
https://blog.xiaolegun.cn/post/writing-recovery-device-tree-for-new-device/