首页 快速上手 Android Custom ROM 适配 - Prebuilt Vendor
文章
取消

快速上手 Android Custom ROM 适配 - Prebuilt Vendor

快速上手 Android Custom ROM 适配 - Prebuilt Vendor

⚠️ 注意:本人非专业 Android 开发者,本文仅供参考,如有错误,欢迎指正!

⚠️ 注意:本人非专业 Android 开发者,本文仅供参考,如有错误,欢迎指正!

⚠️ 注意:本人非专业 Android 开发者,本文仅供参考,如有错误,欢迎指正!

本文章以适配小米 10S 为例, 小米 10S 为 VAB 设备, 不兼容 GKI, VNDK 版本 30。

编译服务器系统: Ubuntu 22.04

什么是 Prebuilt Vendor, 为什么要用 Prebuilt Vendor

Prebuilt Vendor, 顾名思义, 预编译 Vendor。指的是使用厂商已经编译好的 Vendor 进行 Custom 适配。这样可以大大降低适配难度, 减少适配调试时间。

Google 在 Android 8.0 引入了 PT(Project Treble), 这使得 Android Custom ROM 的适配和调试难度大大降低。再加上今年 Google 再次引入 GRF(Google Requirements Freeze) aka Vendor Freeze, 这使得适配难度再次降低。

在 Android 8.0 之前, 要适配 Custom ROM, 需要通过 AOSP, CAF 或其它芯片, 硬件厂商开源的源码编译设备硬件所需要的运行库, HAL 或驱动, 且每次 Android 大版本升级都需要重新编译, 且极有可能在新版本 Andorid 运行或编译中出现问题, 需要等待源码更新修复或自己手动修复。

在引入 PT, GRF 后, 我们可以直接使用Stock ROM 中的 Vendor, 只需要编译 Kernel 和 System 即可, 且因为 GRF 的引入, Vendor 至少可以兼容 3 个大版本的 Android 更新。(例如小米 12 预装 Android 12, VNDK 版本 32, 则此版本的 Vendor image 至少可以兼容到 Android 15, VNDK 35)

需要准备的东西

  1. 可以编译 Android 的 PC 或服务器
  2. MIUI 官方刷机包
  3. 国际互联网连接
  4. 小米 10S

准备开始

安装所需依赖

1
2
3
4
5
6
# 安装编译依赖
sudo apt install bc bison build-essential ccache curl flex g++-multilib gcc-multilib git gnupg gperf imagemagick lib32ncurses5-dev lib32readline-dev lib32z1-dev libelf-dev liblz4-tool libncurses5 libncurses5-dev libsdl1.2-dev libssl-dev libxml2 libxml2-utils lzop pngcrush rsync schedtool squashfs-tools xsltproc zip zlib1g-dev git

# 配置 repo
sudo curl https://storage.googleapis.com/git-repo-downloads/repo > /usr/bin/repo
sudo chmod a+x /usr/bin/repo

同步 LineageOS 源码

在设备适配初期, 我推荐使用 LineageOS 进行设备的 bring up

1
2
3
4
5
6
7
8
9
# 新建文件夹用于存放源码
mkdir lineage

# 初始化 repo
repo init -u https://github.com/LineageOS/android.git -b lineage-19.1 # 同步完整仓库, 带提交历史, 占用空间大
repo init -u https://github.com/LineageOS/android.git -b lineage-19.1 --depth=1 # 仅拉取最新提交, 不带提交历史, 占用空间小

# 开始同步
repo sync

开始适配

解包 MIUI

我们需要原厂系统中的部分文件来保证部分功能的正常使用, 例如 IMS, 所以我们需要先解包原厂系统

1
2
3
4
5
6
7
8
9
10
# 下载解包工具
git clone https://github.com/ShivamKumarJha/android_tools --depth=1
cd android_tools

# 初始化工具所需环境
chmod +x setup.sh
sudo bash setup.sh

# 开始解包
./tools/rom_extract.sh MIUI_PACKAGE.zip

提取所需文件

提取 vendor 以及 odm

