为自己的安卓手机 (Xiaomi 10s) 编译 Android/LineageOS 系统。

1. 编译环境

1.1 WSL

Windows 为例,需要 安装适用于 Linux 的 Windows 子系统 WSL 。在管理员模式下打开 PowerShellWindows Cmd 命令提示符,使用如下命令安装。

# 默认安装 wsl, 安装的 Linux 分发版为 Ubuntu
wsl --install

# 装其他 Linux 发行版,详情可见 https://learn.microsoft.com/zh-cn/windows/wsl/install#change-the-default-linux-distribution-installed
# wsl --install -d <Distribution Name>

安装以后需要设置用户名和密码,若 wsl 无法使用本地代理,解决方法如下。

# 报错信息
wsl: 检测到 localhost 代理配置,但未镜像到 WSL。NAT 模式下的 WSL 不支持 localhost 代理。

# 解决方法
# 修改网络模式为镜像 (networkingMode=mirrored) 并开启自动代理 (autoProxy=true)
# 官方文档:
# https://learn.microsoft.com/zh-cn/windows/wsl/networking#mirrored-mode-networking
# https://learn.microsoft.com/zh-cn/windows/wsl/networking#auto-proxy
# 
# 打开 C:\Users\<UserName>\.wslconfig 配置文件,找到并修改如下内容(没有此文件新建即可)
# .wslconfig 文件内容如下
[wsl2]	
networkingMode=mirrored
autoProxy=true

# 重启 wsl (需新开一个 PowerShell 或 Cmd 窗口执行)
wsl --shutdown
wsl

# 验证结果
# 修改网络模式为镜像后,wsl 的 ip 地址会和 windows 本机的相同
# 使用 ifconfig 检查 ip 地址是否一致即可
ifconfig

1.2 REPO

repoAndroid 为了方便管理多个 git 库而开发的 Python 脚本。repo 的出现,并非为了取代 git,而是为了让 Android 开发者更为有效的利用 gitAndroid 源码包含数百个 git 库,仅下载这么多 git 库就是一项繁重的任务,所以 Android 就引入了 repo

# 添加镜像源
export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo'

# 更新镜像源
sudo apt update

# repo 官方文档
# https://gerrit.googlesource.com/git-repo/+/HEAD/README.md
# 安装 repo
sudo apt-get install repo

1.3 挂载硬盘

这里分为两类,一是将 AOSP 源码放在 Windows 文件系统中,二是将源码放置在 Linux 文件系统中。建议使用 Liunx 文件系统,使用 WindowsNTFS 文件系统编译极易出现各种错误。

1.3.1 将源码放在 Windows 文件系统中

将源码放在 Windows 文件系统中,需要修改文件目录为区分大小写,用管理员身份打开 Powershell,执行以下命令。

极容易出现各种问题,不推荐

# 修改文件目录 G:\\aosp 区分大小写
fsutil.exe file setCaseSensitiveInfo G:\\aosp enable

打开 WSL , 查看硬盘挂载位置。

# 查看磁盘信息
# Filesystem      Size  Used Avail Use% Mounted on
# ...
# G:\             708G  123M  708G   1% /mnt/g
sudo df -h

# 如果 df -h 找不到硬盘信息,则需要手动挂载
# 新建挂载点
sudo mkdir /mnt/g
# 挂载 g 盘
sudo mount -t drvfs E: /mnt/g

1.3.2 将源码放在 Linux 文件系统中

使用 Windows 磁盘管理工具压缩一个至少 500G 的空分区出来单独给 WSL 编译使用。 这里压缩磁盘得到一个 707.46GB 的空分区,如图 1.1 所示。

图1.1

使用 DiskGenius 新建分区,把压缩完的磁盘新建为 Ext4 分区,如图 1.2 所示。

图1.2

点击确定后保存更改,如图 1.3 所示, 其中 序号 1 表示第二个分区

图1.3

进入 PowerShell,列出 Windows 中的可用磁盘并将新分区挂载到 WLS 中。

