OFFSET偏移
← All articlesOFFSET / FIELD NOTESNO. 142

Waydroid architecture: LXC, Binder, and session bridging

Understand Waydroid through container management, user sessions, and Android services.

English translation · Code and commands follow the Chinese original.

IN THESE READING PATHSWaydroid ↗
About 15 min readStart reading

Scope and prerequisites#

The commands and outputs use generic examples. Kernel, image, package, and service versions affect the exact 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 that delete data or force a stop can cause data loss. On NixOS, manage 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 stages of a rebuild are distinct from functional verification after startup.

Waydroid is neither an emulator nor a virtual machine: it does not boot a separate kernel. Its architecture centers on three principles: "direct access to the host kernel + Linux namespace isolation + Binder IPC bridging." It runs a complete Android userspace, LineageOS, as an LXC container on a standard Linux kernel.


Core architecture overview: a three-tier privilege-separation model#

Waydroid divides Android containerization into three privilege tiers. Each tier runs in a different user context, with strictly defined IPC over D-Bus.

graph TB
    subgraph Host["Linux 宿主 (root/用户空间)"]
        KERNEL["Linux Kernel<br/>binder + ashmem + memfd + PSI"]
        DBUS_SYS["D-Bus System Bus<br/>(root 权限域)"]
        DBUS_SES["D-Bus Session Bus<br/>(用户权限域)"]
    end

    subgraph Waydroid["Waydroid 管理层 (Python)"]
        CM["ContainerManager<br/>id.waydro.Container<br/>root 执行 / system bus"]
        SM["SessionManager<br/>id.waydro.Session<br/>用户执行 / session bus"]
        PK["授权策略<br/>按实际 D-Bus/发行版核对"]
    end

    subgraph Container["LXC Android 容器"]
        INIT["/init (Android init)<br/>PID 1 inside container"]
        HAL["HAL 层服务<br/>GPU / Audio / Input / Sensors"]
        APP["Android Apps<br/>LineageOS A13 (Android 13)"]
    end

    subgraph Services["后台桥接服务"]
        UM["user_manager"]
        CM2["clipboard_manager"]
        NM["notification_manager"]
    end

    KERNEL -->|"binder memfd"| Container
    DBUS_SYS -->|"root 鉴权"| CM
    DBUS_SES -->|"用户会话"| SM
    PK -->|"授权检查"| CM
    CM -->|".Start() D-Bus 调用"| Container
    SM -->|"Wayland socket<br/>PulseAudio pipe"| Container
    Services -->|"gbinder Python/C bridge"| HAL

    style Host fill:#1a1a2e,stroke:#0f3460,color:#e0e0e0
    style Waydroid fill:#0f3460,stroke:#16213e,color:#e0e0e0
    style Container fill:#533483,stroke:#3b2665,color:#e0e0e0
    style Services fill:#1b4332,stroke:#2d6a4f,color:#e0e0e0

The three-tier privilege-separation design

Tier Component Execution privileges D-Bus bus Responsibility
Tier 1 ContainerManager root System Bus Container lifecycle: create, start, stop, freeze/unfreeze
Tier 2 SessionManager Regular user Session Bus Session management: configure Wayland/PulseAudio mount points and manage Android application launches
Tier 3 Background services (*_manager) Regular user Session Bus Data bridging: user management, clipboard synchronization, and notification forwarding

Why separate privileges? ContainerManager needs root privileges to operate LXC: configuring network namespaces, mounting filesystems, and managing cgroups. Putting all the logic in a root process would expand the attack surface. Waydroid isolates the "minimum set of operations requiring root" in ContainerManager and runs all other session logic as a regular user.

The dual D-Bus design

Waydroid uses two D-Bus buses to isolate privilege domains:

sequenceDiagram
    participant User as 普通用户 (UID 1000)
    participant SM as SessionManager<br/>(session bus)
    participant PK as PolicyKit
    participant CM as ContainerManager<br/>(system bus)
    participant LXC as LXC Container

    User->>SM: waydroid session start
    SM->>CM: D-Bus: id.waydro.Container.Start()
    Note over SM,CM: 跨总线调用 (system bus)
    CM->>PK: 检查 org.waydro.container.start
    PK-->>CM: 授权通过
    CM->>LXC: lxc-start -F -n waydroid -- /init
    LXC-->>CM: 容器运行中
    CM-->>SM: 返回状态
    SM-->>User: 会话就绪
  • System Bus (id.waydro.Container): Registered in /usr/share/dbus-1/system-services/, activated on demand by the D-Bus daemon, and run as root
  • Session Bus (id.waydro.Session): Managed by the user session's D-Bus daemon and run as a regular user

D-Bus security boundaries Access to the system bus depends on D-Bus policy, the service implementation, and the distribution's authorization integration. Do not assume that all calls go through PolicyKit; the action/path listed in the original notes was not confirmed in the upstream files reviewed.


The LXC container model#

Waydroid's central insight is that Android userspace needs a set of Linux kernel APIs, not virtual hardware. LXC provides isolation through namespaces while allowing the container to use the host kernel directly.

Why not boot a separate kernel?

对比三种方案:

硬件辅助虚拟化 (QEMU/KVM):
  宿主内核 → KVM → 独立内核 → Android 用户空间
  使用独立客体内核,性能和 GPU 集成取决于虚拟化配置

全虚拟化 (VirtualBox):
  宿主内核 → Hypervisor → 独立内核 → Android 用户空间
  同上,且需要内核模块

LXC 容器 (Waydroid):
  宿主内核 ← lxc-start ← /init (Android init) ← Android 用户空间
  不启动独立客体内核,直接复用宿主驱动 (GPU/dri/renderD128)

The Linux namespace matrix

Waydroid uses the following combination of namespaces to build the container's isolated environment:

Namespace Purpose Specific use in Waydroid
User UID/GID mapping Do not assume subordinate UID mappings exist; inspect the actual lxc.idmap and runtime credentials. Waydroid commonly uses a privileged container
PID Process-tree isolation Inside the container, /init sees itself as PID 1, completely isolated from the host process space
UTS Hostname isolation The container can have its own hostname without affecting the host
Net Network-stack isolation Creates a separate waydroid0 bridge; Android has its own network interfaces and routing table
Mount Filesystem isolation /system and /vendor are mounted with overlayfs; /data comes from the user-data directory
IPC Interprocess-communication isolation Android's System V IPC does not leak into the host

The container root filesystem

# Waydroid 根文件系统位置
/var/lib/waydroid/
├── rootfs/           # 容器内看到的 /
│   ├── system/       # LineageOS A13 系统镜像 (system.img, 只读)
│   ├── vendor/       # 厂商镜像 (vendor.img, 只读)
│   ├── data/         # 用户数据 (来自 ~/.local/share/waydroid/data)
│   └── ...
├── images/
│   ├── system.img    # → 挂载到 rootfs/system (只读, lower layer)
│   └── vendor.img    # → 挂载到 rootfs/vendor (只读, lower layer)
├── waydroid.prop     # Android 系统属性覆盖
└── lxc/              # 动态生成的 LXC 配置文件

The OverlayFS layering model system.img and vendor.img are first mounted using their actual filesystems, then overlaid according to the layout. Writable overlays for system/vendor are separate layers from Android's /data. The user-data directory is mounted at /data; it is not a general-purpose upperdir for the system image. This means the system images themselves are not modified, and upgrades only require replacing the .img files.

Dynamically generated LXC configuration

Unlike most LXC containers, which use static configuration files, Waydroid dynamically generates its LXC configuration at every startup. The core generation logic is in tools/helpers/lxc.py:

# 配置生成管道(简化)
def set_lxc_config():
    # 1. 复制基础配置片段
    copy_base_snippets("data/configs/config_*")

    # 2. 根据 LXC 版本适配
    apply_lxc_version_specific(lxc_version)

    # 3. 设置 AppArmor / Seccomp profile
    set_lxc_config_item("lxc.apparmor.profile", "unconfined")  # 示例,以实际模板为准
    set_lxc_config_item("lxc.seccomp.profile", "/var/lib/waydroid/waydroid.seccomp")

    # 4. 动态生成 binder 节点挂载条目
    generate_nodes_lxc_config()    # binder, vndbinder, hwbinder

    # 5. 动态生成会话相关挂载条目
    generate_session_lxc_config()  # Wayland socket, PulseAudio, data dir