因为是 prebuilt vendor, 所以我们需要从原厂系统中提取出 vendor, odm 镜像

1
2
unzip MIUI_PACKAGE.zip
./../android_tools/tools/Firmware_extractor/tools/update_payload_extractor/extract.py payload.bin --output_dir images/

然后将 images 目录下的 vendor.img 和 odm.img 复制到 device/xiaomi/thyme-prebuilt/

提取 kernel

因为是适配早期我们使用 prebuilt kernel 简化适配流程

1
2
3
4
5
6
7
# 下载 magiskboot 用于解包 boot.img 以及 vendor_boot.img
wget https://github.com/TeamWin/external_magisk-prebuilt/raw/android-12.1/prebuilt/magiskboot_x86
chmod a+x magiskboot_x86

# 解包 boot.img 以及 vendor_boot.img
./magiskboot_x86 unpack boot.img
./magiskboot_x86 unpack vendor_boot.img

然后将解出来的 kernel, dtbdtbo.img 复制到 device/xiaomi/thyme-prebuilt/

开始 Bring up device tree

初始化 device tree 并编译 recovery

我们需要先初始化 device tree, 然后编译 recovery, 来验证 kernel 的可用性

Android.mk

这是 Android make 编译系统中的编译配置文件, Android 编译系统会 include 源码目录下的所有 Android.mk 文件, 包括设备文件夹中的 Android.mk 我们需要在这个文件 include 当前文件夹下的所有 makefile 文件, 否则 Android 编译系统不会去 include 设备文件夹中的其它 makefile 文件。

1
2
3
4
5
LOCAL_PATH := $(call my-dir) # 设置一个 LOCAL_PATH 变量, 并将当前文件夹的路径赋值给它
ifeq ($(TARGET_DEVICE),thyme) # 如果 TARGET_DEVICE 变量等于 thyme
subdir_makefiles=$(call first-makefiles-under,$(LOCAL_PATH)) # 设置一个 subdir_makefiles 变量, 并将当前文件夹下的所有 makefile 文件赋值给它
$(foreach mk,$(subdir_makefiles),$(info including $(mk) ...)$(eval include $(mk))) # 遍历 subdir_makefiles 变量中的所有 makefile 文件, 并 include 进来
endif # 结束 if 语句
Android.bp

这是 Android 引入 soong 编译系统后的编译配置文件, Android 编译系统会 include 源码目录下的所有 Android.bp 文件, 包括设备文件夹中的 Android.bp 目前我们不需要编译外部的 soong 模块, 因此直接创建一个空的 Android.bp 文件即可

1
2
3
soong_namespace { // soong 编译系统的命名空间
    imports: [], // 导入的 soong 模块路径
}
AndroidProducts.mk

这是用于定义设备编译配置的地方, 我们可以在这里定义多个设备配置用于编译多个设备或同设备的变种版本

1
2
3
4
5
6
7
PRODUCT_MAKEFILES := \
    $(LOCAL_DIR)/lineage_thyme.mk # 要使用的设备编译配置文件

COMMON_LUNCH_CHOICES := \ # 定义可用的 lunch 选项
    lineage_thyme-eng \ # eng 版本
    lineage_thyme-userdebug \ # userdebug 版本
    lineage_thyme-user # user 版本
lineage_thyme.mk

这是我们的设备编译配置文件, 我们需要在这里定义设备的基本信息, 以及设备的编译配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Inherit common products
$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk) # 继承 core_64_bit.mk 编译配置
$(call inherit-product, $(SRC_TARGET_DIR)/product/full_base_telephony.mk) # 继承 full_base_telephony.mk 编译配置

# Inherit device configurations
$(call inherit-product, device/xiaomi/thyme/device.mk) # 继承 device/xiaomi/thyme/device.mk 编译配置

# Inherit common ArrowOS configurations
$(call inherit-product, vendor/arrow/config/common.mk) # 继承 vendor/arrow/config/common.mk 编译配置

PRODUCT_CHARACTERISTICS := nosdcard # 产品特性, 这里表示不支持 sd 卡