# 列出 Windows 中的可用磁盘
# DeviceID           Caption               Partitions Size          Model
# --------           -------               ---------- ----          -----
# \\.\PHYSICALDRIVE1 WDC WD22EJRX-89BEMY0  2          2000396321280 WDC WD22EJRX-89BEMY0
# 
GET-CimInstance -query "SELECT * from Win32_DiskDrive"

# 将 ext4 分区挂载到 WLS 中
# 出现如下提示表明挂载成功
# 已成功将磁盘装载为“/mnt/wsl/PHYSICALDRIVE1p2”。
# 注意: 如果已修改 /etc/wsl.conf 中的 automount.root 设置,则位置将不同。
# 若要卸载和分离磁盘,请运行“wsl.exe --unmount \\.\PHYSICALDRIVE1”。
wsl --mount \\.\PHYSICALDRIVE1 --partition 2 -o rw

进入 WSL , 使用 lsblk 命令查看挂载的分区如下。

# 列出所有可用块设备信息
# NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
# ......
#                       /
# sdd      8:48   0   1.8T  0 disk
# ├─sdd1   8:49   0   1.1T  0 part
# └─sdd2   8:50   0 707.5G  0 part /mnt/wsl/PHYSICALDRIVE1p2
sudo lsblk

# 查看磁盘信息
# Filesystem      Size  Used Avail Use% Mounted on
# ...
# /dev/sdd2       697G   12K  661G   1% /mnt/wsl/PHYSICALDRIVE1p2
sudo df -h

# 新建挂载点
sudo mkdir /mnt/g
# 挂载 sdd2 到 /mnt/g 方便使用
sudo mount /dev/sdd2 /mnt/g

# 查看磁盘挂载结果
# Filesystem      Size  Used Avail Use% Mounted on
# ...
# /dev/sdd2       697G   12K  661G   1% /mnt/g
sudo df -h

注意:WSL 重启后需要重新进入 PowerShell 挂载分区,使用如下命令重新挂载即可。

wsl --mount \\.\PHYSICALDRIVE1 --partition 2 -o rw

2. 源码下载

下载不含历史提交的 Android/LineageOS 源码,大约需要 100G 空间,请注意空间分配。在 wsl 中使用如下命令下载 Android/LineageOS 源码。

# 打开分区挂载目录 /mnt/g
cd /mnt/g/
# 创建一个存放源码的目录 aosp
mkdir aosp
# 如果提示没有权限则需要更改下目录所有者
# sudo chown -R 用户名:用户组 /mnt/g

# 添加 repo 镜像源,每次拉起源码都会检查 repo 版本
export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo'

# --git-lfs 把大文件存储在 Git 仓库之外,可以减小 Git 仓库本身的体积,使克隆 Git 仓库的速度加快
# --depth=1 只克隆最近一次commit, 限制 clone 的深度,不会下载 Git 协作的历史记录
repo init -u https://github.com/LineageOS/android.git -b lineage-22.1 --git-lfs --depth=1

# 后续更新源码
repo sync -c --no-tags


# 若提示连接 github.com 失败,可以单独给 git 设置代理
# git config --global http.proxy http://127.0.0.1:2080
# git config --global https.proxy http://127.0.0.1:2080
# 查看当前设置的代理
# git config --global --get http.proxy
# git config --global --get https.proxy

3. 内核移植

本文示例手机为 Xiaomi 10s,找到相应的移植适配,相关代码库如下所示:

使用 git clone 将上面的代码库下载下来去替换对应的 aosp 文件夹,没有对应的文件夹则新建,对应关系如下所示:

# git clone https://github.com/TheMuppets/proprietary_vendor_xiaomi_sm8250-common --depth=1
# 目标文件夹 aosp/vendor/xiaomi/sm8250-common

# git clone https://github.com/TheMuppets/proprietary_vendor_xiaomi_thyme --depth=1
# 目标文件夹 aosp/vendor/xiaomi/thyme

# git clone https://github.com/LineageOS/android_kernel_xiaomi_sm8250 --depth=1
# 目标文件夹 aosp/kernel/xiaomi/sm8250

# git clone https://github.com/LineageOS/android_device_xiaomi_thyme --depth=1
# 目标文件夹 aosp/device/xiaomi/thyme

