Android 系统源码编译

Android 14 (android-14.0.0_r25)源码编译备忘。整个过程不算困难但也并不顺利,所以在此记录一下那些或大或小的坑。

写在前面

最近在花时间梳理 Android 应用开发知识,并且复习了 Android Framework 这一块。为了看代码和改代码更方便,所以我试着编译了 Android 14 的源码。当然需要说明的是,有几个有利的条件助力我折腾 AOSP 源码:

  • 一是硬件上的。新的 AOSP 源码只支持在 Linux 上编译,我的工作机器一直是 MacBook。机缘巧合之下,我最近有了一台迷你 PC(Ubuntu 系统、64GB 内存),性能足够编译 AOSP
  • 二是软件上的
    • AI 很强大,它精通 Android Framework。借助 AI 我能比前些年更轻松地理解 Android Framework
    • 我最近才认真地了解了一下 Cuttlefish 模拟器,发现它非常适合我的工作模式:在远程 Linux 编译 Android 系统,在 MacBook 的浏览器上访问远端已经编好的系统。在此之前,我尝试过 VNC 远程桌面方式访问远程的 Goldfish 模拟器,使用体验非常差劲

小资料:Android 12 是最后一个可以在 MacOS 上编译的版本。更多信息见这里

macOS used to be a supported platform for building AOSP; however, as of June 21, 2021, support has been removed.

So, if you’re building a version of AOSP from before then, it should work, but no new versions (including the current one) will support that.

You can still build Android 12 on macOS (even on Apple Silicon Macs). However, for some unknown reason Google removed macOS support in the fresh Android 13.

说实话,编译 Android 14 整个过程不算困难但也并不顺利,所以在此记录一下那些或大或小的坑。

机器配置

系统:Ubuntu 20.04
CPU:AMD Ryzen 7 7730U (R7-5800H)
内存:64GB 内存
网络:可正常访问谷歌

代码分支

分支:android-14.0.0_r25

原打算编译 Android 14 最后一个修订版本 android-14.0.0_r50,但发现该版本中 lunch 命令发生一些变化,所以选用了 android-14.0.0_r25 分支。

编译 Android

第1步,下载 repo 工具:

1
2
3
4
mkdir ~/bin
PATH=~/bin:$PATH
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo

第2步,下载 aosp 代码。如果是国内网络环境,建议从清华 Android 镜像站下载 aosp-latest.tar

1
2
3
4
5
curl -OC - https://mirrors.tuna.tsinghua.edu.cn/aosp-monthly/aosp-latest.tar # 下载初始化包
tar xf aosp-latest.tar
cd AOSP # 解压得到的 AOSP 工程目录
# 这时 ls 的话什么也看不到,因为只有一个隐藏的 .repo 目录
repo sync # 正常同步一遍即可得到完整目录

我的机器上遇到 repo sync 进度 100%,但仍然提示失败、目录下看不到源码的错误。原因和解决方案见常见问题那一节。

第3步,检出目标分支。可以从这里找到完整的分支列表。

1
2
repo init -b android-14.0.0_r25
repo sync -c -j4

第4步,选择构建目标

1
2
source build/envsetup.sh
lunch

我的机器上遇到遇到无法显示 lunch menu 的问题,具体表现是:

  • 使用 android-14.0.0_r50 分支编译时,提示 Warning: Cannot display lunch menu.,不展示目标菜单
  • 使用 android-14.0.0_r25 分支编译时,正常展示目标菜单

我通过回退到 android-14.0.0_r25 这个分支来规避了这个问题。问题原因和解决方案见常见问题那一节。

第5步,开始构建

1
m # 或者 make -j16

两个构建命令的区别如下:

m make -jX
功能 AOSP提供的快捷命令 GNU Make工具的命令
依赖识别 自动识别当前目录中的项目及依赖 不能自动识别依赖
单模块编译 支持 不支持
工作目录 AOSP项目任意目录 根目录
并发数量 默认为当前系统的CPU核心数 通过-j参数指定

构建快结束时,我又遇到一个错误:无法生成 openwrt_rootfs_customization_x86_64.img。问题原因和解决方案见常见问题那一节。

经过漫长的几个小时终于成功构建 Android 14!

使用 Cuttlefish

Android 源码构建成功后,产物中是包括 Cuttlefish 相关的命令的:

  • launch_cvd 命令用于启动 Cuttlefish 模拟器
  • stop_cvd 命令用于关闭 Cuttlefish 模拟器

但是,要想成功启动 Cuttlefish 模拟器,还必须对当前机器进行相应的配置。这些代码可以在 google/android-cuttlefish 这个项目中找到。

编译和安装

第1步,检出并编译 Cuttlefish 代码

1
2
3
git clone https://github.com/google/android-cuttlefish.git
git checkout stable # 使用 stable 分支代码
tools/buildutils/build_packages.sh

编译成功后,当前目录中会多出来几个 *.deb 包。

提示:

  • 建议使用 build_packages.sh 脚本编译,会比 Android 官网提供的编译步骤简单很多
  • build_packages.sh 脚本依赖 bazel 编译工具来生成 base_*.deb 包,所以编译前需要确保已安装 bazel

第2步,安装上一步生成的 deb 包(只需安装 base 和 user 包):

1
sudo apt install ./cuttlefish-base_*.deb ./cuttlefish-user_*.deb

第3步,修改当前用户权限并重启系统

1
2
sudo usermod -aG kvm,cvdnetwork,render $USER
sudo reboot

