AOSP Android 15 编译 Pixel 6 ROM:源码、内核与镜像集踩坑记录

目标:记录一次 Pixel 6 / Android 15 从系统源码、vendor blobs 到本地内核镜像集的完整构建过程。

这次做的不是“随便编一个 AOSP”,而是把 AOSP Android 15 + Pixel 6 vendor blobs + 本地编译的 GS101/raviole 6.1 内核 组合成一套一致的镜像集。重点是记录构建链路、版本选择和容易忽略的内核模块一致性问题。

最终结论先放前面:

  • AOSP 目标:aosp_oriole-bp1a-userdebug
  • AOSP tag:android-15.0.0_r20
  • Build ID:BP1A.250305.019
  • 设备:Pixel 6 / oriole
  • 平台:GS101 / raviole
  • 内核分支:android-gs-raviole-6.1-android15-qpr2-beta
  • 内核版本:Linux 6.1.99
  • 系统镜像构建成功
  • 本地内核构建成功
  • 本地内核已接入 AOSP 镜像生成
  • 已修正 vendor_dlkm.img 仍沿用官方预编译镜像的问题

这篇文章记录完整过程和踩坑点。


1. 为什么先做“干净基线”

在改系统或内核之前,建议先确认三件事:

1. Android 15 的 Pixel 6 产品目标能正常构建。 2. Pixel 6 对应的 6.1 内核能从源码构建。 3. AOSP 输出镜像里的 boot.imgvendor_boot.imgdtbo.imgvendor_dlkm.img 是同一套内核产物。

如果这一步没打通,后面任何启动失败、模块加载失败、SELinux 报错,都很难判断是“基础环境问题”还是“后续改动问题”。

这样做的价值是把基础环境问题提前排除。后续如果出现启动失败、模块加载失败或 vendor 服务异常,才有明确的对照基线。


2. 版本选择

AOSP

android-15.0.0_r20
BP1A.250305.019
aosp_oriole-bp1a-userdebug

Pixel 6 vendor blobs

vendor 包安装到:

~/Android_SRC/aosp/vendor/google_devices/oriole

对应版本:

BP1A.250305.019

Kernel

Pixel 6 当前手机上原系统是 Android 13 时代的 5.10 内核:

Linux localhost 5.10.177-android13-4-00003-ga7208022a7ea-ab10815828

但目标系统是 Android 15,所以没有继续沿用 5.10,而是选择 Google 官方的 raviole 6.1 分支:

android-gs-raviole-6.1-android15-qpr2-beta

这个分支不是单纯 GKI,它包含 GS101、raviole/oriole 设备树和配套模块,适合 Pixel 6 / Pixel 6 Pro。


3. AOSP Pixel 6 基线构建

构建命令:

source build/envsetup.sh
lunch aosp_oriole-bp1a-userdebug
m -j12 droid

首次完整构建结果:

#### build completed successfully (01:35:02 (hh:mm:ss)) ####

输出目录:

~/Android_SRC/aosp/out/target/product/oriole

关键镜像包括:

boot.img
vendor_boot.img
dtbo.img
dtb.img
system.img
system_ext.img
product.img
vendor.img
vendor_dlkm.img
vbmeta.img
vbmeta_system.img
bootloader.img
radio.img

到这里,只能说明 AOSP + vendor blobs 的 Pixel 6 产品目标能构建,还不能说明本地内核已经接入。


4. Pixel 6 6.1 内核构建

内核源码目录:

~/Android_SRC/pixel_kernel

稳定可用的构建命令:

tools/bazel --batch run \
  --config=stamp \
  --config=raviole \
  --config=no_download_gki \
  --config=no_download_gki_fips140 \
  --kernel_package=@//aosp \
  //private/devices/google/raviole:gs101_raviole_dist

这里有一个 WSL 相关坑:Bazel 常驻 server 模式会触发 Netty / event loop 相关异常。使用 --batch 可以稳定绕过。

内核 dist 输出:

~/Android_SRC/pixel_kernel/out/raviole/dist

关键产物:

Image.lz4
boot.img
dtbo.img
vendor_dlkm.img
modules.load
vendor_dlkm.modules.load
vendor_dlkm.modules.blocklist
*.ko

本次内核构建完成 567 个 action,耗时约 11 分 42 秒。


5. 接入本地内核时遇到的第一个坑

AOSP 的 device/google/gs101/BoardConfig-common.mk 会通过 TARGET_KERNEL_DIR 读取内核产物。

一开始直接这样做:

TARGET_KERNEL_DIR=/home/tower/Android_SRC/pixel_kernel/out/raviole/dist m -j12 droid

结果失败。原因是 Soong 不接受源码树外部路径:

Path is outside directory

