【fkw学习笔记】Android 13 AOSP 源码添加系统预置应用实战指南

Android 13 AOSP 源码添加系统预置应用实战指南

前言

在 Android 系统开发中,将自定义应用(APK)预置到系统镜像中,是一个常见需求。比如开发一个企业定制 ROM,或者在模拟器中预置自己的测试应用。本文将详细记录我在 Android 13 AOSP 源码上,将 MyApp.apk 添加为系统预置应用的完整探索过程和实战经验。

一、问题定义

1.1 什么是系统预置应用?

系统预置应用是指直接打包到 Android 系统镜像中的应用,开机后自动安装,无需用户手动安装。常见的系统预置应用包括:

  • 设置应用(Settings)
  • 启动器(Launcher3)
  • 相机、相册、拨号等系统组件

1.2 我们的目标

MyApp.apk 添加到 Android 13 AOSP 系统镜像中,使其在模拟器启动后自动显示在应用列表中。

普通应用 vs 系统预置应用的区别

二、探索过程:从观察系统应用开始

2.1 为什么先观察现有系统应用?

面对一个陌生的代码库,最佳的学习方法是观察已有的实现。Settings 和 Launcher3 都是 AOSP 自带的系统应用,它们是如何被添加进系统的?通过研究它们的配置,我们可以找到添加预置应用的"标准答案"。

2.2 寻找 Settings 和 Launcher3 的构建配置

首先,我在 packages/apps/ 目录下找到了 Settings 和 Launcher3 的源码:

复制代码
packages/apps/Settings/
packages/apps/Launcher3/

packages/apps 目录结构截图

让我先查看 Settings 的配置:

bash 复制代码
# 查看 Settings 目录结构
ls -la packages/apps/Settings/

结果显示:

  • Android.bp - 构建配置文件
  • src/ - 源码目录
  • AndroidManifest.xml - 应用清单文件
  • apk/ - 预编译的 APK(如果有)

Settings 目录结构截图

2.3 理解 Android.bp 构建配置

Settings 的 Android.bp 使用的是 android_app 模块类型(从源码编译):

bp 复制代码
android_app {
    name: "Settings",
    defaults: ["platform_app_defaults"],
    platform_apis: true,
    certificate: "platform",
    system_ext_specific: true,
    privileged: true,
    required: [
        "privapp_whitelist_com.android.settings",
        "settings-platform-compat-config",
    ],
    static_libs: ["Settings-core"],
    uses_libs: ["org.apache.http.legacy"],
}

关键参数解读:

  • name: "Settings" - 模块名称
  • certificate: "platform" - 使用平台级签名
  • privileged: true - 特权应用,安装到 /system/priv-app/
  • system_ext_specific: true - 安装到 system_ext 分区

2.4 探索 Launcher3 的配置

Launcher3 同样使用 android_app 模块类型。让我查看它的构建配置:

bp 复制代码
android_app {
    name: "Launcher3QuickStep",
    // ... 配置省略
    privileged: true,
    certificate: "platform",
}

Launcher3 Android.bp 配置截图

2.5 重要发现:两种不同的构建方式

在观察过程中,我发现系统应用有两种构建方式:

构建方式 模块类型 适用场景
android_app 从源码编译 Settings、Launcher3 等系统组件
android_app_import 导入预编译 APK 预编译的 APK 文件

这给了我一个重要启示:如果我有一个预编译的 APK,应该使用 android_app_import 模块类型


三、初步尝试:简单粗暴的方法

3.1 第一步:放置 APK 并创建配置

按照初步理解,我把 MyApp.apk 放到 packages/apps/MyApp/ 目录下,并创建了 Android.bp

bp 复制代码
android_app_import {
    name: "MyApp",
    apk: "MyApp.apk",
    presigned: true,
    product_specific: true,
    privileged: false
}

3.2 在产品配置中添加

然后在 build/make/target/product/sdk_phone_x86_64.mk 中添加:

mk 复制代码
PRODUCT_PACKAGES += \
    MyApp

3.3 编译测试

运行编译命令:

bash 复制代码
source build/envsetup.sh
lunch sdk_phone_x86_64
make -j$(nproc)

编译成功 了!但是当我启动模拟器后,MyApp 并没有显示


四、问题排查:深入分析

4.1 检查 APK 是否被正确打包

首先,我检查了 APK 是否被正确打包到系统镜像:

bash 复制代码
# 在模拟器中检查
adb shell ls /product/app/MyApp/

结果:APK 文件存在于 /product/app/MyApp/MyApp.apk

4.2 检查应用是否被安装

bash 复制代码
adb shell pm list packages | grep -i myapp

结果:没有输出,说明应用根本没有被安装

4.3 关键发现:product_specific 的陷阱

通过 ADB 检查分区挂载情况:

bash 复制代码
adb shell cat /proc/mounts | grep product

输出:

复制代码
/dev/block/dm-2 /product ext4 ro,seclabel,relatime 0 0