Why generate it dynamically?

  1. LXC version compatibility: Configuration-directive syntax differs between LXC versions, such as lxc.cgroup versus lxc.cgroup2, requiring detection and adaptation at startup
  2. Variable Binder-node paths: /dev/binder may use legacy binder or binderfs; paths and naming rules vary with the kernel version
  3. Session paths depend on runtime state: The Wayland socket path, $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY, and the PulseAudio socket path are determined by the user session

Binder IPC in depth#

Binder is Android's core IPC mechanism and is critical to Waydroid working correctly. Without Binder, Android system services cannot communicate.

Why is Binder the critical communication layer?

Android's system architecture is built on Binder IPC. On a physical device, the Binder driver transfers data between processes in kernel space, avoiding the overhead of repeated userspace/kernel-space copies in traditional IPC mechanisms such as pipes and sockets. In Waydroid, the Android container runs on the same host kernel, which provides the Binder driver. Android processes inside the container use the host kernel's binder device nodes directly.

graph LR
    subgraph Host["宿主内核空间"]
        BINDER["/dev/binder<br/>binder 驱动"]
        VNDBINDER["/dev/vndbinder<br/>vendor binder"]
        HWBINDER["/dev/hwbinder<br/>hardware binder"]
    end

    subgraph Container["Android 容器"]
        SM["servicemanager"]
        HAL["HAL 服务"]
        APP["应用进程"]
    end

    SM -->|"注册/查询服务"| BINDER
    HAL -->|"vendor HAL 通信"| VNDBINDER
    APP -->|"应用层 IPC"| BINDER
    HAL -->|"硬件抽象层"| HWBINDER

    style Host fill:#1a1a2e,stroke:#0f3460,color:#e0e0e0
    style Container fill:#533483,stroke:#3b2665,color:#e0e0e0

The three Binder nodes

Node Purpose Android counterpart
/dev/binder General-purpose IPC at the framework layer; all system services registered with servicemanager communicate through this node frameworks/native/libs/binder
/dev/vndbinder Isolated IPC channel between vendor HALs and the framework (Project Treble) /dev/vndbinder in Android 8+
/dev/hwbinder IPC within hardware HALs, specifically for communication over HIDL interfaces /dev/hwbinder in Android 8+

BinderFS vs. traditional Binder

传统固定 Binder 节点(与内核版本无严格分界):
  /dev/binder          ← 单一设备节点,所有 binder 通信共用
  权限:由实际 udev/发行版/LXC 设备配置管理,不向整个 binderfs 递归放权
  缺点:无法动态分配 binder 节点,所有 IPC 共享一个设备

BinderFS(自 Linux 5.0 引入):
  /dev/binderfs/       ← binder 文件系统
  ├── binder           ← 通过 ioctl(BINDER_CTL_ADD) 动态分配
  ├── vndbinder
  └── hwbinder
  优势:支持多个独立的 binder 实例,安全隔离更好

The following is illustrative pseudocode for preparing Binder nodes. The actual drivers.py detects existing nodes, allocates them, and creates links as needed. Do not execute this code or apply identical permissions to every binderfs node:

def setupBinderNodes():
    # 1. 探测宿主内核是否有 binder 模块
    if not probe_binder_modules():
        raise BinderError("binder kernel module not found")

    # 2. 如果内核支持 binderfs,优先使用
    if binderfs_available():
        mount_binderfs("/dev/binderfs")
        # 通过 ioctl 分配三个节点
        ioctl(binderfs_fd, BINDER_CTL_ADD, "binder")
        ioctl(binderfs_fd, BINDER_CTL_ADD, "vndbinder")
        ioctl(binderfs_fd, BINDER_CTL_ADD, "hwbinder")
    else:
        # 3. 回退到传统 /dev/binder 节点
        use_legacy_binder_node("/dev/binder")

    # 4. 核对实际设备规则与最小访问范围;不对全部节点 chmod 666

