既然Mac也是ARM架构,同时AVD的镜像也支持Project Treble,那么是否可以在Mac上运行其他的GSI镜像呢?
首先下载AVD的镜像,这里我用的是安卓14,下载后的镜像目录在~/Library/Android/sdk/system-images/android-34/default/arm64-v8a,里面可以看到一个system.img,和其他各种文件。后续的操作在Ubuntu虚拟机下进行。
解包system.img
原本我以为AVD的system.img可以直接挂载为ext4,后来发现这个实际上是qemu的raw格式文件,里面有两个分区,p1是vbmeta,p2是super分区。
从官方CI(https://ci.android.com/builds/submitted/11997608/aosp_cf_arm64_phone-userdebug/latest)下载cvd-host_package.tar.gz获取相关工具,或者自行编译。
然后解包:
losetup /dev/loop7 system.img && kpartx -a /dev/loop7 lpunpack /dev/mapper/loop7p2 lpunpack_out/ kpartx -d /dev/loop7 && losetup -d /dev/loop7
其中,loop7这个数字根据实际情况修改,lpunpack_out是解包目录。
成功解包之后,可以在解包目录下看到如下文件:
product.img system.img system_dlkm.img system_ext.img vendor.img
这里面的system.img就是真正的system镜像,可以直接挂载为ext4。
拆分GSI镜像
AVD里面product system system_ext是三个独立的分区,而许多GSI镜像是直接把product和system_ext放在system分区的根目录里,所以要先进行拆分。
创建三个ext4镜像文件(system.img system_ext.img product.img),例如下面的命令创建了一个1G大小的system.img,格式化为ext4并挂载,其他镜像也类似,镜像大小和挂载目录可以自行修改:
dd if=/dev/zero of=system.img bs=1G count=1 mkfs.ext4 system.img mount system.img mnt_system
挂载GSI镜像,如果rw不行就挂载为ro:
mount -o ro gsi.img mount_gsi
将GSI中的product目录的内容复制到product镜像的根目录,system_ext目录的内容放到system_ext镜像的根目录,并把所有除了product和system_ext以外的所有文件放到system镜像,最后umount保存。
替换system.img
将上面解包得到的system.img替换为GSI镜像,product和system_ext也是如此,然后重新打包:
lpmake --metadata-size 65536 --super-name super --metadata-slots 1 --device super:7516192768 --group main:6493691904 \ --partition system:readonly:2134192128:main --image system=new_system.img \ --partition vendor:readonly:93540352:main --image vendor=vendor.img \ --partition product:readonly:2114105344:main --image product=new_product.img \ --partition system_ext:readonly:2147483648:main --image system_ext=new_system_ext.img \ --partition system_dlkm:readonly:4370432:main --image system_dlkm=system_dlkm.img \ --output super.img
其中,–device super:7516192768是生成的super.img的文件大小,每个readonly:xxx:main(如system:readonly:2134192128:main)里面是每个img的大小,–group main:6493691904是几个img的大小总和。运行过程中出现“Invalid sparse file format at header magic”是正常现象。
扩展system.img
假如新的super.img更大了,大过了原本的super分区,那就需要用qemu-img对system.img进行扩容,例如在原来的基础上扩展4G容量:
qemu-img resize -f raw system.img +4G
然后删除原本的super分区,重新创建更大的分区(gdisk删除和新建分区的步骤这里就不展示了):
sgdisk -e system.img gdisk system.img
写入super分区
losetup /dev/loop7 system.img && kpartx -a /dev/loop7 cat super.img > /dev/mapper/loop7p2 kpartx -d /dev/loop7 && losetup -d /dev/loop7
上面这是Linux下的操作,但是在虚拟机下写入速度太慢了,所以这一部分操作我直接在Mac下进行:
hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount system.img cat super.img > /dev/disk4s2 hdiutil detach /dev/disk4
跟上面类似,loop7、disk4后面的数字根据实际情况修改。
生成新的system.img之后就可以运行了,为了方便查看log,这里使用命令行来运行:
emulator -avd AVD_NAME -show-kernel -verbose -selinux permissive
其中,AVD_NAME是模拟器的名称,可以通过“emulator -list-avds来查看”,同时这里将SELinux设为了permissive,不然有的GSI会卡开机。
【未完待续】
参考
[1] https://android.stackexchange.com/questions/236065/what-is-the-format-of-android-m1-initial-preview-system-img
[2] https://www.reddit.com/r/AndroidStudio/comments/sepdg8/unpackrepack_systemimg/
[3] https://apple.stackexchange.com/questions/129921/how-to-mount-raw-disk-images
[4] https://blog.senyuuri.info/posts/2022-04-27-patching-android-super-images/