/product 分区确实挂载了,但为什么应用没有安装?

问题根源product_specific: true 会将 APK 安装到 product 分区,但 PackageManager 不会自动安装 product 分区的应用,除非额外配置。

Android 13 动态分区示意图, system/product/vendor 分区的区别

五、再次尝试:移除 product_specific

5.1 修改 Android.bp

我移除了 product_specific: true

bp 复制代码
android_app_import {
    name: "MyApp",
    apk: "MyApp.apk",
    presigned: true,
    privileged: false
}

5.2 编译测试

重新编译后,再次报错:

复制代码
FAILED: 
build/make/core/artifact_path_requirements.mk:30: error: Build failed.
build/make/target/product/sdk_phone_x86_64.mk produces files inside 
build/make/target/product/generic_system.mks artifact path requirement. 
Offending entries:
system/app/MyApp/MyApp.apk

六、深入理解:generic_system.mk 的限制

6.1 什么是 artifact path requirements?

artifact path requirements 是 AOSP 用来限制哪些文件可以被打包到哪个分区的机制。这是 Android 13 采用动态分区后的新特性。

6.2 查看 generic_system.mk 的限制

我查看了 build/make/target/product/generic_system.mk

makefile 复制代码
# Enable mainline checking
PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := true

这意味着 sdk_phone_x86_64 继承的配置严格限制了 APK 的安装路径。

6.3 解决方案:添加到白名单

查看其他产品是如何解决这个问题的:

build/target/product/emulator_system.mk 中,我发现了:

makefile 复制代码
PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST := \
    system/lib/libemulator_multidisplay_jni.so \
    system/lib64/libemulator_multidisplay_jni.so \
    system/priv-app/MultiDisplayProvider/MultiDisplayProvider.apk \

关键发现 :可以通过 PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST 添加白名单!

6.4 最终配置方案

修改 sdk_phone_x86_64.mk

mk 复制代码
PRODUCT_PACKAGES += \
    MyApp

PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \
    system/app/MyApp/MyApp.apk

七、新的问题:签名错误

7.1 编译后的新错误

修改配置后,编译成功。但当我通过 ADB 手动安装测试时:

bash 复制代码
adb install -r /data/local/tmp/MyApp.apk

报错:

复制代码
Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES: 
Failed to collect certificates from ...]

7.2 检查原始 APK 签名

我检查了原始 APK:

bash 复制代码
unzip -l packages/apps/MyApp/MyApp.apk | grep -i META-INF

结果:没有任何签名文件!(.RSA, .DSA, .EC, CERT.RSA 等都不存在)

7.3 理解 presigned 和 default_dev_cert

查看 AOSP 文档和其他应用配置:

  • presigned: true - APK 必须有有效签名,构建系统不会重新签名
  • default_dev_cert: true - APK 没有签名或使用测试签名,构建系统会使用默认开发证书签名

7.4 最终解决方案

修改 Android.bp

bp 复制代码
android_app_import {
    name: "MyApp",
    apk: "MyApp.apk",
    default_dev_cert: true,
    privileged: false
}

【配图位置 7:default_dev_cert vs presigned 的区别说明图】


八、最终验证:成功添加系统预置应用

8.1 完整配置

packages/apps/MyApp/Android.bp:

bp 复制代码
android_app_import {
    name: "MyApp",
    apk: "MyApp.apk",
    default_dev_cert: true,
    privileged: false
}

build/make/target/product/sdk_phone_x86_64.mk:

mk 复制代码
PRODUCT_PACKAGES += \
    MyApp

PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \
    system/app/MyApp/MyApp.apk

8.2 编译并验证

bash 复制代码
source build/envsetup.sh
lunch sdk_phone_x86_64
make -j$(nproc)

编译成功后,重启模拟器。

8.3 验证结果

bash 复制代码
# 检查 APK 位置
adb shell ls /system/app/MyApp/

# 检查应用是否已安装
adb shell pm list packages | grep -i myapp

模拟器中成功显示 MyApp 的截图

输出结果:

复制代码
/system/app/MyApp/MyApp.apk
package:com.example.myapplication

MyApp 已成功添加为系统预置应用!


九、核心要点总结

9.1 三步成为系统预置应用

三步成为系统预置应用
Step 1: 使用 android_app_import
Step 2: 配置 PRODUCT_PACKAGES
Step 3: 处理 artifact path 限制
不是 android_app

不是 android_library
预编译 APK 专用模块
PRODUCT_PACKAGES += MyApp
PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST +=

system/app/MyApp/MyApp.apk

9.2 关键配置参数说明

参数 说明 推荐值
android_app_import 模块类型,用于导入预编译 APK 必须
name 模块名称 自定义
apk APK 文件路径 自定义
default_dev_cert 使用 AOSP 默认开发证书签名 APK 无签名时必须
presigned APK 已有有效签名 APK 有签名时使用
privileged 特权应用 普通应用用 false
product_specific 安装到 product 分区 不推荐,会导致不自动安装