Common Binder failures If the host kernel was not compiled with a usable Binder implementation, either built in or as a module, the Waydroid container will crash. The first thing Android init does after starting is open /dev/binder to communicate with servicemanager. If binder is unavailable, the entire Android framework cannot initialize.

Automatic Binder protocol-version detection

Android's Binder protocol has undergone several incompatible changes across API levels. Waydroid needs to select the correct protocol version for the Android image running in the container.

Protocol-version detection logic (tools/helpers/protocol.py):

API Level Android version Binder protocol Explanation
< 28 Android 8.x and earlier aidl Original AIDL protocol
28-29 Android 9-10 aidl2 Introduced protocol-version negotiation
30 Android 11 aidl3 Structured binder transport
31-32 Android 12-12L aidl4 Extended transport format
33-34 Android 13-14 aidl3 Falls back to aidl3 (Android 13 deprecated aidl4)

Note that aidl/aidl2 and the other names here are protocol-selection names used by libgbinder, not version numbers indicating a simple replacement of Android's kernel Binder. The service-manager protocol also changes for API 35/36; check the current protocol.py.

gbinder: the Python/C bridge library

gbinder is one of the most important glue libraries in the Waydroid ecosystem. It is a Python wrapper that calls the underlying libgbinder.so C library through CFFI, allowing Python code to communicate directly with Android Binder services:

# gbinder 使用示例:获取 Android 内的服务
import gbinder

# 连接到 servicemanager
sm = gbinder.ServiceManager("/dev/binder")

# 获取 IUserMonitor 服务
user_monitor = sm.get_service("user_monitor")
resp, status = user_monitor.transact(code=1, parcel=data)

# 获取 INotifications 服务
notifications = sm.get_service("notifications")

Waydroid's three background services, user_manager, clipboard_manager, and notification_manager, are all Python programs that use gbinder to communicate with Android system services inside the container.

How Binder nodes enter the container

Binder nodes are bind-mounted into the container through LXC's lxc.mount.entry directive. generate_nodes_lxc_config() creates these entries dynamically:

# 传统 binder 模式生成的 LXC 配置片段
lxc.mount.entry = /dev/binder dev/binder none bind,create=file 0 0
lxc.mount.entry = /dev/vndbinder dev/vndbinder none bind,create=file 0 0
lxc.mount.entry = /dev/hwbinder dev/hwbinder none bind,create=file 0 0

# binderfs 模式
lxc.mount.entry = /dev/binderfs/binder dev/binder none bind,create=file 0 0
lxc.mount.entry = /dev/binderfs/vndbinder dev/vndbinder none bind,create=file 0 0
lxc.mount.entry = /dev/binderfs/hwbinder dev/hwbinder none bind,create=file 0 0

The startup sequence in detail#

From the moment a user runs waydroid session start until the Android desktop is ready, Waydroid goes through a carefully coordinated, multistage startup process.

The complete startup sequence

sequenceDiagram
    participant CLI as waydroid CLI
    participant SM as SessionManager
    participant CM as ContainerManager
    participant DRV as Driver Probe
    participant LXC as LXC Config Gen
    participant CT as LXC Container
    participant INIT as Android /init

    CLI->>SM: waydroid session start

    Note over SM: Step 1: D-Bus 会话验证
    SM->>SM: 检查 D-Bus session bus 可用性
    SM->>SM: 验证 $WAYLAND_DISPLAY 已设置

    Note over SM,CM: Step 2: ContainerManager.Start()
    SM->>CM: D-Bus: id.waydro.Container.Start()
    CM->>CM: PolicyKit 权限检查

    Note over CM,DRV: Step 3: Binder/ashmem 驱动探测
    CM->>DRV: probe_binder_modules()
    CM->>DRV: setupBinderNodes()
    CM->>DRV: probe_ashmem()

    Note over CM,LXC: Step 4: LXC 配置生成
    CM->>LXC: set_lxc_config()
    LXC->>LXC: copy base configs
    LXC->>LXC: LXC version adaptation
    LXC->>LXC: generate_nodes_lxc_config()
    LXC->>LXC: generate_session_lxc_config()

    Note over CM: Step 5: OverlayFS 挂载
    CM->>CM: mount system.img + vendor.img
    CM->>CM: mount user data overlay

    Note over CM,CT: Step 6: lxc-start
    CM->>CT: lxc-start -F -n waydroid -- /init

    Note over CT,INIT: Step 7: Android init 启动 HAL
    INIT->>INIT: init.rc 解析
    INIT->>INIT: 启动 servicemanager
    INIT->>INIT: 启动 surfaceflinger
    INIT->>INIT: 启动 audioserver, cameraserver...

    Note over SM: Step 8: 后台服务启动
    CT-->>SM: 容器就绪信号
    SM->>SM: 启动 user_manager (gbinder)
    SM->>SM: 启动 clipboard_manager (gbinder)
    SM->>SM: 启动 notification_manager (gbinder)

    SM-->>CLI: 会话就绪

