跪拜 Guibai
← All articles
GitHub

An Android Terminal That Survives Screen-Off: Inside Zorv AI's proot Sandbox

By QUOR ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Android's process-killing behavior makes long-running terminal sessions fragile. This architecture shows how to keep a shell alive across screen-off and app-switch events on Android 14+ using `specialUse` foreground services — a pattern any app that needs persistent background compute can adopt.

Summary

The Zorv AI app ships a complete terminal emulator built around proot and an Ubuntu 24.04 ARM64 rootfs, giving any Android device a real Linux toolchain — Python, apt, bash — without requiring root access. A PTY pseudo-terminal implementation over `/dev/ptmx` with `fork/exec` handles interactive shell sessions, while a `specialUse` foreground service pins the shell process so it survives screen-off, app switches, and even device reboots via a boot-completed receiver.

Twelve standardized ACI capabilities expose the terminal to external apps through AIDL, HTTP, and MCP, alongside four standard Android IPC channels: ContentProvider, Deep Link, Intent, and BroadcastReceiver. A 15-second patrol loop inside the keep-alive service detects dead sessions and rebuilds them automatically.

The rootfs downloads from Ubuntu mirrors as an ~80MB compressed tarball and unpacks to about 300MB under the app's private files directory. A bootstrap script can layer Node.js, Python, Rust, Go, Java, or OpenSSH runtimes on top, shared across all terminal sessions.

Takeaways
A proot sandbox with Ubuntu 24.04 ARM64 rootfs delivers a full Linux userspace on Android without root, at about 300MB unpacked.
The PTY implementation opens `/dev/ptmx`, forks a child, and redirects stdin/stdout/stderr to the slave device for interactive shell sessions.
A `specialUse` foreground service holds the shell child process; because the system almost never kills foreground services, the terminal survives screen-off and app switches.
A 15-second patrol loop inside the keep-alive service checks session liveness and auto-rebuilds dead sessions.
Twelve ACI capabilities (`exec`, `create_session`, `destroy_session`, `send_input`, `get_session_status`, `list_sessions`, `set_session_env`, `get_session_env`, `list_capabilities`, `get_service_status`, `get_audit_log`, `help`) are exposed via AIDL, HTTP, and MCP.
Four standard Android IPC channels — ContentProvider, Deep Link (`quro://terminal/...`), Intent actions, and six BroadcastReceiver actions — let external apps invoke the terminal.
A `BOOT_COMPLETED` receiver restarts the foreground service after device reboot, restoring terminal keep-alive automatically.
Command execution defaults to a 14-second timeout and automatically degrades from the proot Linux environment to the device shell when the rootfs is not ready.
CMS runtimes (Node.js, Python, Rust, Go, Java, SSH) install via a bootstrap script and are shared across all terminal sessions.
Android 14+ compatibility requires `foregroundServiceType="specialUse"` plus a `<property>` tag in the manifest explaining the keep-alive purpose.
Conclusions

The `specialUse` foreground service type is the correct escape hatch for Android 14+ apps that need persistent background processes but don't fit `dataSync`, `mediaPlayback`, or `location` categories — and Google Play will inspect the required `<property>` justification.

Forking the shell child process inside the foreground service process, rather than in the UI process, is the architectural decision that makes keep-alive work: the service's elevated priority extends to its child processes automatically.

The 15-second patrol-and-rebuild loop is a pragmatic self-healing pattern for long-lived background sessions on a platform that offers no guaranteed process persistence.

Exposing terminal capabilities through both a structured ACI protocol and raw Android IPC channels (ContentProvider, Deep Link, Intent, BroadcastReceiver) covers the spectrum from programmatic integration to simple URL-based invocation.

Bundling proot as a `.so` inside the app's native library directory avoids external binary dependencies and keeps the sandbox self-contained within the APK.

Concepts & terms
proot
A userspace implementation of `chroot` that uses `ptrace` to intercept syscalls, allowing an unprivileged process to run a Linux distribution rootfs without root permissions. It fakes root privileges and remaps file paths so the guest rootfs sees a conventional filesystem layout.
PTY (pseudo-terminal)
A pair of device files — a master (`/dev/ptmx`) and a slave (`/dev/pts/N`) — connected by a kernel driver. Writing to the master appears as input on the slave; reading from the master captures output written to the slave. This is how terminal emulators provide an interactive shell interface.
specialUse foreground service
An Android 14+ foreground service type for use cases that don't fit the predefined categories (dataSync, mediaPlayback, location, etc.). It requires a `<property>` tag in the manifest explaining the purpose, and is subject to Google Play review.
ACI (AIDL Capability Interface)
A cross-process invocation protocol used by Zorv AI that defines standardized capabilities callable via AIDL, HTTP, or MCP. Each capability has a name, input parameters, and a structured return value.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