# git clone https://github.com/LineageOS/android_device_xiaomi_sm8250-common --depth=1
# 目标文件夹 aosp/device/xiaomi/sm8250-common

# git clone https://github.com/LineageOS/android_hardware_xiaomi --depth=1
# 目标文件夹 aosp/hardware/xiaomi

4. 编译 LineageOS

按照 LineageOS 官方 wiki ,在编译前需要安装必要环境。

# 查看 ubuntu 版本
# ...
# Description:    Ubuntu 24.04.2 LTS
# Release:        24.04
lsb_release -a

# 由于是 ubuntu 24.04 的版本,直接安装以下软件包即可
sudo apt-get install bc bison build-essential ccache curl flex g++-multilib gcc-multilib git git-lfs gnupg gperf imagemagick lib32readline-dev lib32z1-dev libelf-dev liblz4-tool lz4 libsdl1.2-dev libssl-dev libxml2 libxml2-utils lzop pngcrush rsync schedtool squashfs-tools xsltproc zip zlib1g-dev

查看当前 WSL 可用内存,尽量配置大内存供 WSL 使用。

# 查看内存使用情况
#                total        used        free      shared  buff/cache   available
# Mem:           15866         963        7203           3        8026       14902
# Swap:           4096           4        4091
free -m

# 查看到只有 16g 内存可以使用,用于编译不太够
# 电脑总内存 32G,分配 30G 内存和 32G swap 给 wsl 使用
# 配置 wsl 可用内存
# 打开文件 C:\Users\YourUsername\.wslconfig,没有此文件可则新建
# 添加以下内容并保存
[wsl2]
memory=30GB
swap=16GB

# 重启 wsl (需新开一个 PowerShell 或 Cmd 窗口执行)
wsl --shutdown
wsl

相关软件包安装和配置完成后,即可开始系统编译。

# 开启缓存以加速后续构建
# https://wiki.lineageos.org/devices/thyme/build/#turn-on-caching-to-speed-up-build

# 进入源代码的根目录
cd /mnt/g/aosp/

# 依次执行以下命令
# 加载命令, 初始化环境变量,注意 . 后面有个空格
. build/envsetup.sh

# 配置编译版本为 userdebug,可以通过 adb 获取 root
lunch lineage_thyme-ap4a-userdebug

# 编译并打包
mka bacon

# 若一切顺利,则会在 out/target/product/thyme/ 下生成线刷包文件
# [100% 12356/12356 0s remaining] build out/target/product/thyme/lineage-22.1-20250309-UNOFFICIAL-thyme.zip
# Package Complete: out/target/product/thyme/lineage-22.1-20250309-UNOFFICIAL-thyme.zip
# 
#### build completed successfully (01:04:27 (hh:mm:ss)) ####

常见错误

1. lfs 仓库拉取失败

lfs 仓库大文件拉取失败,示例日志如下。

