雪山深处

编译 Android GSI 简易上手

编译 Android GSI 简易上手

2023 年 08 月 14 日 • 11 分钟

技术

前言

最近入手了一个平板电脑:酷比魔方掌玩 mini,性价比和可玩性都十分不错,官方还提供 BL 解锁和原厂镜像,可以让我大胆刷机了。我上一次刷机时差不多是十年前了,十年后的现在玩法多了很多,就比如今天的主角——GSI。

什么是 GSI

Generic System Image(GSI) 通用系统镜像,是一种通用的 Android 系统镜像。只要你的设备符合以下三点要求,GSI 的大门就会向你打开。

  • 已解锁 Bootloader
  • 符合 Treble 架构要求
  • 设备出厂版本为 Android 9(API 28)或以上

也就是说,现在如果想刷机是不用苦苦等待某个有着一样设备的大佬来作包了,这真是太棒了!

你可以在这里查看并下载到一些 GSI:

为什么要自己编译 GSI

Github 上已有大量的官方/非官方 GSI,完全可以下载、解压、刷机、双清、开机一气呵成,为什么还要自己编译呢?

这是因为 GSI 仍然不是那么“通用”,我在平板上前前后后刷了 Pixel Experience Plus、EvoluationX、crDroid、Project Elixir 和 DrepFest。一些 Rom 会保留你的设备型号和制造商,一些则会修改为:Google Treble vanilla;一些能正常显示平板的 UI(如分栏的设置界面),一些则不行。

为了解决这些问题,就需要我们自己修改源码和编译了。

准备编译

理论基础

了解以下知识点:

  • 基础的计算机知识
  • Linux 操作
  • Git 使用

本文以安卓 13 为例,目前 DerpFest GSI 已更新至安卓 14,若想编译安卓 14 则需修改文中的 git/repo 指令来拉取对应的分支(你应该知道怎么做😋)。

软硬件需求

  • Linux
  • 300 GB 以上的硬盘空间
  • 10 GB 以上运行内存、swap(越多越好)
  • 可以畅连 GitHub 和 Google 的网络环境

也可以使用 WSL2 进行,可在 %UserProfile% 下新建一个 .wslconfig 文件,并粘贴以下配置:

[wsl2]
memory=10GB
swap=16GB

环境配置(以 Ubuntu 22.04.2 为例)

参考:

安装 Android SDK

下载 platform-tools:

解压:

unzip platform-tools-latest-linux.zip -d ~

编辑 ~/.profile

# 将 Android SDK platform tools 添加到 PATH
if [ -d "$HOME/platform-tools" ] ; then
    PATH="$HOME/platform-tools:$PATH"
fi

安装依赖

直接执行:

sudo apt update
sudo apt install bc bison ccache build-essential curl flex g++-multilib gcc-multilib git gnupg gperf libxml2 lib32z1-dev liblz4-tool libncurses5-dev libsdl1.2-dev libwxgtk3.0-gtk3-dev imagemagick git lunzip lzop schedtool squashfs-tools xsltproc zip zlib1g-dev openjdk-8-jdk python2 perl  xmlstarlet virtualenv xz-utils rr jq libncurses5 pngcrush lib32ncurses-dev git-lfs libxml2 openjdk-11-jdk-headless

安装 repo

直接执行:

mkdir -p ~/bin
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo

查看 ~/.profile 是否包含 ~/bin 目录,若没有,添加以下代码

# 将用户 bin 添加到 PATH
if [ -d "$HOME/bin" ] ; then
    PATH="$HOME/bin:$PATH"
fi

别忘了 source 一下

source ~/.profile

配置 Git

配置你的 Git 信息

git config --global user.name "your username"
git config --global user.email yourmail@example.com

配置缓存

配置缓存,如果你只打一个包的话,25~50GB 就够了。

export USE_CCACHE=1
export CCACHE_COMPRESS=1
export CCACHE_MAXSIZE=50G # 50 GB

拉取代码

一些 GSI 提供了十分便捷的打包脚本,可以一键拉代码、打 patch、编译,但是这里还是一步步的介绍一下,参考:

初始化 repo 仓库

