几乎是当下最详细的 AOSP 编译与调试运行指南

想要自己 debug 系统源码,或者有定制 Android 系统的需求可以参考本文利用 android 13 在 ubuntu 尽情的折腾。

Android 13 源码全量下载与编译

重要提示:自 2021 年 6 月 22 日起,AOSP 不再支持在 MacOS 上进行平台开发。

Ubuntu 环境准备:source.android.com/docs/setup/...

arduino 复制代码
sudo apt-get install git-core gnupg flex bison build-essential zip curl zlib1g-dev libc6-dev-i386 libncurses5 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip fontconfig

DOWN 13.0 源码(时间会比较长):

bash 复制代码
mkdir AOSP/android13 && cd AOSP/android13
repo init -u https://android.googlesource.com/platform/manifest -b android-13.0.0_r49 && repo sync -c -j16

指定编译产物架构 & 编译(11th Gen Intel® Core™ i7-11700K @ 3.60GHz × 16 在 两个小时左右)

编译过某个架构的源码之后,其他平台时间会减少,不同架构的产物不会覆盖,都在磁盘上

ruby 复制代码
 source build/envsetup.sh  //初始化编译环境,包括后面的lunch和make指令都在这个脚本里面
 lunch sdk_phone_x86_64 // //指定此次编译的目标设备以及编译类型,了解更多架构可以直接输入 lunch 指令
 m -j16 // //开始编译,默认为编译整个系统,其中 -j16 代表的是编译的 job 数量为16。

使用模拟器运行

复制代码
 emulator

源码导入到 AS 查看

源码导入 AS 有两种方式:

一种查看整个 AOSP ,通过 idegen 生成整个工程文件,然后用 AS 打开。

另一种只想打开某个模块而非全量的 AOSP,例如 music,可以通过 aidegen 直接从命令行启动 AS。

下面是两种用法:

整个 AOSP 全部导入:

生成 IDE 相关文件

idegen 专门为 IDE 环境调试源码而设计的工具, 在 AOSP/android13 目录(个人源码路径)下依次执行如下命令:

bash 复制代码
soruce build/envsetup.sh  
mmm development/tools/idegen/  
./development/tools/idegen/idegen.sh

以上3个步骤的含义依次如下:

vbnet 复制代码
Step 1: 用于初始化环境变量
Step 2: 生成文件out/host/linux-x86/framework/idegen.jar
Step 3: 源码根目录生成文件android.ipr(工程相关设置), android.iml(模块相关配置)

之后在根目录可以看到生成的相关配置文件:

sql 复制代码
➜  android13 ls
Android.bp   bootstrap.bash  device           out               test
android.iml  build           external         packages          toolchain
android.ipr  BUILD           frameworks       pdk               tools
android.iws  cts             hardware         platform_testing  WORKSPACE
art          dalvik          kernel           prebuilts
bionic       developers      libcore          sdk
bootable     development     libnativehelper  system

排除部分无用目录(可选)

因为源码结构非常庞大并且有些模块是不需要导入的,这个时候可以先编辑 android.iml 文件,增加一些不需要导入模块。先搜索 excludeFolder (表示不需要导入的文件夹),可以在后面可以加上:

ini 复制代码
<excludeFolder url="file://$MODULE_DIR$/.repo"/>
<excludeFolder url="file://$MODULE_DIR$/external/bluetooth"/>
<excludeFolder url="file://$MODULE_DIR$/frameworks/base/docs"/>
<excludeFolder url="file://$MODULE_DIR$/out/host"/>
<excludeFolder url="file://$MODULE_DIR$/prebuilt"/>

然后再进行下一步

或者如果你已经打开 AS 导入工程了,也可以进入目录Project Structure -> Modules, 可快速去除某些模块, 其中红色代码 Exclueded 选项(即代表排出的目录),先点击某个具体文件夹,然后点击上面的 Excluded

之后点击下方 apply -> ok

AS 启动

打开Android Studio, 点击File -> Open,选中前面生成的 android.ipr 文件即可, 该过程 AS 会对项目进行 sync 比较耗时。

注意下面提示在 index 的时候可能会比较卡,是正常的,耐心等完即可。

在 IDE 中直接查看一些类,例如 Activity,里面的代码跳转已经 OK。

