雪山深处

编译 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 论坛上的 这个办法

GSI 分享

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

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

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

47 Comments
  • Latest
  • Oldest
  • Hottest
沙丁鱼2024-09-20

大佬您好,您的文章镜像我可以转载到酷安吗。
我买了掌玩2代发现您的制作的镜像依旧好用tieba_antic

TalaxyAdmin2024-09-20

@沙丁鱼:

可以呀,注明出处即可tieba_lovely

Momo54182024-07-24

大佬能出一期编译ROM的教程吗

TalaxyAdmin2024-07-25

@Momo5418:

这个最近没有动力摸索,估计得以后再看看了。不过感觉和编译 GSI 应该蛮相似的。

Momo54182024-07-25

@Talaxy:

主要是设备树和内核源码难搞

Momo54182024-07-25

@Talaxy:

要是能编译的话,自己手动编译一个rom,那多香.

TalaxyAdmin2024-07-25

@Momo5418:

哈哈,设备树和内核源码就得靠设备商或者赛博海盗了。

Momo54182024-07-25

@Talaxy:

内核用预编译内核,设备树我直接移植,也不是不行,现在就差大佬出教程了。

TalaxyAdmin2024-07-25

@Momo5418:

啊这,你说的这些我都是一知半解tieba_sob我觉得你不需要教程,大佬头衔给你

Momo54182024-07-25

@Talaxy:

我可没搞过ROM编译,仅仅会编译TWRP,既然会有GSI编译教程那就会有ROM编译教程,期待出教程哦tieba_agree

TalaxyAdmin2024-07-25

@Momo5418:

正如之前说的那样,我目前和将来估计都找不到对此的动力,所以你的期待不一定能得到回应tieba_joy还是快快自学成才吧,刚去拜访了你的博客,相信你的实力!

andforce2024-07-11

你好博主,最近刚开始接触GSI这块,有几个问题不太明白:
我看网上很多关于GSI的编译的文章都是用的跟博主差不多的方式,很少有人基于AOSP源码直接编译的,想知道为什么这么干呢?
是因为AOSP编译出来的不能直接用?还是说不太好用?
为什么很多人会在github单独维护GSI项目?

TalaxyAdmin2024-07-12

@andforce:

感谢捧场!我尝试解答一下:

  1. 直接编译出的固件不一定适配你使用的设备,编译一个适配的固件需要拿到这个设备的设备树(device tree)等等来进行编译,在没有这些的情况下,只能编译 GSI 这些泛用固件了。
  2. 因为一个系统不可能适配所有设备(至少安卓不可能),所以最优解就是:一个仓库维护系统,一个仓库维护特定设备所需要的特定代码,这样既方便维护也不影响编译(编译时两个仓库的代码一拉,打下 pathch 就行了)GSI 也是同理。
張先生2024-05-06

如果不打 对应的 patch,可以构建吗?打patch 是为了兼容更多设备吗?还是该UI风格?

TalaxyAdmin2024-05-06

@張先生:

打 patch 是为了把固件变为 GSI 并修复些 GSI 的 bug。

替身2024-05-25

@Talaxy:

gsi 可以编译user版本吗?大佬,可以加一下我微信吗?可以一起交流一下 微信号 【已隐藏】

TalaxyAdmin2024-05-25

@替身:

这个我不太清楚呢。关于 GSI,我只是满足自己需求这样,没有太深入去了解。我掌握的基本都在文章里体现了,其他的可能帮不上了tieba_grievance(微信号先帮你隐藏掉了)

tzhx122024-05-05

博主你好,我想问一下derpfest gsi项目下的已编译好的release,刷入后会出现证书链中有证书报错,密钥认证证书不受信任的情况,遇到这种情况自己编译gsi能够解决吗

TalaxyAdmin2024-05-06

@tzhx12:

这个情况我没遇到过,无论是自己编译还是直接用仓库 release 下的tieba_grievance

張先生2024-05-06

@Talaxy:

可以搞个交流群吗?

TalaxyAdmin2024-05-06

@張先生:

我不是很有时间和精力管理群聊,你可以建一个,我加进去有空就看一看。

张先生2024-05-01

这个只能最低是安卓13吗?我看这个仓库分支最低是安卓13https://github.com/KoysX/treble_DerpFest_GSI.git

TalaxyAdmin2024-05-02

@张先生:

是的,更低的得去找找其他仓库了

匿名2024-05-03

@Talaxy:

我看第3.2步,在device/phh/treble/ 目录下没有看到generate.sh文件