PRODUCT_BRAND := Xiaomi # 产品品牌
PRODUCT_DEVICE := thyme # 产品设备名
PRODUCT_MANUFACTURER := Xiaomi # 产品制造商
PRODUCT_MODEL := M2102J2SC # 产品型号
PRODUCT_NAME := lineage_thyme # 产品名

PRODUCT_GMS_CLIENTID_BASE := android-xiaomi # 产品 GMS 客户端 ID
BoardConfig.mk

这是设备的板级配置文件, 我们需要在这里定义设备的硬件配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
DEVICE_PATH := device/xiaomi/thyme # 定义一个 DEVICE_PATH 变量,     指向设备文件夹

# A/B
AB_OTA_UPDATER := true # 开启 A/B OTA 更新

AB_OTA_PARTITIONS += \ # A/B OTA 更新的分区
    boot \
    dtbo \
    product \
    system \
    system_ext \
    vbmeta \
    vbmeta_system

# Architecture
TARGET_ARCH := arm64 # 指定目标架构为 arm64
TARGET_ARCH_VARIANT := armv8-a # 指定目标架构变体为 armv8-a
TARGET_CPU_ABI := arm64-v8a # 指定目标 CPU ABI 为 arm64-v8a
TARGET_CPU_ABI2 := # 指定目标 CPU ABI 2 为空
TARGET_CPU_VARIANT := generic # 指定目标 CPU 变体为 generic
TARGET_CPU_VARIANT_RUNTIME := kryo385 # 指定目标 CPU 变体运行时为 kryo385

TARGET_2ND_ARCH := arm # 指定第二目标架构为 arm
TARGET_2ND_ARCH_VARIANT := armv8-a # 指定第二目标架构变体为 armv8-a
TARGET_2ND_CPU_ABI := armeabi-v7a # 指定第二目标 CPU ABI 为 armeabi-v7a
TARGET_2ND_CPU_ABI2 := armeabi # 指定第二目标 CPU ABI 2 为 armeabi
TARGET_2ND_CPU_VARIANT := generic # 指定第二目标 CPU 变体为 generic
TARGET_2ND_CPU_VARIANT_RUNTIME := kryo385 # 指定第二目标 CPU 变体运行时为 kryo385

# Bootloader
TARGET_BOOTLOADER_BOARD_NAME := kona # 指定目标 bootloader 名为 kona, 该值可以从 MIUI 的 vendor/build.prop 中获取

# Kernel
BOARD_BOOT_HEADER_VERSION := 3 # 指定内核头版本为 3, 内核头版本从 3 开始支持 vendor_boot 分区
BOARD_KERNEL_BASE := 0x00000000 # 指定内核基地址为 0x00000000
BOARD_KERNEL_BINARIES := kernel # 指定内核二进制文件名为 kernel
BOARD_KERNEL_CMDLINE := console=ttyMSM0,115200n8 androidboot.hardware=qcom  # 指定内核命令行
BOARD_KERNEL_CMDLINE += androidboot.console=ttyMSM0 androidboot.memcg=1
BOARD_KERNEL_CMDLINE += lpm_levels.sleep_disabled=1
BOARD_KERNEL_CMDLINE += msm_rtb.filter=0x237 service_locator.enable=1
BOARD_KERNEL_CMDLINE += androidboot.usbcontroller=a600000.dwc3 swiotlb=0
BOARD_KERNEL_CMDLINE += loop.max_part=7 cgroup.memory=nokmem,nosocket
BOARD_KERNEL_CMDLINE += pcie_ports=compat loop.max_part=7
BOARD_KERNEL_CMDLINE += iptable_raw.raw_before_defrag=1
BOARD_KERNEL_CMDLINE += ip6table_raw.raw_before_defrag=1
BOARD_KERNEL_CMDLINE += androidboot.selinux=permissive
BOARD_KERNEL_IMAGE_NAME := Image # 指定内核镜像名为 Image
BOARD_KERNEL_PAGESIZE := 4096 # 指定内核页大小为 4096
BOARD_KERNEL_SEPARATED_DTBO := true # 指定内核分离 DTBO
BOARD_MKBOOTIMG_ARGS := --header_version $(BOARD_BOOT_HEADER_VERSION) # 指定 mkbootimg 参数, 这里指定了内核头版本
KERNEL_LD := LD=ld.lld # 指定内核链接器为 ld.lld
TARGET_KERNEL_ADDITIONAL_FLAGS := DTC_EXT=$(shell pwd)/prebuilts/misc/linux-x86/dtc/dtc # 指定使用外部 DTC
TARGET_KERNEL_APPEND_DTB := false # 指定不追加 DTB
BOARD_INCLUDE_DTB_IN_BOOTIMG := true # 指定在 boot.img 中包含 DTB
TARGET_KERNEL_ARCH := arm64 # 指定内核架构为 arm64
TARGET_KERNEL_HEADER_ARCH := arm64 # 指定内核头架构为 arm64