FAILED: out/target/product/thyme/obj/APPS/webview_intermediates/package.apk
/bin/bash -c "(rm -f out/target/product/thyme/obj/APPS/webview_intermediates/package.apk ) && (cp \"external/chromium-webview/prebuilt/arm64/webview.apk\" \"out/target/product/thyme/obj/APPS/webview_intermediates/package.apk\" ) && (if (zipinfo out/target/product/thyme/obj/APPS/webview_intermediates/package.apk 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then out/host/linux-x86/bin/zip2zip -i out/target/product/thyme/obj/APPS/webview_intermediates/package.apk -o out/target/product/thyme/obj/APPS/webview_intermediates/package.apk.tmp -0 'lib/**/*.so' && mv -f out/target/product/thyme/obj/APPS/webview_intermediates/package.apk.tmp out/target/product/thyme/obj/APPS/webview_intermediates/package.apk ; fi ) && (out/host/linux-x86/bin/zip2zip -i out/target/product/thyme/obj/APPS/webview_intermediates/package.apk -o out/target/product/thyme/obj/APPS/webview_intermediates/package.apk.tmp -x 'lib/**/*.so' -X lib/arm64-v8a/libwebviewchromium.so -X lib/armeabi-v7a/libwebviewchromium.so && mv -f out/target/product/thyme/obj/APPS/webview_intermediates/package.apk.tmp out/target/product/thyme/obj/APPS/webview_intermediates/package.apk ) && (if (zipinfo out/target/product/thyme/obj/APPS/webview_intermediates/package.apk '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then out/host/linux-x86/bin/zip2zip -i out/target/product/thyme/obj/APPS/webview_intermediates/package.apk -o out/target/product/thyme/obj/APPS/webview_intermediates/package.apk.tmp -0 \"classes*.dex\" && mv -f out/target/product/thyme/obj/APPS/webview_intermediates/package.apk.tmp out/target/product/thyme/obj/APPS/webview_intermediates/package.apk ; fi ) && (mv out/target/product/thyme/obj/APPS/webview_intermediates/package.apk out/target/product/thyme/obj/APPS/webview_intermediates/package.apk.unsigned ) && (prebuilts/jdk/jdk21/linux-x86/bin/java -XX:OnError=\"cat hs_err_pid%p.log\" -XX:CICompilerCount=6 -XX:+UseDynamicNumberOfGCThreads -Djava.library.path=\$(dirname out/host/linux-x86/lib64/libconscrypt_openjdk_jni.so) -jar out/host/linux-x86/framework/signapk.jar   build/make/target/product/security/testkey.x509.pem build/make/target/product/security/testkey.pk8  out/target/product/thyme/obj/APPS/webview_intermediates/package.apk.unsigned out/target/product/thyme/obj/APPS/webview_intermediates/package.apk.signed ) && (mv out/target/product/thyme/obj/APPS/webview_intermediates/package.apk.signed out/target/product/thyme/obj/APPS/webview_intermediates/package.apk )"
zip2zip.go:82: zip: not a valid zip file
12:08:47 ninja failed with: exit status 1

解决方法

# 查看是否安装 git lfs
# 示例输出如下表明已安装 git lfs
# git-lfs/3.4.1 (GitHub; linux amd64; go 1.22.2)
# git lfs <command> [<args>]
git lfs

# 重新初始化仓库并同步代码
repo init -u https://github.com/LineageOS/android.git -b lineage-22.1 --git-lfs --depth=1
# 后续更新源码
repo sync -c --no-tags

# 重新编译并打包
mka bacon


# 还是报同样的错则可以手动下载文件后编译
cd external/chromium-webview/prebuilt/arm64/
git rev-parse --git-dir
git config --global --add safe.directory external/chromium-webview/prebuilt/arm64/
git lfs pull

5. 刷写 LineageOS

按如下命令刷写编译好的 LineageOS 系统。

需要设备已解锁 BootLoader

# 进入线刷包所在目录
# 注:boot.img 也在此目录下面
cd /mnt/g/out/target/product/thyme/

# 进入 recovery
adb -d reboot sideload

# 手机进入 recovery 后刷写系统
adb -d sideload lineage-22.1-20250309-UNOFFICIAL-thyme.zip

# 提示1:若手机 recovery 上提示签名验证失败是否仍然安装请选择 Yes 继续安装
# 签名验证失败仅表明原来的系统和当前刷入的系统签名不一致

# 提示2:正常情况下,adb 会报告Total xfer: 1.00x,
# 但在某些情况下,即使进程成功,输出也会在 47% 处停止并报告 adb: failed to read command: Success。
# 在某些情况下它会报告 adb: failed to read command: No error或 ,adb: failed to read command: Undefined error: 0
# 以上几种情况都表明刷写成功。

6. 使用 AS 查看源码

6.1 安装 Linux Desktop

由于源码位置在 Ext4 分区中,Windows 无法访问,需要在 wsl 中安装 Linux Desktop

# 安装 xrdp
# 用于微软的远程桌面工具来访问
sudo apt install xrdp 

# 修改 xrdp 运行端口
sudo vim /etc/xrdp/xrdp.ini
# 修改下面这一行,将默认的 3389 改成其他端口即可
# port=3390


