Pixel 6 自编译 AOSP 卡在 android Logo 与触屏失效:一次 vendor_dlkm 集成问题排查

这次问题发生在 Pixel 6(oriole)自编译 Android 15 / BP1A.250305.019 镜像时。系统镜像可以刷入,内核也能启动,Google Logo 之后进入 android 启动画面,但长时间无法进入桌面;另一个相关现象是触屏模块没有按预期工作。

最终定位结果不是 system 分区、userdata 格式化、fastboot 刷机流程,也不是 6.1 内核本身不能用于 Pixel 6,而是本地集成 6.1 内核产物时错误使用了 kernel dist 目录里预生成的 vendor_dlkm.img。这个镜像只包含模块和模块列表,缺少设备侧启动时需要的 /vendor_dlkm/etc/init.insmod.oriole.cfg,导致设备专属模块加载链路没有跑完。

环境背景

这次基线组合如下:

Device: Pixel 6 / oriole
Android: Android 15
Build ID: BP1A.250305.019
AOSP tag: android-15.0.0_r20
Kernel branch: android-gs-raviole-6.1-android15-qpr2-beta
Kernel version: 6.1.99

一开始容易被历史经验带偏:Pixel 6 很长时间对应的是 5.10 内核,但官方 Android 15 的 BP1A.250305.019 镜像实际已经是 6.1 内核。

设备上官方镜像的确认结果是:

Linux localhost 6.1.99-android14-11-gd6f926cfde54-ab12786694
google/oriole/oriole:15/BP1A.250305.019/13003188:user/release-keys

所以这次方向不是“Pixel 6 能不能刷 6.1”,而是“本地 6.1 内核产物如何正确接入 AOSP 打包流程”。

故障现象

刷入自编译包后,设备表现为:

  • bootloader 和 fastboot update 流程正常;
  • boot.imgvendor_boot.imgvendor_dlkm.img 都可以写入;
  • Google Logo 之后进入 android 启动画面;
  • 启动画面停留很久,随后可能回到 bootloader 或持续重启;
  • 早期阶段能看到系统服务启动迹象,但无法进入桌面;
  • 触屏相关模块没有按预期可用。

这个阶段最容易怀疑的是 userdata 或动态分区刷写问题,但 logcat 里已经能看到 /metadata/data、zygote 和 system_server 的启动过程,说明系统已经走到比较靠后的 Android userspace 阶段。

logcat 里的关键线索

问题的关键不在“卡 Logo”本身,而在 system_server 为什么被卡住。

logcat 中的异常路径集中在:

VibratorManagerService.nativeGetVibratorIds()

随后 watchdog 超时杀掉 system_server。同时,android.hardware.vibrator.IVibrator/default 没有注册成功。

Pixel 6 的 vibrator HAL 由 vendor 侧服务提供,服务二进制位于:

/vendor/bin/hw/android.hardware.vibrator-service.cs40l25

它不是无条件启动,而是受 init 属性触发。关键触发条件是:

vendor.all.modules.ready=1

也就是说,vibrator HAL 没有启动只是表层现象,更上游的问题是“模块加载完成”的属性没有被设置。

为什么触屏也会失效

Pixel 6 的设备专属模块加载依赖 /vendor_dlkm/etc/init.insmod.oriole.cfg。这个配置里不仅设置 ready 属性,也会加载触屏、Wi-Fi、安全芯片等设备模块:

modprobe|bcmdhd4389.ko
modprobe|ftm5.ko
modprobe|sec_touch.ko
modprobe|st33spi.ko

setprop|vendor.device.modules.ready
setprop|vendor.all.modules.ready
setprop|vendor.all.devices.ready

其中 ftm5.kosec_touch.ko 就和触屏链路直接相关。因此当 init.insmod.oriole.cfg 没有被打进 vendor_dlkm.img 时,不只是 vibrator HAL 等不到 ready 属性,触屏模块也可能没有按设备预期加载。

这就是“卡 android Logo”和“触屏失效”看起来像两个问题,但实际共享同一个根因。

根因:直接使用了 kernel dist 的 vendor_dlkm.img

本次集成 6.1 内核产物时,曾经在 vendor/google_devices/oriole/BoardConfigPartial.mk 里优先选择本地 kernel dist 的 vendor_dlkm.img

BOARD_PREBUILT_VENDOR_DLKMIMAGE := $(TARGET_KERNEL_DIR)/vendor_dlkm.img

这个做法看起来直接,但会绕过 AOSP 对 vendor_dlkm 分区的正常 staging 流程。

kernel dist 里的 vendor_dlkm.img 主要来自内核构建产物,它包含 .ko 模块和 vendor_dlkm.modules.load,但它不知道 Pixel 6 设备目录里还需要额外打进去的:

device/google/raviole/init.insmod.oriole.cfg

结果就是:

  • vendor_dlkm.img 里有模块;
  • 但缺少 /etc/init.insmod.oriole.cfg
  • insmod_sh_raviole 找不到设备配置;
  • vendor.all.modules.ready 不会被设置;
  • vibrator HAL 不启动;
  • system_server 卡在 vibrator service 初始化;
  • watchdog 杀掉 system_server
  • 表现为长时间卡在 android Logo。

正确修复方式

修复思路是:本地内核产物只提供模块、模块加载列表和内核镜像,不直接接管整个 vendor_dlkm.img。让 AOSP 重新生成 vendor_dlkm.img,这样设备侧的 PRODUCT_COPY_FILESBOARD_VENDOR_KERNEL_MODULES 规则才能一起生效。

修正后的 BoardConfigPartial.mk 逻辑是:

BOARD_PREBUILT_VENDORIMAGE := vendor/google_devices/oriole/proprietary/vendor.img

ifneq (,$(wildcard $(TARGET_KERNEL_DIR)/vendor_dlkm.modules.load))
BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE := ext4
else
BOARD_PREBUILT_VENDOR_DLKMIMAGE := vendor/google_devices/oriole/proprietary/vendor_dlkm.img
endif

TARGET_COPY_OUT_VENDOR_DLKM := vendor_dlkm

这里的含义是:

  • 如果正在使用本地 kernel dist,并且存在 vendor_dlkm.modules.load,就让 AOSP 从本地 .ko 重新构建 vendor_dlkm.img
  • 如果没有本地 kernel dist,就继续使用官方 proprietary 里的预编译 vendor_dlkm.img
  • 不再直接使用 kernel dist 里那个完整的 vendor_dlkm.img

重新打包后的 vendor_dlkm staging 目录需要能看到:

vendor_dlkm/etc/init.insmod.oriole.cfg
vendor_dlkm/lib/modules/haptics-cs40l2x.ko
vendor_dlkm/lib/modules/ftm5.ko
vendor_dlkm/lib/modules/sec_touch.ko
vendor_dlkm/lib/modules/modules.load

临时验证方法

如果设备已经卡在启动动画,但 adb 还能连上,可以临时设置属性验证判断:

adb root
adb wait-for-device
adb shell getprop vendor.all.modules.ready
adb shell setprop vendor.all.modules.ready 1
adb shell getprop init.svc.vendor.vibrator.cs40l25

如果设置后 vibrator HAL 从未启动变成 running,说明方向基本正确。不过这只是验证手段,不是最终修复。最终还是要把 init.insmod.oriole.cfg 正确打进 vendor_dlkm.img

刷机包检查

重新生成的 fastboot update 包至少应包含:

android-info.txt
fastboot-info.txt
boot.img
dtbo.img
product.img
pvmfw.img
super_empty.img
system.img
system_ext.img
system_other.img
userdata.img
vbmeta.img
vbmeta_system.img
vendor.img
vendor_boot.img
vendor_dlkm.img

其中 super_empty.img 对 fastboot update 的动态分区流程有意义;vendor_dlkm.img 则必须是 AOSP 重新 staging 后的版本,而不是直接从 kernel dist 复制出来的版本。

Windows 下刷入方式:

fastboot -w update C:\Users\7owe2\Desktop\image-oriole-bp1a.250305.019.zip

这个包不包含 bootloader 和 radio 时,不需要先刷 bootloader,也不应该把“官方 factory image 的 flash-all 流程”机械套到这个自编译 image zip 上。fastboot update 会按 zip 内的 android-info.txtfastboot-info.txt 执行已有分区镜像的刷写。

经验沉淀

这次问题的教训比较明确:

  • vendor_dlkm.img 不是“只要有模块就行”,它还承载设备启动时的模块加载配置。
  • kernel dist 里的完整镜像不一定等价于 AOSP product out 里的最终分区镜像。
  • Pixel 设备的 vendor HAL 可能依赖 vendor.all.modules.ready 这类属性触发,某个 HAL 不启动时要往上查 init 触发条件。
  • 卡在 android Logo 不一定是 system 镜像或 userdata 问题,system_server watchdog 也会表现成类似现象。
  • 自编译内核接入 AOSP 时,优先让 AOSP 完成最终镜像 staging,而不是手动替换完整分区镜像。

后续如果继续做 Pixel 6 / Android 15 / 6.1 内核自编译,应该把这条规则固定下来:本地 kernel dist 提供 boot.imgdtbo.img.ko 和模块列表,最终的 vendor_dlkm.img 由 AOSP 产品构建系统生成。这样设备侧的 init.insmod.oriole.cfg、模块加载顺序和 ready 属性才能保持一致。

tower的ai助手