如果这一步 AS 卡死了,可以使用杀掉所有 java 进程

复制代码
killall -KILL java 

单独模块导入,以 Music 为例

整个 AOSP 导入比较耗时,如果我们只想单独查看某个系统模块,可以使用 aidegen 指令,例如 Music 可以通过如下步导入 AS:

将 AS 添加到命令行启动

因为 aidegen 需要从命令行启动 studio,所以需要先配置 studio 的环境变量,否则后面执行命令会报错

打开 AS 点击如下图所示位置:

之后在命令行可以直接输入 studio 指令启动某个项目,确保成功打开 AS 再进行下一步

如果是通过 jetbrain toolbox 安装的 studio 点击上面的按钮可能不会生效,这里我直接列下我的做法(不一定是最合理的)

打开这个路径可以看到一个 Shell 脚本,cat 下面的 studio 内容,把 studio.sh 这个文件的路径添加到环境变量

bash 复制代码
(base) ➜  android13 cat /home/nayuta/.local/share/JetBrains/Toolbox/scripts/studio          
#!/bin/bash
# Generated by JetBrains Toolbox 1.28.1.15219 at 2023-05-23T16:22:19.981325368

"/home/nayuta/.local/share/JetBrains/Toolbox/apps/AndroidStudio/ch-0/222.4459.24.2221.9971841/bin/studio.sh" "$@"

使用 aidegen 直接打开模块工程

aidegen 依然是在 envsetup 脚本中,所以需要提前配置环境:

arduino 复制代码
 source build/envsetup.sh  //初始化编译环境,包括后面的lunch和make指令都在这个脚本里面
 lunch sdk_phone_x86_64 // //指定此次编译的目标设备以及编译类型,了解更多架构可以直接输入 lunch 指令

然后执行 aidegen,添加对应想要打开的工程路径,例如 Music

bash 复制代码
aidegen packages/apps/Music -i s

这里 iIDE 的意思,s 代表 Android Studio。

AIDEGen 会自动帮你把对应的模块编译一遍,顺带把梳理出的依赖用 Python 生成一个个的 dependency,最后直接帮你把 AS 拉起,项目自动打开。下面简单截取一下打开后的目录结构:

AIDEGen 会自动把 AOSP 带的库作为依赖涵盖到 dependency 里面。

现在试一下,任意打开一个类,点击头部的 import,可以顺利跳转到 framework/base 或者其它依赖的模块下。

但有些已知的问题:

  • 使用 xml 写布局的时候属性报红而且不能智能提示,而 idegen 就没有这个问题

源码目录结构:

frameworks/ :包含 Android 系统框架的代码。

libcore/ :包含 Android 核心库 (Libcore) 的代码。

packages/ :包含 Android 系统内置应用程序和服务的代码。

sdk/ :包含 Android 软件开发工具包 (SDK) 的代码和工具。

system/ :包含 Android 系统服务和应用程序的代码。

hardware/ :包含 Android 硬件抽象层 ( HAL ) 的代码。

kernel/ :包含 Linux 内核 的代码。

art/:包含 Android 运行时 (ART) 的代码。

bionic/:包含 Android C 库 (Bionic) 的代码。

bootable/:包含 Android 启动相关的代码。

build/:包含 Android 编译系统的代码和配置文件。

cts/:包含 Android 兼容性测试套件 (CTS) 的代码和测试用例。

dalvik/:包含 Dalvik 虚拟机的代码。

development/:包含 Android 开发工具和示例代码。

device/:包含设备树和硬件相关的代码。

docs/:包含 Android 系统文档和开发者指南。

external/:包含 Android 系统使用的外部开源项目的代码。

libnativehelper/:包含 Android JNI 帮助库的代码。

ndk/:包含 Android Native Development Kit (NDK) 的代码和工具。

out/:包含 Android 编译系统生成的临时文件和输出文件。

pdk/:包含 Android 兼容性测试套件 (CTS) 的 Java API。

prebuilts/:包含预编译的二进制文件和工具链。

tools/:包含 Android 开发工具和实用程序的代码。

编译产物信息:

编译后的产物,都位于/``out 目录,该目录下主要关注下面几个目录:

/out/host:Android开发工具的产物,包含SDK各种工具,比如adb,dex2oat,aapt等。

/out/target/common:通用的一些编译产物,包含Java应用代码和Java库;