A closer look at each step

The Python functions in the source notes and the workflow below are illustrative pseudocode. They are neither a line-by-line account of the upstream implementation nor directly runnable programs.

Step 1: D-Bus session validation

# SessionManager 首先检查:
# - D-Bus session bus socket 是否存在 ($DBUS_SESSION_BUS_ADDRESS)
# - Wayland display 是否设置 ($WAYLAND_DISPLAY)
# - 必要目录是否存在 (~/.local/share/waydroid)

Step 2: ContainerManager.Start The first communication across the D-Bus buses. If ContainerManager is not running, the system bus daemon activates it automatically on demand through /usr/share/dbus-1/system-services/id.waydro.Container.service.

Step 3: Binder/ashmem driver detection Checks whether the host kernel has loaded the required Android drivers. If the kernel lacks the binder module and modprobe binder_linux fails, startup stops at this step with an explicit error message.

Step 4: LXC configuration generation Dynamically generates the complete LXC configuration, including:

  • Base configuration: networking, cgroups, and capabilities
  • Binder-node bind-mount entries
  • Session-related mounts: Wayland, PulseAudio, and the data directory
  • Security profiles: AppArmor and Seccomp

Step 5: Image mounting and OverlayFS

A .img file cannot be used directly as an overlay lowerdir. Mount the image at a directory first, then create the overlay with separate upperdir/workdir directories. The following illustrates the mechanism; verify paths against the actual generated configuration, and do not manually overwrite mounts used by a running Waydroid instance:

# 概念示例,不是 Waydroid 的精确启动命令
mount -o loop,ro /path/to/system.img /path/to/lower
mount -t overlay overlay \
  -o lowerdir=/path/to/lower,upperdir=/path/to/upper,workdir=/path/to/work \
  /path/to/merged

Android's /data is a separate data mount. OTA upgrades and data migrations still require backups and version-compatibility checks.

Step 6: lxc-start The core command that starts the container:

lxc-start -F -n waydroid -- /init
# -F: foreground 模式(Waydroid 需要持续监控容器状态)
# -n waydroid: 容器名称
# -- /init: Android init 作为容器 PID 1

Why /init rather than /sbin/init? Android's init program is at /init, rather than the traditional Linux path of /sbin/init. This is a characteristic feature of Android's filesystem design. Android init reads init.rc and init.waydroid.rc, then starts all the core Android services, including servicemanager, surfaceflinger, and audioserver, in order.

Step 7: Android init starts the HAL services The container's /init starts services in the order defined by init.rc:

  1. servicemanager — Binder service registry
  2. hwservicemanager — HIDL hardware-service management
  3. vndservicemanager — vendor-service management
  4. surfaceflinger — compositing and display
  5. audioserver, cameraserver, and mediaserver — multimedia HALs

Step 8: Background-service startup Once SessionManager confirms that the container is ready, it starts the three Python background services in sequence:

# 后台服务启动策略(session_manager.py 内逻辑)
services = [
    ("user_manager",        UserManager),
    ("clipboard_manager",   ClipboardManager),
    ("notification_manager", NotificationManager),
]

for name, cls in services:
    service = cls()
    service.connect_to_binder()     # 通过 gbinder 连接 Android 服务
    service.register_dbus()         # 在 session bus 上注册 D-Bus 接口
    service.run()                   # 进入事件循环