ifeq ($(TARGET_PREBUILT_KERNEL),) # 如果没有使用预编译内核
  TARGET_KERNEL_CONFIG := thyme_defconfig # 指定内核配置文件为 thyme_defconfig
  TARGET_KERNEL_CLANG_COMPILE := true # 指定使用 Clang 编译内核
  TARGET_KERNEL_SOURCE := kernel/xiaomi/thyme # 指定内核源码路径为 kernel/xiaomi/thyme
endif

# Metadata
BOARD_USES_METADATA_PARTITION := true # 使用 metadata 元数据加密

# Partitions
BOARD_BOOTIMAGE_PARTITION_SIZE := 201326592 # 指定 boot 分区大小为 201326592
BOARD_DTBOIMG_PARTITION_SIZE := 33554432 # 指定 dtbo 分区大小为 33554432
BOARD_FLASH_BLOCK_SIZE := 262144 # 指定刷入块大小为 262144
BOARD_USERDATAIMAGE_PARTITION_SIZE := 114135379968 # 指定 userdata 分区大小为 114135379968
BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE := 100663296 # 指定 vendor_boot 分区大小为 100663296

BOARD_THYME_DYNAMIC_PARTITIONS_PARTITION_LIST := product system system_ext # 指定动态分区列表
BOARD_SUPER_PARTITION_SIZE := 9126805504 # 指定 super 分区大小为 9126805504
BOARD_SUPER_PARTITION_GROUPS := thyme_dynamic_partitions # 指定 super 分区组为 thyme_dynamic_partitions
BOARD_THYME_DYNAMIC_PARTITIONS_SIZE := 4559208448 # 指定动态分区组 thyme_dynamic_partitions 大小为 4559208448

BOARD_PARTITION_LIST := $(call to-upper, $(BOARD_THYME_DYNAMIC_PARTITIONS_PARTITION_LIST)) # 遍历 BOARD_THYME_DYNAMIC_PARTITIONS_PARTITION_LIST 并赋值给 BOARD_PARTITION_LIST
$(foreach p, $(BOARD_PARTITION_LIST), $(eval BOARD_$(p)IMAGE_FILE_SYSTEM_TYPE := ext4)) # 遍历 BOARD_PARTITION_LIST 并赋值给 p, 然后设置 BOARD_$(p)IMAGE_FILE_SYSTEM_TYPE := ext4
$(foreach p, $(BOARD_PARTITION_LIST), $(eval TARGET_COPY_OUT_$(p) := $(call to-lower, $(p)))) # 遍历 BOARD_PARTITION_LIST 并赋值给 p, 然后设置 TARGET_COPY_OUT_$(p) := $(call to-lower, $(p))

BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE := f2fs # 指定 userdata 分区文件系统类型为 f2fs

# Platform
BOARD_USES_QCOM_HARDWARE := true # 指定使用 Qualcomm 硬件
TARGET_BOARD_PLATFORM := kona # 指定平台为 kona

