3. AOSP源码工程与构建系统

Android 系统定制的一切工作,都建立在 AOSP(Android Open Source Project)源码工程之上。理解源码如何被组织、下载、配置、编译,以及最终产物如何刷入设备,是 Framework 定制工程师的基本功。本章围绕“源码工程 + 构建系统 + 产物 + 刷机”这条主线,把 repo、Soong、Ninja、Make、lunch 等关键工具串成一条可操作的工程链路。

一、AOSP 源码工程的整体结构

AOSP 并不是一个单一的 Git 仓库,而是由数百个 Git 仓库组成的“多仓工程”。Google 使用 repo 工具对这些仓库进行统一管理。repo 本身是一个 Python 脚本,它封装了 Git,并通过一个 manifest 清单文件描述“哪些仓库、放在哪个路径、使用哪个分支或 revision”。

典型 AOSP 根目录下会看到以下关键内容:

  • .repo/:repo 的元数据目录,包含 manifest、project 列表和本地 Git 镜像信息。
  • build/:构建系统核心,包含 Soong、Make、Kati、环境脚本等。
  • frameworks/:Framework 层源码,如 frameworks/base、frameworks/native。
  • system/、packages/、device/、vendor/:系统组件、应用、设备配置与厂商代码。
  • out/:编译输出目录,所有中间产物和最终镜像都在这里生成。

理解“多仓 + manifest + out 目录”这三件事,就抓住了 AOSP 工程结构的骨架。

二、源码下载与 repo 工具

下载 AOSP 的标准流程是:先安装 repo,再 init 指定 manifest 分支,最后 sync 拉取全部仓库。以 Android 14 为例:

mkdir ~/aosp && cd ~/aosp
repo init -u https://android.googlesource.com/platform/manifest -b android-14.0.0_r1
repo sync -c -j8 --no-tags --no-clone-bundle

其中 -c 表示只拉取当前分支,-j8 表示 8 个并发任务,--no-tags 可减少无用标签下载。repo 常用命令还包括:

  • repo status:查看所有仓库的改动状态。
  • repo forall -c "git checkout .":批量回退所有仓库改动。
  • repo manifest -r -o pinned.xml:导出当前各仓库的精确 revision,便于复现构建。
  • repo start mybranch --all:为所有仓库创建本地开发分支。
在团队协作中,建议用 repo manifest -r 固化版本,避免“同一分支不同时间 sync 出不同代码”导致构建结果不可复现。

三、构建系统:Soong、Ninja、Make 与 Kati 的协作

Android 7 之后,构建系统从纯 Make 逐步迁移到 Soong + Ninja。今天 AOSP 的构建流程可以概括为:Soong 解析 Android.bp → 生成 Ninja 文件 → Ninja 执行编译 → Make/Kati 处理少量遗留模块。

各组件职责如下:

组件输入职责输出
SoongAndroid.bp解析模块定义,生成构建图out/soong/build.ninja
KatiAndroid.mk把 Makefile 转换为 Ninjaout/build-*.ninja
Ninja*.ninja按依赖图并行执行编译命令目标文件、镜像
MakeMakefile顶层入口,调度 Soong/Kati/Ninja最终镜像

一个典型的 Android.bp 模块定义如下:

cc_library_shared {
    name: "libmylog",
    srcs: ["mylog.cpp"],
    shared_libs: ["liblog"],
    cflags: ["-Wall"],
}

Soong 使用 Blueprint 语法,比 Android.mk 更简洁、可静态分析。新增模块时优先写 Android.bp,只有在必须兼容旧逻辑时才使用 Android.mk。

四、lunch 与编译流程

编译前必须先“选择目标”,即 lunch。lunch 决定了产品(product)、设备(device)、变体(variant)三要素,例如:

source build/envsetup.sh
lunch aosp_arm64-userdebug
make -j16

变体含义:user 为发布版,userdebug 可调试并支持 root,eng 为工程版。Framework 定制通常使用 userdebug。常用编译命令:

  • m:从当前目录向上查找并编译。
  • mm:编译当前目录下的模块。
  • mmm frameworks/base:编译指定目录模块。
  • make snod:仅重新生成 system.img,快速验证 Framework 改动。
修改 Framework 后若只执行 make snod,需确保相关模块已编译进 system.img,否则刷机后看不到改动。首次或大范围改动建议完整 make -j。

五、编译产物与刷机流程

编译完成后,产物集中在 out/target/product/<device>/ 下,常见镜像包括:

  • system.img:系统分区,包含 Framework、系统应用。
  • vendor.img:厂商分区,含 HAL、驱动配置。
  • boot.img:内核与 ramdisk。
  • userdata.img:用户数据分区。
  • super.img:动态分区合并镜像(Android 10+)。

刷机通常使用 fastboot:

adb reboot bootloader
fastboot flash system system.img
fastboot flash boot boot.img
fastboot reboot

若设备使用动态分区,则需先 fastboot flash super super.img,或使用 fastboot update 刷入完整包。刷机前务必确认设备已解锁 bootloader,并备份 userdata。

AOSP 构建与刷机链路 源码下载 repo init / sync repo 多仓管理 manifest / project lunch 选择目标 product / variant 构建系统 Soong / Ninja 构建系统内部协作 Android.bp Soong 解析 Android.mk Kati 转换 build.ninja Ninja 执行 编译产物 out/target/product/<device>/ system.img · vendor.img · boot.img · super.img · userdata.img fastboot 刷机 flash system / boot / super → reboot

六、常见误区与复盘

在实际工程中,初学者最容易踩的坑集中在以下几点:

  1. 忘记 source 环境脚本:未执行 source build/envsetup.sh 就使用 m、mm,命令找不到。
  2. lunch 选错变体:用 user 变体调试 Framework,导致 adb root 不可用。
  3. 只 make snod 不编译模块:Framework 改动未进入 system.img,刷机后无效果。
  4. repo sync 后未重新 lunch:产品配置变化后未重新选择目标,构建结果异常。
  5. 刷机分区不匹配:动态分区设备直接 flash system.img 失败,应使用 super.img 或 fastboot update。
建议把“repo manifest -r 固化版本 → lunch 选择目标 → make -j 完整编译 → fastboot 刷机验证”作为标准闭环,每次改动都按此流程复现,能显著降低环境差异带来的问题。