这里以 DerpFest 为例,首先需要新建一个目录用于放置我们的全部源码。

mkdir DerpFest
cd DerpFest

随后初始化 repo:

repo init -u https://github.com/DerpFest-AOSP/manifest.git -b 13

执行完后,目录下会生成一个 .repo 文件夹。如果你是第一次接触 repo 的话,它是用于管理多个 git 仓库的,manifest 相当于是 repo 的“配置仓库”,用于定义一个 repo 有哪些 git 仓库以及它们的远程地址和本地路径等。

拉取 GSI 仓库

git clone https://github.com/KoysX/treble_DerpFest_GSI.git -b 13

由于 GSI 一般是第三方开发者维护的且为了避免影响正常的系统源码,因此 GSI 部分源码都是放置在独立的一个仓库,通过打 patch 的方式来进行合并。仓库内还有一个 manifest.xml 文件,需要拷贝至 .repo/local_manifests 来让 repo 拉取 treble 部分的代码。

mkdir -p .repo/local_manifests
cp treble_DerpFest_GSI/manifest.xml .repo/local_manifests/derp.xml

同步仓库

拉取源码:

repo sync -c --force-sync --optimized-fetch --no-tags --no-clone-bundle --prune -j$(nproc --all)

这一步大概会下载 60GB 的文件并用掉 100GB 左右的硬盘空间,在你等待之余不妨起来活动一下。

源码和配置

仓库同步完后,赶紧来看一看这 100GB 的大宝贝!仓库下的结构应该和下面的类似:

DerpFest
├── .repo
│   ├── ...
│   ├── local_manifests
│   │    └── derp.xml    # 由下方的 manifest.xml 复制而来
│   └── manifest.xml
├── ...
├── device
│   ├── ...
│   └── phh
│       └── terble
│           ├── ...
│           ├── derp.mk    # 由下方的 derp.mk 复制而来
│           └── generate.sh
├── vendor
│   ├── ...
│   └── derp
│       └── config
│            └── *.mk
├── treble_app
└── treble_DerpFest_GSI
    ├── patches
    │    ├── misc
    │    ├── pre
    │    └── trebledroid
    ├── apply-patches.sh
    ├── manifest.xml
    └── derp.mk

一些我觉得比较有用的配置文件在上面标出来了,省略号部分以后再来探索吧,对于走完打包流程和修改设备信息,修改这些配置文件就足够了。

treble_DerpFest_GSI/derp.mk 复制到 device/phh/treble,并切换到该目录下:

cp treble_DerpFest_GSI/derp.mk device/phh/treble/derp.mk
cd device/phh/treble

适配设备尺寸

查看 derp.mk 文件:

$(call inherit-product, vendor/derp/config/common_full_phone.mk)
$(call inherit-product, vendor/derp/config/common.mk)
$(call inherit-product, vendor/derp/config/BoardConfigDerpFest.mk)
$(call inherit-product, vendor/derp/config/BoardConfigSoong.mk)
$(call inherit-product, device/derp/sepolicy/common/sepolicy.mk)
-include vendor/derp/build/core/config.mk

TARGET_HAS_FUSEBLK_SEPOLICY_ON_VENDOR := true
TARGET_USES_PREBUILT_VENDOR_SEPOLICY := true
SELINUX_IGNORE_NEVERALLOWS := true
#BOARD_EXT4_SHARE_DUP_BLOCKS := true
BUILD_BROKEN_DUP_RULES := true

TARGET_NO_KERNEL_OVERRIDE := true
TARGET_NO_KERNEL_IMAGE := true

TARGET_FACE_UNLOCK_SUPPORTED := true
TARGET_BOOT_ANIMATION_RES := 1080
EXTRA_UDFPS_ANIMATIONS := true
USE_LEGACY_BOOTANIMATION := false

#PRODUCT_PACKAGES += \
#      OnePlusCameraHelper

PRODUCT_SYSTEM_DEFAULT_PROPERTIES += \
    ro.system.ota.json_url=https://raw.githubusercontent.com/KoysX/treble_DerpFest_GSI/13/ota.json