# Recovery
BOARD_INCLUDE_RECOVERY_DTBO := true # 指定包含 recovery DTBO
BOARD_USES_RECOVERY_AS_BOOT := true # 指定 recovery 在 boot 分区中
TARGET_NO_RECOVERY := true # 指定设备没有 recovery 分区
TARGET_RECOVERY_FSTAB := $(DEVICE_PATH)/recovery/recovery.fstab # 指定 recovery fstab 文件
TARGET_RECOVERY_PIXEL_FORMAT := RGBX_8888 # 指定 recovery 像素格式
TARGET_USERIMAGES_USE_F2FS := true # 指定 userdata 使用 f2fs

# Verified Boot
BOARD_AVB_ENABLE := true # 指定启用 AVB
BOARD_AVB_MAKE_VBMETA_IMAGE_ARGS += --flags 3 # 指定 AVB flags 为 3
BOARD_AVB_MAKE_VBMETA_IMAGE_ARGS += --set_hashtree_disabled_flag # 指定 AVB 为禁用
BOARD_AVB_VBMETA_SYSTEM := system system_ext# 指定使用 AVB 的分区
BOARD_AVB_VBMETA_SYSTEM_ALGORITHM := SHA256_RSA2048 # 指定 AVB 加密算法为 SHA256_RSA2048
BOARD_AVB_VBMETA_SYSTEM_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem # 指定 AVB 密钥
BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP) # 指定 AVB 回滚索引
BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX_LOCATION := 1 # 指定 AVB 回滚索引位置

# VNDK
BOARD_VNDK_VERSION := current # 指定 VNDK 版本为 current
device.mk

这是设备的编译配置文件, 在这里面可以 include 其它编译配置文件, 也可以指定要编译的模块, 功能类同 lineage_thyme.mk。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# Enable updating of APEXes
$(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk) # 启用 apex 更新

# Virtual A/B
$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota.mk) # 启用虚拟 A/B

# Enable Dalvik
$(call inherit-product, frameworks/native/build/phone-xhdpi-6144-dalvik-heap.mk) # 导入 6G dalvik 配置

# API
PRODUCT_SHIPPING_API_LEVEL := 30 # 指定预装 Android API 级别为 30, 例如 thyme 预装 Android 11, 故此处为 30

# A/B
AB_OTA_POSTINSTALL_CONFIG += \
    RUN_POSTINSTALL_system=true \
    POSTINSTALL_PATH_system=system/bin/otapreopt_script \
    FILESYSTEM_TYPE_system=ext4 \
    POSTINSTALL_OPTIONAL_system=true

# Boot animation
TARGET_SCREEN_HEIGHT := 2400 # 指定屏幕高度为 2400
TARGET_SCREEN_WIDTH := 1080 # 指定屏幕宽度为 1080

# Common init scripts
PRODUCT_PACKAGES += \
    init.recovery.qcom.rc # 编译额外的自定义 rc 脚本

# fastbootd
PRODUCT_PACKAGES += \
    android.hardware.fastboot@1.0-impl-mock \ # 编译 fastbootd
    fastbootd

# F2FS utilities
PRODUCT_PACKAGES += \
    sg_write_buffer \ # 编译 f2fs 工具
    f2fs_io \
    check_f2fs

# Partitions
PRODUCT_USE_DYNAMIC_PARTITIONS := true # 指定使用动态分区
PRODUCT_BUILD_SUPER_PARTITION := false # 指定不编译 super 分区

# Soong namespaces
PRODUCT_SOONG_NAMESPACES += \
    $(LOCAL_PATH) # 指定 soong 命名空间

# Update engine
PRODUCT_PACKAGES += \
    otapreopt_script \ # 编译 otapreopt 脚本
    update_engine \ # 编译 update_engine
    update_engine_sideload \ # 编译 update_engine_sideload
    update_verifier \ # 编译 update_verifier

PRODUCT_PACKAGES_DEBUG += \
    update_engine_client \ # 编译 update_engine_client

PRODUCT_HOST_PACKAGES += \
    brillo_update_payload \ # 编译 brillo_update_payload