# 安装 xfce4 桌面
sudo apt install xfce4

# 为当前用户指定登录session类型
vim ~/.xsession
# 添加如下内容
xfce4-session


# 启动 xrdp 服务
# 若报错 Starting xrdp (via systemctl): xrdp.serviceJob for xrdp.service canceled.
# 再次尝试启动即可
sudo /etc/init.d/xrdp start

接下来使用 windows 远程连接 wsl 即可。如图 6.1 所示,其中的 localhostwslip 地址。

图6.1

接着使用 wsl 的用户名和密码登录,如图 6.2 所示。

图6.2

登录成功,如图 6.3 所示。

图6.3

6.2 安装 Android Studio

# 进入用户下载文件夹中
cd ~/Downloads/

# 下载 android-studio-2024.3.1.13-linux
wget https://r4---sn-2x3edn7z.gvt1-cn.com/edgedl/android/studio/ide-zips/2024.3.1.13/android-studio-2024.3.1.13-linux.tar.gz

# 解压到用户文件夹中
tar -zxvf android-studio-2024.3.1.13-linux.tar.gz -C ~/

# 进入解压出来的 android-studio 的 bin 目录
cd ~/android-studio/bin/

# 安装 android studio
# 根据安装流程安装即可
./studio.sh


# 设置启动图标
sudo vim /usr/share/applications/android-studio.desktop
# 输入以下内容
[Desktop Entry]
Type=Application
Name=Android Studio
Comment=Android Studio Integrated Development Environment
Icon=/home/你的用户名/android-studio/bin/studio.png
Exec=/home/你的用户名/android-studio/bin/studio.sh
Terminal=false

[Desktop Entry]
Type=Application
Name=Android Studio
Comment=Android Studio Integrated Development Environment
Icon=/home/jaye/android-studio/bin/studio.png
Exec=/home/jaye/android-studio/bin/studio.sh
Terminal=false

# 重启 wsl (需新开一个 PowerShell 或 Cmd 窗口执行)
wsl --shutdown
wsl

重启后即可在 Applications 中找到 Android studio,如图 6.4 所示。

图6.4

6.3 生成 AS 工程文件

需要完整编译一遍 aosp,源码的依赖才能完整。

# 进入 aosp 源码所在目录
cd /mnt/g/aosp/

# 加载命令, 初始化环境变量
. build/envsetup.sh

# 配置编译版本为 userdebug
# 缺少则会报错
# build/make/core/release_config.mk:273: error: No release config set for target; please set TARGET_RELEASE, or if building on the command line use 'lunch <target>-<release>-<build_type>', where release is one of: .
lunch lineage_thyme-ap4a-userdebug

# 生成文件 out/host/linux-x86/framework/idegen.jar
make idegen

# 在源码根目录生成工程文件 android.ipr 和 模块配置文件 android.iml
# 若报错 Permission denied 可忽略, 成功执行日志如下
# ...
# find: ‘out/target/product/thyme/root/d’: Permission denied
# Read excludes: 5ms
# Traversed tree: 203311ms
. development/tools/idegen/idegen.sh

# 执行完成后,在 aosp 根目录下会生成两个文件
# ipr 工程文件,在 Android studio 中打开时选择此文件,导入源码工程
# android.ipr
# iml 模块配置文件,在此文件中排除不需要的源码路径
# android.iml

6.4 排除不需要的源码路径

编辑 android.iml 文件,根据需要增加 excludeFolder,排除不需要的源码路径,同时需要删除所有的 <orderEntry type="module-library">...</orderEntry>,确保浏览代码时可以直接跳转到源码文件。

跳转源码文件的设置也可以在 Android Studio 的 project structure -> project settings -> modules -> dependencies 中修改

# 安装 gedit 文件编辑器

# 在 gedit 文本编辑器中打开 android.iml
gedit android.iml

