AOSP build envsetup.sh 完全解析
一、文件概述
build/envsetup.sh 是 AOSP 构建系统的入口脚本。每次打开终端准备编译 Android 时,你执行的第一条命令就是:
source build/envsetup.sh
它的核心职责是:为当前 shell 环境注入一系列构建辅助函数、设置环境变量、配置 PATH、加载补全脚本和 vendor 配置,让你能够使用 lunch、m、mmm 等便捷命令来编译 Android。
二、执行流程总览
当你 source build/envsetup.sh 时,脚本按以下顺序执行:
1. _gettop_once() → 定位源码树根目录
2. source shell_utils.sh → 加载公共工具函数(gettop、getoutdir 等)
3. 定义所有辅助函数 → lunch、m、mm、mmm、tapas、banchan 等
4. validate_current_shell → 校验当前 shell 类型(bash/zsh)
5. set_global_paths() → 设置全局 PATH(不依赖 lunch 的路径)
6. source_vendorsetup() → 加载 device/vendor/product 下的 vendorsetup.sh
7. addcompletions() → 注册 Tab 补全
三、核心函数详解
3.1 源码树定位
| 函数 | 作用 |
|---|---|
_gettop_once() |
脚本开头调用,通过查找 build/make/core/envsetup.mk 向上遍历目录来定位源码根目录 |
gettop() |
来自 shell_utils.sh,后续所有函数都通过 $(gettop) 获取根目录路径 |
croot() |
快速跳转到源码根目录(cd $TOP),也可传子目录参数 |
原理:通过从当前目录向上查找 build/make/core/envsetup.mk 文件来确定根目录,同时处理了符号链接的情况。
3.2 构建目标选择(最核心)
lunch — 选择完整产品构建配置
lunch # 交互式菜单选择
lunch aosp_x86_64-eng # 直接指定目标
内部流程:
1. 解析参数 → product=TARGET_PRODUCT, variant=TARGET_BUILD_VARIANT
2. 调用 build_build_var_cache() → 一次性从构建系统获取所有变量
3. 导出环境变量:
- TARGET_PRODUCT
- TARGET_BUILD_VARIANT
- TARGET_BUILD_TYPE=release
- TARGET_PLATFORM_VERSION(如有)
4. 调用 set_stuff_for_environment() → 设置 PATH、OUT 等
5. 调用 printconfig() → 打印当前配置
6. 调用 destroy_build_var_cache() → 清理缓存变量
lunch 目标格式:<product>-<variant>[-<version>]
- product:产品名,如
aosp_x86_64、sdk_phone_x86_64 - variant:构建变体,三选一:
eng:工程版,包含调试工具,root 权限,开发首选userdebug:类似 user 但保留 root 和调试,性能测试用user:正式用户版,限制权限,最终发布用
可选目标来源:COMMON_LUNCH_CHOICES(定义在各 AndroidProducts.mk 中)
tapas — 构建未捆绑应用(APK)
tapas Settings Messaging arm64 userdebug
tapas MyApp x86 eng
用于只编译一个或几个 APK,不需要完整系统镜像。参数顺序随意,脚本通过正则自动识别架构/变体/应用名。
banchan — 构建未捆绑模块(APEX)
banchan com.android.art arm64 eng
类似 tapas,但用于 APEX 模块(如 ART 运行时、媒体编解码器等)。
choosecombo — 交互式逐步选择
choosecombo
依次让你选择:Build Type → Product → Variant,适合不确定目标名的场景。
3.3 构建命令
| 命令 | 作用 | 示例 |
|---|---|---|
m |
从根目录编译指定目标(最常用) | m SystemUI |
mm |
编译当前目录下的模块及其依赖 | 在 frameworks/base 下执行 mm |
mmm |
编译指定目录下的模块及其依赖 | mmm frameworks/base:Services |
mma |
同 mm,编译当前目录模块+依赖 |
- |
mmma |
同 mmm,编译指定目录模块+依赖 |
- |
make |
包装原始 make,自动使用 soong_ui | make -j8 |
底层实现:这些函数都调用 _trigger_build(),最终执行:
build/soong/soong_ui.bash --build-mode --all-modules --dir="$(pwd)" "$@"
soong_ui.bash 是 Soong 构建系统的入口,负责解析 Blueprint 文件、生成 Ninja 构建图、执行编译。
3.4 构建变量缓存系统
| 函数 | 作用 |
|---|---|
build_build_var_cache() |
一次性调用 soong_ui 获取所有需要的构建变量,存入 shell 变量 |
get_build_var() |
从缓存读取构建变量值(如 TARGET_ARCH) |
get_abs_build_var() |
从缓存读取构建变量的绝对路径值(如 PRODUCT_OUT) |
destroy_build_var_cache() |
清除缓存变量 |
为什么要缓存? 每次调用 soong_ui 获取变量开销很大,缓存机制将所有变量一次性获取,大幅提升 lunch 等函数的执行速度。
3.5 环境与路径设置
set_lunch_paths() — lunch 相关路径
设置依赖 lunch 选择的路径,加入 PATH:
SOONG_HOST_OUT_EXECUTABLES → Soong 主机工具
HOST_OUT_EXECUTABLES → 主机工具
LLVM_BINUTILS → Clang/LLVM 工具链
asuite (acloud/aidegen/atest) → 开发工具
JAVA_TOOLCHAIN → Java 工具链
同时设置关键环境变量:
| 变量 | 含义 |
|---|---|
ANDROID_PRODUCT_OUT |
产品输出目录(如 out/target/product/generic_x86_64) |
OUT |
同 ANDROID_PRODUCT_OUT 的简写 |
ANDROID_HOST_OUT |
主机输出目录 |
ANDROID_JAVA_HOME |
Java Home 路径 |
PYTHONPATH |
Python 包搜索路径 |
set_global_paths() — 全局路径
设置不依赖 lunch 的路径:
build/bazel/bin → Bazel 工具
development/scripts → 开发脚本
prebuilts/devtools/tools → 预编译开发工具
prebuilts/android-emulator → 模拟器
dtc/libufdt → 设备树编译工具(Linux)
3.6 代码搜索工具(grep 系列)
| 函数 | 搜索目标 |
|---|---|
cgrep |
C/C++ 文件(.c .cc .cpp .h .hpp) |
jgrep |
Java 文件(.java) |
ktgrep |
Kotlin 文件(.kt) |
rsgrep |
RenderScript 文件(.rs) |
gogrep |
Go 文件(.go) |
pygrep |
Python 文件(.py) |
ggrep |
Gradle 文件(.gradle) |
jsongrep |
JSON 文件(.json) |
tomlgrep |
TOML 文件(.toml) |
resgrep |
资源 XML(res/*.xml) |
mangrep |
AndroidManifest.xml |
mgrep |
Makefile 和 Blueprint(.mk .bp) |
owngrep |
OWNERS 文件 |
sepgrep |
sepolicy 文件 |
sgrep |
所有源码文件(C/C++/Java/Kotlin/XML 等) |
treegrep |
跨目录搜索源码(不区分大小写) |
使用示例:
cgrep "BufferQueue" # 在所有 C/C++ 文件中搜索
jgrep "ActivityManagerService" # 在所有 Java 文件中搜索
mgrep "LOCAL_INIT_RC" # 在 Makefile/Blueprint 中搜索
所有 grep 函数都会自动排除 .repo、.git、out 目录。
3.7 模块操作工具
| 函数 | 作用 | 示例 |
|---|---|---|
allmod |
列出所有模块名 | allmod | grep Settings |
gomod |
跳转到模块所在目录 | gomod SystemUI |
pathmod |
获取模块路径 | pathmod SystemUI |
outmod |
获取模块输出文件路径 | outmod SystemUI |
dirmods |
列出目录下所有模块 | dirmods frameworks/base |
bmod |
获取模块的 Bazel 标签 | bmod SystemUI |
installmod |
adb 安装模块 APK | installmod Settings |
refreshmod |
刷新模块信息缓存 | refreshmod |
这些函数依赖 $ANDROID_PRODUCT_OUT/module-info.json,如果模块信息过期,先执行 refreshmod。
3.8 设备交互工具
| 函数 | 作用 |
|---|---|
syswrite |
禁用 verity、remount 分区为可写 |
coredump_setup |
在设备上启用 core dump |
coredump_enable |
为指定进程启用 core dump |
core |
发送 SIGSEGV 并拉取 core 文件 |
smoketest |
运行冒烟测试 |
provision |
刷入所有分区(fastboot) |
getbugreports |
从设备拉取 bugreport |
key_home/key_back/key_menu |
模拟按键 |
avbtool |
自动编译并调用 avbtool |
3.9 目录导航
| 函数 | 作用 |
|---|---|
croot |
跳转到源码根目录 |
cproj |
向上跳转到包含 Android.mk 的目录 |
godir |
按文件名搜索并跳转到对应目录 |
3.10 其他辅助函数
| 函数 | 作用 |
|---|---|
hmm() |
显示帮助信息 |
printconfig() |
打印当前构建配置 |
pez() |
执行命令并彩色显示成功/失败 |
showcommands() |
显示 ninja 实际执行的命令 |
qpid() |
简化版 ps,输出 <pid> <进程名> |
四、脚本末尾的自动执行
脚本最后四行是自动执行的,不需要手动调用:
validate_current_shell # 校验 bash/zsh,配置补全支持
set_global_paths # 设置全局 PATH
source_vendorsetup # 加载 device/vendor/product/vendorsetup.sh
addcompletions # 注册 Tab 补全
source_vendorsetup 的作用
遍历 device/、vendor/、product/ 目录,查找并 source 所有 vendorsetup.sh 文件。这些文件通常由芯片厂商(如 Qualcomm、MTK)提供,用于添加自己的 lunch 目标。
如果存在 allowed-vendorsetup_sh-files 白名单文件,则只加载白名单中的 vendorsetup.sh。
五、环境变量速查
| 变量 | 含义 |
|---|---|
TOP |
源码根目录(可手动设置跳过自动查找) |
ANDROID_BUILD_TOP |
源码根目录(由 set_stuff_for_environment 设置) |
ANDROID_PRODUCT_OUT |
产品输出目录 |
OUT |
产品输出目录(简写) |
ANDROID_HOST_OUT |
主机输出目录 |
ANDROID_SOONG_HOST_OUT |
Soong 主机输出目录 |
TARGET_PRODUCT |
当前选择的产品名 |
TARGET_BUILD_VARIANT |
当前构建变体(eng/userdebug/user) |
TARGET_BUILD_TYPE |
构建类型(release/debug) |
TARGET_BUILD_APPS |
tapas 模式下指定的应用列表 |
ANDROID_JAVA_HOME |
Java Home |
ANDROID_QUIET_BUILD |
设为 true 可静默构建输出 |
SANITIZE_HOST |
设为 address 启用 ASAN |
OUT_DIR |
自定义输出目录(默认 out) |
OUT_DIR_COMMON_BASE |
多树共享输出基目录 |
六、典型工作流
6.1 完整系统编译
source build/envsetup.sh
lunch sdk_phone_x86_64-eng # 选择模拟器 x86_64 工程版
m -j$(nproc) # 全量编译
6.2 只编译某个模块
source build/envsetup.sh
lunch sdk_phone_x86_64-eng
m SystemUI # 编译 SystemUI 模块
6.3 编译单个 APK(不需要完整系统)
source build/envsetup.sh
tapas Settings x86_64 eng
m -j$(nproc)
6.4 编译 APEX 模块
source build/envsetup.sh
banchan com.android.art arm64 eng
m -j$(nproc)
6.5 修改代码后增量编译
# 在模块目录下
cd frameworks/base
mm # 只编译当前目录的模块
七、架构关系图
┌─────────────────────────────────────────────────┐
│ source build/envsetup.sh │
├─────────────────────────────────────────────────┤
│ 1. _gettop_once() → 定位源码根目录 │
│ 2. source shell_utils.sh → gettop/getoutdir │
│ 3. 定义所有辅助函数 │
│ ├─ 构建目标选择: lunch/tapas/banchan │
│ ├─ 构建命令: m/mm/mmm/mma/mmma/make │
│ ├─ 代码搜索: cgrep/jgrep/sgrep/... │
│ ├─ 模块操作: allmod/gomod/pathmod/... │
│ ├─ 设备交互: syswrite/provision/... │
│ └─ 环境管理: set_lunch_paths/set_global_paths│
│ 4. validate_current_shell → 校验 shell │
│ 5. set_global_paths → 全局 PATH │
│ 6. source_vendorsetup → 加载厂商配置 │
│ 7. addcompletions → Tab 补全 │
└─────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ lunch <product>-<variant> │
├─────────────────────────────────────────────────┤
│ 1. 解析 product 和 variant │
│ 2. build_build_var_cache() → 获取构建变量 │
│ 3. 导出 TARGET_PRODUCT / TARGET_BUILD_VARIANT │
│ 4. set_stuff_for_environment() │
│ ├─ set_lunch_paths() → 更新 PATH │
│ └─ 设置 ANDROID_PRODUCT_OUT 等 │
│ 5. printconfig() → 显示配置 │
│ 6. destroy_build_var_cache() → 清理缓存 │
└─────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ m / mm / mmm → 编译 │
├─────────────────────────────────────────────────┤
│ _trigger_build() │
│ → build/soong/soong_ui.bash --make-mode │
│ → 解析 Android.bp / Android.mk │
│ → 生成 build.ninja │
│ → 执行 ninja 编译 │
└─────────────────────────────────────────────────┘
八、常见问题
Q: 为什么必须用 source 而不是 sh 执行?
source(或 .)在当前 shell 中执行脚本,这样定义的函数和环境变量才能保留在当前 shell 中。如果用 sh 执行,函数和变量只在子 shell 中生效,执行完就丢失了。
Q: lunch 后切换了目标,之前的编译产物还在吗?
在。不同产品的输出在不同目录:out/target/product/<device_name>/。切换目标只是改变环境变量指向。
Q: 如何自定义 lunch 目标?
- 在
build/make/target/product/或device/下创建产品.mk文件 - 在对应的
AndroidProducts.mk中将.mk文件加入PRODUCT_MAKEFILES - 将
<product_name>-eng等加入COMMON_LUNCH_CHOICES
Q: m 和 make 有什么区别?
m使用 Soong 构建系统(soong_ui.bash --make-mode),是推荐的方式make也被包装为调用soong_ui.bash,但只有在源码树下才会自动替换- 两者最终效果相同,
m更简洁
Q: 如何查看构建系统实际执行的命令?
showcommands # 显示 ninja 命令
showcommands --regenerate # 重新生成并显示
九、添加系统 App 的最佳实践与原理
9.1 整体架构:从源码到系统镜像
源码 (Android.bp / Android.mk)
│
▼ Soong 解析
模块定义 (name, type, 依赖关系)
│
▼ 产品配置 (.mk)
PRODUCT_PACKAGES += <模块名>
│
▼ 构建系统
模块被编译并安装到对应分区目录
│
▼ 打包
生成 system.img / system_ext.img / product.img / vendor.img
核心原理:一个模块能否进入最终系统镜像,取决于两个条件:
- 模块定义:通过
Android.bp或Android.mk声明模块的名称、类型、源码、依赖 - 产品配置:通过
.mk文件中的PRODUCT_PACKAGES将模块名加入产品包列表
缺一不可——只有定义没有配置,模块不会被打包;只有配置没有定义,构建会报错找不到模块。
9.2 步骤一:创建 App 模块定义(Android.bp)
9.2.1 普通系统 App
放在 /system/app/ 或 /product/app/,没有特权,无法使用系统 API。
android_app {
name: "MyApp",
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
manifest: "AndroidManifest.xml",
sdk_version: "current", // 使用公开 SDK API
certificate: "platform", // 使用平台签名(可选)
}
9.2.2 特权系统 App(Privileged App)
放在 /system/priv-app/ 或 /system_ext/priv-app/,可使用 @SystemApi 和特权权限。
android_app {
name: "MyPrivApp",
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
manifest: "AndroidManifest.xml",
platform_apis: true, // 使用平台 API(@SystemApi)
certificate: "platform", // 必须用平台签名
privileged: true, // 声明为特权 App
system_ext_specific: true, // 安装到 system_ext 分区
required: [
"privapp_whitelist_com.example.myprivapp", // privapp 权限白名单
],
}
关键属性解析:
| 属性 | 含义 | 可选值 |
|---|---|---|
name |
模块名,即 PRODUCT_PACKAGES 中引用的名称 | 任意合法标识符 |
sdk_version |
使用的 SDK 版本 | "current"、"31" 等,不设则用 platform_apis |
platform_apis |
使用平台隐藏 API | true/不设 |
certificate |
签名证书 | "platform"、"shared"、"media"、"testkey" |
privileged |
是否为特权 App | true/不设 |
system_ext_specific |
安装到 system_ext 分区 | true/不设 |
product_specific |
安装到 product 分区 | true/不设 |
vendor |
安装到 vendor 分区 | true/不设 |
required |
同时安装的依赖模块 | 模块名列表 |
static_libs |
静态 Java 依赖 | 模块名列表 |
uses_libs |
运行时共享库(<uses-library>) |
模块名列表 |
optimize.proguard_flags_files |
Proguard 混淆配置 | 文件路径 |
9.2.3 分区与安装位置对照
| 属性设置 | 安装位置 | 说明 |
|---|---|---|
| 默认(不设分区属性) | /system/app/ 或 /system/priv-app/ |
系统分区 |
system_ext_specific: true |
/system_ext/app/ 或 /system_ext/priv-app/ |
系统扩展分区 |
product_specific: true |
/product/app/ 或 /product/priv-app/ |
产品分区 |
vendor: true |
/vendor/app/ |
厂商分区 |
privileged: true 决定是否进入 priv-app/ 子目录。
9.2.4 privapp 权限白清单
特权 App 在 AndroidManifest.xml 中声明的权限必须在白名单中预先声明,否则权限不会授予。
创建 privapp_whitelist_com.example.myprivapp.xml:
<?xml version="1.0" encoding="utf-8"?>
<permissions>
<privapp-permissions package="com.example.myprivapp">
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.WRITE_SETTINGS"/>
</privapp-permissions>
</permissions>
在 Android.bp 中定义白名单模块:
prebuilt_etc {
name: "privapp_whitelist_com.example.myprivapp",
src: "privapp_whitelist_com.example.myprivapp.xml",
sub_dir: "permissions",
system_ext_specific: true, // 与 App 分区一致
}
9.3 步骤二:将 App 加入产品配置
9.3.1 在现有产品 .mk 文件中添加
根据 App 所属分区,选择对应的 .mk 文件:
| 分区 | 推荐的 .mk 文件 |
|---|---|
| system | build/make/target/product/handheld_system.mk |
| system_ext | build/make/target/product/handheld_system_ext.mk |
| product | build/make/target/product/handheld_product.mk 或 aosp_product.mk |
| vendor | build/make/target/product/emulator_vendor.mk 或设备厂商 .mk |
添加方式:
PRODUCT_PACKAGES += \
MyApp \
MyPrivApp \
9.3.2 创建自定义产品配置(推荐)
为自己的产品创建独立的 .mk 文件,避免修改 AOSP 原始文件:
# device/mycompany/myproduct/my_product.mk
$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk)
# 添加自定义 App
PRODUCT_PACKAGES += \
MyApp \
MyPrivApp \
# 产品属性
PRODUCT_BRAND := MyBrand
PRODUCT_NAME := my_product
PRODUCT_DEVICE := my_device
PRODUCT_MODEL := My Product
然后在 AndroidProducts.mk 中注册:
PRODUCT_MAKEFILES := \
$(LOCAL_DIR)/my_product.mk
COMMON_LUNCH_CHOICES := \
my_product-eng \
my_product-userdebug \
my_product-user \
9.3.3 通过 vendorsetup.sh 添加(厂商方式)
在 device/mycompany/myproduct/vendorsetup.sh 中:
add_lunch_combo my_product-eng
注意:add_lunch_combo已废弃,推荐使用COMMON_LUNCH_CHOICES。
9.4 完整示例:添加一个系统 App
假设要添加一个名为 MyLauncher 的系统桌面 App:
目录结构:
packages/apps/MyLauncher/
├── Android.bp
├── AndroidManifest.xml
├── privapp_whitelist_com.example.mylauncher.xml
├── res/
│ ├── layout/
│ └── values/
└── src/
└── com/example/mylauncher/
└── MyLauncherActivity.java
Android.bp:
android_app {
name: "MyLauncher",
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
manifest: "AndroidManifest.xml",
platform_apis: true,
certificate: "platform",
privileged: true,
system_ext_specific: true,
required: [
"privapp_whitelist_com.example.mylauncher",
],
}
prebuilt_etc {
name: "privapp_whitelist_com.example.mylauncher",
src: "privapp_whitelist_com.example.mylauncher.xml",
sub_dir: "permissions",
system_ext_specific: true,
}
产品配置(在 handheld_system_ext.mk 或自定义 .mk 中):
PRODUCT_PACKAGES += \
MyLauncher \
编译验证:
source build/envsetup.sh
lunch sdk_phone_x86_64-eng
m MyLauncher # 单独编译验证
m -j$(nproc) # 全量编译
十、添加库的最佳实践与原理
10.1 原生库(C/C++)
10.1.1 动态共享库(.so)
最常用的库类型,运行时动态加载,多个模块可共享。
cc_library_shared {
name: "libmyutils",
srcs: [
"myutils.cpp",
"logger.cpp",
],
shared_libs: [
"liblog",
"libbase",
],
export_include_dirs: ["include"], // 导出头文件目录
cflags: [
"-Wall",
"-Werror",
],
}
安装位置:/system/lib64/libmyutils.so(64 位)或 /system/lib/libmyutils.so(32 位)
10.1.2 静态库(.a)
编译时链接到目标中,不单独安装。通常作为其他模块的依赖。
cc_library_static {
name: "libmyutils_static",
srcs: [
"myutils.cpp",
"logger.cpp",
],
cflags: [
"-Wall",
"-Werror",
],
export_include_dirs: ["include"],
}
使用时在其他模块中引用:
cc_library_shared {
name: "libmyfeature",
srcs: ["myfeature.cpp"],
static_libs: [
"libmyutils_static", // 静态链接
],
shared_libs: [
"liblog",
],
}
10.1.3 头文件库
只导出头文件,不编译任何源码,用于跨模块共享头文件。
cc_library_headers {
name: "libmyutils_headers",
export_include_dirs: ["include"],
}
使用:
cc_library_shared {
name: "libmyfeature",
header_libs: ["libmyutils_headers"],
// ...
}
10.1.4 原生库关键属性
| 属性 | 含义 |
|---|---|
shared_libs |
运行时动态链接的共享库(传递依赖) |
static_libs |
编译时静态链接的库 |
whole_static_libs |
静态链接整个归档(--whole-archive) |
header_libs |
只链接头文件 |
export_include_dirs |
导出头文件目录给依赖方 |
host_supported |
是否支持主机编译 |
vendor_available |
是否对 vendor 模块可见 |
apex_available |
可被哪些 APEX 模块使用 |
system_ext_specific / product_specific / vendor |
分区归属 |
10.1.5 依赖类型选择原则
┌─────────────────────────────────────┐
│ 什么时候用什么依赖? │
├─────────────────────────────────────┤
│ │
│ shared_libs: 运行时需要,多模块共享 │
│ → liblog, libbase, libcutils │
│ → 生成 .so,多个进程共享内存 │
│ │
│ static_libs: 编译时合并进目标 │
│ → 只被一个模块使用的工具库 │
│ → 不产生独立 .so,减小加载开销 │
│ │
│ whole_static_libs: 强制包含全部符号 │
│ → 需要注册机制的库(如插件注册) │
│ → 避免链接器优化掉"未引用"的代码 │
│ │
│ header_libs: 只需头文件 │
│ → 跨模块共享类型定义/内联函数 │
│ │
└─────────────────────────────────────┘
10.2 Java 库
10.2.1 Java 共享库(可安装)
可被多个 App 在运行时共享的 Java 库,安装到 /system/framework/。
java_library {
name: "com.example.mylib",
srcs: ["src/**/*.java"],
sdk_version: "current",
installable: true, // 必须设为 true 才会安装
}
App 中引用:
android_app {
name: "MyApp",
libs: ["com.example.mylib"], // 运行时依赖
// ...
}
10.2.2 Java 静态库
编译时合并进 App 的 DEX 中,不单独安装。
java_library {
name: "com.example.mylib_static",
srcs: ["src/**/*.java"],
sdk_version: "current",
// 不设 installable 或 installable: false
}
App 中引用:
android_app {
name: "MyApp",
static_libs: ["com.example.mylib_static"], // 静态合并
// ...
}
10.2.3 Java 库依赖选择
| 场景 | 使用方式 | 属性 |
|---|---|---|
| 多个 App 共享同一库 | libs(运行时依赖) |
App 端用 libs,库端设 installable: true |
| 库只被一个 App 使用 | static_libs(编译时合并) |
App 端用 static_libs |
| 库使用平台 API | libs + platform_apis |
库端设 platform_apis: true |
10.3 将库加入产品配置
与 App 相同,在产品 .mk 文件中通过 PRODUCT_PACKAGES 添加:
PRODUCT_PACKAGES += \
libmyutils \
com.example.mylib \
对于只在调试版本中需要的库:
PRODUCT_PACKAGES_DEBUG += \
libmyutils_test \
对于主机端工具:
PRODUCT_HOST_PACKAGES += \
my_host_tool \
10.4 预编译库(Prebuilt)
当只有编译好的 .so 或 .jar,没有源码时,使用预编译模块。
10.4.1 预编译原生库
cc_prebuilt_library_shared {
name: "libmyvendor",
srcs: ["lib/arm64-v8a/libmyvendor.so"],
vendor: true,
compile_multilib: "64",
}
10.4.2 预编译 Java 库
java_import {
name: "com.example.mylib_prebuilt",
jars: ["mylib.jar"],
sdk_version: "current",
installable: true,
}
10.4.3 预编译 APK
android_app_import {
name: "MyPrebuiltApp",
apk: "MyPrebuiltApp.apk",
certificate: "platform",
privileged: true,
system_ext_specific: true,
dex_preopt: {
enabled: false, // 禁用 dex 优化(可选)
},
}
十一、构建系统原理:Soong 与 Ninja
11.1 构建系统演进
Make (旧) → Soong (当前) → Bazel (未来)
- Make:早期 Android 使用 GNU Make,通过
Android.mk定义模块 - Soong:Android 8.0 起引入,使用
Android.bp替代Android.mk - Bazel:正在逐步迁移中,
b命令已可用
11.2 Soong 构建流程
Android.bp ──→ soong ──→ .ninja 文件 ──→ ninja ──→ 编译产物
Android.mk ──→ soong ──→ .ninja 文件 ──→ ninja ──→ 编译产物
- soong 读取所有
Android.bp文件,解析模块定义和依赖关系 - soong 生成
build.ninja文件(Ninja 构建描述) - ninja 根据
build.ninja执行实际编译(并行、增量) - 编译产物输出到
out/目录
11.3 Android.bp 与 Android.mk 的关系
| 特性 | Android.bp | Android.mk |
|---|---|---|
| 格式 | 类 JSON 声明式 | Make 命令式 |
| 变量/条件 | 不支持(通过 soong_config 实现) | 完全支持 |
| 推荐度 | 推荐(新模块必须用) | 旧模块保留,逐步迁移 |
| 模块类型 | android_app、cc_library_shared 等 |
BUILD_PACKAGE、BUILD_SHARED_LIBRARY 等 |
11.4 关键构建变量
| 变量 | 含义 |
|---|---|
PRODUCT_OUT |
产品输出目录 |
TARGET_ARCH |
目标架构(arm64、x86_64 等) |
TARGET_DEVICE |
目标设备名 |
OUT_DIR |
构建输出根目录(默认 out) |
SOONG_OUT |
Soong 中间文件目录 |
十二、常见问题(进阶)
Q: 添加了 App 但 lunch 后编译找不到?
检查 PRODUCT_PACKAGES 中的名称是否与 Android.bp 中 name 完全一致(区分大小写)。
Q: 特权 App 的权限不生效?
- 确认
privileged: true已设置 - 确认
privapp_whitelistXML 已通过required关联 - 确认 XML 中
package名与 App 的AndroidManifest.xml中package一致 - 确认白名单模块的分区属性与 App 一致
Q: 如何让 App 只在特定产品中包含?
创建自定义产品 .mk 文件,只在那个产品的 PRODUCT_PACKAGES 中添加该 App。
Q: 如何添加 JNI 原生库给 App 使用?
- 定义
cc_library_shared模块 - 在
android_app中通过jni_libs引用:
android_app {
name: "MyApp",
srcs: ["src/**/*.java"],
jni_libs: ["libmyjni"], // 自动打包到 APK 的 lib/ 目录
}
Q: 如何控制 32/64 位编译?
cc_library_shared {
name: "libmyutils",
compile_multilib: "both", // 同时编译 32 和 64 位
// "first" = 只编译首选架构
// "64" = 只编译 64 位
// "32" = 只编译 32 位
}
Q: 如何让库对 vendor 分区可用?
cc_library_shared {
name: "libmyutils",
vendor_available: true, // 允许 vendor 模块链接
// 或使用 vendor: true 直接放到 vendor 分区
}
Q: PRODUCT_PACKAGES 和 PRODUCT_PACKAGES_DEBUG 的区别?
PRODUCT_PACKAGES:所有变体(eng/userdebug/user)都包含PRODUCT_PACKAGES_DEBUG:只在 eng 和 userdebug 变体中包含PRODUCT_HOST_PACKAGES:主机端工具,不安装到设备
十三、构建产物全景分类与路径映射
以下路径定义来源于build/make/core/envsetup.mk,PRODUCT_OUT展开为out/target/product/<device>/。
13.1 产物类型总览
AOSP 构建产物
├── 原生产物(C/C++)
│ ├── 可执行文件 cc_binary → bin/
│ ├── 动态共享库 .so cc_library_shared → lib64/ 或 lib/
│ ├── 静态库 .a cc_library_static → 仅中间产物,不安装
│ └── 头文件库 cc_library_headers → 仅编译时,不安装
│
├── Java 产物
│ ├── APK android_app → app/ 或 priv-app/
│ ├── Java 共享库 .jar java_library → framework/
│ ├── Java 静态库 java_library → 仅中间产物,不安装
│ └── AAR android_library → 仅中间产物,不安装
│
├── 配置/资源产物
│ ├── XML/JSON/配置文件 prebuilt_etc → etc/
│ ├── 权限白名单 prebuilt_etc → etc/permissions/
│ ├── 字体/媒体资源 prebuilt_etc → etc/ 或 media/
│ └── init rc 文件 prebuilt_etc → etc/init/
│
├── APEX 模块
│ └── .apex / .capex apex → apex/
│
├── HAL/驱动产物
│ ├── HAL 实现 cc_library_shared → lib64/hw/ 或 lib/hw/
│ ├── 内核模块 .ko kernel_module → lib/modules/
│ └── 固件 prebuilt_etc → etc/firmware/
│
└── 测试产物
├── 原生测试 cc_test → nativetest64/ 或 nativetest/
├── Java 测试 android_test → testcases/
└── 兼容性测试 cts_* → testcases/
13.2 分区→构建输出→设备路径 完整映射
以 PRODUCT_OUT = out/target/product/emulator_x86_64 为例:
system 分区
| 产物类型 | 构建变量 | 构建输出路径(相对 PRODUCT_OUT) | 设备上的路径 | 打包到 |
|---|---|---|---|---|
| 可执行文件 | TARGET_OUT_EXECUTABLES |
system/bin/ |
/system/bin/ |
system.img |
| 可执行文件(可选) | TARGET_OUT_OPTIONAL_EXECUTABLES |
system/xbin/ |
/system/xbin/ |
system.img |
| 64位共享库 | TARGET_OUT_SHARED_LIBRARIES |
system/lib64/ |
/system/lib64/ |
system.img |
| 32位共享库 | 2ND_TARGET_OUT_SHARED_LIBRARIES |
system/lib/ |
/system/lib/ |
system.img |
| Java 共享库 | TARGET_OUT_JAVA_LIBRARIES |
system/framework/ |
/system/framework/ |
system.img |
| 普通 App | TARGET_OUT_APPS |
system/app/ |
/system/app/ |
system.img |
| 特权 App | TARGET_OUT_APPS_PRIVILEGED |
system/priv-app/ |
/system/priv-app/ |
system.img |
| 配置文件 | TARGET_OUT_ETC |
system/etc/ |
/system/etc/ |
system.img |
| APEX 模块 | — | system/apex/ |
/system/apex/ |
system.img |
| 字体 | — | system/fonts/ |
/system/fonts/ |
system.img |
| 媒体 | — | system/media/ |
/system/media/ |
system.img |
system_ext 分区
| 产物类型 | 构建变量 | 构建输出路径 | 设备上的路径 | 打包到 |
|---|---|---|---|---|
| 可执行文件 | TARGET_OUT_SYSTEM_EXT_EXECUTABLES |
system_ext/bin/ |
/system_ext/bin/ |
system_ext.img |
| 64位共享库 | TARGET_OUT_SYSTEM_EXT_SHARED_LIBRARIES |
system_ext/lib64/ |
/system_ext/lib64/ |
system_ext.img |
| 32位共享库 | 2ND_..._SYSTEM_EXT_SHARED_LIBRARIES |
system_ext/lib/ |
/system_ext/lib/ |
system_ext.img |
| Java 共享库 | TARGET_OUT_SYSTEM_EXT_JAVA_LIBRARIES |
system_ext/framework/ |
/system_ext/framework/ |
system_ext.img |
| 普通 App | TARGET_OUT_SYSTEM_EXT_APPS |
system_ext/app/ |
/system_ext/app/ |
system_ext.img |
| 特权 App | TARGET_OUT_SYSTEM_EXT_APPS_PRIVILEGED |
system_ext/priv-app/ |
/system_ext/priv-app/ |
system_ext.img |
| 配置文件 | TARGET_OUT_SYSTEM_EXT_ETC |
system_ext/etc/ |
/system_ext/etc/ |
system_ext.img |
product 分区
| 产物类型 | 构建变量 | 构建输出路径 | 设备上的路径 | 打包到 |
|---|---|---|---|---|
| 可执行文件 | TARGET_OUT_PRODUCT_EXECUTABLES |
product/bin/ |
/product/bin/ |
product.img |
| 64位共享库 | TARGET_OUT_PRODUCT_SHARED_LIBRARIES |
product/lib64/ |
/product/lib64/ |
product.img |
| 32位共享库 | 2ND_..._PRODUCT_SHARED_LIBRARIES |
product/lib/ |
/product/lib/ |
product.img |
| Java 共享库 | TARGET_OUT_PRODUCT_JAVA_LIBRARIES |
product/framework/ |
/product/framework/ |
product.img |
| 普通 App | TARGET_OUT_PRODUCT_APPS |
product/app/ |
/product/app/ |
product.img |
| 特权 App | TARGET_OUT_PRODUCT_APPS_PRIVILEGED |
product/priv-app/ |
/product/priv-app/ |
product.img |
| 配置文件 | TARGET_OUT_PRODUCT_ETC |
product/etc/ |
/product/etc/ |
product.img |
vendor 分区
| 产物类型 | 构建变量 | 构建输出路径 | 设备上的路径 | 打包到 |
|---|---|---|---|---|
| 可执行文件 | TARGET_OUT_VENDOR_EXECUTABLES |
vendor/bin/ |
/vendor/bin/ |
vendor.img |
| 64位共享库 | TARGET_OUT_VENDOR_SHARED_LIBRARIES |
vendor/lib64/ |
/vendor/lib64/ |
vendor.img |
| 32位共享库 | 2ND_..._VENDOR_SHARED_LIBRARIES |
vendor/lib/ |
/vendor/lib/ |
vendor.img |
| Java 共享库 | TARGET_OUT_VENDOR_JAVA_LIBRARIES |
vendor/framework/ |
/vendor/framework/ |
vendor.img |
| 普通 App | TARGET_OUT_VENDOR_APPS |
vendor/app/ |
/vendor/app/ |
vendor.img |
| 特权 App | TARGET_OUT_VENDOR_APPS_PRIVILEGED |
vendor/priv-app/ |
/vendor/priv-app/ |
vendor.img |
| 配置文件 | TARGET_OUT_VENDOR_ETC |
vendor/etc/ |
/vendor/etc/ |
vendor.img |
| HAL 模块 | — | vendor/lib64/hw/ |
/vendor/lib64/hw/ |
vendor.img |
| 固件 | — | vendor/etc/firmware/ |
/vendor/etc/firmware/ |
vendor.img |
odm 分区
| 产物类型 | 构建变量 | 构建输出路径 | 设备上的路径 | 打包到 |
|---|---|---|---|---|
| 可执行文件 | TARGET_OUT_ODM_EXECUTABLES |
odm/bin/ |
/odm/bin/ |
odm.img |
| 64位共享库 | TARGET_OUT_ODM_SHARED_LIBRARIES |
odm/lib64/ |
/odm/lib64/ |
odm.img |
| 32位共享库 | 2ND_..._ODM_SHARED_LIBRARIES |
odm/lib/ |
/odm/lib/ |
odm.img |
| 普通 App | TARGET_OUT_ODM_APPS |
odm/app/ |
/odm/app/ |
odm.img |
| 特权 App | TARGET_OUT_ODM_APPS_PRIVILEGED |
odm/priv-app/ |
/odm/priv-app/ |
odm.img |
| 配置文件 | TARGET_OUT_ODM_ETC |
odm/etc/ |
/odm/etc/ |
odm.img |
13.3 模块类型 → 安装路径 速查表
以下是 Android.bp 模块类型与默认安装位置的对应关系:
| Android.bp 模块类型 | 产物后缀 | 默认安装子路径 | 示例 |
|---|---|---|---|
android_app |
.apk |
app/ 或 priv-app/ |
Settings.apk → system_ext/priv-app/Settings/ |
android_app_import |
.apk |
app/ 或 priv-app/ |
同上,预编译 APK |
cc_binary |
无 | bin/ |
vold → system/bin/vold |
cc_library_shared |
.so |
lib64/ 或 lib/ |
liblog.so → system/lib64/liblog.so |
cc_library_static |
.a |
不安装(仅中间产物) | — |
cc_library_headers |
无 | 不安装(仅编译时) | — |
cc_test |
无 | nativetest64/ 或 nativetest/ |
— |
java_library (installable) |
.jar |
framework/ |
com.android.nfc_extras.jar |
java_library (默认) |
.jar |
不安装(仅中间产物) | — |
android_library |
.aar |
不安装(仅中间产物) | — |
prebuilt_etc |
各种 | etc/ |
权限 XML → system/etc/permissions/ |
apex |
.apex |
apex/ |
com.android.art.apex → system/apex/ |
sh_binary |
.sh |
bin/ |
— |
kernel_module |
.ko |
lib/modules/ |
— |
13.4 源码推荐放置位置
AOSP 源码树有约定俗成的目录组织方式:
| 产物类型 | 推荐源码位置 | 说明 |
|---|---|---|
| 系统 App | packages/apps/<AppName>/ |
如 Settings、Launcher3 |
| 系统 UI | frameworks/base/packages/SystemUI/ |
SystemUI 特殊,在 frameworks 下 |
| 系统服务 | frameworks/base/services/ |
Java 系统服务 |
| 原生守护进程 | system/<daemon>/ |
如 vold、installd |
| 原生共享库 | system/<lib>/ 或 frameworks/<lib>/ |
如 liblog、libbase |
| HAL 模块 | hardware/interfaces/<hal>/ |
HIDL/AIDL 定义 |
| HAL 实现 | hardware/<vendor>/<hal>/ |
厂商 HAL 实现 |
| 驱动/内核模块 | device/<vendor>/<device>/ |
厂商设备目录 |
| 厂商 App | vendor/<vendor>/apps/ |
厂商定制 App |
| 产品定制 | device/<vendor>/<product>/ |
产品配置 .mk |
| APEX 模块 | system/apex/<module>/ 或模块自身目录 |
如 ART 在 art/ |
| 测试 | <module>/tests/ |
各模块的 tests 子目录 |
| 预编译 | prebuilts/<category>/ |
预编译的 APK、JAR、SO |
关键原则:
- 系统级模块放
system/或frameworks/ - 应用放
packages/apps/ - 厂商相关放
device/或vendor/ - 预编译产物放
prebuilts/
13.5 构建输出目录结构
构建完成后,out/ 目录结构如下:
out/
├── host/ # 主机端产物
│ ├── linux-x86/ # Linux 主机工具
│ │ ├── bin/ # 主机可执行文件(aapt2、soong_zip 等)
│ │ ├── lib64/ # 主机共享库
│ │ └── framework/ # 主机 Java 库
│ └── common/ # 通用主机产物
│
├── target/ # 目标设备产物
│ ├── common/ # 通用目标产物
│ │ ├── obj/ # 中间产物
│ │ │ ├── JAVA_LIBRARIES/ # Java 库中间产物
│ │ │ ├── APPS/ # APK 中间产物
│ │ │ ├── SHARED_LIBRARIES/ # .so 中间产物
│ │ │ ├── EXECUTABLES/ # 可执行文件中间产物
│ │ │ ├── STATIC_LIBRARIES/ # .a 中间产物
│ │ │ └── ETC/ # 配置文件中间产物
│ │ └── R/ # 资源编译产物
│ │
│ └── product/<device>/ # 最终设备产物(PRODUCT_OUT)
│ ├── system/ # system 分区内容
│ │ ├── bin/ # 可执行文件
│ │ ├── lib64/ # 64位共享库
│ │ ├── lib/ # 32位共享库
│ │ ├── framework/ # Java 共享库
│ │ ├── app/ # 普通 App
│ │ ├── priv-app/ # 特权 App
│ │ ├── etc/ # 配置文件
│ │ │ ├── permissions/ # 权限白名单
│ │ │ ├── init/ # init rc 文件
│ │ │ ├── public.libraries.txt # 公开原生库列表
│ │ │ └── ...
│ │ ├── apex/ # APEX 模块
│ │ ├── fonts/ # 字体
│ │ └── media/ # 媒体资源
│ ├── system_ext/ # system_ext 分区内容
│ ├── product/ # product 分区内容
│ ├── vendor/ # vendor 分区内容
│ ├── odm/ # odm 分区内容
│ ├── data/ # data 分区内容
│ ├── root/ # rootfs 内容
│ ├── ramdisk/ # ramdisk 内容
│ ├── symbols/ # 带 debug 符号的副本
│ ├── obj/ # 目标中间产物
│ ├── testcases/ # 测试产物
│ ├── *.img # 各分区镜像文件
│ │ ├── system.img
│ │ ├── system_ext.img
│ │ ├── product.img
│ │ ├── vendor.img
│ │ ├── odm.img
│ │ ├── boot.img
│ │ ├── vbmeta.img
│ │ └── super.img # 动态分区总镜像
│ ├── android-info.txt # 设备兼容性信息
│ └── module-info.json # 模块信息索引
│
└── soong/ # Soong 构建系统中间产物
├── .intermediates/ # 所有模块的中间产物
│ ├── system/
│ ├── frameworks/
│ ├── packages/
│ └── ...
├── build.ninja # Ninja 构建描述文件
├── combined-<product>.ninja # 合并的 Ninja 文件
└── soong.variables # 构建变量快照
13.6 从源码到设备的完整链路
以 Settings App 为例,追踪完整链路:
1. 源码位置
packages/apps/Settings/
├── Android.bp → 定义 android_app { name: "Settings", ... }
├── src/ → Java/Kotlin 源码
└── res/ → 资源文件
2. 产品配置
build/make/target/product/handheld_system_ext.mk
→ PRODUCT_PACKAGES += Settings
3. 编译中间产物
out/soong/.intermediates/packages/apps/Settings/Settings/android_common/
├── Settings.apk
├── classes.dex
└── proguard/
4. 安装到分区目录
out/target/product/emulator_x86_64/system_ext/priv-app/Settings/
└── Settings.apk
5. 打包到镜像
out/target/product/emulator_x86_64/system_ext.img
(包含 /system_ext/priv-app/Settings/Settings.apk)
6. 刷入设备后的位置
/system_ext/priv-app/Settings/Settings.apk
以原生共享库 liblog 为例:
1. 源码位置
system/logging/liblog/
└── Android.bp → cc_library_shared { name: "liblog", ... }
2. 产品配置
基础系统默认包含(通过 base_system.mk 等间接引入)
3. 编译中间产物
out/soong/.intermediates/system/logging/liblog/liblog/android_arm64_armv8-a_shared/
└── liblog.so
4. 安装到分区目录
out/target/product/emulator_x86_64/system/lib64/liblog.so
5. 打包到镜像
out/target/product/emulator_x86_64/system.img
(包含 /system/lib64/liblog.so)
6. 刷入设备后的位置
/system/lib64/liblog.so
13.7 特殊产物说明
APEX 模块
APEX (Android Pony EXpress) 是 Android 10+ 引入的模块化更新机制。
源码: art/、system/i18n/、system/media/ 等
↓ 编译
中间产物: out/soong/.intermediates/.../com.android.art.apex
↓ 安装
分区目录: out/target/product/<device>/system/apex/com.android.art.apex
↓ 打包
镜像: system.img 内 /system/apex/
↓ 刷入
设备: /system/apex/com.android.art.apex
↓ 运行时
挂载: /apex/com.android.art/ (由 apexd 挂载)
APEX 内部结构:
com.android.art.apex
├── apex_manifest.json
├── AndroidManifest.xml
├── apex_payload.img (ext4 微型镜像)
│ ├── bin/ → dex2oat、profman 等
│ ├── lib64/ → libart.so 等
│ └── etc/ → 配置文件
└── apex_pubkey
HAL 模块
HAL (Hardware Abstraction Layer) 模块有特殊的命名和安装规则:
源码: hardware/interfaces/audio/aidl/
↓ 编译
产物: android.hardware.audio-service.so
↓ 安装
分区: vendor/lib64/hw/android.hardware.audio-service.so
↓ 设备
路径: /vendor/lib64/hw/
HAL 库命名规则:<module>.<type>.so 或 <module>-service.so
公开原生库
如果要让第三方 App 通过 System.loadLibrary() 加载你的 .so,需要:
- 在
public.libraries.txt中声明库名 - 该文件安装到
/system/etc/public.libraries.txt - 或扩展文件
/system/etc/public.libraries-<company>.txt
cc_library_shared {
name: "libmypublic",
// ...
// 不需要特殊属性,但需要添加到 public.libraries.txt
}
对应的产品配置:
PRODUCT_COPY_FILES += \
device/mycompany/myproduct/public.libraries-mycompany.txt:$(TARGET_COPY_OUT_SYSTEM)/etc/public.libraries-mycompany.txt
13.8 分区选择决策树
你的模块应该放哪个分区?
┌─ 是否是 AOSP 核心系统组件?
│ ├─ 是 → /system
│ └─ 否 ↓
│
├─ 是否需要系统 API 或特权权限?
│ ├─ 是 → /system_ext
│ └─ 否 ↓
│
├─ 是否是产品定制 App(浏览器、输入法等)?
│ ├─ 是 → /product
│ └─ 否 ↓
│
├─ 是否依赖硬件或 SoC?
│ ├─ 是 → /vendor
│ └─ 否 ↓
│
├─ 是否是 ODM 定制(区别于 SoC 厂商)?
│ ├─ 是 → /odm
│ └─ 否 ↓
│
└─ 默认 → /system
分区选择原则:
| 分区 | 职责 | 可否独立升级 | OTA 影响 |
|---|---|---|---|
| system | AOSP 核心框架 | 跟随系统 OTA | 全量/增量 |
| system_ext | 系统扩展服务 | 跟随系统 OTA | 全量/增量 |
| product | 产品定制 App | 跟随系统 OTA | 全量/增量 |
| vendor | SoC/硬件相关 | 可独立升级 | 需兼容 |
| odm | ODM 定制 | 可独立升级 | 需兼容 |
注意:system / system_ext / product 通常一起 OTA,vendor / odm 可以单独升级但需要保持 ABI 兼容。
十四、Android 分区深度解析
以下分区定义来源于build/make/core/board_config.mk和build/make/core/envsetup.mk。
14.1 分区总览
Android 设备的存储被划分为多个独立分区,每个分区有独立的文件系统和镜像:
┌─────────────────────────────────────────────────────┐
│ super 分区 (动态分区) │
│ ┌──────────┬──────────────┬──────────┬───────────┐ │
│ │ system │ system_ext │ product │ vendor │ │
│ │ .img │ .img │ .img │ .img │ │
│ └──────────┴──────────────┴──────────┴───────────┘ │
│ ┌──────────┬───────────┐ │
│ │ odm │ vendor_dlkm│ │
│ │ .img │ .img │ │
│ └──────────┴───────────┘ │
├─────────────────────────────────────────────────────┤
│ boot.img │ vbmeta.img │ system_dlkm.img │
├─────────────────────────────────────────────────────┤
│ userdata │ cache │ misc │ metadata │
└─────────────────────────────────────────────────────┘
14.2 各分区详解
system — AOSP 核心系统
| 属性 | 说明 |
|---|---|
| 构建变量 | TARGET_COPY_OUT_SYSTEM = system |
| 镜像文件 | system.img |
| 挂载点 | /system |
| 文件系统 | ext4 / erofs |
| 只读 | 是(运行时不可修改) |
| OTA | 跟随系统全量/增量更新 |
内容:Android 框架核心、系统服务、原生守护进程、系统库、基础 App
/system/
├── bin/ → 系统可执行文件(vold, installd, surfaceflinger...)
├── lib64/ → 64位共享库(liblog.so, libbase.so...)
├── lib/ → 32位共享库
├── framework/ → Java 核心库(framework.jar, services.jar...)
├── app/ → 普通系统 App(Browser2, Camera2...)
├── priv-app/ → 特权系统 App(SettingsIntelligence...)
├── etc/ → 系统配置文件
│ ├── permissions/ → 权限定义和白名单
│ ├── init/ → init .rc 脚本
│ ├── security/ → 安全策略
│ ├── public.libraries.txt → 公开原生库列表
│ └── ...
├── apex/ → APEX 模块(com.android.art.apex...)
├── fonts/ → 系统字体
├── media/ → 系统音效/壁纸
├── usr/ → 用户资源(键盘布局、ICU 数据)
└── build.prop → 系统属性
关键规则:
sdk_phone_x86_64等产品有 artifact path 限制,非系统核心模块不允许安装到/system/- 这就是为什么我们的
libsysdemo_jni.so必须设system_ext_specific: true
system_ext — 系统扩展
| 属性 | 说明 |
|---|---|
| 构建变量 | TARGET_COPY_OUT_SYSTEM_EXT = system_ext(独立分区)或 system/system_ext(非独立) |
| 镜像文件 | system_ext.img(独立时) |
| 挂载点 | /system_ext |
| 只读 | 是 |
| OTA | 跟随系统更新 |
内容:需要系统 API 或特权权限的 App 和服务,与框架紧密耦合但不是框架本身
/system_ext/
├── bin/ → 扩展可执行文件
├── lib64/ → 扩展共享库(libsysdemo_jni.so...)
├── lib/ → 32位扩展共享库
├── framework/ → 扩展 Java 库
├── app/ → 扩展普通 App
├── priv-app/ → 扩展特权 App(Settings, SystemUI, SysDemo...)
├── etc/ → 扩展配置
│ └── permissions/ → 扩展权限白名单
└── build.prop → 扩展属性
与 system 的区别:
- system 放 AOSP 通用核心,system_ext 放 OEM 扩展
- system_ext 的模块可以使用
@SystemApi和特权权限 - system_ext 可以独立为分区,也可以作为 system 的子目录(
system/system_ext/)
何时选择 system_ext:
- App 需要
platform_apis: true - App 需要
privileged: true - App 需要
certificate: "platform" - 库需要被特权 App 链接
product — 产品定制
| 属性 | 说明 |
|---|---|
| 构建变量 | TARGET_COPY_OUT_PRODUCT = product(独立分区)或 system/product(非独立) |
| 镜像文件 | product.img(独立时) |
| 挂载点 | /product |
| 只读 | 是 |
| OTA | 跟随系统更新 |
内容:可按产品线定制的 App、配置、运营商定制
/product/
├── bin/ → 产品特定可执行文件
├── lib64/ → 产品特定共享库
├── lib/ → 32位产品库
├── framework/ → 产品 Java 库
├── app/ → 产品 App(Calendar, Camera2, LatinIME...)
├── priv-app/ → 产品特权 App(罕见)
├── etc/ → 产品配置
│ ├── apns-conf.xml → APN 配置
│ └── permissions/ → 产品权限
└── build.prop → 产品属性
与 system_ext 的区别:
- product 放用户可替代的 App(浏览器、输入法、相机)
- system_ext 放系统必需的特权 App(Settings、SystemUI)
- product 的 App 通常用
sdk_version: "current",不需要系统 API
典型场景:
- 同一套系统,手机版 product 装手机 App,TV 版装 TV App
- 运营商定制:不同运营商的 product 分区包含不同预装 App
vendor — 硬件/SoC 厂商
| 属性 | 说明 |
|---|---|
| 构建变量 | TARGET_COPY_OUT_VENDOR = vendor(独立分区)或 system/vendor(非独立) |
| 镜像文件 | vendor.img |
| 挂载点 | /vendor |
| 只读 | 是 |
| OTA | 可独立更新(需保持 ABI 兼容) |
内容:SoC 厂商提供的 HAL、驱动、固件、BSP
/vendor/
├── bin/ → 厂商可执行文件(rild, gpsd...)
├── lib64/ → 厂商共享库
│ ├── hw/ → HAL 模块(android.hardware.audio-service.so...)
│ └── ...
├── lib/ → 32位厂商库
│ └── hw/ → 32位 HAL 模块
├── etc/ → 厂商配置
│ ├── firmware/ → 固件文件
│ ├── init/ → 厂商 init .rc
│ └── vintf/ → VINTF 清单
├── app/ → 厂商 App
├── framework/ → 厂商 Java 扩展
└── build.prop → 厂商属性
关键规则:
- vendor 分区必须保持 ABI 稳定,因为可以独立于 system OTA
- vendor 中的库需要声明
vendor_available: true才能被 vendor 模块链接 - NDK 库天然对 vendor 可用,非 NDK 库需要显式声明
odm — ODM 定制
| 属性 | 说明 |
|---|---|
| 构建变量 | TARGET_COPY_OUT_ODM = odm 或 vendor/odm |
| 镜像文件 | odm.img |
| 挂载点 | /odm |
| 只读 | 是 |
| OTA | 可独立更新 |
内容:ODM(原始设计制造商)的定制内容,区分于 SoC 厂商
/odm/
├── bin/ → ODM 可执行文件
├── lib64/ → ODM 共享库
├── lib/ → 32位 ODM 库
├── etc/ → ODM 配置
│ └── firmware/ → ODM 固件
├── app/ → ODM App
└── build.prop → ODM 属性
vendor 与 odm 的关系:
- vendor = SoC 厂商(高通、联发科)提供的内容
- odm = 设备制造商(小米、OPPO)在 SoC 基础上的定制
- 同一 SoC 的多款设备共享 vendor,但各有不同的 odm
其他分区
| 分区 | 挂载点 | 内容 |
|---|---|---|
| boot | — | Linux 内核 + ramdisk |
| vbmeta | — | 验证启动元数据(Verified Boot) |
| userdata | /data |
用户数据(可写) |
| cache | /cache |
OTA 缓存、恢复日志 |
| system_dlkm | /system_dlkm |
系统内核模块(.ko) |
| vendor_dlkm | /vendor_dlkm |
厂商内核模块 |
| odm_dlkm | /odm_dlkm |
ODM 内核模块 |
| metadata | /metadata |
加密元数据 |
| misc | — | 恢复模式通信区 |
14.3 分区独立性对照
| 分区 | 独立镜像 | 独立 OTA | 运行时只读 | 可被 system 链接 | 可被 vendor 链接 |
|---|---|---|---|---|---|
| system | ✅ | 跟随主 OTA | ✅ | — | ❌(反向依赖) |
| system_ext | ✅ | 跟随主 OTA | ✅ | ✅ | ❌ |
| product | ✅ | 跟随主 OTA | ✅ | ✅ | ❌ |
| vendor | ✅ | ✅ 独立 OTA | ✅ | ❌ | — |
| odm | ✅ | ✅ 独立 OTA | ✅ | ❌ | ✅ |
依赖规则:
- vendor/odm 不能依赖 system 的内部 API(只能用 NDK 或 VNDK)
- system 可以依赖 system_ext 和 product
- product 可以依赖 system 和 system_ext
14.4 out/ 目录完整解析
构建输出 out/ 目录的每个子目录作用:
out/
│
├── host/ # 主机端产物(在编译机上运行的工具)
│ ├── linux-x86/ # Linux x86_64 主机
│ │ ├── bin/ # 主机可执行文件
│ │ │ ├── aapt2 # Android 资源打包工具
│ │ │ ├── soong_zip # Soong ZIP 工具
│ │ │ ├── merge_zips # ZIP 合并工具
│ │ │ └── ...
│ │ ├── lib64/ # 主机共享库
│ │ └── framework/ # 主机 Java 库
│ └── common/ # 通用主机产物
│ └── obj/ # 主机中间产物
│
├── target/ # 目标设备产物(要刷入手机的)
│ ├── common/ # 跨产品通用产物
│ │ ├── obj/ # 通用中间产物
│ │ │ ├── JAVA_LIBRARIES/ # Java 库编译中间产物
│ │ │ ├── APPS/ # APK 编译中间产物
│ │ │ ├── SHARED_LIBRARIES/ # .so 编译中间产物
│ │ │ ├── EXECUTABLES/ # 可执行文件中间产物
│ │ │ ├── STATIC_LIBRARIES/ # .a 静态库中间产物
│ │ │ ├── ETC/ # 配置文件中间产物
│ │ │ ├── APEX/ # APEX 中间产物
│ │ │ └── NATIVE_TESTS/ # 原生测试中间产物
│ │ ├── R/ # 资源编译产物
│ │ └── docs/ # 文档产物
│ │
│ └── product/<device>/ # 最终设备产物 = PRODUCT_OUT
│ │
│ │ ── 分区目录(对应设备上的挂载点)──
│ ├── system/ # → /system
│ │ ├── bin/ # 系统可执行文件
│ │ ├── lib64/ # 64位系统库
│ │ ├── lib/ # 32位系统库
│ │ ├── framework/ # Java 核心框架库
│ │ ├── app/ # 普通系统 App
│ │ ├── priv-app/ # 特权系统 App
│ │ ├── etc/ # 系统配置
│ │ │ ├── permissions/ # 权限定义
│ │ │ ├── init/ # init 脚本
│ │ │ ├── security/ # 安全策略
│ │ │ └── public.libraries.txt # 公开库列表
│ │ ├── apex/ # APEX 模块
│ │ ├── fonts/ # 字体
│ │ ├── media/ # 媒体资源
│ │ └── build.prop # 系统属性
│ │
│ ├── system_ext/ # → /system_ext
│ │ ├── priv-app/ # 特权扩展 App
│ │ ├── app/ # 普通扩展 App
│ │ ├── lib64/ # 扩展库
│ │ ├── etc/ # 扩展配置
│ │ └── build.prop # 扩展属性
│ │
│ ├── product/ # → /product
│ │ ├── app/ # 产品 App
│ │ ├── lib64/ # 产品库
│ │ ├── etc/ # 产品配置
│ │ └── build.prop # 产品属性
│ │
│ ├── vendor/ # → /vendor
│ │ ├── bin/ # 厂商可执行文件
│ │ ├── lib64/hw/ # HAL 模块
│ │ ├── etc/firmware/ # 固件
│ │ └── build.prop # 厂商属性
│ │
│ ├── odm/ # → /odm
│ │
│ │ ── 非分区目录 ──
│ ├── root/ # → / (rootfs)
│ │ ├── init # init 进程
│ │ ├── default.prop # 默认属性
│ │ ├── fstab.* # 文件系统表
│ │ └── verity_key # dm-verity 公钥
│ │
│ ├── ramdisk/ # boot.img 中的 ramdisk 内容
│ │
│ ├── data/ # → /data(不在只读镜像中)
│ │ ├── app/ # 用户安装的 App
│ │ ├── nativetest64/ # 原生测试
│ │ └── local/tmp/ # 临时文件
│ │
│ ├── symbols/ # 带 debug 符号的副本(用于调试)
│ │ ├── system/bin/ # 未 stripped 的可执行文件
│ │ └── system/lib64/ # 未 stripped 的 .so
│ │
│ ├── obj/ # 目标设备中间产物
│ │ ├── APK/ # APK 中间产物
│ │ ├── SHARED_LIBRARIES/ # .so 中间产物
│ │ └── ...
│ │
│ ├── testcases/ # 测试产物(CTS、VTS 等)
│ │
│ │ ── 镜像文件 ──
│ ├── system.img # system 分区镜像
│ ├── system_ext.img # system_ext 分区镜像
│ ├── product.img # product 分区镜像
│ ├── vendor.img # vendor 分区镜像
│ ├── odm.img # odm 分区镜像
│ ├── boot.img # 内核 + ramdisk
│ ├── vbmeta.img # 验证启动元数据
│ ├── super.img # 动态分区总镜像
│ ├── cache.img # cache 分区
│ ├── userdata.img # 用户数据分区
│ │
│ │ ── 元数据文件 ──
│ ├── android-info.txt # 设备兼容性信息
│ ├── module-info.json # 模块信息索引(gomod/pathmod 用)
│ ├── all_modules.txt # 所有模块列表(allmod 用)
│ ├── installed-files.txt # 已安装文件列表
│ └── clean_steps.mk # 清理步骤
│
├── soong/ # Soong 构建系统中间产物
│ ├── .intermediates/ # 所有模块的编译中间产物
│ │ ├── packages/apps/SysDemo/ # 我们的 SysDemo
│ │ │ ├── SysDemo/ # APK 中间产物
│ │ │ │ └── android_common/ # 通用架构
│ │ │ │ ├── SysDemo.apk # 编译出的 APK
│ │ │ │ └── classes.dex # DEX 文件
│ │ │ └── libsysdemo_jni/ # JNI 库中间产物
│ │ │ ├── android_x86_64_shared/ # 64位 x86_64
│ │ │ │ └── libsysdemo_jni.so
│ │ │ └── android_x86_shared/ # 32位 x86
│ │ │ └── libsysdemo_jni.so
│ │ ├── frameworks/base/ # 框架中间产物
│ │ ├── system/ # 系统模块中间产物
│ │ └── ...
│ ├── build.ninja # 主 Ninja 构建文件
│ ├── combined-<product>.ninja # 合并的 Ninja 文件
│ ├── soong.variables # 构建变量快照
│ └── bootstrap.ninja # Soong 自举 Ninja 文件
│
└── dist/ # 分发产物(m dist 输出)
└── sdk_phone_x86_64/ # 按产品名
├── sdk_phone_x86_64-img.zip # 完整镜像 ZIP
├── sdk_phone_x86_64-symbols.zip # 符号文件 ZIP
└── android-info.txt # 设备信息
14.5 out/ 目录与 Android 系统目录对应关系
| out/ 中的路径 | 设备上的路径 | 说明 |
|---|---|---|
product/<dev>/system/ |
/system |
系统分区 |
product/<dev>/system_ext/ |
/system_ext |
系统扩展分区 |
product/<dev>/product/ |
/product |
产品分区 |
product/<dev>/vendor/ |
/vendor |
厂商分区 |
product/<dev>/odm/ |
/odm |
ODM 分区 |
product/<dev>/root/ |
/ |
根文件系统 |
product/<dev>/data/ |
/data |
数据分区 |
product/<dev>/system/bin/ |
/system/bin/ |
系统可执行文件 |
product/<dev>/system/lib64/ |
/system/lib64/ |
64位系统库 |
product/<dev>/system/framework/ |
/system/framework/ |
Java 框架库 |
product/<dev>/system/app/ |
/system/app/ |
普通系统 App |
product/<dev>/system/priv-app/ |
/system/priv-app/ |
特权系统 App |
product/<dev>/system/etc/ |
/system/etc/ |
系统配置 |
product/<dev>/system/apex/ |
/system/apex/ |
APEX 模块 |
product/<dev>/system_ext/priv-app/ |
/system_ext/priv-app/ |
特权扩展 App |
product/<dev>/vendor/lib64/hw/ |
/vendor/lib64/hw/ |
HAL 模块 |
product/<dev>/vendor/etc/firmware/ |
/vendor/etc/firmware/ |
固件 |
product/<dev>/root/init |
/init |
init 进程 |
product/<dev>/symbols/ |
— | 仅调试用,不刷入设备 |
14.6 Android 运行时目录结构(设备上)
设备启动后的完整目录结构:
/ # 根目录 (rootfs, ramdisk)
├── init # init 进程(PID 1)
├── default.prop # 默认系统属性
├── fstab.* # 文件系统挂载表
├── verity_key # dm-verity 验证公钥
│
├── system/ # [system.img] 系统分区(只读)
│ ├── bin/ # 原生可执行文件
│ ├── lib64/ # 64位共享库
│ ├── lib/ # 32位共享库
│ ├── framework/ # Java 核心库
│ ├── app/ # 普通系统 App
│ ├── priv-app/ # 特权系统 App
│ ├── etc/ # 配置文件
│ ├── apex/ # APEX 模块
│ ├── fonts/ # 字体
│ ├── media/ # 媒体资源
│ ├── usr/ # 用户数据(键盘布局等)
│ └── build.prop # 系统属性
│
├── system_ext/ # [system_ext.img] 系统扩展分区(只读)
│ ├── priv-app/ # 特权扩展 App
│ ├── app/ # 普通扩展 App
│ ├── lib64/ # 扩展库
│ └── etc/ # 扩展配置
│
├── product/ # [product.img] 产品分区(只读)
│ ├── app/ # 产品 App
│ ├── lib64/ # 产品库
│ └── etc/ # 产品配置
│
├── vendor/ # [vendor.img] 厂商分区(只读)
│ ├── bin/ # 厂商可执行文件
│ ├── lib64/ # 厂商库
│ │ └── hw/ # HAL 模块
│ ├── etc/ # 厂商配置
│ │ └── firmware/ # 固件
│ └── build.prop # 厂商属性
│
├── odm/ # [odm.img] ODM 分区(只读)
│
├── data/ # [userdata.img] 用户数据分区(可写)
│ ├── app/ # 用户安装的 App
│ ├── data/ # App 私有数据
│ ├── local/tmp/ # 临时文件
│ ├── dalvik-cache/ # DEX 缓存
│ ├── system/ # 系统设置的备份
│ ├── misc/ # 杂项数据
│ └── property/ # 持久化属性
│
├── cache/ # [cache.img] 缓存分区(可写)
│ ├── recovery/ # 恢复日志
│ └── backup/ # 备份
│
├── apex/ # APEX 运行时挂载点
│ ├── com.android.art/ # ART 运行时
│ ├── com.android.i18n/ # 国际化
│ └── com.android.media/ # 媒体编解码器
│
├── dev/ # 设备节点(tmpfs)
│ ├── binder # Binder 设备
│ └── ...
│
├── proc/ # 进程信息(procfs)
├── sys/ # 内核参数(sysfs)
├── sdcard/ # SD 卡 / 虚拟 SD(FUSE)
├── storage/ # 存储挂载点
│ ├── emulated/ # 虚拟存储
│ └── self/ # 当前用户存储
│
├── mnt/ # 临时挂载点
│ ├── vendor/ # vendor 分区挂载(动态分区)
│ └── product/ # product 分区挂载
│
├── d/ # 内核调试(debugfs)
├── linkerconfig/ # 动态链接器配置
└── metadata/ # 加密元数据
14.7 关键对应关系速查
从源码到设备的完整路径追踪:
Android.bp 中设置 out/ 中的路径 设备上的路径
───────────────── ────────────────── ──────────────────
(默认) product/<d>/system/app/ /system/app/
privileged: true product/<d>/system/priv-app/ /system/priv-app/
system_ext_specific: true product/<d>/system_ext/priv-app/ /system_ext/priv-app/
product_specific: true product/<d>/product/app/ /product/app/
vendor: true product/<d>/vendor/app/ /vendor/app/
cc_binary product/<d>/system/bin/ /system/bin/
cc_library_shared product/<d>/system/lib64/ /system/lib64/
cc_library_shared (ext) product/<d>/system_ext/lib64/ /system_ext/lib64/
cc_library_shared (vendor) product/<d>/vendor/lib64/ /vendor/lib64/
cc_library_shared (vendor hw) product/<d>/vendor/lib64/hw/ /vendor/lib64/hw/
java_library (installable) product/<d>/system/framework/ /system/framework/
prebuilt_etc product/<d>/system/etc/ /system/etc/
prebuilt_etc (permissions) product/<d>/system/etc/permissions/ /system/etc/permissions/
apex product/<d>/system/apex/ /system/apex/
十五、AOSP 调试与性能分析实战教程
以 SysDemo 闪退问题为引子,系统讲解 AOSP 开发中常用的调试和性能分析方法。
15.1 App 闪退调试
15.1.1 第一步:看 logcat
这是排查闪退最基本也是最重要的手段:
# 实时查看日志
adb logcat
# 只看某个 App 的日志(按包名过滤)
adb logcat --pid=$(adb shell pidof com.android.sysdemo)
# 只看某个 TAG 的日志
adb logcat -s SysDemo
# 只看崩溃相关日志
adb logcat *:E
# 看完整的 crash 信息
adb logcat -b crash
# 组合:看 SysDemo TAG + 所有 ERROR
adb logcat -s SysDemo *:E
# 保存日志到文件
adb logcat -d > logcat.txt
常见闪退日志模式:
# 1. JNI 库加载失败
E/AndroidRuntime: java.lang.UnsatisfiedLinkError: dlopen failed: library "libsysdemo_jni.so" not found
# 2. JNI 方法找不到
E/AndroidRuntime: java.lang.UnsatisfiedLinkError: No implementation found for native method
# 3. sharedUserId 导致 PackageManager 拒绝
E/PackageManager: Package com.android.sysdemo has sharedUserId but no matching signature
# 4. SELinux 拒绝
avc: denied { transition } for comm="init" scontext=u:r:init:s0 tcontext=u:r:system_app:s0
# 5. 权限拒绝
E/AndroidRuntime: java.lang.SecurityException: Permission denied
# 6. ClassNotFound / ActivityNotFound
E/AndroidRuntime: android.content.ActivityNotFoundException: Unable to find explicit activity class
15.1.2 第二步:检查 App 是否正确安装
# 查看 App 是否安装
adb shell pm list packages | grep sysdemo
# 查看 App 安装路径
adb shell pm path com.android.sysdemo
# 查看 App 详细信息
adb shell dumpsys package com.android.sysdemo
# 检查 APK 中的 JNI 库
adb shell ls -la /system_ext/priv-app/SysDemo/SysDemo.apk
adb shell ls -la /system_ext/priv-app/SysDemo/lib/x86_64/
# 检查独立安装的 .so
adb shell ls -la /system_ext/lib64/libsysdemo_jni.so
15.1.3 第三步:检查 SELinux
# 查看 SELinux 模式
adb shell getenforce
# 临时关闭 SELinux(仅调试用,需要 root)
adb shell setenforce 0
# 查看 SELinux 拒绝日志
adb shell dmesg | grep avc
adb logcat -b events -s audit
# 查看 App 的 SELinux 上下文
adb shell ps -Z | grep sysdemo
# 查看文件的安全上下文
adb shell ls -Z /system_ext/priv-app/SysDemo/
15.1.4 第四步:检查签名和权限
# 查看 App 签名信息
adb shell dumpsys package com.android.sysdemo | grep -A5 "signatures"
# 查看平台签名(对比)
adb shell dumpsys package android | grep -A5 "signatures"
# 查看授予的权限
adb shell dumpsys package com.android.sysdemo | grep -A20 "granted=true"
# 查看 privapp 权限状态
adb shell dumpsys package com.android.sysdemo | grep -A10 "privapp"
15.1.5 第五步:手动启动 Activity 看详细错误
# 通过 am 启动,可以看到更详细的错误信息
adb shell am start -n com.android.sysdemo/.SysDemoActivity
# 带调试选项启动
adb shell am start -D -n com.android.sysdemo/.SysDemoActivity
# 带堆栈跟踪
adb shell am start --user 0 -n com.android.sysdemo/.SysDemoActivity
15.1.6 常见闪退原因速查
| 闪退现象 | 可能原因 | 排查命令 |
|---|---|---|
| 安装后图标不显示 | privapp 权限白名单缺失 | adb logcat -s PackageManager *:E |
| 点击图标立即闪退 | sharedUserId 签名不匹配 | adb logcat -s PackageManager |
| 启动后短暂白屏闪退 | JNI 库加载失败 | adb logcat -s AndroidRuntime |
| 启动后几秒闪退 | Activity 代码异常 | adb logcat -b crash |
| 启动后 ANR | 主线程阻塞 | adb pull /data/anr/traces.txt |
| SELinux 拒绝 | 缺少 SELinux 策略 | adb shell dmesg | grep avc |
15.2 系统级调试
15.2.1 dumpsys — Android 系统诊断瑞士军刀
# 列出所有 dumpsys 服务
adb shell dumpsys -l
# 查看内存信息
adb shell dumpsys meminfo com.android.sysdemo
# 查看 CPU 使用
adb shell dumpsys cpuinfo
# 查看 Activity 栈
adb shell dumpsys activity activities
# 查看最近的广播
adb shell dumpsys activity broadcasts
# 查看服务列表
adb shell dumpsys activity services
# 查看 Battery 信息
adb shell dumpsys battery
# 查看 Graphics 信息
adb shell dumpsys gfxinfo com.android.sysdemo
# 查看网络信息
adb shell dumpsys netstats
# 查看 Window 信息
adb shell dumpsys window
# 查看 Input 事件
adb shell dumpsys input
15.2.2 原生进程调试
# 查看进程信息
adb shell ps -A | grep sysdemo
adb shell ps -Z | grep sysdemo # 带 SELinux 上下文
# 查看进程的内存映射
adb shell cat /proc/<pid>/maps
# 查看进程打开的文件
adb shell lsof -p <pid>
# 查看进程的命令行
adb shell cat /proc/<pid>/cmdline
# 查看进程的环境变量
adb shell cat /proc/<pid>/environ
# 查看进程的堆栈(需要 root)
adb shell debuggerd -b <pid>
# 发送信号调试
adb shell kill -SIGSEGV <pid> # 触发 tombstone
adb shell kill -SIGABRT <pid> # 触发 abort
15.2.3 Tombstone 分析
当原生代码崩溃时,Android 会生成 tombstone 文件:
# 查看 tombstone 列表
adb shell ls -lt /data/tombstones/
# 查看最新的 tombstone
adb shell cat /data/tombstones/tombstone_00
# 拉取到本地分析
adb pull /data/tombstones/ ./tombstones/
Tombstone 关键信息:
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'Android/sdk_phone_x86_64/up1a...'
Revision: '0'
ABI: 'x86_64'
Timestamp: 2024-01-01 12:00:00
pid: 1234, tid: 1234, name: com.android.sysdemo >>> com.android.sysdemo <<<
uid: 1000
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
Cause: null pointer dereference
backtrace:
#00 pc 0000000000012345 /system_ext/lib64/libsysdemo_jni.so (Java_com_android_sysdemo_SysDemoActivity_getNativeGreeting+53)
#01 pc 0000000000001234 /system_ext/lib64/libart.so (art_quick_generic_jni_trampoline+148)
...
用 addr2line 解析地址:
# 找到编译产物中的未 stripped 版本
find out/ -name "libsysdemo_jni.so" -path "*/symbols/*"
# 解析地址
prebuilts/clang/host/linux-x86/clang-r487747c/bin/llvm-addr2line \
-e out/target/product/emulator_x86_64/symbols/system_ext/lib64/libsysdemo_jni.so \
0x12345
15.2.4 GDB 调试原生代码
# 启动 gdbserver(设备端)
adb shell gdbserver :5039 --attach <pid>
# 端口转发
adb forward tcp:5039 tcp:5039
# 主机端连接(使用 AOSP 自带的 gdb)
# 先 source envsetup.sh && lunch
gdbclient.py -p <pid>
# 或手动启动
$ANDROID_TOOLCHAIN/aosp-x86_64-linux-android-gdb \
-e out/target/product/emulator_x86_64/symbols/system_ext/lib64/libsysdemo_jni.so
(gdb) target remote :5039
(gdb) break Java_com_android_sysdemo_SysDemoActivity_getNativeGreeting
(gdb) continue
15.2.5 strace 跟踪系统调用
# 跟踪某个进程
adb shell strace -p <pid>
# 跟踪启动过程
adb shell strace -f -e trace=open,read,write,ioctl am start -n com.android.sysdemo/.SysDemoActivity
# 只看文件操作
adb shell strace -e trace=file -p <pid>
# 只看网络操作
adb shell strace -e trace=network -p <pid>
# 保存到文件
adb shell strace -o /data/local/tmp/strace.log -p <pid>
15.3 Java 层调试
15.3.1 JDWP 调试(Android Studio / jdb)
# 查看可调试的进程
adb shell ps -A | grep sysdemo
adb jdwp
# 转发调试端口
adb forward tcp:8700 jdwp:<pid>
# 用 jdb 连接
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700
15.3.2 DropBox 查看历史崩溃
# 查看所有崩溃记录
adb shell dumpsys dropbox
# 只看最近的崩溃
adb shell dumpsys dropbox --print
# 按类型过滤
adb shell dumpsys dropbox system_app_crash
adb shell dumpsys dropbox data_app_crash
15.3.3 ANR 分析
# 触发 ANR(调试用)
adb shell am broadcast -a android.intent.action.ANR_MONITOR
# 获取 ANR traces
adb pull /data/anr/traces.txt
# 查看最近的 ANR
adb shell dumpsys dropbox system_app_anr
ANR traces 关键信息:
"main" prio=5 tid=1 Sleeping
| group="main" sCount=1 dsCount=0 flags=1 obj=0x12345678 self=0xabcdef
| sysTid=1234 nice=0 cgrp=default sched=0/0 handle=0x12345678
| state=S schedstat=( 12345678 90123456 789 ) utm=12 stm=3 core=0 HZ=100
at java.lang.Thread.sleep(Native Method)
at com.android.sysdemo.SysDemoActivity.onCreate(SysDemoActivity.java:15)
...
15.4 性能分析(Profile)
15.4.1 systrace / Perfetto — 系统级性能追踪
Perfetto(推荐,Android 10+):
# 抓取 5 秒的系统 trace
adb shell perfetto \
-c - --txt \
-o /data/misc/perfetto-traces/trace \
<<EOF
buffers: {
size_kb: 63488
}
data_sources: {
config {
name: "linux.ftrace"
ftrace_config {
ftrace_events: "sched/sched_switch"
ftrace_events: "power/cpu_frequency"
ftrace_events: "sched/sched_wakeup"
ftrace_events: "sched/sched_waking"
}
}
}
duration_ms: 5000
EOF
# 拉取 trace 文件
adb pull /data/misc/perfetto-traces/trace ./trace.pb
# 在浏览器打开 https://ui.perfetto.dev 上传分析
systrace(传统方式):
# 在 AOSP 源码树下
python2 system/extras/systrace/systrace.py --time=5 -o trace.html sched freq idle am wm
# 或使用 Android SDK 的 systrace
$ANDROID_HOME/platform-tools/systrace/systrace.py --time=5 -o trace.html am wm view
15.4.2 Simpleperf — 原生代码性能分析
# 抓取 CPU profile(采样 10 秒)
adb shell simpleperf stat -p <pid> sleep 10
# 记录调用栈(采样模式)
adb shell simpleperf record -p <pid> -g --duration 10 -o /data/local/tmp/perf.data
# 拉取到本地
adb pull /data/local/tmp/perf.data .
# 报告(文本)
adb shell simpleperf report -i /data/local/tmp/perf.data
# 报告(交互式 UI)
# 使用 pprof 工具
python2 system/extras/simpleperf/scripts/report.py -i perf.data
# 生成火焰图
python2 system/extras/simpleperf/scripts/report_sample.py \
-i perf.data -o perf.proto \
--protobuf
# 然后在 https://ui.perfetto.dev 打开
15.4.3 Android Studio Profiler — Java/Kotlin 性能分析
# 1. 在 Android Studio 中打开 Profiler
# 2. 选择设备和进程
# 3. 可以分析:
# - CPU 使用(方法追踪)
# - 内存分配
# - 网络请求
# - 电量消耗
15.4.4 方法追踪(Method Tracing)
# 开始追踪
adb shell am profile start com.android.sysdemo /data/local/tmp/trace.trace
# 操作 App...
# 停止追踪
adb shell am profile stop com.android.sysdemo
# 拉取 trace 文件
adb pull /data/local/tmp/trace.trace
# 用 Android Studio 的 Profiler 打开分析
# 或用 dmtracedump 转换
dmtracedump -g trace.trace -o trace.svg
15.4.5 内存分析
# 查看进程内存概览
adb shell dumpsys meminfo com.android.sysdemo
# 查看详细内存分布
adb shell dumpsys meminfo com.android.sysdemo -d
# 导出 Java 堆
adb shell am dumpheap -n com.android.sysdemo /data/local/tmp/heap.hprof
adb pull /data/local/tmp/heap.hprof
# 用 Android Studio / MAT / jhat 分析
# 转换为标准 hprof(如果需要)
hprof-conv heap.hprof heap-std.hprof
# 查看原生内存分配
adb shell simpleperf record -p <pid> -g --trace-offcpu --duration 10 \
-e native_alloc,native_free -o /data/local/tmp/native_mem.data
15.4.6 启动时间分析
# 查看 App 冷启动时间
adb shell am start-activity -W -n com.android.sysdemo/.SysDemoActivity
# 输出示例:
# ThisTime: 234
# TotalTime: 234
# WaitTime: 245
# Complete
# 查看 Activity 启动耗时
adb logcat -s ActivityManager:I ActivityThread:I
# 使用 systrace 分析启动
adb shell am start -n com.android.sysdemo/.SysDemoActivity
# 同时抓取 systrace,关注 am_proc_start → ActivityManager:onStartActivity
15.5 模拟器专属调试技巧
15.5.1 模拟器启动与连接
# 启动模拟器(使用编译出的镜像)
emulator -verbose -show-kernel -no-window
# 带内核调试启动
emulator -verbose -show-kernel -no-snapshot -qemu -s -S
# 连接 GDB 到内核
gdb vmlinux
(gdb) target remote :1234
15.5.2 模拟器高级选项
# 关闭 SELinux
emulator -selinux permissive
# 指定内存大小
emulator -memory 4096
# 启用 KVM 加速
emulator -enable-kvm
# 指定分辨率
emulator -skin 1080x1920
# 不使用快照(干净启动)
emulator -no-snapshot
# 重置用户数据
emulator -wipe-data
15.5.3 fastboot 刷机(模拟器)
# 进入 fastboot 模式
adb reboot bootloader
# 刷入 system_ext 镜像
fastboot flash system_ext out/target/product/emulator_x86_64/system_ext.img
# 刷入 system 镜像
fastboot flash system out/target/product/emulator_x86_64/system.img
# 刷入所有镜像
fastboot flashall -w
# 重启
fastboot reboot
15.6 日志与调试工具速查
| 工具 | 用途 | 命令示例 |
|---|---|---|
logcat |
查看 Android 日志 | adb logcat -s TAG *:E |
dmesg |
查看内核日志 | adb shell dmesg | grep avc |
dumpsys |
系统状态转储 | adb shell dumpsys meminfo <pkg> |
tombstone |
原生崩溃分析 | adb shell cat /data/tombstones/tb_00 |
simpleperf |
原生性能分析 | adb shell simpleperf record -p <pid> -g |
perfetto |
系统级 trace | adb shell perfetto -c - --txt |
strace |
系统调用跟踪 | adb shell strace -p <pid> |
gdbserver |
远程 GDB | adb shell gdbserver :5039 --attach <pid> |
am |
Activity 管理 | adb shell am start -W -n pkg/.Act |
pm |
包管理 | adb shell pm path com.android.sysdemo |
input |
模拟输入 | adb shell input tap 540 960 |
screencap |
截屏 | adb shell screencap /sdcard/screen.png |
screenrecord |
录屏 | adb shell screenrecord /sdcard/demo.mp4 |
bugreport |
完整诊断报告 | adb bugreport bugreport.zip |
15.7 调试工作流总结
App 闪退/异常
│
├─ Java 层异常?
│ ├─ adb logcat -b crash → 看 AndroidRuntime
│ ├─ adb logcat -s PackageManager → 看安装/权限问题
│ └─ adb shell am start -W -n ... → 看启动耗时和错误
│
├─ 原生层崩溃?
│ ├─ adb logcat *:F → 看 FATAL EXCEPTION
│ ├─ adb shell cat /data/tombstones/tb_00 → 看 tombstone
│ ├─ llvm-addr2line → 解析崩溃地址
│ └─ gdbserver + gdbclient.py → GDB 远程调试
│
├─ SELinux 拒绝?
│ ├─ adb shell getenforce → 检查 SELinux 模式
│ ├─ adb shell dmesg | grep avc → 看 AVC 拒绝
│ └─ adb shell setenforce 0 → 临时关闭(仅调试)
│
├─ 性能问题?
│ ├─ 启动慢 → am start -W + systrace
│ ├─ 卡顿 → dumpsys gfxinfo + systrace
│ ├─ 内存 → dumpsys meminfo + heap dump
│ └─ CPU → simpleperf record + report
│
└─ 系统级问题?
├─ adb bugreport → 完整诊断
├─ adb shell dumpsys -l → 列出所有服务
└─ perfetto → 系统级 trace