张先生2024-05-03

@Talaxy:

大佬有交流群吗?

TalaxyAdmin2024-05-03

@匿名:

有成功执行 2.4 中的拉取 GSI 那步吗?

TalaxyAdmin2024-05-03

@张先生:

这个没有呢tieba_grievance

匿名2024-05-04

@Talaxy:

執行成功了的

張先生2024-05-04

@Talaxy:

device目录下面,phh目录都没有的,更没有看到generate.sh 文件

TalaxyAdmin2024-05-04

@張先生:

generate.sh 是 phh treble 仓库的。在 repo sync之前需要拷贝 manifest.xml 文件至 .repo/local_manifests,如果这步执行有误就会出现如上情况,我推测应该是这步出了问题。可以删除项目文件重头来过。我之后也会放出我这边编译好的镜像方便大家。

張先生2024-05-04

@Talaxy:

error: in sync -c --force-sync --optimized-fetch --no-tags --no-clone-bundle --prune -j8: duplicate path prebuilts/vndk/v28 in /home/binghuang/DerpFest/.repo/manifest.xml 复制过去然后repo sync 后会报这个错误

張先生2024-05-04

@Talaxy:

repo init -u https://github.com/DerpFest-AOSP/manifest.git -b 13 为什么不能加上–depth 1 之同步一层?加了之后,执行repo sync就没有报错了,但是会找不到phh目录和generate.sh 文件

張先生2024-05-04

@Talaxy:

git clone https://github.com/KoysX/treble_DerpFest_GSI.git 克隆这一布的时候,好像也要制定分支吧?我看这个默认现在是android14

張先生2024-05-04

@Talaxy:

在执行第三个 patch的时候提示这个,有影响吗?
binghuang@binghuang-virtual-machine:~/DerpFest$ bash ./treble_DerpFest_GSI/apply-patches.sh ~/DerpFest/treble_DerpFest_GSI pre
fatal: 之前的变基目录 .git/rebase-apply 仍然存在,但却提供了 mbox。
后面又继续执行
binghuang@binghuang-virtual-machine:~/DerpFest$ source build/envsetup.sh
ccache not found/installed!
报了这个错误,
最后编译失败了
[100% 398/398] analyzing Android.bp files and generating ninja file at out/soong/build.ninja
FAILED: out/soong/build.ninja
cd “(dirname "out/host/linux-x86/bin/soong_build")" && BUILDER="PWD/(basename "out/host/linux-x86/bin/soong_build")" && cd / && env -i "BUILDER” --top “$TOP” --soong_out “out/soong”
–out “out” -o out/soong/build.ninja --globListDir build --globFile out/soong/globs-build.ninja -t -l out/.module_paths/Android.bp.list --available_env out/soong/soong.environment.available
–used_env out/soong/soong.environment.used.build Android.bp
Killed
00:01:03 soong bootstrap failed with: exit status 1
ninja: build stopped: subcommand failed.

failed to build some targets (39:30 (mm:ss))

TalaxyAdmin2024-05-06

@張先生:

是的,需要指定为 13 的分支,后续我会更新到文章中

TalaxyAdmin2024-05-06

@張先生:

打 patch 报错可能是之前的 patch 没打成功,这一步经常出错,最好在 repo sync 后把各个仓库的源码回退到 patch 更新的时间附近,会减少出错。一些 patch 没成功,跳过可能也没什么问题,一些可能不行,具体得深入到源码了。编译的报错可以去问问 chatgpt。

匿名2024-02-13

想问问pixelos在掌玩mini上有没有设置分栏或一些对平板的优化

TalaxyAdmin2024-02-13

@匿名:

@匿名: 这个我没有装过,不太清楚诶,你可以用 dsu 安装试试

匿名2024-02-20

@匿名:

@匿名: 基于 andyyan 编译的 lineage os 系统都有优化

匿名2024-02-11

卧槽我也是想给掌玩mini编译一个gsi

Anonymous2023-10-08

有編譯好的GSI分享嗎?

TalaxyAdmin2023-10-10

@Anonymous:

@Anonymous: 我这边暂时没有呢,如有需要可在这里查看并下载一些编译好了的

匿名2023-09-17

[Bug]: “path can not be blank” error when deploying on Netlify using redirect rule configuration #2083 你这个waline问题怎么解决的

TalaxyAdmin2023-09-23

@匿名:

@匿名: 无法解决,只能放弃重定向

Powered by Waline v3.5.7