AOSP build envsetup.sh 完全解析

一、文件概述

build/envsetup.sh 是 AOSP 构建系统的入口脚本。每次打开终端准备编译 Android 时,你执行的第一条命令就是:

source build/envsetup.sh

它的核心职责是:为当前 shell 环境注入一系列构建辅助函数、设置环境变量、配置 PATH、加载补全脚本和 vendor 配置,让你能够使用 lunchmmmm 等便捷命令来编译 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_64sdk_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.gitout 目录。


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 目标?

  1. build/make/target/product/device/ 下创建产品 .mk 文件
  2. 在对应的 AndroidProducts.mk 中将 .mk 文件加入 PRODUCT_MAKEFILES
  3. <product_name>-eng 等加入 COMMON_LUNCH_CHOICES

Q: mmake 有什么区别?

  • 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

核心原理:一个模块能否进入最终系统镜像,取决于两个条件:

  1. 模块定义:通过 Android.bpAndroid.mk 声明模块的名称、类型、源码、依赖
  2. 产品配置:通过 .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.mkaosp_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 ──→ 编译产物
  1. soong 读取所有 Android.bp 文件,解析模块定义和依赖关系
  2. soong 生成 build.ninja 文件(Ninja 构建描述)
  3. ninja 根据 build.ninja 执行实际编译(并行、增量)
  4. 编译产物输出到 out/ 目录

11.3 Android.bp 与 Android.mk 的关系

特性 Android.bp Android.mk
格式 类 JSON 声明式 Make 命令式
变量/条件 不支持(通过 soong_config 实现) 完全支持
推荐度 推荐(新模块必须用) 旧模块保留,逐步迁移
模块类型 android_appcc_library_shared BUILD_PACKAGEBUILD_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.bpname 完全一致(区分大小写)。

Q: 特权 App 的权限不生效?

  1. 确认 privileged: true 已设置
  2. 确认 privapp_whitelist XML 已通过 required 关联
  3. 确认 XML 中 package 名与 App 的 AndroidManifest.xmlpackage 一致
  4. 确认白名单模块的分区属性与 App 一致

Q: 如何让 App 只在特定产品中包含?

创建自定义产品 .mk 文件,只在那个产品的 PRODUCT_PACKAGES 中添加该 App。

Q: 如何添加 JNI 原生库给 App 使用?

  1. 定义 cc_library_shared 模块
  2. 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.mkPRODUCT_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,需要:

  1. public.libraries.txt 中声明库名
  2. 该文件安装到 /system/etc/public.libraries.txt
  3. 或扩展文件 /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.mkbuild/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 = odmvendor/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