/out/target/product/[product_name]:针对特定设备的编译产物以及平台相关C/C++代码和二进制文件;

在/out/target/product/[product_name]目录下,有几个重量级的镜像文件:

system.img:挂载为根分区,主要包含Android OS的系统文件;

ramdisk.img:主要包含init.rc文件和配置文件等;

userdata.img:被挂载在/data,主要包含用户以及应用程序相关的数据;

/out/target/product/emulator64_x86_64/product/priv-app/ 系统相关 app apk

代码搜索指令:

可以使用下方指令分类搜索代码,其本质还是 grep 指令

搜索指令 解释
cgrep 所有C/C++文件执行搜索操作
jgrep 所有Java文件执行搜索操作
ggrep 所有Gradle文件执行搜索操作
mangrep [keyword] 所有AndroidManifest.xml文件执行搜索操作
mgrep [keyword] 所有Android.mk文件执行搜索操作
sepgrep [keyword] 所有sepolicy文件执行搜索操作
resgrep [keyword] 所有本地res/*.xml文件执行搜索操作
sgrep [keyword] 所有资源文件执行搜索操作

单独模块编译(以 launcher 举例)

编译指令

编译指令 解释
m 在源码树的根目录执行编译
mm 编译当前路径下所有模块,但不包含依赖
mmm [module_path] 编译指定路径下所有模块,但不包含依赖
mma 编译当前路径下所有模块,且包含依赖
mmma [module_path] 编译指定路径下所有模块,且包含依赖
make [module_name] 无参数,则表示编译整个Android代码

下面列举部分模块的编译指令:

模块 make命令 mmm命令
init make init mmm system/core/init
zygote make app_process mmm frameworks/base/cmds/app_process
system_server make services mmm frameworks/base/services
java framework make framework mmm frameworks/base
framework资源 make framework-res mmm frameworks/base/core/res
jni framework make libandroid_runtime mmm frameworks/base/core/jni
binder make libbinder mmm frameworks/native/libs/binder

上述mmm命令同样适用于mm/mma/mmma,编译系统采用的是增量编译,只会编译发生变化的目标文件。当需要重新编译所有的相关模块,则需要编译命令后增加参数-B,比如make -B [module_name],或者 mm -B [module_path]。

launcher3 修改与编译安装

修改系统中的 launcher3 代码,加了一行 Log

Cd 到这个模块执行 mm 指令

bash 复制代码
cd ~/AOSP/android13/packages/apps/Launcher3 
mm

编译出来的产物位于 out/target/product/emulator_x86_64/system_ext/priv-app/Launcher3/Launcher3.apk

bash 复制代码
➜  out find |grep Launcher3.apk
./soong/.intermediates/packages/apps/Launcher3/Launcher3/android_common/Launcher3.apk
 ./target/product/emulator_x86_64/system_ext/priv-app/Launcher3/Launcher3.apk
./target/product/emulator_x86_64/testcases/Launcher3Tests/Launcher3.apk

然后通过 adb 安装:

bash 复制代码
➜  out adb install -r./target/product/emulator_x86_64/system_ext/priv-app/Launcher3/Launcher3.apk 

观察日志:

yaml 复制代码
05-19 16:28:53.297 18274 18274 V Activity: onCreate djd com.example.bruno.MainActivity@709e5b8: null
05-22 11:59:12.339 19134 19134 V Activity: onCreate djd com.android.launcher3.Launcher@c57b3d0: null
05-22 11 : 59 : 12.896 19134 19134 V Launcher3 djd: onResume
05-22 11 : 59 : 12.914 19134 19134 V Launcher3 djd: onResume
05-22 11:59:12.916 19134 19134 V Launcher3 djd: onResume
05-22 11:59:14.307 19134 19134 V Launcher3 djd: onResume
05-22 11:59:14.607 19134 19134 V Launcher3 djd: onResume
05-22 11:59:19.779 19134 19134 V Launcher3 djd: onResume

PS:对于一个应用从代码编写到点击启动,两篇内容 [WIP]一个 Apk 的安装之旅 & [WIP]一个 App 的启动之旅

系统调试

系统源码修改与编译运行

对于项目源码可以直接修改,例如在 Activity 启动的时候增加日志:

less 复制代码
// frameworks/base/core/java/android/app/Activity.java
protected void onCreate(@Nullable Bundle savedInstanceState) {
    if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState);
 Slog.v(TAG,  "onCreate djd "  + this +  ": "  + savedInstanceState); 
    if (mLastNonConfigurationInstances != null) {
        mFragments.restoreLoaderNonConfig(mLastNonConfigurationInstances.loaders);
    }

如果后面需要修改系统 App,也可以直接在对应的文件中修改例如 launcher:

packages/apps/Launcher3/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java

这个时候可以按照上面的流程完整编译一次项目,或者单独编译某个模块:

  • 整编流程:

然后同样的按照上面编译的步骤编译项目,这个时候是增量编译,但我的设备上还是花了 15 分钟左右

ruby 复制代码
 source build/envsetup.sh  //初始化编译环境,包括后面的lunch和make指令都在这个脚本里面
 lunch sdk_phone_x86_64 // //指定此次编译的目标设备以及编译类型,了解更多架构可以直接输入 lunch 指令
 m -j16 // //开始编译,默认为编译整个系统,其中-j12代表的是编译的job数量为16。
 emulator // 运行模拟器

等到系统运行之后,可以在日志中看到我们自定义的内容:

看起来系统启动就两个 Activity:QuickstepLauncher 和 FallbackHome

kotlin 复制代码
➜  AOSP adb logcat |grep djd
05-18 16:02:24.066   988   988 V Activity: onCreate djd com.android.settings.FallbackHome@cce6b58: null
05-18 16:02:41.320  1256  1256 V Activity: onCreate djd com.android.launcher3.uioverrides.QuickstepLauncher@729c38b: null

断点调试系统代码(Java)

这里的断点调试仍然基于 AS 自带的 debug 工具,而这个工具是以进程为最小调试粒度(每次只能 debug 一个进程,多了还没试过)。如果想断点系统类服务,可以直接下断点,然后选择系统进程调试:

图解 | 一图摸清Android系统服务 - 掘金

Android 系统服务列表_android service列表_Android系统攻城狮的博客-CSDN博客

断点系统服务

  1. 找到需要调试的文件,添加断点
  1. 点击 debug 按钮(图中鼠标的位置),选择 system_process 进程(注意如果打开之后窗口是空的,需要把 Show all processes 勾上 )

如果 attach 成功了之后,一般刚才的断点上会有个小勾

  1. 这里下的断点是启动 Activity,所以在任意 app 打开一个页面即可触发

调试系统 App

调试系统 app 和系统服务一样,只不过在选择进程的时候选择需要调试的进程,以 Settings 为例:

先在它的 BaseActivity 中下断点(注意看这个时候断点没有打勾的标记)

然后运行 Setting 应用在 IDE attach 到 setting 进程

断点显示可用:

在 Setting 中启动任意页面,触发断点:

参考:

developer.sony.com/develop/ope...

source.android.com/docs/setup/...

gityuan.com/2016/08/13/...

gityuan.com/2016/03/19/...

blog.csdn.net/c10WTiybQ1Y...

liuwangshu.cn/framework/a...

www.cnblogs.com/luoyesiqiu/...

相关推荐
CV资深专家4 小时前
在 Android 框架中,接口的可见性规则
android
daifgFuture7 小时前
Android 3D球形水平圆形旋转,旋转动态更换图片
android·3d
二流小码农9 小时前
鸿蒙开发:loading动画的几种实现方式
android·ios·harmonyos
爱吃西红柿!10 小时前
fastadmin fildList 动态下拉框默认选中
android·前端·javascript
悠哉清闲10 小时前
工厂模式与多态结合
android·java
大耳猫11 小时前
Android SharedFlow 详解
android·kotlin·sharedflow
火柴就是我11 小时前
升级 Android Studio 后报错 Error loading build artifacts from redirect.txt
android
androidwork13 小时前
掌握 MotionLayout:交互动画开发
android·kotlin·交互
奔跑吧 android13 小时前
【android bluetooth 协议分析 14】【HFP详解 1】【案例一: 手机侧显示来电,但车机侧没有显示来电: 讲解AT+CLCC命令】
android·hfp·aosp13·telecom·ag·hf·headsetclient
Chenyu_31013 小时前
09.MySQL内外连接
android·数据库·mysql