这一步遇到两个错误,

  • 第一个错误是网络不通导致无法正确下载编译依赖,比如 npm install 安装依赖库时一直卡住。这个问题没有好的办法,只能耐心观察是哪个环节的网络不通,然后针对性的加代理
  • 第二个错误是 android-cuttlefish 本身的代码问题导致我的机器上无法安装 base_*.deb (可以生成但无法安装)。为此,我给项目提了个 PR pull #615,没想到项目负责人 @jemoreira 2小时内就给合入了。具体的问题原因和解决方案见常见问题那一节。

运行和使用

第1步,启动 Cuttlefish 模拟器。在 Linux 机器上的 AOSP 项目根目录执行命令:

1
launch_cvd --start_webrtc=true

第2步,访问 Cuttlefish 模拟器。在浏览器中访问 http://192.168.1.30:8443,

当然,这里又有个小插曲:Ubuntu 本地浏览器可以正常访问 Cuttlefish 模拟器,而 MacBook 的浏览器一直打不开页面。后来发现是防火墙问题,将对应的端口放行后能正常访问了。问题原因和解决方案见常见问题那一节。

常见问题

repo sync 显示 100% 但是目录下看不到源码

1
2
3
4
5
6
7
8
9
10
info: A new version of repo is available
repo: Updating release signing keys to keyset ver 2.3
warning: repo is not tracking a remote branch, so it will not receive updates
fatal: GitCommandError: git command failure
Project: repo
Args: reset --keep v2.46^0
Stdout:
None
Stderr:
error: Entry '.github/workflows/test-ci.yml' not uptodate. Cannot merge.

以上错误导致没法 checkout 出代码 ,所以目录是空的。错误原因是 .repo/repo 代码更新时有冲突,解决办法是手动更新一下:

1
2
cd .repo/repo
git pull

更新后再次执行 repo sync -j4 可以正常检出代码。

1
2
Checking out: 100% (1355/1355), done in 12m0.742s
repo sync has finished successfully.

无法显示 lunch menu

这个帖子的说法,错误原因是某些本应在 Android 15 才生效的、跟构建流程相关的变更,其实已经合入到 android-14.0.0_r50 分支,导致 lunch 的用法发生变化。

The latest Android 14.0.0-r50 revision made changes to the build process and configuration. They are deprecating certain releases and features of Android T and older (13). It will default to Android (U) specific Build IDs.

我自己的验证结果是,android-14.0.0_r50 分支上执行 lunch -- 是可以构建成功的,

1
lunch aosp_cf_x86_64_phone-ap1a-eng

另外,推测谷歌优化了 Android 编译速度。我原先选择的目标是 aosp_cf_x86_64_phone-ap1a-usereng,编译系统提示我使用 eng 代替 usereng 以获得更快的编译速度:

Want FASTER LOCAL BUILDS? Use -eng instead of -userdebug (however for performance benchmarking continue to use userdebug)

无法生成 openwrt_rootfs_customization_x86_64.img

错误日志如下:

1
2
3
4
5
6
7
8
9
10
11
[ 94% 50186/53071] //external/openwrt-prebuilts:openwrt_rootfs_customization_x86_64 generate openwrt_rootfs_customization_x86_64.img
F2FS-tools: mkfs.f2fs Ver: 1.16.0 (2023-04-11)

Info: Disable heap-based policy
Info: Debug level = 0
Info: Trim is enabled
Info: open /dev/loop0 failed errno:13
Error: Not available on mounted device!
exit status 255
21:24:25 ninja failed with: exit status 1
There was 1 action that completed after the action that failed. See verbose.log.gz for its output.

这并不是一个典型的错误。错误原因是我这台机器上当前用于编译 Android 源码的用户没有访问 /dev/loop0 设备的权限,解决方法是正确设置权限:

1
sudo usermod -aG disk $USER

AMD 机器无法安装 base_*.deb

base_*.deb 依赖于 grub-efi 库。所以如果机器上没有正确安装 grub-efi 库的话,无法安装该 deb 包。

相关的依赖配置代码在 base/debian/control 中。可以看到,arm64 平台的依赖项是 grub-efi-arm64-bin,而非 arm64 平台的依赖项是 grub-efi-ia32bin

但问题在于我的 AMD 机器上安装的是 grub-efi-adm64-bin。grub-efi 库是一个非常基础的库(我猜测安装操作系统时就已经装好这个库了),卸载重装的风险比较大。

总结一下,错误原因是 Cuttlefish 对非 arm64 平台的 grub-efi 依赖项配置不精细导致无法兼容 AMD 平台。所以我在这个 PR pull #615 对 AMD 平台进行了兼容处理,确保 base_*.deb 可正常安装。

浏览器中无法访问 Cuttlefish

Cuttlefish:WebRTC 流式传输中提到,

除使用 TCP:8443 之外,还会使用其他端口建立连接并运行。 如果从与执行 Cuttlefish 的计算机不同的计算机进行连接,则必须在防火墙中允许这些端口。所需端口的列表如下:

  • TCP:15550..15599
  • UDP:15550..15599

错误原因是我的 Ubuntu 未开放远程访问 Cuttlefish 需要的相关端口。解决方法是修改防火墙,允许我的 MacBook 访问 Linux 机器对应的端口:

1
2
3
sudo ufw allow from 192.168.1.0/24 proto tcp to any port 8443
sudo ufw allow from 192.168.1.0/24 proto tcp to any port 15550:15599
sudo ufw allow from 192.168.1.0/24 proto udp to any port 15550:15599

总结

前后加起来足足花了十几个小时,有些艰辛。个人感觉编译 Android 14 过程中最大的障碍或许在于网络问题(很无奈地把自己逼成一个”代理”配置专家),其次是某些代码或配置不完善。

但无论如何,编译好了,也跑起来了!另外,这个 PR pull #615 虽小,但也成就感满满:哈哈,我能以比较快的速度给一个并不熟悉的系统修 bug,并且得到他们的认可。

参考