Optimizing startup time Even with an SSD, binderfs, and preloaded kernel modules, cold-start time depends on the image, device, caches, and service state. Record container startup, Android-ready, and UI-ready times separately; a fixed number of seconds cannot be guaranteed.


The security model#

When analyzing security boundaries, check namespaces, devices, system calls, capabilities, and authorization rules separately. The diagram below does not promise that five layers of protection are enabled by default. Waydroid shares the host kernel and should not be treated as a strong isolation boundary for untrusted applications.

Five layers of security protection

graph TB
    subgraph 安全层
        L1["第 1 层:Linux Namespaces<br/>PID/Net/Mount/UTS/IPC 视图隔离"]
        L2["第 2 层:AppArmor 检查<br/>所查上游默认 unconfined"]
        L3["第 3 层:PolicyKit 鉴权<br/>需核对 D-Bus 与授权规则"]
        L4["第 4 层:Seccomp Filter<br/>系统调用白名单/黑名单过滤"]
        L5["第 5 层:Capabilities 检查<br/>所查上游权限较宽"]
    end

    L1 --> L2 --> L3 --> L4 --> L5

    style L1 fill:#1a1a2e,stroke:#0f3460,color:#e0e0e0
    style L2 fill:#0f3460,stroke:#16213e,color:#e0e0e0
    style L3 fill:#1b4332,stroke:#2d6a4f,color:#e0e0e0
    style L4 fill:#3b2665,stroke:#533483,color:#e0e0e0
    style L5 fill:#5c4d7d,stroke:#7b68ae,color:#e0e0e0

Layer 1: Linux namespace isolation

Android processes inside the container are strictly confined to a separate namespace environment:

  • The PID namespace ensures that Android processes cannot see other processes on the host or send signals to them
  • The network namespace ensures that Android's network stack is completely separate
  • The mount namespace ensures that Android cannot access sensitive paths on the host filesystem

Layer 2: AppArmor profiles

The following three names illustrate the hardening design from the original notes; they are not verified upstream default profiles. Upstream config_3 currently uses unconfined. To determine whether AppArmor is enabled, inspect distribution patches and the actual process state:

Profile Path Restriction target
lxc-waydroid /etc/apparmor.d/lxc-waydroid The LXC container as a whole: restrict filesystem and network access
waydroid-adbd /etc/apparmor.d/waydroid-adbd The ADB daemon inside the container
waydroid-android_app /etc/apparmor.d/waydroid-android_app Android application processes inside the container
# 容器启动时指定的 AppArmor profile
lxc.apparmor.profile = unconfined # 此上游 config_3 的值,不代表已受 AppArmor 限制

# 查看当前加载的 Waydroid 相关 AppArmor profile
sudo aa-status | grep waydroid

AppArmor considerations for NixOS users On NixOS, AppArmor support requires security.apparmor.enable = true to take effect. If it is not enabled, Waydroid skips the AppArmor layer, reducing security protection without preventing startup.

Layer 3: PolicyKit authorization

The XML below illustrates only the structure of a custom PolicyKit action, not a verified default Waydroid policy. Check the installed package files and calling code for the actual policy:

<!-- /usr/share/polkit-1/actions/id.waydro.Container.policy -->
<action id="org.waydro.container.start">
    <description>Start Waydroid container</description>
    <message>Authentication is required to start the Waydroid container</message>
    <defaults>
        <allow_any>no</allow_any>
        <allow_inactive>no</allow_inactive>
        <allow_active>auth_admin_keep</allow_active>
    </defaults>
</action>

auth_admin_keep in the example allows authorization results to be cached temporarily; it does not describe Waydroid's actual authorization model.

Layer 4: Seccomp system-call filtering

Waydroid uses Seccomp BPF filters to restrict the system calls that can be executed inside the container:

# waydroid.seccomp 黑名单示例
# 禁止通过 handle 绕路访问文件(防止容器逃逸尝试)
open_by_handle_at  errno 1

# 禁止加载内核模块(容器内无需也绝不应加载模块)
init_module        errno 1
finit_module       errno 1

# 禁止直接操作内核密钥环
add_key            errno 1
request_key        errno 1

The Seccomp profile is loaded as part of the LXC configuration when the container starts:

lxc.seccomp.profile = /var/lib/waydroid/waydroid.seccomp

Layer 5: minimal capabilities

The current upstream config_base retains a broad set of capabilities, including sys_admin, sys_ptrace, and sys_time. The following is retained as an illustration of a custom, tighter policy. It is not the upstream default, and cap.keep and cap.drop should certainly not be configured together blindly:

# Waydroid 容器中保留的 capabilities
lxc.cap.drop = mac_admin mac_override
lxc.cap.drop = sys_admin   # 自定义加固示意,会影响 Android 所需操作;上游仍保留 sys_admin
lxc.cap.drop = sys_module  # 禁止加载/卸载内核模块
lxc.cap.drop = sys_time    # 自定义示意;当前上游 capability 列表包含 sys_time
lxc.cap.drop = sys_ptrace  # 禁止 ptrace 其他进程
lxc.cap.keep = net_admin   # 保留:网络配置需要
lxc.cap.keep = net_raw     # 保留:原始 socket 需要(DHCP 等)

Privilege-separation summary

┌─────────────────────────────────────────────────────┐
│ root (UID 0)                                        │
│  └─ ContainerManager (system bus)                   │
│     - 可以做的事:LXC 生命周期、mount 操作、          │
│       binder 节点管理                               │
│     - 职责不包括:访问用户会话数据、操作剪贴板、     │
│       管理通知                                      │
├─────────────────────────────────────────────────────┤
│ 普通用户 (UID 1000)                                  │
│  ├─ SessionManager (session bus)                    │
│  │   - 管理 Wayland/PulseAudio 挂载                 │
│  │   - 控制 Android 应用窗口                        │
│  └─ 后台服务 (user/clipboard/notification_manager)  │
│      - 通过 gbinder 与 Android HAL 通信             │
│      - 操作限于用户数据范围                          │
└─────────────────────────────────────────────────────┘

Security red lines Although the Waydroid container provides multiple layers of isolation, it shares the host kernel. If there is a kernel-level vulnerability within Android, the attack surface remains. Therefore:

  1. Keep the host kernel updated, especially for important security patches
  2. Do not run Android applications as root
  3. For untrusted applications, consider running them under a separate user or in a VM

Index of key source files#

Source file Responsibility Language
tools/helpers/lxc.py Dynamic LXC configuration generation and container-lifecycle management Python
tools/helpers/drivers.py Binder/ashmem driver detection and binderfs configuration Python
tools/helpers/protocol.py Binder protocol-version detection (aidl-aidl4) Python
tools/actions/container_manager.py System D-Bus service: root-level container management Python
tools/actions/session_manager.py Session D-Bus service: user-session management Python
tools/services/user_manager.py User-management background service Python
tools/services/clipboard_manager.py Clipboard-synchronization service Python
tools/services/notification_manager.py Notification-forwarding service Python
data/configs/ LXC base-configuration fragment template Configuration file

Verification sources#

Official Waydroid installation instructions.

Official Waydroid command documentation.

Waydroid upstream source.

Upstream config_base.

Upstream config_3.

Protocol selection.

Binder detection.

References & further reading

  1. Official Waydroid installation instructions ↗
  2. Official Waydroid command documentation ↗
  3. Waydroid upstream source ↗
  4. Upstream config_base ↗
  5. Upstream config_3 ↗
  6. Protocol selection ↗
  7. Binder detection ↗
  8. Linux kernel BinderFS documentation ↗

Adapted from technical notes. Examples use generic names.

Continue along a reading path

Waydroid

  1. 01Waydroid architecture: LXC, Binder, and session bridging
  2. 02Waydroid installation and basic setup
  3. 03Waydroid integration on NixOS
  4. 04Waydroid GPU detection and hardware acceleration
  5. 05Waydroid bridges, NAT, and security boundaries
  6. 06Waydroid properties, multi-window mode, and ARM translation
  7. 07Waydroid troubleshooting and FAQ
  8. 08Waydroid tips: windows, translation, and shared directories
CONTINUE READING / NEXT ARTICLEWaydroid installation and basic setup

Check Wayland, kernel, and graphics requirements, then install images and verify startup layer by layer.