24.12.18 更新
houdini兼容层支持android8/9 (仅限armv7,从chromeos android9镜像dump,cros的9只有32位)

24.12.11 更新
解决13无法启动问题,12以下崩溃严重问题。已和正常linux运行redroid无异。文章大幅更新,之前看过的建议重看(

24.12.10 更新
解决64only问题,可启动32版本。

前言

睡觉时突然寻思可能和内核有关,于是又倒腾了四天,修复完成

手机空间不够,某天想上云星铁的时候看到排队人数直接劝退,于是寻思能不能自己搞一台云安卓设备,然后看到群友在香橙派上用redroid挂BA脚本。看了看手头的设备后,做NAS的N5105已经虚拟化了一堆,有核显的x86就剩我的J4125软路由了,反正性能过剩。(

采用的设备为J4125,openwrt为lean的lede,并且已开启docker。接下来的操作全部基于docker开启的条件,需要修改源码重新编译,因为redroid最低为android8,需要在内核开启binder。


源码的内核配置部分修改

截止到这篇文章时,lean的lede在x86_64平台上默认内核版本为6.6。所以修改target/linux/x86/config-6.6。

大坑是因为redroid的readme没有说内核要开哪些东西,加上openwrt原本就不能算“正常”linux。readme能看到的只有binder和ashmem,而这两玩意都是android内核源码里才用的。redroid的作者提供了redroid-modules,里面有binder和ashmem模块,但是因为较旧所以在6.6内核上需要修复。但其实Binder已经进内核主线了(drivers/android/),而ashmem(drivers/staging/android)可以被memfd代替,故不需要用到redroid-modules。

在config-6.6文件最底部追加下述

CONFIG_PSI=y
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_ANDROID_BINDERFS=y
CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder,vndbinder,binderfs"
CONFIG_UDMABUF=y
CONFIG_DMABUF_HEAPS=y
CONFIG_DMABUF_SYSFS_STATS=y
CONFIG_DMABUF_HEAPS_SYSTEM=y
CONFIG_CMA=y
CONFIG_CMA_DEBUGFS=y
CONFIG_CMA_SYSFS=y
CONFIG_CMA_AREAS=7
CONFIG_DMA_CMA=y
CONFIG_CMA_SIZE_MBYTES=0
CONFIG_CMA_SIZE_SEL_MBYTES=y
CONFIG_CMA_ALIGNMENT=8
CONFIG_DMABUF_HEAPS_CMA=y
CONFIG_XEN_GRANT_DMA_ALLOC=y
CONFIG_XEN_GNTDEV_DMABUF=y
  • 主要要开的就是binder和dmabuf,剩下的是为了把DMABUF部分全开起来而开的。

  • PSI是因为android 10开始弃用了LMK而使用了LMKD(低内存终止守护程序)而开的。redroid的issue区有提到lmkd起不来的情况,故开启先。

  • DMA_HEAP是需要开启codec2支持,不然scrcpy会在redroid中报错List of video encoders: (none)。在issue中[server] INFO: List of video encoders: (none)有提到。

24.12.10更新:

CONFIG_X86_X32=y
CONFIG_COMPAT=y
CONFIG_COMPAT_FOR_U64_ALIGNMENT=y
CONFIG_SYSVIPC_COMPAT=y
CONFIG_KVM_COMPAT=y
CONFIG_HAVE_ARCH_MMAP_RND_COMPAT_BITS=y
CONFIG_ARCH_MMAP_RND_COMPAT_BITS=8
CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES=y
CONFIG_COMPAT_BINFMT_ELF=y

追加该部分,开启对32位支持
其中,CONFIG_X86_X32_ABI=y 默认已在x64/config-6.6中开启。

CONFIG_BINFMT_MISC=y

添加该选项,解决转译层脚本binfmt没有/proc/sys/fs /binfmt_misc来执行脚本的问题(但是有没有这玩意好像并不影响转译层使用)

24.12.11更新,解决安卓13无法启动,12以下部分app崩溃问题,12和11 systemui闪退问题

CONFIG_AUDITSYSCALL=y
CONFIG_BUG_ON_DATA_CORRUPTION=y
CONFIG_CROSS_MEMORY_ATTACH=y
CONFIG_INET_DIAG_DESTROY=y
CONFIG_IP6_NF_MATCH_RPFILTER=m
CONFIG_IP6_NF_RAW=y
CONFIG_IPV6_MIP6=y
CONFIG_IPV6_OPTIMISTIC_DAD=y
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_IPV6_VTI=y
CONFIG_IP_NF_ARPTABLES=m
CONFIG_IP_NF_MATCH_ECN=m
CONFIG_IP_NF_MATCH_TTL=m
CONFIG_IP_NF_TARGET_MASQUERADE=m
CONFIG_IP_NF_TARGET_REDIRECT=m
CONFIG_NETFILTER_XT_MATCH_BPF=m
CONFIG_NETFILTER_XT_MATCH_CONNMARK=m
CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
CONFIG_NETFILTER_XT_MATCH_MARK=m
CONFIG_NETFILTER_XT_MATCH_STRING=m
CONFIG_NETFILTER_XT_MATCH_U32=m
CONFIG_NETFILTER_XT_TARGET_CONNMARK=m
CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m
CONFIG_NETFILTER_XT_TARGET_MARK=m
CONFIG_NETFILTER_XT_TARGET_NFLOG=m
CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
CONFIG_NETFILTER_XT_TARGET_TRACE=m
CONFIG_NF_CONNTRACK_NETBIOS_NS=m
CONFIG_NF_CONNTRACK_SANE=m
CONFIG_NF_CT_PROTO_DCCP=y
CONFIG_NF_CT_PROTO_UDPLITE=y
CONFIG_NET_IPVTI=y
CONFIG_NO_HZ=y
CONFIG_PM_WAKELOCKS=y
CONFIG_QUOTA=y
CONFIG_QUOTACTL=y
CONFIG_RD_LZ4=y

通过对照Android内核配置文档
android-base.config
其中,有一部分追加到config-6.6会被覆盖

CONFIG_IA32_EMULATION=y

该部分在64/config-6.6中修改,IA32解决运行32位程序问题。

CONFIG_PSI
CONFIG_AUDIT
CONFIG_PROFILING
CONFIG_STACKPROTECTOR_STRONG
CONFIG_TASKSTATS
CONFIG_TASK_IO_ACCOUNTING
CONFIG_TASK_XACCT

这部分开启需要在openwrt的menuconfig中开启,根据相关名字搜索,编译时检查下开没开就行。
大概是以下这些,是menuconfig中的,不是内核。

CONFIG_KERNEL_PERF_EVENTS=y
CONFIG_KERNEL_PROFILING=y
CONFIG_KERNEL_TASKSTATS=y
CONFIG_KERNEL_TASK_DELAY_ACCT=y
CONFIG_KERNEL_TASK_IO_ACCOUNTING=y
CONFIG_KERNEL_TASK_XACCT=y
CONFIG_KERNEL_KPROBES=y
CONFIG_KERNEL_KPROBE_EVENTS=y
CONFIG_KERNEL_BPF_EVENTS=y
CONFIG_KERNEL_DEVTMPFS=y
CONFIG_KERNEL_DEVTMPFS_MOUNT=y
CONFIG_KERNEL_AUDIT=y
CONFIG_KERNEL_CC_STACKPROTECTOR_STRONG=y
CONFIG_KERNEL_STACKPROTECTOR_STRONG=y
CONFIG_PACKAGE_kmod-crypto-chacha20poly1305=y
CONFIG_PACKAGE_kmod-crypto-xcbc=y
CONFIG_PACKAGE_kmod-sched-act-police=y
CONFIG_PACKAGE_kmod-sched-bpf=y
CONFIG_PACKAGE_kmod-xfrm-interface=y

源码的内核patch部分修改

大坑二号

在上面部分,开启了dmabuf后scrcpy报错依旧没变化。然后发现/dev下并没有dma_heap节点。手动创建无果,最后发现/sys/class/dma_heap/system/下面什么都没有。于是经过一翻搜寻后,在openwrt官方的issue下找到了这篇dma-heap is not getting integrated into the kernel
由于904-debloat_dma_buf.patch这个补丁修改了dmabuf的makefile,导致system_heap.c根本没有进行编译。故对其补丁进行修改。
补丁位置:target/linux/generic/hack-6.6/904-debloat_dma_buf.patch

/drivers/dma-buf/Makefile部分

@@ -1,12 +1,14 @@

@@ -1,12 +1,15 @@

然后

+obj-$(CONFIG_DMABUF_HEAPS)            += heaps/

+dma-buf-objs-$(CONFIG_DMABUF_HEAPS_SYSTEM) += heaps/system_heap.o
+dma-buf-objs-$(CONFIG_DMABUF_HEAPS_CMA)        += heaps/cma_heap.o

即可。
不放心的话编译时去build_dir中的linux kernel部分找下这块有没有生成.o就行了。


(可选)内核模块添加

openwrt_redroid_kernel_modules
forward port了ashmem和ion。
ashmem来自5.15源码,5.18中弃用。
ion来自5.10源码。
因为ashmem可用memfd替代,ion用dmabuf代替。

拉下来放到target/linux/x86即可。然后config-6.6添加对应的config

CONFIG_ASHMEM=y
CONFIG_ION=y
CONFIG_ION_SYSTEM_HEAP=y
CONFIG_ION_CMA_HEAP=y

镜像选用(deprecated)

该部分已过时,现在所有镜像均正常使用,以下内容供参考

~~在x86_64的openwrt上测试了所有redroid镜像,最后发现能正常使用的只有Android 14 64bit only。
arm64没有设备测。~~

其中,android8.1,9,10,12都可以启动,adb可以连接,但scrcpy连接报空指针,但是控制台看service list可以发现服务没起来。logcat基本都是刷net相关问题,但都是fake error,具体不知道什么原因导致没有启动。

android11,13的镜像运行几秒后自动退出,宿主机的dmesg可以看到相关日志,说是打不开/dev/binder。但是在/dev/binder手动挂载上去的情况下一样报错。(binder挂载参考issue:There is no /dev/binder.... binder is always missing in /dev/ )在issue binderfs create failed中有提到相关,但是最新的11镜像已经修复,而我这边测得就是最新镜像,故跳过该版本。

android14镜像能运行但是进shell发现连logd服务都没启动。

android12_64only版本可以启动,scrcpy连进去后有画面,但是systemui一直崩溃,参照issue redroid-12.0.0-arm64 SystemUI持续崩溃没有改善。

~~android13_64only启动失败,好像也是binder问题。
此为本人测试,仅供参考。~~

最后只有14的64only启动正常。


redroid docker参数配置

贴出来的为我个人使用的参数,供参考:

Intel设备兼容层建议选用libhoudini,AMD设备建议用libndk_translation。(chromeos就是这么干的)
如果是houdini,启动参数为:

androidboot.hardware=mt6891
androidboot.redroid_width=720
androidboot.redroid_height=1280
androidboot.redroid_dpi=320
androidboot.redroid_gpu_mode=host
androidboot.redroid_fps=60
androidboot.use_memfd=true
ro.product.cpu.abilist=x86_64,arm64-v8a,x86,armeabi-v7a,armeabi
ro.product.cpu.abilist64=x86_64,arm64-v8a ro.dalvik.vm.isa.arm64=x86_64
ro.product.cpu.abilist32=x86,armeabi-v7a,armeabi
ro.enable.native.bridge.exec=1
ro.enable.native.bridge.exec64=1
ro.dalvik.vm.native.bridge=libhoudini.so

如果是android8/9的houdini,由于只兼容32位,启动参数为:

androidboot.hardware=mt6891 androidboot.redroid_width=720 androidboot.redroid_height=1280 androidboot.redroid_dpi=320 androidboot.redroid_gpu_mode=host androidboot.redroid_fps=60 androidboot.use_memfd=true ro.product.cpu.abilist=x86_64,x86,armeabi-v7a,armeabi ro.product.cpu.abilist64=x86_64 dalvik.vm.isa.x86.variant=sandybridge dalvik.vm.isa.x86.features=default dalvik.vm.isa.arm.variant=generic dalvik.vm.isa.arm.features=default ro.product.cpu.abilist32=x86,armeabi-v7a,armeabi
ro.enable.native.bridge.exec=1 ro.dalvik.vm.native.bridge=libhoudini.so

如果是ndk:

androidboot.redroid_width=720
androidboot.redroid_height=1280
androidboot.redroid_dpi=320
androidboot.use_memfd=true
androidboot.redroid_gpu_mode=host
androidboot.redroid_fps=60
ro.product.cpu.abilist=x86_64,arm64-v8a,x86,armeabi-v7a,armeabi
ro.product.cpu.abilist64=x86_64,arm64-v8a ro.dalvik.vm.isa.arm64=x86_64
ro.product.cpu.abilist32=x86,armeabi-v7a,armeabi
ro.enable.native.bridge.exec=1
ro.enable.native.bridge.exec64=1
ro.dalvik.vm.native.bridge=libndk_translation.so

其中,memfd代替ashmem,720p减小性能消耗,我intel uhd600可以驱动所以gpu_mode是宿主机。

刷入了GAPPS的话建议加上

ro.setupwizard.mode=DISABLED

跳过gapps带的开机向导。


redroid-script定制镜像

原版本redroid-script在openwrt上因为缺失相关依赖,故我fork了一份进行修改以支持openwrt(仅限x86_64)。并且修复了houdini支持,具体可看记录一次在redroid上解决libhoudini受namespace影响无法加载的问题
另外还做了其他方面的一些改进和更新。

使用该工具可以免去拉源码重新编译的情况下添加兼容层,magisk,gapps,widevine,以及某些小的修复。
rote66/redroid-script-openwrt

固件需带有python3
假设保存的位置为/mnt/sda1
下压缩包或者git的方式拉下来
建议创建venv环境,即:

cd /mnt/sda1/redroid-script-openwrt
# 创建venv,创一次就够了
python -m venv venv
# 激活虚拟环境
source venv/bin/activate
# 安装依赖,一次就够
pip install -r requirements.txt

工具相关参数

# -a指定安卓版本,例如11.0.0 12.0.0
python redroid.py -a 11.0.0

# -g 为安装opengapps(11.0及以下),-l 为litegapps(全版本支持),-d 为mindgapps(12-14)
python redroid.py -g

# -m 为安装magisk, -w 为widevine
python redroid.py -m

# -i 为houdini兼容层,-n为ndk兼容层。不要同时添加防止意外冲突。
python redroid.py -i

# -u 为修复android12 systemui崩溃的情况,11没效果(该方案参考镜像选用部分提到的issue,但内核配置完整情况下不会出现崩溃)。 -p 为添加nodata-perm脚本,修复一些游戏因为文件系统本来应该为sdcardfs/erofs而造成的权限不对黑屏问题。)
python redroid.py -u

# 如果是安卓11,添加magisk,添加houdini,即组合为
python redroid.py -a 11.0.0 -mi
# 以此例推

(可选)openwrt添加相关启动命令

echo 1 > /sys/fs/cgroup/cpuset/docker/cgroup.clone_children

用于修复logcat里一直刷的一个couldn't write错误,couldn't write [pid] to /dev/cpuset/foreground/tasks: No space left on device

mkdir /dev/binderfs
mount -t binder binder /dev/binderfs

手动挂载binderfs,实测不需要手动挂载也能启动android12和14的64only版本。


踩坑&常见问题合集

该部分并非局限于openwrt

太多了,不知道从哪写了。从更新部分开始写起吧(

  • Q:任何镜像都无法启动,包括64only。内核日志部分能看到输出全是服务启动,然后被send signal 9杀死,反复循环。
    A:检查内核是不是纯64位,连32ABI都没开。我在5.10/5.15内核上测试就是这样。而6.6的配置文件因为默认开了CONFIG_X86_X32就不会。
  • Q:仅64only部分能开,情况如同本文镜像选用部分如同。
    A:检查内核开没开CONFIG_IA32_EMULATION,不开这个无法运行32位可执行文件。

13 64only同样开不起来,卡binder,非常奇怪。
为了排除上面这两问题,一开始想到的是内核版本太高了,binder可能太新了,毕竟android才刚开始支持6.6,老版本系统可能不支持。结果降到5.10/5.15(毕竟5.10还没移除android部分驱动)发现如出一辙。排除了binder问题。又为了排除镜像问题,将dockerhub上所有redroid相关镜像全部pull下来一个个测。。。将近7页的镜像。。排除环境问题还特意倒腾了下docker in docker,也没成。最后突然想起来是不是压根不兼容32位,单纯考虑了x86_64支持32而没想自身内核是不是64only。

  • Q:13开不起来,14/15正常,12/11 ui闪退,14以下装gapps全部闪退
    A:内核部分没全,参考内核配置-对照安卓内核配置文档修改部分。除了CONFIG_PREEMPT是真不用管,(开了连i915都编译不过去)。

  • Q:兼容层及游戏情况?
    A:11以下没有兼容层,莫得转译。11-14可以使用houdini和ndk。15目前没有任何兼容层支持。游戏方面,以原神和BA国服/国际服为例。(没有完整测试,自测)
    11-ndk:BA国服/国际服可进入下载条,原神可进入登录
    11-houdini:BA国服可进入下载,国际服要打补丁(redroid-script封装的话已打补丁),补丁来源issue:Blue Archive crash on loading screen
    12:原神均可进入登录,houdini在BA国际服正常,国服闪退
    13/14:原神可进入登录,BA无论哪个服打开直接系统崩溃。

  • Q:redroid官方镜像已集成ndk为什么script工具还有内置ndk
    A:官方镜像ndk来源于zhouziyang/libndk_translation,不太清楚提取自哪。工具集成的ndk主要来源于supremegamers/vendor_google_proprietary_ndk_translationprebuilt,查看对应分支就知道来自哪了。其中13以上的ndk/houdini来源于我仓库的fork,截止目前提取自chromeos R130。

  • Q:如何自己从chromeos提取兼容层
    A:通过ChromeOS提取libhoudini/libndk_translation兼容层

后面想到再补充。


后记

确实没想到我居然搞完整了,没白费通宵一周
之前是抱着挂脚本的心态搞得,后面变成了单纯想搞定而搞(
无论能不能当成云手机挂脚本,都弥补了一些我ios存储空间不够的问题。有NAS,有win虚拟机,正好差个云android(

UHD600还凑合,高特效居然没吃满GPU。

deprecated
最终还是没能达到我想要的效果,在14 64only镜像上,BA国服运行后黑屏,看到logcat中写system died。。。米家崩/原/铁全部闪退,log看到是unity相关库的问题,应该是ndk翻译层兼容性问题。方舟闪退,log报什么fingerprint太长?但是看issue好像是说改了fingerprint也没用。能正常运行的测试出来只有FGO和公主连结R。。。。。

scrcpy通吃PC,android,ios
iOS上没上商店不知道,我越狱直装的。
scrcpy-mobile(ios版)
scrcpy-android
scrcpy (PC)

Openwrt虽然是Linux但是相比正常的linux发行版还是太不一样(例如没有systemd,我都不知道切cgroup版本的命令在op上是啥不需要管op上的cgroup版本)。所以在这上面运行redroid花的时间比较长,相关资料没有。全靠根据日志去翻redroid的issue。再加上修改内核部分我没有编译成modules,直接builtin了,(确保无玄学问题)所以每次都得编译然后刷入。

也测试过类似的东西dockdroid,这玩意就是部署一个linux发行版,在里面kvm跑blissos。但是图形加速必须有X11,op没这玩意。直通显卡的话没试,直通感觉在op上会很麻烦,有兴趣的可以试试。吃性能比redroid大不少。(

下次有设备还是在原生arm64上跑把,应该兼容性会强很多。