# Vendor boot
PRODUCT_COPY_FILES += \
    $(LOCAL_PATH)/rootdir/etc/fstab.qcom:$(TARGET_COPY_OUT_VENDOR_RAMDISK)/first_stage_ramdisk/fstab.qcom # 复制 fstab 到 vendor ramdisk

# VNDK
PRODUCT_TARGET_VNDK_VERSION := 30 # 指定 VNDK 版本为 30, 该值可以在 vendor/build.prop 中找到
导入 recovery 所需基本文件

这是 fstab 文件, 用于指定分区的挂载点, 以及挂载点的属性, 例如是否可读写等。 必须要有这个文件, 否则 Linux Kernel 会无法挂载所需的分区, 导致 Android 或 Recovery 无法启动

这个文件可以在 vendor 分区中被找到, 具体可以使用此命令查找

1
2
cd dump/vendor
find | grep fstab.qcom

找到后 copy 到设备文件夹中的 rootdir/etc/ 目录下

然后我们可以在 device.mk 中使用 PRODUCT_COPY_FILES 复制到 ramdisk 或者 vendor ramdisk 中, 也可以在 rootdir 目录中新建 Android.mk 去定义自定义模块然后在 device.mk 中指定编译,

开始编译 recovery

完成上述步骤后我们便可以开始编译 recovery 来测试了

1
2
3
. build/envsetup.sh # 初始化编译环境
lunch lineage_thyme-userdebug # 初始化设备编译环境
m bootimage # 编译 boot image, 由于是 A/B 设备, 故此处编译 boot image 而不是 recovery image

编译完成后刷入设备

1
2
fastboot flash boot out/target/product/thyme/boot.img # 刷入 boot image
fastboot reboot recovery # 重启到 recovery

如果设备可以正常进入编译后到 recovery 我们便可以进入下一步,否则请参考文章末的 debug 指南进行 debug。

完善 device tree 准备开始编译 Android

由于这部分内容较多,具体请参考本人 GitHub 仓库中的提交历史 此文章仅挑出其中一些重要的部分进行说明

proprietary-files.txt, extract-files.shsetup-makefiles.sh

这三个文件复制 vendor tree 中的文件生成,改动和提取 如果要生成或修改 vendor tree 中的文件,请务必使用 extract-files.sh 脚本提取生成或修改。 详情请参考 LineageOS 官方文档:

  1. https://wiki.lineageos.org/extracting_blobs_from_zips
  2. https://wiki.lineageos.org/proprietary_blobs
proprietary-files.txt

该文件属于一个清单,用于列出需要从原厂系统中提取的文件,以及它们的目标位置。 extract-files.sh 会遍历这里面的文件,然后从原厂系统中提取出来,放到 vendor tree 中。

extract-files.sh

该脚本会根据 proprietary-files.txt 中的文件,从原厂系统中提取出来,放到 vendor tree 中,然后调用 setup-makefiles.sh 脚本生成 makefile。

setup-makefiles.sh

该脚本会被 extract-files.sh 调用,用于生成 makefile。

FCMmanifest.xmlcompatibility_matrix.device.xml

这是 HAL 的清单文件,用于指定设备支持的 HAL,以及它们的版本。Android 会根据清单中的 HAL 来加载对应的 HAL。 详情请参考 AOSP 官方文档: https://source.android.com/docs/core/architecture/vintf?hl=zh-cn

bootctrl 以及 gpt-utilsmtk_plpath_utils

A/B 设备需要这些模块用于进行无损升级,详情请参考 AOSP 官方文档: https://source.android.com/docs/core/ota/ab

⚠️ 注意: 高通设备与联发科设备所使用的 bootctrl 不同

高通设备的 bootctrl 以及 gpt-utils 可以从 CAF 或 CLO 中拿取。

  1. CAF
  2. CLO

⚠️ 注意: 高通已经在 2022 年 5 月 31 日停止对 CAF 的更新,并决定在 2023 年 5 月 31 日彻底停用,所以建议使用 CLO 中的 bootctrl 和 gpt-utils

联发科设备的 bootctrl 以及 mtk_plpath_utils 请参考:

  1. bootctrl
  2. mtk_plpath_utils

