English | 简体中文
Anland runs the graphical Linux apps of a container on a rooted Android device and shows each of them as a native Android window — its own entry in the app switcher, its own Surface, its own soft-keyboard focus. There is no virtual screen and no mirroring: every app gets a real window, and Android's own compositor keeps drawing the screen.
It ships as a SukiSU/KernelSU module (anland-awl): a root daemon, a host APK, the session inside the container, and an audio bridge. The companion launcher anland-shell manages the containers and starts the apps.
Important
Branch status. main is the active development branch — the refactored 6.x
architecture. As a consequence of the rewrite it does not yet reach the feature
completeness of 5.x: if you want stability and the full desktop experience, use
the legacy branch
(5.x) instead. 5.x stays in maintenance until 6.x catches up with the 5.x
feature set (complete desktop, virtual keyboard, accessibility, …).
Maintenance is bug fixes only — and only functional bugs that affect usage;
intermittent glitches that a restart of the consumer app clears are out of
scope.
Why the rewrite? 5.x exchanged frames over a private display protocol (the "Anland Display Protocol"): every compositor — KWin, Weston, … — needed its own adaptation backend written against that protocol, and each of those had to be maintained separately. The 6.x refactor makes Wayland itself the frame-exchange protocol, so stock compositors work unmodified and only the host side needs maintaining.
A standard Wayland compositor — Weston, Sway, a desktop session — takes over a machine's display: it owns the whole screen, composites every client into one output, reads input from evdev, and clients reach it through the wayland-0 socket. On a phone that model degrades into "one fullscreen remote-desktop window". Anland keeps the protocol half of a compositor and replaces the display half with Android itself:
| Standard compositor | Anland | |
|---|---|---|
| Screen | Owns the display, composites everything into one output | Owns no display — Android's SurfaceFlinger keeps compositing |
| Windows | Internal window management inside a single output | Every xdg_toplevel becomes one real Android window (its own Activity and Surface) |
| Connection | A wayland-0 unix socket file |
No socket file — connections are fds passed over Android binder |
| Client identity | All clients are the same session user | Every client connects with its own uid; windows are owned per app |
| Input | evdev/libinput from DRM devices | Each window's Android touch/key/IME events, translated to Wayland |
| Lifecycle | A user session process | A root daemon in its own SELinux domain — not an app uid, so OEM "battery optimization" cannot freeze it |
What that design buys you:
- Linux windows are first-class Android windows. They appear in the app switcher, split-screen and behave like ordinary app windows — each with its own focus and its own IME state, candidates following the cursor.
- Hidden windows park their clients. With no Android surface attached, the daemon stops draining that window's buffers, so a minimized Linux app neither spins nor burns GPU frames.
- Foreground apps are scheduled like foreground apps. While a window is attached, its client's whole process tree is moved into Android's top-app cgroups — the Linux app in front of you is scheduled like any foreground Android app, not like a background daemon.
- Sound included. A PulseAudio bridge plays the container's audio through Android's own audio stack.
- X11 apps included. The in-container session runs a patched rootless Xwayland, so X11 apps show up as windows the same way Wayland apps do.
- Real-device fixes the stock stack lacks. A bubblewrap that survives KernelSU+SuSFS fake mount ids, and an Xwayland that does accelerated GL on Adreno GPUs (kgsl/turnip) — see
patches/. - Any Android app can join in. The
libawlclient library lets a third-party app connect over binder and host its own Wayland windows, scoped to that app's uid.
flowchart LR
subgraph LC["Linux container (Droidspaces)"]
APP["GUI apps — Wayland & X11"]
SESS["anlandx session<br/>Xwayland + session D-Bus"]
end
subgraph AND["Android"]
D["waylandbridge root daemon<br/>Wayland protocol + GPU"]
H["host APK<br/>one Activity per window"]
SF["SurfaceFlinger"]
P["PulseAudio bridge"]
end
APP --> SESS
SESS -- "wayland socket" --> D
D -- "binder: attach / control / input" --> H
H -- "per-window Surfaces" --> SF
APP -- "pulse socket" --> P
The pieces:
waylandbridge— the daemon: a single statically-linked ELF holding both the Wayland protocol side and the GPU renderer. It renders each window's frames directly into the Surface of that window's Android Activity.- Host APK (
com.anlandnext) — spawns one Activity per window, feeds it input, and provides the window list and settings UI. libawl— the client library (AAR) that third-party apps embed to host their own windows.- anlandx — the session inside the container: links the daemon's socket, runs rootless Xwayland with a small X window manager, and publishes the app environment.
- pulse — PulseAudio for Android (OpenSL ES / AAudio sinks) so container apps have sound.
- module — the SukiSU/KernelSU packaging: boot service, SELinux domain, contexts generated at flash time from the device's live system files.
The daemon composites each window through one of two backends, selected by
sc_enabled in the daemon config (/data/adb/modules/anland-awl/config.json,
default 1; applied when a window attaches):
- SC (SurfaceControl) — efficient, low overhead, scanout direct. Every wayland layer becomes a SurfaceControl sibling and SurfaceFlinger/HWC does the compositing — the daemon composites nothing, and a client's dma-buf can be scanned out zero-copy on an HWC plane. The trade-off: surface movement responds more slowly, since positions land through SF transactions. Best for content whose surfaces mostly sit still — games.
- EGL (GL renderer) — smooth movement, higher GPU usage. A per-window GPU compositor samples every layer into one buffer and presents it through eglSwapBuffers. Surface movement is smooth, at the price of a GPU composite every frame. Best for scrolling content — web browsing.
- A rooted arm64 device (SukiSU or KernelSU)
- A Droidspaces Linux container
- The host APK installed — windows attach through it, and audio requires it
-
Get the three artifacts:
build/module/anland-awl.zip,build/anland-wayland.apk,build/anlandx.tar.gz— build them (below) or take them from CI. -
Flash the zip in your root manager and reboot. The daemon starts at boot (log:
/data/local/tmp/awl_daemon.log). -
Install
anland-wayland.apk. -
Inside the container, set up the session:
tar xzf anlandx.tar.gz && bash anlandx/setupanlandx.sh -
Manage containers and launch apps with anland-shell.
Linux host with JDK 17, an Android SDK (ANDROID_HOME and JAVA_HOME set; missing pieces are auto-installed), plus meson/ninja/patch for the audio part.
git submodule update --init --recursive
make # waylandbridge + host APK + module zip + anlandx tarballCI builds every push/PR and uploads the artifacts.
- anland-shell — the launcher: container management and the app grid that drives all of this.
GPL-3.0. Bundled third-party components (libwayland, PulseAudio, bubblewrap, Xwayland, …) keep their own licenses.