解决方式是把内核 dist 放到 AOSP 树内:

mkdir -p ~/Android_SRC/aosp/device/google/raviole-kernels/local-dist

cp -a ~/Android_SRC/pixel_kernel/out/raviole/dist/. \
  ~/Android_SRC/aosp/device/google/raviole-kernels/local-dist/

然后使用相对路径构建:

source build/envsetup.sh
lunch aosp_oriole-bp1a-userdebug

TARGET_KERNEL_DIR=device/google/raviole-kernels/local-dist \
  m -j12 droid

结果:

#### build completed successfully (21:24 (mm:ss)) ####

日志里 boot.img 的 kernel size 是:

kernel_size: 16481167

本地 Image.lz4 大小也是 16481167,说明 boot.img 已经确实使用本地内核。


6. 最关键的坑:vendor_dlkm.img 没有自动切换

第一次集成本地内核后检查产物时间戳,发现一个危险现象:

  • boot.img 刷新了
  • dtbo.img 刷新了
  • vendor_boot.img 刷新了
  • vendor_dlkm.img 没刷新

这意味着系统可能变成:

boot.img:本地内核
vendor_dlkm.img:官方 vendor 包里的旧模块镜像

这类不一致非常危险。内核能启动,不代表模块一定匹配;后续出现模块加载失败、驱动异常、设备功能异常时会很难排查。

原因在:

vendor/google_devices/oriole/BoardConfigPartial.mk

原始逻辑:

BOARD_PREBUILT_VENDOR_DLKMIMAGE := vendor/google_devices/oriole/proprietary/vendor_dlkm.img

也就是说,官方 vendor 包强制指定了预编译 vendor_dlkm.img

修正为:

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

修正后重跑增量构建:

#### build completed successfully (11:22 (mm:ss)) ####

日志中能看到:

Copy: out/target/product/oriole/vendor_dlkm.img

最终哈希确认:

b5766a77c801ac8fdb58f7361ffcf57cec32a9e729655304681f7568cbe98e17  out/target/product/oriole/vendor_dlkm.img
b5766a77c801ac8fdb58f7361ffcf57cec32a9e729655304681f7568cbe98e17  device/google/raviole-kernels/local-dist/vendor_dlkm.img

到这里,才能说本地内核和模块镜像已经成套接入。


7. WSL 环境下的几个经验

7.1 自动睡眠会让构建“慢得离谱”

之前出现过五小时只编译到约 60% 的情况,后面判断主要原因是 Windows/WSL 自动睡眠。

AOSP 构建期间 CPU 高负载是正常的,但系统睡眠会让构建进程暂停,甚至引入时间戳异常。

建议:

  • 构建前关闭自动睡眠
  • 接电
  • 开高性能模式
  • 不要把构建会话误认为“后台任务”

7.2 Missing restat 不一定是失败

WSL 下出现过:

ninja: Missing `restat`? An output file is older than the most recent input

这通常和 WSL / Windows 时间戳偏差有关。只要最后没有 FAILED:,并且出现:

#### build completed successfully ####

就先不要把它当成阻塞问题。

7.3 CPU 比内存更容易成为瓶颈

机器配置:

AMD Ryzen 7 8845H
64 GB RAM

实际观察是 CPU 更容易吃满,内存没有明显瓶颈。m -j12 能跑,但温度会长期在 80 到 85 摄氏度附近。

如果想降低温度和噪音:

m -j8 droid

代价是编译时间变长。


8. 刷机前检查

构建完成后,建议重点检查输出目录:

~/Android_SRC/aosp/out/target/product/oriole

关键日志:

~/Android_SRC/aosp/build-oriole-bp1a-first.log
~/Android_SRC/aosp/build-oriole-local-kernel.log
~/Android_SRC/pixel_kernel/build-raviole-first.log

刷机前至少确认:

  • bootloader 是否解锁
  • adb / fastboot 是否识别
  • 当前 slot 是否记录
  • 是否有回滚方案
  • 刷入完整镜像集,不只刷 boot.img

首次启动后验证:

  • uname -a
  • vendor_dlkm 模块加载
  • 显示
  • 触控
  • Wi-Fi / 蓝牙
  • 相机
  • 基带
  • vendor service
  • simpleperf cs-etm 是否能产生非空 AUX 数据

结论

这次最大的经验是:不要只看 boot.img 是否换成了本地内核。

Pixel 6 这类设备上,vendor_boot.imgdtbo.imgvendor_dlkm.img 和 AVB 元数据都要作为一套整体看。尤其是 vendor_dlkm.img,如果仍然来自官方 vendor 预编译镜像,后续模块问题会很隐蔽。

完成这些检查后,再进入真机刷写和启动验证。

tower的ai助手