# 示例配置如下
# 仅仅保留了 frameworks/base/ 和 特定手机相关的 device/xiaomi/
<excludeFolder url="file://$MODULE_DIR$/.repo"/>
<excludeFolder url="file://$MODULE_DIR$/android"/>
<excludeFolder url="file://$MODULE_DIR$/art"/>
<excludeFolder url="file://$MODULE_DIR$/bionic"/>
<excludeFolder url="file://$MODULE_DIR$/bootable"/>
<excludeFolder url="file://$MODULE_DIR$/build"/>
<excludeFolder url="file://$MODULE_DIR$/cts"/>
<excludeFolder url="file://$MODULE_DIR$/dalvik"/>
<excludeFolder url="file://$MODULE_DIR$/developers"/>
<excludeFolder url="file://$MODULE_DIR$/development"/>
<excludeFolder url="file://$MODULE_DIR$/external"/>
<excludeFolder url="file://$MODULE_DIR$/hardware"/>
<excludeFolder url="file://$MODULE_DIR$/kernel"/>
<excludeFolder url="file://$MODULE_DIR$/libcore"/>
<excludeFolder url="file://$MODULE_DIR$/libnativehelper"/>
<excludeFolder url="file://$MODULE_DIR$/lineage"/>
<excludeFolder url="file://$MODULE_DIR$/lineage-sdk"/>
<excludeFolder url="file://$MODULE_DIR$/out"/>
<excludeFolder url="file://$MODULE_DIR$/packages"/>
<excludeFolder url="file://$MODULE_DIR$/pdk"/>
<excludeFolder url="file://$MODULE_DIR$/platform_testing"/>
<excludeFolder url="file://$MODULE_DIR$/prebuilt"/>
<excludeFolder url="file://$MODULE_DIR$/sdk"/>
<excludeFolder url="file://$MODULE_DIR$/system"/>
<excludeFolder url="file://$MODULE_DIR$/test"/>
<excludeFolder url="file://$MODULE_DIR$/toolchain"/>
<excludeFolder url="file://$MODULE_DIR$/tools"/>
<excludeFolder url="file://$MODULE_DIR$/trusty"/>
<excludeFolder url="file://$MODULE_DIR$/vendor"/>

<excludeFolder url="file://$MODULE_DIR$/device/common"/>
<excludeFolder url="file://$MODULE_DIR$/device/generic"/>
<excludeFolder url="file://$MODULE_DIR$/device/google"/>
<excludeFolder url="file://$MODULE_DIR$/device/google_car"/>
<excludeFolder url="file://$MODULE_DIR$/device/lineage"/>
<excludeFolder url="file://$MODULE_DIR$/device/qcom"/>
<excludeFolder url="file://$MODULE_DIR$/device/sample"/>

<excludeFolder url="file://$MODULE_DIR$/frameworks/av"/>
<excludeFolder url="file://$MODULE_DIR$/frameworks/compile"/>
<excludeFolder url="file://$MODULE_DIR$/frameworks/ex"/>
<excludeFolder url="file://$MODULE_DIR$/frameworks/hardware"/>
<excludeFolder url="file://$MODULE_DIR$/frameworks/layoutlib"/>
<excludeFolder url="file://$MODULE_DIR$/frameworks/libs"/>
<excludeFolder url="file://$MODULE_DIR$/frameworks/minikin"/>
<excludeFolder url="file://$MODULE_DIR$/frameworks/multidex"/>
<excludeFolder url="file://$MODULE_DIR$/frameworks/native"/>
<excludeFolder url="file://$MODULE_DIR$/frameworks/opt"/>
<excludeFolder url="file://$MODULE_DIR$/frameworks/proto_logging"/>
<excludeFolder url="file://$MODULE_DIR$/frameworks/rs"/>
<excludeFolder url="file://$MODULE_DIR$/frameworks/wilhelm"/>

6.5 修改 Android Studio 配置参数

打开 Android Studio ,依次选择 设置图标 -> Edit custom Properties,添加如下内容:

# 避免文件大小超出限制
idea.max.intellisense.filesize=100000

6.6 在 Android Studio 中导入工程

打开 Android Studio ,选择 open,打开在 aosp 源码根目录生成的 android.ipr 文件即可。Android Studio 会把当前的配置和索引保存到文件 android.iws 中,用于后续打开源码加速。打开的源码如图 6.5 所示。

图6.5