如果上面的两个模块对您的联发科设备不起作用,请考虑使用 prebuilt 的 bootctrl 和 mtk_plpath_utils。

VoLTE

我们需要编译部分组建以及从原厂系统提取部分文件来支持 VoLTE。

高通设备参考:

  1. https://github.com/Lynnrin-Studio/android_device_xiaomi_thyme/commit/e6d415fbc9c1ad947ceea4e2860a7a1101e8feec
  2. https://github.com/Lynnrin-Studio/android_device_xiaomi_thyme/blob/twelve/proprietary-files.txt#L78-L87

联发科设备参考:

  1. https://github.com/Lynnrin-Studio/android_device_xiaomi_chopin/blob/lineage-18.1/device.mk#L105-L121
  2. https://github.com/Lynnrin-Studio/android_device_xiaomi_chopin/blob/lineage-18.1/proprietary-files.txt#L5-L41

同时请确保所编译的 ROM 以及添加了 MTK IMS 的支持,具体可以参考: https://gerrit.pixelexperience.org/q/topic:mtk-ims

Overlay

Overlay 是个很重要的东西,可以动态的调节一些系统特性,比如说状态栏的高度,圆角的大小等

部分 Overlay 配置可以从原厂系统中提取,具体路径在

  1. vendor/overlay
  2. product/overlay

如果需要自己添加 overlay 可以参考这些链接来检查哪些 overlay 是可用的,下面列出的是比较常用的:

  1. framework/base
  2. framework/base/packages/SystemUI
  3. packages/apps/Settings

如果需要找更多的 overlay 可以进入 cs.android.com 查找相应 app 模块的 res/values/ 目录。

SELinux

SELinux 是一个安全机制,可以防止一些恶意的 app 读取系统文件,但是这个机制也会导致一些问题,比如错误的 Sepolicy rules 可能会导致部分硬件或软件工作不正常。甚至无法启动操作系统。 在适配初期我建议将 SELinux 设为宽容 等到硬件和软件的适配工作基本完成后再将 SELinux 设置为 Enforcing。

关于 Sepolicy rules 的编写可以参考这些链接:

  1. https://source.android.com/security/selinux/
  2. https://www.cnblogs.com/schips/p/android-selinux_about_avc.html
  3. https://lineageos.org/engineering/HowTo-SELinux

硬件功能的修复

一般 prebuilt vendor tree 编译出来的 ROM 大部分硬件都可以正常工作,但是部分机型的硬件可能需要一些额外的修复才能正常工作,比如说指纹,呼吸灯等

屏下指纹

⚠️ 注意: 本部分以小米 10S 为例,该机型采用光学屏下指纹,Global HBM。如果你的设备是 Local HBM 具体实现方式可能稍有不同

在小米 10S上由于使用的是屏下指纹,所以不能直接使用 vendor 内已经编译好的 Fingerprint 2.1 HAL,在 Android 12 或以上系统需要手动编译一个 Fingerprint 2.3 HAL 用来处理 UDFPS 事件,在 Android 12 以下系统需要使用 Lineage inscreen fingerprint HAL 来处理指纹事件。

UDFPS 是 Google 在 Android 12 中新增的一种屏下指纹的实现方式,具体可以阅读相关源码

Android 12 以下系统

具体实现方式可以参考这个提交历史

⚠️ 注意: 由于小米 10S 使用 Global HBM,因此我们需要进行 kernel dimming 或 framework dimming,否则在使用指纹解锁时全屏幕亮度将调至最高,因为小米 10S 使用 prebuilt kernel,因此只能使用 framework 进行 dimming 具体实现参考这个提交

Android 12 及以上系统

具体实现方式可以参考这个提交历史

此外我们还需要在 overlay 中设置部分参数, 以使得屏下指纹识别功能正常工作, 具体参考这个提交

