Scope and Prerequisites#
Commands and output use generic examples. Kernel, image, package, and service versions affect specific behavior; check the documentation for the versions you use as you read.
Before modifying containers, images, mounts, or firewall rules, back up Android data and stop the relevant sessions. Examples involving deletion or forced stops may lose data. Manage NixOS system and user-environment configuration through Nix; the generic Linux sections using apt/pacman, direct writes to /etc, and pip installation do not apply to a NixOS host. The build, test, and switch rebuild stages are distinct from functional verification after startup.
Waydroid behavior is controlled by a set of
persist.waydroid.*system properties covering multi-window mode, display dimensions, input spoofing, kernel compatibility, and more. This chapter provides a common-property and version checklist, detailed multi-window configuration, ARM translator installation, and memfd kernel-compatibility tuning.
1. The persist Property System#
Waydroid exposes tuning interfaces through Android system properties. Common runtime-behavior properties have the persist.waydroid. prefix; startup properties also include ro., sys., and others, so that prefix does not describe every property. The persist properties follow Android's persist semantics: values survive container restarts rather than disappearing when the session ends.
1.1 Common Properties and Version Checks
| Property | Allowed Values | Default | Purpose |
|---|---|---|---|
persist.waydroid.multi_windows |
true / false |
false |
Multi-window mode: each App appears as a separate Linux window, and Apps can run concurrently |
persist.waydroid.width |
Positive integer (px) | 0 (adaptive) | Override display width: fix Android's internal rendering width |
persist.waydroid.height |
Positive integer (px) | 0 (adaptive) | Override display height: fix Android's internal rendering height |
persist.waydroid.fake_wifi |
Comma-separated package names/wildcards; 91-character limit | Empty | Spoof WiFi connection status for selected applications |
persist.waydroid.fake_touch |
Comma-separated package names/wildcards; 91-character limit | Empty | Spoof touchscreen input events for selected applications |
From the original notes: persist.waydroid.xkb_layout |
Not verified as an official interface | No guaranteed default | Not listed on the official properties page; check input layout against the actual image and compositor |
persist.waydroid.invert_colors |
true / false |
Version-dependent | Swap RGBA/BGRA color spaces for display debugging; not a dark-theme switch |
From the original notes: persist.waydroid.stretch |
Not verified as an official interface | No guaranteed default | Successfully setting a property does not prove that the image consumes it |
persist.waydroid.suspend |
true / false |
Usually true on supported kernels | Allow the container to suspend after the display times out with no active applications; check the specific version |
persist.waydroid.cursor_on_subsurface |
true / false |
true |
Cursor rendering: true uses a subsurface layer; false uses an overlay |
persist.waydroid.uevent |
true / false |
false |
Enable full uevent forwarding for Apps that need to detect hardware events |
1.2 Multi-window Mode
waydroid prop set persist.waydroid.multi_windows trueWhen set to true, Waydroid's "main interface" icon disappears from the application menu. Each Android App then behaves as a separate window on the Linux desktop: it can be resized and moved freely and appears separately in the taskbar. Multiple Apps can run simultaneously without pausing one another.
Multi-window Mode Is Especially Useful for Portrait Apps In the default fullscreen mode, portrait Apps have large blank areas on either side. In multi-window mode, you can make the window tall and narrow so the App runs at a more natural aspect ratio.
1.3 Override Display Dimensions
# 将 Android 内部渲染宽度固定为 1080px
waydroid prop set persist.waydroid.width 1080
# 将 Android 内部渲染高度固定为 1920px
waydroid prop set persist.waydroid.height 1920This setting does not resize the Waydroid window. It changes the screen resolution perceived internally by Android. It is useful for games requiring a specific resolution; for example, some mobile games need at least 720p to start. Set the property to zero or remove it to restore adaptive sizing.
1.4 Spoof WiFi and Touch Input
Some Apps, especially certain mobile games, check whether the device is connected to WiFi and supports touch input. In Waydroid, networking is provided through a bridge, which Android identifies as an "Ethernet connection"; mouse clicks are not recognized as touch events. These two spoofing properties can inject simulated hardware state on a per-application basis:
# 让 com.example.game 以为设备连接了 WiFi
waydroid prop set persist.waydroid.fake_wifi com.example.game
# 让 com.example.game 以为设备支持触屏
waydroid prop set persist.waydroid.fake_touch com.example.gameSelecting multiple applications: fake_wifi and fake_touch accept comma-separated package-name lists and support the * wildcard. The official documentation specifies a 91-character limit. Do not use newlines to enter multiple values in a single property.
1.5 Three Ways to Set Properties
| Method | Command/File | When It Takes Effect | Persistence |
|---|---|---|---|
waydroid prop set |
waydroid prop set <key> <value> |
Most settings require a user-session restart | persist properties are retained according to the image; check non-persist properties separately |
Declare startup overrides in waydroid.cfg under [properties] |
Edit /var/lib/waydroid/waydroid.cfg and apply the configuration as required by your version |
At the next container startup | Configuration is retained, but generated files may still be rebuilt |
waydroid prop get |
waydroid prop get <key> |
Query only | N/A |
Recommended approach:
- Quick tests: use
waydroid prop setand record both old and new values. Many properties require a session restart; restore the old value or set it to empty to undo the change. - Permanent configuration: manage startup overrides in waydroid.cfg under [properties] according to your current version. Avoid directly editing the generated base.prop, which upgrades may overwrite.
Avoid Race Conditions When combining runtime prop set with startup overrides in waydroid.cfg, understand the image loading order and the precedence of persist values. Avoid editing the generated base.prop; use a single configuration source and verify values after restarting.
1.6 Query All Current Waydroid Properties
# 列出所有 persist.waydroid.* 属性及其当前值
waydroid prop get persist.waydroid.multi_windows
waydroid prop get persist.waydroid.width
waydroid prop get persist.waydroid.fake_wifi
waydroid prop get persist.waydroid.fake_touch
# 进入 Android shell 后用 getprop 列出所有
sudo waydroid shell
getprop | grep waydroid2. Multiple Windows and Multitasking#
Multi-window mode is one of Waydroid's most distinctive desktop-integration features. It takes Android Apps out of the Waydroid main interface's sandbox and gives them independent windows like native Linux applications.
2.1 Enabling It and Changes in Behavior
waydroid prop set persist.waydroid.multi_windows true
sudo systemctl restart waydroid-containerBehavior changes after enabling it:
| Item | Before (Default Mode) | After (Multi-window Mode) |
|---|---|---|
| Waydroid main interface icon | Appears in the application menu | Disappears from the menu; you can still open it with waydroid show-full-ui |
| App launching | Launches inside the Waydroid window | Each App appears separately on the Linux desktop |
| App window management | Controlled by the Android window manager | Controlled by the host window manager (KWin / Mutter / Hyprland) |
| Running multiple Apps simultaneously | Switching Apps pauses the previous one | All Apps run concurrently without pausing one another |
| Window resizing | Unsupported | Supported, subject to the App's own limitations |
| Closing a window | The App moves to the background | The App does not stop; force-stop it in Android settings |
2.2 Android Freeform Windows
Alongside multi-window mode, it is recommended to enable "freeform windows" in Android's developer options:
- Open Android Settings → About phone → tap “Build number” repeatedly as prompted by Android, usually 7 times, to enable developer options
- Open Settings → System → Developer options → enable "Force freeform windows"
- Also in developer options, set "Smallest width" to a suitable dp value, such as 600dp for a tablet layout
"Freeform windows" and Waydroid multi-window mode are two separate mechanisms at different layers:
- Waydroid multi-window mode: maps each App to a separate host-side window controlled by the host window manager
- Android freeform windows: enables windowed behavior inside Android; if an App still forces fullscreen in multi-window mode, enabling this option may help
Best Practice Verify the combined behavior with the actual target application; using both is not guaranteed to be better. Waydroid multi-window mode handles host-side windows, while Android freeform windows make the App's internal behavior compatible with windowing.
2.3 Multitasking Behavior
In multi-window mode, Android Apps behave more like traditional Linux applications:
- Background Apps keep running: idle gaming, music playback, and file downloads do not pause when you switch to another App
- Resource use: GPU rendering continues for every visible App, so opening more Apps consumes additional CPU/GPU resources
- Closing a window does not exit the App: clicking Close only hides the window; the Android process continues in the background. Force-stop it through Android settings or with
sudo waydroid shell am force-stop com.example.app
flowchart LR
subgraph Host["Linux 宿主"]
WM["窗口管理器<br/>KWin / Mutter / Hyprland"]
TASKBAR["任务栏"]
end
subgraph Android["Android 容器内"]
AM["Activity Manager"]
APP1["App A 进程"]
APP2["App B 进程"]
APP3["App C 进程"]
end
WM <-->|"Wayland protocol<br/>独立 wl_surface"| APP1
WM <-->|"独立 wl_surface"| APP2
WM <-->|"独立 wl_surface"| APP3
TASKBAR -->|"显示每个 App 图标"| WM
AM -->|"管理生命周期"| APP1
AM -->|"管理生命周期"| APP2
AM -->|"管理生命周期"| APP32.4 Fullscreen Apps and Virtual Desktop Strategies
Some Apps, especially games, still force fullscreen after launch even when multi-window mode is enabled. The following strategies can help:
Toggle with F11: Waydroid provides F11 to switch between fullscreen and windowed modes. Press F11 once to leave fullscreen and again to restore it.
Requirements for F11 Toggling Some games take exclusive keyboard input in fullscreen mode, preventing Waydroid from capturing F11. In that case, switch to another window before pressing F11, or use Waydroid's toolbar.
Virtual desktop strategy: use your host desktop environment's virtual desktops/workspaces:
| Desktop Environment | Virtual Desktop Support | Controls |
|---|---|---|
| GNOME | Dynamic workspaces | Switch with Super + PageUp/PageDown |
| KDE Plasma | Virtual desktops | Switch with Ctrl + F1/F2/F3...; the number of desktops is configurable |
| Hyprland | Workspace 1-10 | Switch with Super + 1-0 |
| Sway | Workspace | Switch with Super + 数字 |
Workflow: place fullscreen-forcing App A on workspace 1 → switch to workspace 2 → launch App B → the two Apps occupy separate workspaces and run concurrently without conflict.
2.5 Multiple-instance Limits and Alternatives
Waydroid does not support running multiple container instances. Upstream tools are generally designed around a single container/session state; multiple instances are not a standard supported interface. BinderFS can create multiple device instances, so Binder itself should not be described as inherently limited to a single instance.
For scenarios requiring two accounts at once, the following alternatives are available:
| Approach | Mechanism | Complexity |
|---|---|---|
| Shelter | Use Android's Work Profile mechanism to create an isolated work profile inside the same Waydroid instance | Low |
| Island | A Work Profile approach similar to Shelter | Low |
| Virtual Master | A virtual-space App inside Android | Medium |
| Install another emulator | Run a separate Android emulator, such as Android Studio AVD, alongside Waydroid | High |
Recommendation: install Shelter, available on F-Droid, and create a Work Profile inside Waydroid to keep two accounts for the same App online simultaneously. Apps in the Work Profile also appear as separate windows in multi-window mode.
3. ARM Translation (libhoudini / libndk)#
3.1 Why ARM Translation Is Needed
The vast majority of Android devices use the ARM architecture, so many Apps, especially mobile games, ship native libraries compiled only for ARM (arm64-v8a / armeabi-v7a). Waydroid runs on an x86_64 host, and its LineageOS-based Android system is itself an x86_64 build. When an App tries to load a native ARM .so library, the CPU cannot execute ARM instructions directly, resulting in a crash or the App getting stuck on its launch screen.
An ARM translator dynamically translates ARM instructions into x86_64 instructions so that an x86_64 CPU can execute native ARM code.
3.2 Choosing Between libhoudini and libndk
| Aspect | libhoudini | libndk |
|---|---|---|
| Developer | Intel | |
| Technical Foundation | Intel BT (Binary Translation) | Google's ARM → x86 translation layer |
| CPU Optimization | Optimized for instruction scheduling on Intel CPUs | Compatibility varies with translator version, image, and application; CPU brand does not guarantee it |
| Compatibility | Test with the target application | Test with the target application |
| License | Proprietary software (closed-source Intel code) | Proprietary software (closed-source Google code) |
| Installation Source | Extracted from Google's ARM system images | Extracted from NDK system images |
| Package Size | Larger (~100MB+) | Smaller (~60MB) |
Recommendations by CPU Vendor Intel CPU → prefer libhoudini (Intel CPU microarchitecture optimizations and more efficient translation) AMD CPU → prefer libndk (Google's translation layer is friendlier to AMD instruction scheduling; houdini may be unstable on AMD CPUs) These preferences reflect community experience, not verified vendor performance optimizations. Compare them using the same image and APK.
3.3 Install an ARM Translator (Generic Linux)
Use the community-maintained waydroid_script tool:
# 安装依赖
sudo apt install lzip python3-venv
# 克隆仓库
git clone https://github.com/casualsnek/waydroid_script
cd waydroid_script
# 创建虚拟环境
python3 -m venv venv
venv/bin/pip install -r requirements.txt
# 运行主脚本
sudo venv/bin/python3 main.pyInteractive menu steps:
- Select the Android version, which must exactly match your installed Waydroid image; it is usually Android 11 or 13
- Select the operation:
Install - Select the translator to install:
libhoudiniorlibndk. Use Space to select; choose only one - Confirm installation and wait for downloading and deployment to finish
- Restart the Waydroid container:
sudo systemctl restart waydroid-container
3.4 Install Through NUR on NixOS
# NixOS 用户通过 NUR 获取 waydroid-script
# 在 flake.nix 的 inputs 中添加:
{
inputs.nur.url = "github:nix-community/NUR";
}
# 然后在 packages 中添加:
# nur.repos.ataraxiasjel.waydroid-scriptThe Translator Is Tightly Coupled to the Waydroid Version The system libraries used by an ARM translator are closely tied to the Android version. After updating the Waydroid image with
waydroid upgrade, you may need to reinstall or adapt the translator; check the installer and image versions first. A new system image may contain different framework-library versions that are incompatible with the old translator, causing all Apps requiring ARM translation to crash again.
3.5 Verify That ARM Translation Works
Method 1: Use the Package Manager App
- Install Package Manager, an open-source APK analysis tool, in Waydroid
- Open Package Manager and select an App installed from an ARM APK
- Open the App's "Libraries" or "Native libs" tab
- If the App runs normally but has only a
lib/arm64-v8adirectory and nolib/x86_64directory, the translator is working
Method 2: Command-line Checks
# 进入 Android shell
sudo waydroid shell
# 检查 houdini 进程是否存在
ps -A | grep houdini
# 检查关键库文件
ls -la /system/lib64/libhoudini.so # libhoudini
ls -la /system/lib64/arm/ # libndk 或 houdini 的 ARM 库目录
# 检查属性值
getprop ro.product.cpu.abilist
# 成功安装后应同时包含 x86_64 和 arm64-v8aMethod 3: Test an Actual Application
Download an application known to have only an ARM version, such as certain Japanese mobile games, from APKMirror. Install it and try launching it. If it opens normally without crashing, the translator is working correctly.
3.6 Common Problems and Troubleshooting
| Problem | Possible Cause | Solution |
|---|---|---|
| The App still crashes after installing a translator | The App uses special ARM64-only instructions | Try switching to the other translator (houdini ↔ ndk) |
| Installation reports "version mismatch" | The waydroid_script version does not match the system image's Android version | Update waydroid_script to the latest version and confirm the Android version number |
| A particular game crashes, such as Blue Archive | A known compatibility issue requires an App-specific patch | Look for community patches, such as the Blue Archive gist |
| Extremely poor performance with translation | The CPU lacks certain SIMD instructions, or translation is inefficient | Try the other translator; check whether the CPU supports SSSE3/SSE4.1 |
| Installing both translators causes system instability | Translation-library conflicts make runtime behavior unpredictable | Use waydroid_script's Remove function to uninstall the current translator, then install the other one |
Do Not Install Both Translators at Once libhoudini and libndk install different ARM→x86 translation libraries. Having both present makes dynamic-linker (
linker64) behavior uncertain: it may select a translation library unpredictably, or even load both and cause symbol conflicts. Remove the old one before you Install the new one.
3.7 App-level Debugging: Check Native-library Architectures
# 进入 Android shell
sudo waydroid shell
# 查看特定 App 的原生库路径(替换 com.example.app 为实际包名)
ls -la /data/app/~~*/com.example.app-*/lib/
# 输出示例:
# arm64/ -> App 只有 ARM64 库(需要转译器)
# x86_64/ -> App 有 x86_64 库(无需转译,原生性能更好)
# arm64/ + x86_64/ -> App 同时包含两种库(优先使用 x86_64,ARM 库仅作备份)If an App contains both x86_64 and arm64 libraries, Android prefers the x86_64 version, so ARM translation is not used even if a translator is installed. The scenario that truly benefits is an App with only arm64 or armeabi-v7a directories and no x86_64 directory.
4. memfd and Kernel Compatibility#
4.1 Why memfd Is Needed
Binder IPC and ashmem shared memory are different mechanisms. Android's shared-memory userspace previously used ashmem. Supported images can replace that shared-memory path with memfd, but Binder is still essential. However:
- **The old mainline staging implementation of ashmem was removed in Linux 5.18 in favor of standard Linux
memfd(anonymous memory file descriptor) memfdis a native Linux kernel feature and requires no additional module- Waydroid 1.2.1+ introduced support for
sys.use_memfd=trueto automatically use memfd instead of ashmem
4.2 Configure memfd
# 检查当前内核版本
uname -r
# 编辑 waydroid_base.prop
sudo vim /var/lib/waydroid/waydroid_base.propAdd or modify the following in the file:
# 镜像/Waydroid 支持时使用 memfd;不是 Linux 5.18 才引入
sys.use_memfd=trueRestart the container after changing the setting:
sudo systemctl restart waydroid-container4.3 Kernel and Component Compatibility Matrix
| Component | Minimum Version | Recommended Version | Notes |
|---|---|---|---|
| Linux Kernel | Check distribution and package requirements | A currently maintained version | binderfs was introduced in 5.0 and memfd_create in 3.17; Android integration is a separate requirement |
| LXC | 4.0 | 5.0+ | 4.0 supports basic Android container functionality; 5.0+ has better cgroup v2 compatibility |
| Waydroid | Follow distribution dependencies and image requirements | A currently maintained version | The precise version boundaries in the source notes are unverified; check the relevant release and image |
| python3 | 3.8 | 3.11+ | A waydroid_script dependency; does not affect the container itself |
| binder module | anbox-binder / binder_linux-dkms | The Binder implementation actually supplied by the current kernel | See below |
4.4 Check the Binder Kernel Module
Waydroid depends on Binder for Android IPC. Check the current system's Binder status:
# 检查 binder 设备是否存在
ls -la /dev/binder /dev/binderfs
# 检查 binder 模块是否已加载
lsmod | grep binder
# 检查 binderfs 是否挂载
mount | grep binderThree Binder implementation approaches:
| Approach | Applicable Kernels | Characteristics |
|---|---|---|
binder_linux-dkms |
All series | For supported kernels lacking Binder; must be rebuilt after every update, with no guarantee of compatibility across all series |
anbox-binder |
Legacy node/module approach, depending on the actual implementation | Do not treat 5.7 or Android 9/10 as strict compatibility boundaries without verification |
rust_binder |
Depends on the kernel/distribution | Do not infer built-in support from 6.7 alone; check the configuration and modules |
flowchart TD
A[检查运行内核与模块] --> B{Binder 节点可用?}
B -- 是 --> C[检查 BinderFS 和权限]
B -- 否 --> D[选择发行版支持的内核或模块]
C --> E[检查镜像共享内存路径]
E --> F[启动容器并看日志]4.5 Process Limits and Session Hangs
Do not automatically attribute long hangs to delayed PID reclamation. First check Android/LXC logs, OOM events, memory pressure, cgroup pids.max, and actual task counts:
cat /proc/sys/kernel/pid_max
cat /proc/pressure/memory
ps -eLf | wc -l
journalctl -u waydroid-container -bThe original notes' value of 65535 is not a universal optimization: if the current value is 4194304, it lowers the limit instead. Adjust only after confirming that an actual limit has been exhausted, and base the change on evidence. On NixOS, manage this declaratively through boot.kernel.sysctl.
4.6 Supplement: Shared Directories and File Transfer
Although not a kernel setting, sharing files between the Linux host and Waydroid is a common tuning need.
Approach 1: bind mount
# 在宿主上创建共享目录
mkdir -p ~/WaydroidShared
# 挂载到 Android 内部存储
mkdir -p ~/.local/share/waydroid/data/media/0/WaydroidShared
# 确认目标权限与敏感数据范围,再挂载专用目录
sudo mount --bind ~/WaydroidShared \
~/.local/share/waydroid/data/media/0/WaydroidSharedAfter mounting, files placed in ~/WaydroidShared/ on the Linux host immediately appear in the WaydroidShared directory in Android's file manager.
Bind Mounts Must Be Recreated Manually After a Reboot A
mount --binddoes not survive a host reboot. To make it persistent, add it to/etc/fstabor create a systemd mount unit.
Approach 2: ADB Push
# 检查镜像的 ADB 设置与授权,不假定一定已开放
# 查看 Waydroid 的 IP 地址(Android 设置 → 关于手机 → IP 地址)
adb connect <EXAMPLE_IP>:5555
# 推送文件
adb push local_file.apk /sdcard/Download/
# 拉取文件
adb pull /sdcard/Download/file.txt .Approach 3: Per-app Volume Control
Audio from all Android Apps in Waydroid is mixed into one PulseAudio / PipeWire stream. To adjust the volume of individual Apps:
- pavucontrol (PulseAudio GUI): adjust each audio stream separately in the Playback tab
- Sound Quick Settings (Android App): adjust volume per App inside Android
- ShizuTools MixedAudio (Android App / Xposed): more fine-grained audio-source separation
5. Tuning Quick Reference#
The following complete one-time configuration workflow is intended for optimization after an initial Waydroid deployment:
#!/bin/bash
# Waydroid 调优一次性脚本
# === 1. 内核参数 ===
# 不默认修改 pid_max;只有确认耗尽才调整
# NixOS 中按证据使用 boot.kernel.sysctl,不直接写受管理配置
# === 2. memfd ===
# 在 waydroid.cfg 的 [properties] 管理 sys.use_memfd=true(按镜像支持),避免重复追加生成文件
# === 3. 多窗口 ===
waydroid prop set persist.waydroid.multi_windows true
# === 4. 显示尺寸(可选,根据需求调整)===
# waydroid prop set persist.waydroid.width 1440
# waydroid prop set persist.waydroid.height 900
# === 5. 重启容器使配置生效 ===
sudo systemctl restart waydroid-container
echo "Waydroid 调优完成。"
echo "如需安装 ARM 转译器,请手动运行 waydroid_script。"Verification Sources#
Official Waydroid Installation Instructions.
Official Waydroid Command-line Documentation.
Community Translator Installation Tool.
References & further reading
- Official Waydroid Installation Instructions ↗
- Official Waydroid Command-line Documentation ↗
- Waydroid Upstream Source ↗
- Official Property Options ↗
- Community Translator Installation Tool ↗
- memfd_create(2) ↗
Adapted from technical notes. Examples use generic names.
Continue along a reading path
Waydroid
- 01Waydroid architecture: LXC, Binder, and session bridging
- 02Waydroid installation and basic setup
- 03Waydroid integration on NixOS
- 04Waydroid GPU detection and hardware acceleration
- 05Waydroid bridges, NAT, and security boundaries
- 06Waydroid properties, multi-window mode, and ARM translation
- 07Waydroid troubleshooting and FAQ
- 08Waydroid tips: windows, translation, and shared directories