如何在Ubuntu16.04上构建AndroidROM
介绍
Android 是当今世界上最流行的操作系统。 数百家不同的原始设备制造商或 OEM 选择在他们的设备上安装它,因为它是免费的、开源的,并且围绕它构建了一个庞大的应用程序和服务生态系统。 不幸的是,许多 OEM 不会定期推送 Android 的无线 (OTA) 更新。 其他 OEM 仅在设备发布后的有限时间内提供更新。 此外,OEM 倾向于广泛定制 Android,以确保他们的设备具有独特的外观和感觉。 他们的定制包括替代启动器、主题系统用户界面和预安装的应用程序。
如果您想删除所有这些自定义设置,或者如果您想在您的设备上运行最新版本的纯 Android,您可以自己为其构建新固件。 在 Android 改装社区中,此类固件通常被称为 ROM,是 Read Only Memory 的缩写。
在本教程中,您将构建一个基于 Android 开源项目 或简称 AOSP 的 Android Oreo ROM。 为了保持本教程的设备独立性和通用性,我们将仅针对 AOSP 模拟器,但您可以将相同的技术应用于实际设备。
先决条件
为了能够跟进,您需要:
- 一台 Ubuntu 16.04 x64 服务器,具有至少 16 GB 的 RAM、4 个 CPU 和 120 GB 的存储空间,按照 Ubuntu 16.04 初始服务器设置指南 进行设置,包括 sudo 非 root 用户和防火墙. 编译过程需要大量 RAM,更多的 CPU 会加快编译时间。 此外,您将要下载和构建的文件非常大。 DigitalOcean 有 High CPU Droplets 可能非常适合这个项目。
- 按照 如何在 Ubuntu 16.04 上安装 Git 安装 Git。
第 1 步 — 开始屏幕会话
您将在本教程中执行的一些命令可能会运行数小时。 如果您的 PC 和服务器之间的 SSH 连接在命令运行时中断,它们将被突然终止。 为避免这种情况,请使用 screen
实用程序,它允许您在单个终端中运行多个控制台会话。 使用屏幕,您可以从正在运行的会话中分离并稍后重新附加到它。 如果您是 Screen 新手,请在 本教程中了解更多关于在 Ubuntu 上使用 Screen 的信息。
开始一个新的 screen
会话。
screen
当您第一次运行 screen 时,您将看到一份许可协议。 按 Enter 接受许可。
从此时起,如果您的 SSH 连接失败,您的长时间运行的命令将继续在后台运行。 重新建立 SSH 连接后,您将能够通过运行 screen -r
来恢复会话。
接下来,让我们安装编译Android所需的组件。
第 2 步 — 安装依赖项
AOSP 源代码分布在几个不同的 Git 存储库中。 为了让用户更容易下载所有这些存储库,AOSP 社区创建了一个名为 repo 的命令行工具。
我们将使用 wget
下载该工具的最新版本并将其存储在 ~/bin
目录中。 首先,创建~/bin
目录:
mkdir -p ~/bin
然后下载repo
脚本:
wget 'https://storage.googleapis.com/git-repo-downloads/repo' -P ~/bin
注意:如果您担心在您的机器上运行从其他站点下载的脚本的安全性,请检查脚本的内容:
less ~/bin/repo
一旦您对脚本的内容感到满意,请继续学习本教程。
使用 chmod
授予当前用户运行 repo
的权限。
chmod +x ~/bin/repo
repo
工具在内部使用 Git,并要求您创建一个 Git 配置,指定您的用户名和电子邮件地址。 执行这些命令来做到这一点:
git config --global user.name "your name" git config --global user.email "your_email@your_domain.com"
Android 的源代码主要由 Java、C++ 和 XML 文件组成。 要编译源代码,您需要安装 OpenJDK 8、GNU C 和 C++ 编译器、XML 解析库、ImageMagick 和其他几个相关包。 幸运的是,您可以使用 apt
安装所有这些。 在您这样做之前,请确保您更新了服务器的软件包列表。
sudo apt-get update
列表更新后,安装依赖项:
sudo apt-get install openjdk-8-jdk android-tools-adb bc bison build-essential curl flex g++-multilib gcc-multilib gnupg gperf imagemagick lib32ncurses5-dev lib32readline-dev lib32z1-dev libesd0-dev liblz4-tool libncurses5-dev libsdl1.2-dev libssl-dev libwxgtk3.0-dev libxml2 libxml2-utils lzop pngcrush rsync schedtool squashfs-tools xsltproc yasm zip zlib1g-dev
依赖包下载完成后,我们可以使用 repo
脚本来获取 Android 源代码。
第 3 步 — 下载源代码
我们将使用 repo
脚本来执行一些任务来准备我们的工作区。 创建一个新目录来存储您要下载的 Android 源代码:
mkdir -p ~/aosp/oreo
在本教程的其余部分中,您将在此目录中工作,所以现在切换到它:
cd ~/aosp/oreo
该目录必须使用 AOSP 清单存储库 进行初始化,这是一个特殊的 Git 存储库,其中包含一个名为 default.xml
的 XML 文件,它指定了共同构成 AOSP 代码库的所有其他 Git 存储库的路径。
使用整个 AOSP 代码树会变得很麻烦。 因此,您必须另外指定您感兴趣的特定修订或分支的名称。 在本教程中,因为我们正在构建 Oreo ROM,我们将使用 android-8.0.0_r33
分支,其构建 ID 为 OPD1.170816.025
。 您可以从 AOSP 的官方 代号、标签和内部版本号 页面获取所有可用构建 ID 和分支名称的列表。
此外,您将不需要本教程的代码树的整个提交历史。 您可以通过将历史截断到 1
的深度来节省时间和存储空间。
因此,使用 repo init
命令初始化目录并指定以下选项:
repo init -u https://android.googlesource.com/platform/manifest -b android-8.0.0_r33 --depth=1
当提示启用彩色显示时,按 Y,然后按 Enter。
最后,通过运行 repo sync
命令从各个存储库下载实际的 AOSP 文件:
repo sync
上述命令会下载超过 30 GB 的数据,因此请耐心等待完成。 完成后,我们将设置缓存以加快编译速度。
第 4 步 — 准备编译器缓存
为了加快构建速度,您可以使用编译器缓存。 顾名思义,编译器缓存可帮助您避免重新编译已编译的 ROM 部分。
要启用编译器缓存,请设置一个名为 USE_CCACHE
的环境变量。
export USE_CCACHE=1
除非您有大量可用磁盘空间,否则您不希望缓存变得太大,因此您可以限制其大小。 如果您正在为单个设备构建 ROM,则可以将其限制为 15 GB。 为此,请使用 ccache
命令。
prebuilts/misc/linux-x86/ccache/ccache -M 15G
您将看到确认您已进行此更改的输出:
OutputSet cache size limit to 15.0 Gbytes
在编译之前,我们还需要进行一项优化。 接下来让我们这样做。
第 5 步 — 配置 Jack
Jack 服务器负责构建 ROM 的大部分基于 Java 的部分,它需要大量内存。 为避免内存分配错误,您可以使用名为 ANDROID_JACK_VM_ARGS
的环境变量来指定 Jack 可以使用多少内存。 通常,分配大约 50% of 的服务器 RAM 就足够了。 此环境变量还指定其他编译设置。
执行以下命令为 Jack 服务器分配 8 GB RAM 并保留 Jack 需要的默认编译选项:
export ANDROID_JACK_VM_ARGS="-Xmx8g -Dfile.encoding=UTF-8 -XX:+TieredCompilation"
现在您已准备好构建您的 Android ROM。
第 6 步 — 开始构建
AOSP 代码树包含一个名为 envsetup.sh
的脚本,它有几个与构建相关的帮助函数。 虽然许多辅助函数,例如 mm
、mma
和 mmm
,充当 make
命令的快捷方式,但其他的例如 lunch
设置重要的环境变量,其中包括决定 ROM 的 CPU 架构和构建类型。
获取脚本以获得对辅助函数的访问权限。
source build/envsetup.sh
Outputincluding device/asus/fugu/vendorsetup.sh including device/generic/car/car-arm64/vendorsetup.sh including device/generic/car/car-armv7-a-neon/vendorsetup.sh including device/generic/car/car-x86_64/vendorsetup.sh including device/generic/car/car-x86/vendorsetup.sh including device/generic/mini-emulator-arm64/vendorsetup.sh including device/generic/mini-emulator-armv7-a-neon/vendorsetup.sh including device/generic/mini-emulator-mips64/vendorsetup.sh including device/generic/mini-emulator-mips/vendorsetup.sh including device/generic/mini-emulator-x86_64/vendorsetup.sh including device/generic/mini-emulator-x86/vendorsetup.sh including device/google/dragon/vendorsetup.sh including device/google/marlin/vendorsetup.sh including device/google/muskie/vendorsetup.sh including device/google/taimen/vendorsetup.sh including device/huawei/angler/vendorsetup.sh including device/lge/bullhead/vendorsetup.sh including device/linaro/hikey/vendorsetup.sh including sdk/bash_completion/adb.bash
接下来,运行 lunch
并将设备的代号传递给它,并以构建类型为后缀,可以是 eng
、userdebug
或 user
. 虽然 eng
和 userdebug
构建类型会生成最适合测试目的的 ROM,但建议将 user
构建类型用于生产用途。
要构建可以在 AOSP ARM 仿真器上运行的测试 ROM,请将 aosp_arm-eng
传递给 lunch
命令:
lunch aosp_arm-eng
您将看到此输出,其中显示了环境设置:
Output============================================ PLATFORM_VERSION_CODENAME=REL PLATFORM_VERSION=8.0.0 TARGET_PRODUCT=aosp_arm TARGET_BUILD_VARIANT=eng TARGET_BUILD_TYPE=release TARGET_PLATFORM_VERSION=OPD1 TARGET_BUILD_APPS= TARGET_ARCH=arm TARGET_ARCH_VARIANT=armv7-a TARGET_CPU_VARIANT=generic TARGET_2ND_ARCH= TARGET_2ND_ARCH_VARIANT= TARGET_2ND_CPU_VARIANT= HOST_ARCH=x86_64 HOST_2ND_ARCH=x86 HOST_OS=linux HOST_OS_EXTRA=Linux-4.4.0-104-generic-x86_64-with-Ubuntu-16.04-xenial HOST_CROSS_OS=windows HOST_CROSS_ARCH=x86 HOST_CROSS_2ND_ARCH=x86_64 HOST_BUILD_TYPE=release BUILD_ID=OPD1.170816.025 OUT_DIR=out AUX_OS_VARIANT_LIST= ============================================
最后,运行 make
开始构建。 make
支持并行作业,因此您可以通过使用 -j
选项将并行作业的数量设置为服务器中可用的 CPU 数量来大大加快构建速度。
使用 nproc
命令查看你有多少 CPU:
nproc
该命令返回 CPU 的数量:
Output8
然后,您可以将此数字与 make
一起使用来指定并行执行:
make -j8
即使有 8 个 CPU,您也必须等待一个多小时才能完成构建,前提是您的服务器上没有其他活动的 CPU 密集型进程。 构建的持续时间与您拥有的 RAM 数量和 CPU 数量成正比。 如果您想要更快的构建,请考虑使用专用的 High CPU Droplets,它支持多达 32 个 CPU 和 48 GB 内存。
注意: 在构建过程中你会看到很多警告信息。 您可以放心地忽略它们。
ROM 准备好后,您应该会看到一条消息,说明构建已成功完成。 您还可以看到构建的确切持续时间。
Output... Creating filesystem with parameters: Size: 2147483648 Block size: 4096 Blocks per group: 32768 Inodes per group: 8192 Inode size: 256 Journal blocks: 8192 Label: system Blocks: 524288 Block groups: 16 Reserved block group size: 127 Created filesystem with 2266/131072 inodes and 178244/524288 blocks [100% 63193/63193] Install system fs i... out/target/product/generic/system.img out/target/product/generic/system.img+ maxsize=2192446080 blocksize=2112 total=2147483648 reserve=22146432 #### make completed successfully (01:05:44 (hh:mm:ss)) ####
让我们验证事物是否正确构建。
第 7 步 — 验证构建
构建过程的输出由多个文件系统映像组成,它们共同构成 ROM。 您可以在 out/target/product/generic/
目录中找到它们。
ls -l out/target/product/generic/*.img
Output-rw-r--r-- 1 sammy sammy 69206016 Jan 5 18:51 out/target/product/generic/cache.img -rw-rw-r-- 1 sammy sammy 1699731 Jan 5 19:09 out/target/product/generic/ramdisk.img -rw-r--r-- 1 sammy sammy 2147483648 Jan 5 19:10 out/target/product/generic/system.img -rw-r--r-- 1 sammy sammy 576716800 Jan 5 19:09 out/target/product/generic/userdata.img
要测试 ROM,您可以尝试通过运行 emulator
命令来启动模拟器。 如果您在非 GUI 环境中,请确保将 -no-window
和 -noaudio
标志传递给它。
emulator -no-window -noaudio > /dev/null 2>&1 &
要检查模拟器是否能够成功启动,请稍等片刻,然后使用 Android 调试桥接工具 adb
在模拟器上打开一个 shell。
adb shell
如果 ROM 没有问题,您将看到来自模拟器上运行的 shell 的提示。
Output* daemon not running; starting now at tcp:5037 * daemon started successfully generic:/ #
通过键入 exit
并按 ENTER
或按 CTRL+D
退出此 shell。
注意: 如果您在模拟器启动前尝试打开外壳,您将看到一条错误消息,通知您模拟器已离线。 稍等片刻,然后重试。
故障排除
如果您的构建失败,最可能的原因是内存不足。 要修复它,首先通过运行以下命令终止 Jack 服务器:
jack-admin kill-server
然后再次开始构建,但允许的并行作业更少。 例如,以下是如何将并行作业的数量减少到 2 个:
make -j2
如果您的构建由于磁盘空间不足而失败,您可能会尝试多次构建而不清理先前构建的结果。 要丢弃以前构建的结果,可以运行以下命令:
make clobber
或者,您可以使用 DigitalOcean 的 块存储 为您的 Droplet 添加更多磁盘空间。
结论
在本教程中,您成功地为 Android Oreo 构建了一个基于 AOSP 的 ROM。 你今天学到的技术也适用于 AOSP 的所有分支,例如 Lineage OS 和 Resurrection Remix OS。 如果您有开发 Android 应用程序的经验,您可能有兴趣修改 AOSP 代码库的一小部分以使您的 ROM 具有个人风格。
要了解有关构建 AOSP 源代码的更多信息,请浏览 Google Groups 上的 Android 构建论坛 。