9.3 Android 13 分区选择

复制代码
┌─────────────────────────────────────────────────┐
│           Android 13 动态分区架构                 │
├─────────────────────────────────────────────────┤
│  /system/app/      - 普通系统应用 [推荐]           │
│  /system/priv-app/ - 特权系统应用                  │
│  /product/app/     - 产品分区应用 [不推荐]         │
│  /system_ext/app/  - system_ext 分区应用         │
└─────────────────────────────────────────────────┘

十、常见问题排查

问题 1:artifact path 限制错误

错误信息:

复制代码
system/app/MyApp/MyApp.apk in artifact path requirement

解决方案:

在产品 .mk 文件中添加:

mk 复制代码
PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \
    system/app/MyApp/MyApp.apk

问题 2:PRODUCT_COPY_FILES 错误

错误信息:

复制代码
Prebuilt apk found in PRODUCT_COPY_FILES: use BUILD_PREBUILT instead

原因: 不能使用 PRODUCT_COPY_FILES 来复制 APK

解决方案: 使用 PRODUCT_PACKAGES + android_app_import 方式

问题 3:签名相关错误

错误信息:

复制代码
One and only one of certificate, presigned, and default_dev_cert must be set

原因: 未指定签名方式

解决方案: 添加 default_dev_cert: truepresigned: true

问题 4:APK 存在但未安装

现象: ls /product/app/MyApp/ 能看到 APK,但 pm list packages 找不到

原因: 使用了 product_specific: true,导致 APK 安装到 product 分区

解决方案: 移除 product_specific: true,改用默认配置

问题 5:INSTALL_PARSE_FAILED_NO_CERTIFICATES

错误信息:

复制代码
Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES: Failed to collect certificates]

原因: APK 没有签名或签名无效

解决方案: 使用 default_dev_cert: true 让构建系统重新签名


十一、学习方法论

11.1 从观察开始

在面对陌生问题时,观察已有的正确实现是最有效的方法:

  1. 找到类似的系统应用(Settings、Launcher3)
  2. 分析它们的构建配置
  3. 理解每个配置参数的含义
  4. 复用成功的模式

11.2 小步迭代

不要试图一步到位:

  1. 先让编译通过
  2. 再让 APK 正确打包
  3. 最后让应用正确安装

11.3 善用工具

  • adb shell pm list packages - 检查已安装应用
  • adb shell ls /system/app/ - 检查 APK 位置
  • adb logcat | grep -i package - 查看包管理日志
  • unzip -l - 检查 APK 内容

11.4 阅读错误信息

AOSP 的错误信息通常很详细,仔细阅读错误信息往往能得到解决线索。


结语

通过这次探索,我深刻理解了 Android 13 AOSP 的构建系统和动态分区机制。关键是:

  1. 选择正确的模块类型android_app_import 用于预编译 APK
  2. 正确配置签名 :无签名 APK 使用 default_dev_cert: true
  3. 理解分区机制 :优先使用 /system/app/,避免 product_specific
  4. 处理构建限制:通过白名单解决 artifact path 限制

希望这篇指南能帮助你在 Android 13 AOSP 上成功添加系统预置应用!

【配图位置 9:最终成功添加 MyApp 到系统预置应用的完整流程图】


参考资料

  1. AOSP 官方文档:https://source.android.com/
  2. Android.bp 构建配置:https://ci.android.com/
  3. Soong 构建系统:https://source.android.com/docs/setup/create/background
  4. 动态分区:https://source.android.com/docs/setup/create/dynamic-partitions

本文基于 Android 13.0.0_r6 AOSP 源码编写,测试环境为 sdk_phone_x86_64 模拟器。

相关推荐
云起SAAS3 小时前
私域直播系统UniApp源码 多商户商城+直播带货 微信小程序+H5+安卓iOS
android·微信小程序·uni-app·私域直播系统
阿Y加油吧3 小时前
二刷 LeetCode:62. 不同路径 & 64. 最小路径和 复盘笔记
笔记·算法·leetcode
承渊政道3 小时前
【动态规划算法】(两个数组的DP问题深度剖析与求解方法)
数据结构·c++·学习·算法·leetcode·动态规划·哈希算法
bendandawugui3 小时前
PCIe协议学习-浅谈SR-IOV
学习
空中海3 小时前
01. 安卓逆向基础、环境搭建与授权
android
辞旧 lekkk3 小时前
【Qt】初识(上)
开发语言·数据库·qt·学习·萌新
Hhy_11073 小时前
【从零开始学习数据结构 ④】:栈 ——后进先出的艺术
c语言·数据结构·学习·visual studio
2501_927168293 小时前
手机号测吉凶:尾数722手机号吉凶
笔记
星河耀银海3 小时前
JAVA 泛型与通配符:从原理到实战应用
android·java·服务器