几乎是当下最详细的 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/...

相关推荐
张海龙_China16 分钟前
成为谷歌开发者专家(GDE)的经历
android·ios
Python私教1 小时前
鸿蒙 OS 开发零基础快速入门教程
android·华为·harmonyos
weixin_411191843 小时前
安卓LiveData与MutableLiveData的使用
android
壮哥_icon4 小时前
Fragment两种切换方式
android·java·android studio
Dwyane039 小时前
Android Display性能问题教战手册5-SF/HWC内存使用问题
android
约翰先森不喝酒16 小时前
Android RecyclerView 实现 GridView ,并实现点击效果及方向位置的显示
android
wk灬丨17 小时前
Android Choreographer 监控应用 FPS
android·kotlin
大胃粥17 小时前
Android U WMS : Activity 冷启动(2) 添加启动窗口
android
魏大橙18 小时前
长亭WAF绕过测试
android·运维·服务器
志尊宝18 小时前
Android 中使用高德地图实现根据经纬度信息画出轨迹、设置缩放倍数并定位到轨迹路线的方法
android