⚠️ 注意: 由于小米 10S 使用 Global HBM,因此我们需要进行 kernel dimming 或 framework dimming,否则在使用指纹解锁时全屏幕亮度将调至最高,因为小米 10S 使用 prebuilt kernel,因此只能使用 framework 进行 dimming 具体实现参考这个提交 ⚠️ 注意: 由于 Android 13 使用 Kotlin 重写了 UDFPS 部分,因此上面的实现不兼容 Android 13,建议使用 kernel dimming 来获得更好的体验,具体提交可以参考这些

蓝牙音频
Android 12 及以下系统

通常原厂系统都采用 QCOM BT,因此我们需要一些更改来支持 vendor 中的 QCOM BT,具体可以参考这个提交

Android 13

Google 在 Android 13 中模块化了多个组件,其中就包括蓝牙,但是 vendor 中的 QCOM BT 并不兼容这一更改,因此我们需要编译修改过后的 android.hardware.bluetooth.audio 来禁用掉 QCOM BT, 并切换到 AOSP BT

具体实现参考这些提交:

  1. https://review.arrowos.net/q/topic:13-gsi
  2. https://github.com/Lynnrin-Studio/android_device_xiaomi_nabu/commit/b26c98951d1e8c0397983ed4f96224bd9e836489
  3. https://github.com/Lynnrin-Studio/android_device_xiaomi_nabu/commits/arrow-13.0/bluetooth/audio

声音

部分设备可能厂商魔改了 libvolumelistener,因此我们需要替换掉 libvolumelistener 中的,具体可以参考这个提交

debug 指南

设备卡在 oem logo (卡一屏)

造成这个的原因有很多种,这里拿几种出来讲

data 分区未正确格式化
  1. 尝试在 recovery 中格式化 data 分区
  2. 尝试使用 fastboot -w 格式化 data 分区
  3. 尝试使用原厂 recovery 格式化 data 分区
  4. 在 fstab 中关闭 data 分区的加密
Sepolicy rules 的错误配置
  1. 尝试设置 SELinux 状态为 permissive
  2. 获取 logcat 或 pstore 日志,检查是否有相关错误,并修复
未正确配置 vendor image
  1. 检查 BoardConfig 中的 vendor image 配置是否正确
未正确配置 ramdisk
  1. 检查 fstab 是否已经正确复制到 ramdisk 中
未正确配置 kernel 或 prebuilt kernel 不可用
  1. 检查 kernel 是否已经正确配置
  2. 切换到 OSS kernel
BPF 加载失败
  1. 尝试使用以下提交来修复 BPF 加载问题
获取 logcat 或 pstore 日志

如果以上方法都没有解决问题,可以尝试获取日志来分析问题

获取 logcat 日志

如果设备连接到电脑可以被 adb devices 识别到,便可以使用 adb logcat 获取 logcat 日志

可以通过检查 logcat 中带 error, crash, fatal 等关键字的日志来定位问题

获取 pstore 日志

如果设备连接到电脑不能被 adb devices 识别到,则需要获取 pstore 日志

⚠️ 注意: pstore 需要在 kernel 中开启,如果没有开启则无法获取 pstore 日志

  1. 将设备重启到 recovery
  2. 启用 recovery 的 adb (如果没有启用)
  3. 使用 adb pull /sys/fs/pstore 获取 pstore 日志

可以通过检查 pstore 日志中带 error, crash, fatal 等关键字的日志来定位问题

设备卡在开机动画 (卡二屏)

设备卡二屏代表系统已经启动,kernel 部分已经正常工作,但是系统服务没有正常启动 我们可以通过 logcat 日志来分析问题,具体参考上一节的获取 logcat 日志

logcat 日志中出现 linker 错误

一般出现该错误代表有动态库没有正确加载或缺失,具体参考日志中的报错然后补全缺失的动态库

logcat 日志中出现 Permission denied 错误

一般出现该错误代表没有正确配置 SELinux,具体参考日志中的报错然后修复 SELinux

logcat 日志中出现 fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR) 错误

一般出现该错误代表系统服务出现了崩溃,具体参考日志中的报错然后修复崩溃

本文由作者按照 CC BY 4.0 进行授权

小米 10S 类原生上车指南

Motorola Hiphi Guide

Comments powered by Disqus.