AOSP目录结构全景
AOSP(Android Open Source Project)并不是一个普通的单体 Git 仓库。打开源码根目录时, 我们会看到 frameworks/、system/、packages/、hardware/ 等统一排列的目录, 但这些目录背后实际挂载着许多相互独立的 Git project,再由 repo 和 manifest 组合成一棵 可以统一搜索、编译和提交的源码树。
因此,阅读 AOSP 需要同时建立三张地图:
- 仓库地图:代码属于哪个 Git project,由哪个 manifest revision 固定。
- 目录地图:顶层目录和子目录分别承担什么工程职责。
- 运行时地图:这些代码最终运行在哪个进程,通过什么接口协作。
如果只看目录名,很容易把 frameworks/ 等同于 Java Framework,把 system/ 等同于 Linux 系统层,或者把 hardware/ 中的接口定义直接当成设备上的 HAL 实现。本文从多仓模型开始, 逐步把目录结构还原成 Android 的启动、Framework、Native、HAL 和 Kernel 关系。
本文是 Foundations 系列的入口,不要求读者已经使用过 repo。文章先区分 project name 与 本地 path,再说明主要顶层目录的职责,以及如何从一个 Android 问题选择第一批源码入口。 需要实际初始化和同步工作区时,继续阅读 repo初始化与同步。
1. AOSP 源码树
1.1 repo
repo 是建立在 Git 之上的多仓管理工具。它不取代 Git,而是读取 manifest,确定需要管理哪些 project、每个 project 应检出哪个 revision,以及它们在工作区中应挂载到什么路径。
一个 project 通常包含两个容易混淆的名字:
| 名称 | 示例 | 含义 |
|---|---|---|
| 上游 project name | platform/frameworks/base | 远端代码仓库的标识 |
| 本地 project path | frameworks/base | 源码在工作区中的挂载位置 |
platform/frameworks/base 和 frameworks/base 指向同一份 project,但分别用于描述远端仓库 和本地路径。阅读源码时用工作区相对路径定位文件;需要重现某个版本时,再记录 project 与 commit。
下面这张图展示 manifest 如何把独立 project 组织成统一工作区。它只表达源码管理关系,不表示 Android 运行时调用方向。
这套模型带来一个直接结论:AOSP 没有一个覆盖全部源码的单一 project commit。 frameworks/base、frameworks/native、system/core 等 project 在同一个 Android release 下分别对应自己的 commit。分析跨 project 调用链时,需要分别固定每个实际引用的 project。
1.2 remote
理解 AOSP 目录不需要记住完整 manifest 语法,先掌握四个核心元素即可:
| 元素 | 职责 | 阅读重点 |
|---|---|---|
remote | 定义远端获取和代码审查地址 | fetch 根地址、review 地址 |
default | 定义 project 的默认属性 | revision、remote、同步参数 |
project | 定义一个 Git project | name、path、groups、独立 revision |
linkfile | 在工作区创建跨目录入口 | 源文件与目标路径 |
当前 manifest 中的构建 project 声明了 build/make、build/soong 等本地路径,并通过 linkfile 暴露 Android.bp、build/envsetup.sh 等工作区入口。这解释了 build/envsetup.sh 与 build/make/envsetup.sh 之间的关系。工作区根目录中的入口文件不一定 属于根目录自身,它可能由某个 project 通过 manifest 投射出来。
1.3 repo init
repo init 准备 repo client 和 manifest,repo sync 再按 manifest 取得并检出 project。 在目录地图中只需要知道这个职责分界;参数、网络/本地阶段、失败恢复和危险选项由 repo初始化与同步 专门解释。
2. 顶层目录职责
2.1 工程职责
AOSP 顶层目录首先表达代码的工程归属。它并不严格对应 Android 官方架构图中的 Applications、Framework、Native Libraries、HAL 和 Kernel 层。
例如:
frameworks/native既包含应用进程会加载的 Native 库,也包含独立运行的 SurfaceFlinger。packages/modules中的 Mainline 模块可能同时提供 API、系统服务和独立进程。system/core不只是底层库,还包含 init、debuggerd、fs_mgr 等可执行程序。hardware/interfaces主要定义稳定接口,真实设备实现可能位于其他 hardware、vendor 或设备仓库。
所以,目录表的价值是帮助定位代码,不是代替运行时架构。
2.2 核心顶层目录
| 目录 | 主要职责 | 典型内容 |
|---|---|---|
art/ | Android Runtime | Runtime、GC、JIT/AOT、dex2oat |
bionic/ | Android C 标准库与动态链接器 | libc、libm、libdl、linker |
bootable/ | 启动和恢复相关组件 | Recovery、bootloader 公共代码 |
build/ | 构建和产品配置 | Make、Soong、Blueprint、release 配置 |
device/ | 设备与产品定义 | product、BoardConfig、overlay、fstab |
external/ | 第三方开源依赖 | 压缩、图形、网络、编解码等独立项目 |
frameworks/ | Android Framework 与核心 Native 框架 | base、native、av、opt |
hardware/ | HAL 接口与参考实现 | AIDL/HIDL 接口、libhardware |
kernel/ | Linux 内核及 Android 内核代码 | Binder、DRM、Input、调度和内存 |
libcore/ | Java 核心类库 | ojluni、luni |
packages/ | 系统应用、Mainline 模块和服务 | apps、modules、services |
prebuilts/ | 预编译工具与依赖 | JDK、Clang、SDK、构建工具 |
system/ | Android 用户空间底层能力 | init、vold、sepolicy、日志、基础库 |
test/、cts/ | 平台与兼容性测试 | CTS、VTS 关联测试、测试框架 |
tools/ | 平台研发工具 | 兼容性、生成、分析和开发工具 |
trusty/ | Trusty TEE | 内核、应用、库与接口 |
这些目录之下通常仍包含多个 Git project。例如 frameworks/base 和 frameworks/native 是不同 project;packages/modules/Connectivity 与 packages/modules/Wifi 也分别维护。
2.3 frameworks/base
frameworks/base 是理解 Android Framework 最重要的入口之一,但它内部仍有明确分区:
frameworks/base/
├── core/
│ ├── java/ # android.* 核心 Java API 与内部实现
│ ├── jni/ # Java Framework 对应的 JNI 桥接
│ └── res/ # Framework 核心资源
├── services/
│ ├── java/ # SystemServer 等服务启动入口
│ └── core/java/ # AMS、PMS、WMS、Power 等服务实现
├── packages/ # SystemUI 等与 Framework 紧密相关的组件
├── libs/ # WindowManager Shell 等库
├── tools/ # Framework 构建和开发工具
└── tests/ # Framework 测试应用侧的 Activity、View、PowerManager 等 API 多从 core/java/android 开始; 系统侧的 AMS、PMS、WMS 等服务则主要位于 services/core/java/com/android/server。 阅读一次 Binder 调用时,通常需要在这两个区域之间往返。
2.4 native
frameworks/native 是 Native Framework 的核心入口:
frameworks/native/
├── libs/
│ ├── binder/ # Binder Native 客户端、服务端与线程池
│ ├── gui/ # Surface、BufferQueue、BLASTBufferQueue
│ ├── ui/ # GraphicBuffer、Dataspace 等图形类型
│ └── renderengine/ # SurfaceFlinger 使用的渲染引擎
├── services/
│ ├── surfaceflinger/ # 显示合成服务
│ ├── inputflinger/ # 输入读取与分发
│ └── sensorservice/ # 传感器服务
└── cmds/ # servicemanager 等 Native 命令与守护程序这里的 libs 和 services 不应混为一谈。libs/binder 可以被多个进程链接使用, services/surfaceflinger 则构建为长期运行的系统服务进程。
2.5 system
system 下的 project 更接近 Android 用户空间的底层基础设施:
| 路径 | 主要职责 |
|---|---|
system/core/init | first-stage init、SELinux setup、second-stage init |
system/core/fs_mgr | 文件系统挂载、AVB、动态分区相关能力 |
system/core/debuggerd | Native crash 收集与 tombstone |
system/core/libutils | Looper、线程等 Native 基础类型 |
system/sepolicy | 平台、system_ext、product、vendor SELinux 策略 |
system/vold | 卷管理、文件系统、加密和用户存储 |
system/logging | liblog、logd 相关实现 |
system/tools/aidl | AIDL 编译器 |
system 不是 Linux Kernel。它主要是运行在用户空间的 Android 原生代码;真正的驱动和内核机制 仍位于 kernel project。
2.6 packages
packages 的内容可以粗略分为三类:
| 分区 | 示例 | 作用 |
|---|---|---|
packages/apps | Settings、Car/SystemUI | 系统应用 |
packages/modules | Connectivity、Wifi、Media、Permission | Mainline/APEX 可更新模块 |
packages/services | Car 等 | 独立的大型系统服务集合 |
不能因为路径以 packages 开头就把它理解为普通 APK。Mainline 模块可能包含 Java 服务、 Native 库、AIDL、APEX 配置和测试,边界比传统系统应用更宽。
2.7 hardware
hardware/interfaces 主要保存 Android 定义的 HAL 接口。接口描述了 Framework 或 Native 服务 可以依赖什么契约,但不保证某个设备使用相同实现。
device/<vendor>/<product> 则负责把平台能力组合成具体产品,常见内容包括:
- 产品继承和 package 列表;
- BoardConfig 与分区配置;
- init rc、fstab、权限和 SELinux 片段;
- overlay 与资源差异;
- HAL service manifest 和兼容性矩阵。
厂商真正的实现还可能位于 vendor 私有仓库。AOSP 中的参考实现、模拟器实现和某个硬件平台实现 不能被直接描述为所有 Android 设备的统一行为。
3. 运行时层次
目录结构回答“代码放在哪里”,运行时架构回答“代码由谁执行”。下面这张图把常用目录映射到 进程和接口边界。
这不是完整 Android 架构图,而是一张源码导航图。接下来用三个入口说明如何从目录走到运行主体。
3.1 system/core/init
init/main.cpp 根据启动参数进入 first stage、SELinux setup、second stage、ueventd 或 subcontext。 这说明 system/core 中不仅有基础库,也包含负责系统启动的可执行程序。
源码文件:system/core/init/main.cpp
相关函数/类型:main
int main(int argc, char** argv) {
setpriority(PRIO_PROCESS, 0, -20);
if (!strcmp(basename(argv[0]), "ueventd")) {
// 说明:同一可执行文件根据 argv[0] 进入设备节点管理路径。
return ueventd_main(argc, argv);
}
// ...
if (argc > 1) {
if (!strcmp(argv[1], "selinux_setup")) {
// 说明:SELinux setup 是启动过程中的独立阶段。
return SetupSelinux(argv);
}
if (!strcmp(argv[1], "second_stage")) {
// 说明:second stage 继续属性、rc 解析和服务管理。
return SecondStageMain(argc, argv);
}
}
#if defined(FIRST_STAGE_INIT) || defined(RECOVERY)
return FirstStageMain(argc, argv);
#else
LOG(FATAL) << "Second-stage init requires an argument to main()";
#endif
}从这里继续阅读时,应进入 first_stage_init.cpp、selinux.cpp 和 init.cpp,而不是在 main.cpp 中寻找所有实现。入口文件的作用是展示阶段分派,具体职责分布在各子模块。
3.2 base services
SystemServer.main() 进入 run(),随后按 bootstrap、core、other 和 APEX 等阶段启动服务。 AMS、PMS、PowerManagerService 等代码虽然位于不同 package,但共享 system_server 的进程和 启动上下文。
源码文件:frameworks/base/services/java/com/android/server/SystemServer.java
// 符号:SystemServer.main / SystemServer.run
public static void main(String[] args) {
// 说明:Zygote 创建 system_server 后,从这里进入统一服务启动器。
new SystemServer().run();
}
// SystemServer.run() 中的服务启动阶段
try {
t.traceBegin("StartServices");
// ...
// 说明:分阶段启动表达真实依赖,不是任意顺序的服务清单。
startBootstrapServices(t);
startCoreServices(t);
startOtherServices(t);
startApexServices(t);
updateWatchdogTimeout(t);
CriticalEventLog.getInstance().logSystemServerStarted();
} catch (Throwable ex) {
// 说明:关键服务启动失败会沿异常路径终止当前启动阶段。
// ...
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
} finally {
t.traceEnd(); // StartServices
}理解某个 Java 系统服务时,通常应先从 SystemServer 找到创建时机,再进入服务本身的构造、 onStart、systemReady 或 boot phase 回调。只读服务类容易遗漏初始化依赖。
3.3 native services
SurfaceFlinger 的入口位于 frameworks/native/services/surfaceflinger。它初始化 Binder 线程池,创建服务对象,完成初始化并向 ServiceManager 注册,最后进入长期运行循环。
源码文件:frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp
相关函数/类型:main
int main() {
// ...
ProcessState::self()->setThreadPoolMaxThreadCount(4);
sp<ProcessState> ps(ProcessState::self());
// 说明:SurfaceFlinger 自己启动 Binder 线程池,
// 它不是 system_server 内的 Java SystemService。
ps->startThreadPool();
sp<SurfaceFlinger> flinger =
surfaceflinger::createSurfaceFlinger();
// 说明:初始化在服务公开给客户端之前完成。
flinger->init();
// ...
sp<IServiceManager> sm(defaultServiceManager());
sm->addService(
String16(SurfaceFlinger::getServiceName()),
flinger,
false,
IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL |
IServiceManager::DUMP_FLAG_PROTO);
// ...
// 说明:run() 进入 SurfaceFlinger 的主循环。
flinger->run();
}frameworks/native 因而既是库集合,也是独立系统进程的实现位置。判断某段代码运行在哪个进程, 需要继续检查构建目标、main()、rc 文件和服务注册,而不能只看顶层目录。
4. 模块定位
4.1 API路径
适合应用 Framework 功能的路径:
- 在
frameworks/base/core/java/android找公开 API。 - 找 API 持有的 Binder interface 或 manager delegate。
- 在 AIDL、
Stub或 ServiceManager 注册点定位服务端。 - 回到
SystemServer或对应进程入口确认服务如何启动。 - 最后进入 HAL、Native 或 Kernel 路径。
例如,PowerManager 的应用 API 并不是电源策略实现。它通过 IPowerManager 与 PowerManagerService 通信,服务再协调 Display、Dream、Battery、SuspendBlocker 和 Power HAL。
4.2 注册点路径
如果已经知道服务名,可以反向搜索:
# 查找 Java 服务注册
rg 'ServiceManager\\.addService|publishBinderService' frameworks/base/services
# 查找 Native 服务注册
rg 'addService\\(' frameworks/native/services system
# 查找 init 启动的守护进程
rg '^service ' system device hardware -g '*.rc'这些命令是入口筛选,不是事实证明。找到候选位置后,还要检查调用条件、构建配置、feature flag、 产品 overlay、权限检查和测试。
4.3 模块边界
源码目录中的文件不一定属于同一个构建目标。Android.bp 可以回答:
- 最终生成 library、binary、APK 还是 APEX;
- 依赖哪些模块;
- 使用哪些
srcs、defaults和header_libs; - 是否受 product variable、
soong_config或平台条件控制; - 安装到哪个分区或可见性范围。
当类名、目录名和运行时进程不一致时,构建目标往往是连接它们的关键证据。
4.4 模块测试
正向调用链只能说明“成功时可能怎样运行”。一篇可信的源码文章还应搜索:
- 单元测试、集成测试和 CTS/VTS 用例;
- Binder death、权限拒绝、超时与取消;
- feature flag、系统属性、DeviceConfig 与资源 overlay;
- 服务未注册、HAL 不可用和设备能力缺失;
- 多用户、多显示、多设备和进程重启。
很多“Android 就是这样”的结论,在加入这些条件后会变成“满足某个配置和能力时走这条路径”。 后者虽然更复杂,但更接近真实平台行为。
5. 常见误区
5.1 目录边界
frameworks/native 不等于整个 Native 层,system 也不等于 Linux Kernel。 顶层目录是代码组织入口,运行时架构需要结合进程、接口和构建目标判断。
5.2 接口/实现
hardware/interfaces 中的 AIDL/HIDL 定义描述契约。真实设备可能采用不同服务实现、驱动和产品 配置。分析 HAL 时应区分:
- Framework 或 Native 客户端;
- 稳定接口定义;
- AOSP 参考实现;
- 设备或厂商实现;
- Kernel driver。
5.3 版本记录
阅读跨 project 调用链时,release 只能标识整体发布版本,不能代替各 project 的 commit。 升级后若出现行为差异,应分别比对实际涉及的 project,而不是把整棵工作区当作一个 Git 仓库。
5.4 实现/测试
类型名称和文件路径可能保留历史语义。调用方可以确认它是否仍在主路径,测试则描述维护者关心的 行为和边界。源码阅读应在实现、调用方、测试和构建配置之间往返。
5.5 符号定位
行号会随着格式和注释变化而漂移。稳定引用至少应包含 project、commit、路径和符号;行号只是 方便当前版本阅读的辅助信息。
6. 源码阅读流程
面对一个新模块,可以按以下顺序建立认知:
- 确定问题:要解释的是 API、服务、进程、协议、状态机还是故障。
- 确定 project:从 manifest 和本地 path 确认仓库归属。
- 固定版本:记录 release 与该 project 的不可变 commit。
- 找到构建目标:阅读附近的
Android.bp、rc、APEX 或应用 manifest。 - 找到入口:公开 API、
main()、SystemServer、ServiceManager 或 HAL client。 - 追踪主路径:记录调用、线程、Binder、锁、队列和数据对象。
- 补失败路径:搜索错误、超时、权限、死亡通知、回滚与资源清理。
- 检查边界:读取测试、fixture、dump、trace 和配置。
- 画图和列表:只把真实关系转成时序图、状态图、依赖图或表格。
- 验证引用:分享结论前在固定 commit 上重新确认路径和符号。
这套流程比“从一个大类的第一行读到最后一行”更有效。Android 系统代码跨进程、跨语言、 跨 project 的情况非常普遍,先确定边界再深入实现,可以显著减少错误关联。
7. 源码导航实践
AOSP 的目录结构可以概括为三层关系:
- manifest 与
repo把多个独立 Git project 组织成统一工作区; - 顶层目录按工程职责划分 Framework、系统基础、硬件接口、应用、构建和测试代码;
- 真实运行时关系由进程、Binder、HAL、构建目标和驱动边界共同决定。
掌握这三层之后,frameworks/base 不再只是“很大的 Java 目录”, frameworks/native 也不再只是“C++ 代码集合”。读者可以从问题出发,先定位 project 和入口, 再沿调用链进入 SystemServer、Native 服务、HAL 与 Kernel,并用测试和失败路径校正结论。
实际阅读源码时,可以用下面三个任务验证目录定位是否正确:
- 在任意 AOSP project 中运行
repo list .,解释输出中的 path 与 project name。 - 给定“应用启动 Activity”,列出最先阅读的 Framework API、SystemServer 服务和 Native 边界。
- 给定“相机 HAL 调用失败”,区分接口定义、AOSP 服务、设备实现和 Kernel driver 的源码位置。
关于可复现工作区的建立参见 repo初始化与同步;多 project 查询、 分支与差异管理参见 repo日常命令实战。