第一行导入了 vendor/derp/config/ 中的 common_full_phone.mk,查看该目录下我们会发现大量的 makefile 文件,除了 common_full_phone.mk 还有 common_full_tablet.mkcommon_mini_tablet.mk 等。可以根据需要将其替换成对应的 makefile,便可将设备的 UI 适配成合适的尺寸了。不过一些 GSI 可能会自动适应,并不需要这么做。

修改设备制造商和设备名

同一目录下还有一个叫 generate.sh 的文件,查看它,我们会发现在接近 90 行左右的地方有以下代码:

PRODUCT_NAME := $target
PRODUCT_DEVICE := tdgsi_${arch}_$part
PRODUCT_BRAND := google
PRODUCT_SYSTEM_BRAND := google
PRODUCT_MODEL := TrebleDroid $apps_name

这里定义了设备名和设备生产商,我们可以在这里修改为自己设备的原信息,比如我要修改为:ALLDOCUBE zhangwanmini,那么可修改为:

PRODUCT_NAME := $target
PRODUCT_DEVICE := tdgsi_${arch}_$part
PRODUCT_BRAND := ALLDOCUBE
PRODUCT_SYSTEM_BRAND := ALLDOCUBE
PRODUCT_MODEL := zhangwanmini

生成 makefile 文件

最后执行该脚本生成 makefile 文件:

bash generate.sh derp

打 Patch

切换回仓库根目录,执行 treble_DerpFest_GSI/apply-patches.sh 以打上对应的 patch:

cd ../../../
bash ./treble_DerpFest_GSI/apply-patches.sh ~/DerpFest/treble_DerpFest_GSI trebledroid
bash ./treble_DerpFest_GSI/apply-patches.sh ~/DerpFest/treble_DerpFest_GSI misc
bash ./treble_DerpFest_GSI/apply-patches.sh ~/DerpFest/treble_DerpFest_GSI pre

即可将 treble_DerpFest_GSI/patches 下对应的 patches 都合并到源码中。

打 Patch 小技巧

值得一提的是,打 Patch 这一步经常出错,原因不限于源码太新或太久而产生冲突。因此在 repo sync 后你可以手动将源码回退到 patch 更新的日期。比如这样:

repo forall -c 'commitID=`git log --before "2024-01-08 16:00" -1 --pretty=format:"%H"`; git reset --hard $commitID'

即可将 repo 下的所有 git 仓库回退到 2024-01-08 16:00 前的最后一个提交。但有时候即便源码时间都尽可能一致也还是会有失败的情况,为了防止一两个 Patch 打失败就全部中断(有些 Patch 不会影响系统运行),可以修改 treble_DerpFest_GSI/apply-patches.sh 的第 15 行:

for patch in $patches/patches/$tree/$project/*.patch; do
-   git am $patch || exit
+   git am $patch || true
done

这样便能无视打失败的 Patch 了。

开始打包

终于到了最后的打包步骤了,不过在每次打包前还要再配置下:

source build/envsetup.sh
export _JAVA_OPTIONS="-Xmx4g" # 可选,修改 JAVA 可用内存,防止爆堆
lunch treble_arm64_bvN-userdebug

打包:

make -j$(nproc --all) systemimage

如果你有一个 CPU,且 CPU 有 6 个核心,每个核心有 2 个线程,那么上方的命令将会允许 12 个任务同步进行,如果你的内存不是很大,那么你应该把 -j 的值调小些。

打包成功后,即可在提示的目录下找到系统镜像~

成功了!

疑难杂症

JAVA 爆堆

编译时的 metalava 过程会吃掉大量内存,稍有不慎就会爆堆,且由于其一般都在 95% 左右时进行,经常打包打了一个小时后报错,让人血压拉满,可以尝试一下 XDA 论坛上的 这个办法 site preview image

GSI 分享

下面是我编译的 DerpFest GSI,有安卓 13 和 14,都是基于文中提到的仓库和方法编译的,有需要可以自取。再次提醒:刷机有风险!搞机需谨慎!本人不为此造成的任何损失负责。

此外酷安上也有一些大佬们制作的系统,比如 MIUI 和 LineagesOS······可以去看看。

本文最后于2024 年 05 月 08 日更新,一些操作可能已经过时