前言
Waydroid 可以在 Linux 桌面环境中运行接近原生体验的 Android 系统。完成基础部署后,通常可以正常启动 Android、连接网络、登录 Google Play,并安装一部分应用。
但在 x86-64 Linux 主机上,经常会遇到一种典型问题:
- Google Play 可以正常使用;
- 一部分应用可以安装;
- 另一些应用显示“与此设备不兼容”;
- 修改兼容配置后,应用虽然能够安装,却仍然无法启动。
这类问题的核心通常不在网络、Google 账号或 Play 商店,而在于 CPU 指令集架构不匹配。
本文完整记录一次在 Arch Linux、KDE Wayland 和 Waydroid Android 13 环境中,为 Waydroid 安装 Intel Houdini ARM 转译层的过程,包括:
- 如何确认 ABI 不兼容;
- 如何安装 ARM/ARM64 转译层;
- 为什么第一次安装只解决了 Play 商店过滤,却不能真正运行应用;
- 如何处理第三方安装脚本的兼容性问题;
- 如何验证 Houdini、Native Bridge 和 binfmt 是否全部生效;
- 如何在完成后清理备份、缓存和临时文件。
文中的用户名、主机名、个人目录和时间戳均已替换或泛化。
一、问题现象
Waydroid 已经完成以下配置:
- Android 系统可以正常启动;
- 容器网络正常;
- Google Play 可以打开;
- Google 账号可以登录;
- 部分应用可以正常安装;
- Android 版本为 13;
- Waydroid 版本为 1.6.x。
然而,一些常用阅读类应用在 Google Play 中显示:
此应用与当前设备不兼容。
首先检查 Linux 主机架构:
uname -m
输出:
x86_64
再检查 Waydroid 对外报告的 Android ABI:
sudo waydroid shell -- sh -c '
echo "ABI=$(getprop ro.product.cpu.abi)"
echo "ABILIST=$(getprop ro.product.cpu.abilist)"
echo "ANDROID=$(getprop ro.build.version.release)"
echo "SDK=$(getprop ro.build.version.sdk)"
echo "MODEL=$(getprop ro.product.model)"
'
初始结果类似:
ABI=x86_64
ABILIST=x86_64,x86
ANDROID=13
SDK=33
MODEL=WayDroid x86_64 Device
这已经基本确认问题:
当前 Waydroid 只声明支持 x86 和 x86-64,没有声明支持 ARM 或 ARM64。
很多 Android 应用并未发布 x86 版本,只提供:
arm64-v8a
armeabi-v7a
Google Play 检测到设备 ABI 不匹配后,会直接隐藏下载按钮或显示设备不兼容。
二、确认系统中没有现成的 ARM 转译层
在安装任何组件前,先检查 Native Bridge 和常见转译库:
sudo waydroid shell -- sh -c '
echo "NATIVE_BRIDGE=$(getprop ro.dalvik.vm.native.bridge)"
echo "ABILIST=$(getprop ro.product.cpu.abilist)"
for f in \
/system/lib*/libhoudini* \
/system/lib*/libndk_translation* \
/vendor/lib*/libhoudini* \
/vendor/lib*/libndk_translation*
do
[ -e "$f" ] && echo "FOUND=$f"
done
'
结果:
NATIVE_BRIDGE=0
ABILIST=x86_64,x86
没有任何 FOUND= 输出。
这说明系统中既没有 Intel Houdini,也没有 Google NDK Translation。
再检查 Waydroid 配置:
waydroid --version
sudo grep -E \
'^(arch|system_datetime|vendor_datetime|system_ota|vendor_ota|images_path)[[:space:]]*=' \
/var/lib/waydroid/waydroid.cfg
可确认:
- 架构为
x86_64; - 系统镜像为 Android 13 GAPPS;
- 镜像位于 Waydroid 标准数据目录;
- 当前使用独立的 system 和 vendor 镜像。
三、安装前创建完整备份
由于 ARM 转译层会修改 Waydroid 的系统镜像或 overlay,安装前应保留完整数据目录。
先停止 Waydroid:
waydroid session stop 2>/dev/null || true
sudo systemctl stop waydroid-container.service
确认服务停止:
systemctl is-active waydroid-container.service
预期结果:
inactive
然后创建完整副本。
以下示例采用“先改名为备份,再复制回正式路径”的方式:
(
set -euo pipefail
f=/var/lib/waydroid
if ! sudo test -d "$f"; then
echo "错误:目录不存在:$f" >&2
exit 1
fi
stamp=$(sudo date -r "$f" +%Y%m%d-%H%M%S)
old="${f}.${stamp}"
if sudo test -e "$old"; then
echo "错误:备份目标已经存在:$old" >&2
exit 1
fi
sudo mv -- "$f" "$old"
sudo cp -a --reflink=auto --sparse=always -- "$old" "$f"
echo "正式目录:$f"
echo "完整备份:$old"
sudo ls -ld --time-style=long-iso -- "$old" "$f"
sudo du -sh -- "$old" "$f"
)
这种方式有两个好处:
- 备份目录保留原始数据和属性;
- 后续操作修改的是复制出来的正式工作目录。
备份大小约为数 GB,具体取决于已安装应用和 Android 数据量。
四、准备 Houdini 安装脚本
安装所需工具:
sudo pacman -S --needed git python lzip
下载第三方 Waydroid 扩展脚本:
mkdir -p ~/Downloads
cd ~/Downloads
git clone https://github.com/casualsnek/waydroid_script.git
cd waydroid_script
创建独立 Python 虚拟环境:
/usr/bin/python3 -m venv venv
venv/bin/python3 -m pip install --upgrade pip
venv/bin/python3 -m pip install -r requirements.txt
这里明确使用:
/usr/bin/python3
目的是避免 pyenv、Conda 或用户自定义 Python 环境干扰系统工具。
核验环境:
readlink -f venv/bin/python3
venv/bin/python3 --version
git rev-parse HEAD
五、第一次安装:Play 商店允许下载,但应用仍无法运行
停止 Waydroid 后安装 Android 13 Houdini:
waydroid session stop 2>/dev/null || true
sudo systemctl stop waydroid-container.service
cd ~/Downloads/waydroid_script
sudo ./venv/bin/python3 main.py \
--android-version 13 \
install libhoudini
安装脚本输出类似:
INFO: Downloading libhoudini.zip
INFO: Extracting libhoudini.zip
INFO: Copying libhoudini library files
INFO: libhoudini installation finished
重新启动:
sudo systemctl start waydroid-container.service
waydroid show-full-ui
Android 能够正常启动。
检查 ABI:
sudo waydroid shell -- sh -c '
echo "NATIVE_BRIDGE=$(getprop ro.dalvik.vm.native.bridge)"
echo "ABILIST=$(getprop ro.product.cpu.abilist)"
echo "ABILIST32=$(getprop ro.product.cpu.abilist32)"
echo "ABILIST64=$(getprop ro.product.cpu.abilist64)"
'
结果已经发生变化:
NATIVE_BRIDGE=libhoudini.so
ABILIST=x86_64,x86,arm64-v8a,armeabi-v7a,armeabi
ABILIST32=x86,armeabi-v7a,armeabi
ABILIST64=x86_64,arm64-v8a
清除 Google Play 数据:
sudo waydroid shell -- pm clear com.android.vending
waydroid session stop
waydroid show-full-ui
此前显示不兼容的应用,现在可以从 Google Play 正常安装。
但新的问题出现了:
应用能够安装,却无法打开。
这说明 Play 商店已经相信设备支持 ARM,但真正的 ARM 转译执行环境可能并没有生效。
六、发现第一次安装只是“声明兼容”,没有实际挂载 Houdini
检查 Houdini 文件:
sudo waydroid shell -- sh -c '
for f in \
/system/bin/houdini \
/system/bin/houdini64 \
/system/lib/libhoudini.so \
/system/lib64/libhoudini.so
do
[ -e "$f" ] && ls -l "$f" || echo "MISSING=$f"
done
'
结果全部缺失:
MISSING=/system/bin/houdini
MISSING=/system/bin/houdini64
MISSING=/system/lib/libhoudini.so
MISSING=/system/lib64/libhoudini.so
进一步检查主机端文件:
sudo find \
/var/lib/waydroid/overlay \
/var/lib/waydroid/rootfs \
/tmp/houdiniunpack \
\( -type f -o -type l \) \
\( \
-name houdini -o \
-name houdini64 -o \
-name 'libhoudini.so' -o \
-name 'houdini.rc' \
\) \
-print 2>/dev/null
发现文件确实已经被复制到:
/var/lib/waydroid/overlay/system/bin/houdini
/var/lib/waydroid/overlay/system/bin/houdini64
/var/lib/waydroid/overlay/system/lib/libhoudini.so
/var/lib/waydroid/overlay/system/lib64/libhoudini.so
/var/lib/waydroid/overlay/system/etc/init/houdini.rc
但 Android 运行时中完全看不到。
检查配置:
sudo grep -E \
'^[[:space:]]*(mount_overlays|images_path)[[:space:]]*=' \
/var/lib/waydroid/waydroid.cfg
结果:
mount_overlays = False
images_path = /var/lib/waydroid/images
再查看 rootfs:
findmnt /var/lib/waydroid/rootfs
结果表明 rootfs 直接来自只读 ext4 镜像:
/var/lib/waydroid/rootfs /dev/loopX ext4 ro
与此同时,ARM binfmt 处理器也全部缺失:
arm_exe
arm_dyn
arm64_exe
arm64_dyn
这形成了完整的故障链:
- 配置中声明支持 ARM;
- Play 商店因此允许下载 ARM 应用;
- Houdini 文件位于未使用的 overlay 中;
- Android 实际
/system看不到这些文件; - binfmt 没有注册;
- ARM 应用安装后无法启动。
七、重新安装时遇到脚本停止容器错误
确认脚本当前判断:
cd ~/Downloads/waydroid_script
sudo ./venv/bin/python3 -c \
'from tools.container import use_overlayfs; print("SCRIPT_USE_OVERLAYFS =", use_overlayfs())'
结果:
SCRIPT_USE_OVERLAYFS = False
理论上,再次执行安装时应该直接修改 system.img。
但重新运行安装命令后,脚本异常退出:
ERROR: Stopping container
subprocess.CalledProcessError:
Command '['waydroid', 'container', 'stop']'
returned non-zero exit status 0.
这段错误本身很矛盾:
non-zero exit status 0
退出状态明明是 0,却被脚本当成异常。
实际原因是:
waydroid container stop成功退出;- Waydroid 将正常提示写入 stderr;
- 脚本辅助函数把 stderr 内容误判为错误;
- 因此安装流程在真正修改镜像之前就停止。
八、修补第三方脚本的容器停止方式
先备份脚本文件,备份名使用原文件时间戳:
cd ~/Downloads/waydroid_script
f=tools/container.py
old="${f}.$(/usr/bin/date -r "$f" +%Y%m%d-%H%M%S)"
mv -- "$f" "$old"
cp -a -- "$old" "$f"
将:
run(["waydroid", "container", "stop"])
替换为:
run(["systemctl", "stop", "waydroid-container.service"])
可以使用 Python 做精确替换:
/usr/bin/python3 - <<'PY'
from pathlib import Path
path = Path("tools/container.py")
text = path.read_text(encoding="utf-8")
old = ' run(["waydroid", "container", "stop"])\n'
new = ' run(["systemctl", "stop", "waydroid-container.service"])\n'
count = text.count(old)
if count != 1:
raise SystemExit(
f"停止修改:预期匹配 1 处,实际匹配 {count} 处"
)
path.write_text(text.replace(old, new), encoding="utf-8")
print("已修改:", path)
PY
核验差异:
git diff -- tools/container.py
预期:
- run(["waydroid", "container", "stop"])
+ run(["systemctl", "stop", "waydroid-container.service"])
九、处理 root 属主的 Python 缓存
使用 sudo 运行 Python 脚本后,项目目录内生成了 root 属主的 __pycache__。
普通用户执行:
./venv/bin/python3 -m py_compile \
main.py \
tools/container.py \
tools/helper.py
会报:
Permission denied: tools/__pycache__/...
这不是语法错误,而是缓存目录权限问题。
清理缓存:
sudo find . -type d -name '__pycache__' -prune -exec rm -rf -- {} +
然后改用不写入 .pyc 的 AST 语法检查:
./venv/bin/python3 - <<'PY'
import ast
from pathlib import Path
for name in (
"main.py",
"tools/container.py",
"tools/helper.py",
):
path = Path(name)
ast.parse(
path.read_text(encoding="utf-8"),
filename=str(path),
)
print(f"PASS: {path}")
PY
输出:
PASS: main.py
PASS: tools/container.py
PASS: tools/helper.py
十、第二次安装:直接写入 system.img
停止 Waydroid:
waydroid session stop 2>/dev/null || true
sudo systemctl stop waydroid-container.service
重新运行安装,并禁止 Python 写入缓存:
cd ~/Downloads/waydroid_script
sudo env PYTHONDONTWRITEBYTECODE=1 \
./venv/bin/python3 main.py \
--android-version 13 \
install libhoudini
这一次输出明显不同:
INFO: Resizing .../images/system.img
INFO: Mounting .../images/system.img to /tmp/waydroid
INFO: Mounting .../images/vendor.img to /tmp/waydroid/vendor
WARN: /tmp/waydroid/vendor is not a mount point
INFO: Downloading libhoudini.zip
INFO: Extracting libhoudini.zip
INFO: Copying libhoudini library files
INFO: libhoudini installation finished
INFO: Umounting /tmp/waydroid/vendor
INFO: Umounting /tmp/waydroid
关键变化是:
Resizing system.img
Mounting system.img
Copying libhoudini library files
Umounting system.img
这说明 Houdini 不再只是复制到 overlay,而是被直接写入 Android 的系统镜像。
其中:
WARN: /tmp/waydroid/vendor is not a mount point
没有阻止后续流程。Houdini 的主要文件位于 /system,而 system.img 已经成功挂载、写入并卸载。
十一、最终验证
启动 Waydroid:
sudo systemctl start waydroid-container.service
waydroid show-full-ui
等待 Android 就绪后,执行完整验证:
sudo waydroid shell -- sh -c '
echo "========== PROPERTIES =========="
echo "NATIVE_BRIDGE=$(getprop ro.dalvik.vm.native.bridge)"
echo "ABILIST=$(getprop ro.product.cpu.abilist)"
echo "ENABLE_EXEC=$(getprop ro.enable.native.bridge.exec)"
echo "ISA_ARM=$(getprop ro.dalvik.vm.isa.arm)"
echo "ISA_ARM64=$(getprop ro.dalvik.vm.isa.arm64)"
echo
echo "========== HOUDINI FILES =========="
for f in \
/system/bin/houdini \
/system/bin/houdini64 \
/system/lib/libhoudini.so \
/system/lib64/libhoudini.so \
/system/etc/init/houdini.rc
do
if [ -e "$f" ]; then
ls -l "$f"
else
echo "MISSING=$f"
fi
done
echo
echo "========== BINFMT HANDLERS =========="
for n in arm_exe arm_dyn arm64_exe arm64_dyn; do
echo
echo "--- $n ---"
if [ -e "/proc/sys/fs/binfmt_misc/$n" ]; then
cat "/proc/sys/fs/binfmt_misc/$n"
else
echo "MISSING=$n"
fi
done
'
属性结果:
NATIVE_BRIDGE=libhoudini.so
ABILIST=x86_64,x86,arm64-v8a,armeabi-v7a,armeabi
ENABLE_EXEC=1
ISA_ARM=x86
ISA_ARM64=x86_64
Houdini 文件全部存在:
/system/bin/houdini
/system/bin/houdini64
/system/lib/libhoudini.so
/system/lib64/libhoudini.so
/system/etc/init/houdini.rc
四个 binfmt 处理器全部启用:
arm_exe
arm_dyn
arm64_exe
arm64_dyn
32 位 ARM 使用:
interpreter /system/bin/houdini
ARM64 使用:
interpreter /system/bin/houdini64
这意味着:
- Play 商店能够识别 ARM ABI;
- Android Runtime 已加载 Native Bridge;
- Houdini 可执行文件和共享库真实存在;
- ARM ELF 和 ARM64 ELF 都会被交给 Houdini;
- 系统已经具备真正的 ARM/ARM64 转译能力。
重新打开此前无法运行的应用后,应用可以正常启动。
十二、清理安装过程中产生的文件
确认应用可以启动后,可以删除测试期产生的备份和工具,但必须保留当前正式的 Waydroid 数据目录。
需要保留:
/var/lib/waydroid
其中包含:
- 修改后的
system.img; - Houdini 本体;
- Waydroid 配置;
- Android 用户数据;
- 已安装应用。
可以删除:
- 完整 Waydroid 备份;
- 第三方安装脚本仓库;
- Python 虚拟环境;
- Houdini 下载缓存;
- 临时解压目录;
- 临时挂载目录;
- 第一次失败安装留下的无效 overlay 文件;
- 安装日志;
- 脚本源码备份。
清理示例:
(
set -euo pipefail
current=/var/lib/waydroid
backup=/var/lib/waydroid.<备份时间戳>
repo=<用户目录>/Downloads/waydroid_script
cache=<用户目录>/.cache/waydroid-script
log=<用户目录>/Downloads/waydroid-libhoudini-install.log
sudo test -d "$current"
mount_overlays=$(
sudo awk -F= '
/^[[:space:]]*mount_overlays[[:space:]]*=/ {
gsub(/[[:space:]]/, "", $2)
print $2
}
' "$current/waydroid.cfg"
)
if [ "$mount_overlays" != "False" ]; then
echo "停止清理:mount_overlays 状态不符合预期" >&2
exit 1
fi
if findmnt -rn -o TARGET |
grep -qE "^${current}/overlay(/|$)"; then
echo "停止清理:overlay 当前仍处于挂载状态" >&2
exit 1
fi
sudo rm -f -- \
"$current/overlay/system/bin/houdini" \
"$current/overlay/system/bin/houdini64" \
"$current/overlay/system/lib/libhoudini.so" \
"$current/overlay/system/lib64/libhoudini.so" \
"$current/overlay/system/etc/init/houdini.rc"
sudo rm -rf -- "$backup"
rm -rf -- "$repo"
sudo rm -rf -- "$cache"
sudo rm -rf -- /tmp/houdiniunpack
if ! findmnt -rn -o TARGET |
grep -qE '^/tmp/waydroid(/|$)'; then
sudo rm -rf -- /tmp/waydroid
fi
rm -f -- "$log"
)
第一次清理时,缓存中的压缩包是 root 属主,普通用户直接执行:
rm -rf ~/.cache/waydroid-script
会出现:
Permission denied
因此缓存目录需要使用:
sudo rm -rf -- ~/.cache/waydroid-script
最终检查已知路径全部不存在,并确认没有残留完整备份:
sudo find /var/lib -maxdepth 1 -type d \
-name 'waydroid.????????-??????' \
-printf '%p\n'
无输出表示备份已经清理。
最后验证 Houdini 本体仍然存在:
sudo waydroid shell -- sh -c '
echo "NATIVE_BRIDGE=$(getprop ro.dalvik.vm.native.bridge)"
echo "ABILIST=$(getprop ro.product.cpu.abilist)"
for f in \
/system/bin/houdini \
/system/bin/houdini64 \
/system/lib/libhoudini.so \
/system/lib64/libhoudini.so \
/system/etc/init/houdini.rc
do
[ -e "$f" ] && echo "OK=$f" || echo "MISSING=$f"
done
'
只要五个文件全部显示 OK=,删除安装脚本和缓存不会影响 ARM 应用运行。
十三、几个容易误判的关键点
1. Play 商店允许安装,不代表 ARM 转译已经生效
只修改:
ro.product.cpu.abilist
就可能让 Google Play 认为设备支持 ARM。
但如果没有:
/system/bin/houdini
/system/lib64/libhoudini.so
应用仍然无法执行。
因此必须同时检查:
- ABI 属性;
- Native Bridge;
- Houdini 文件;
- binfmt handler。
2. 安装脚本显示完成,不代表 Android 运行时能看到文件
第三方脚本可能成功下载、解压和复制文件,但目标位置未必被当前 Waydroid 挂载。
应当从 Android 容器内部检查:
sudo waydroid shell -- ls -l /system/bin/houdini
而不能只看主机上的 overlay 目录。
3. mount_overlays=False 时,不应依赖 overlay 文件
如果 Waydroid 配置为:
mount_overlays = False
而 Houdini 只存在于:
/var/lib/waydroid/overlay/system/
那么这些文件可能完全不会进入 Android 运行时。
这种情况下应直接写入 system.img,或先彻底确认 overlay 的实际挂载机制。
4. returned non-zero exit status 0 是脚本错误,不是 Waydroid 真失败
这种异常通常说明脚本对 stderr 或命令输出的判断有问题。
退出状态 0 代表命令本身成功。脚本只是因为正常提示文本而误判。
5. root 运行 Python 会污染项目目录权限
通过:
sudo ./venv/bin/python3 ...
运行脚本时,Python 可能在源码目录生成 root 属主的:
__pycache__
*.pyc
后续普通用户执行语法检查就可能遇到权限错误。
可以通过:
sudo env PYTHONDONTWRITEBYTECODE=1 ...
避免生成缓存。
十四、最终结论
在 x86-64 Arch Linux 主机上,Waydroid 默认通常只支持:
x86_64,x86
因此,大量仅提供 ARM 或 ARM64 原生库的 Android 应用会被 Google Play 判定为不兼容。
安装 Houdini 后,设备 ABI 可以扩展为:
x86_64,x86,arm64-v8a,armeabi-v7a,armeabi
但真正可用必须满足四个条件:
ro.dalvik.vm.native.bridge=libhoudini.so- Houdini 可执行文件和共享库存在于 Android 的真实
/system - ARM 与 ARM64 binfmt handler 已注册
- 应用能够实际启动,而不只是允许安装
本次故障的关键并不是“不会安装 Houdini”,而是:
第一次安装把文件放入了未生效的 overlay,导致 Play 商店兼容性过滤已经改变,但 Android 运行时仍然没有 ARM 转译器。
通过修复脚本停止容器的方式,并让脚本直接挂载和修改 system.img,最终完成了完整的 ARM/ARM64 转译支持。
安装成功后,原本显示不兼容的应用不仅可以下载,也可以正常打开。全部临时文件、脚本、缓存和备份也可以在最终验证后安全删除。