ADR 0005: environments MVP and distributions
Context
Issue #5 needs a first usable product: install an Ubuntu environment, optionally with code-server, list and delete environments, and work offline on a bundled distribution.
Decisions
Two distributions instead of a build flag
The original plan used -PbundleArtifacts. Two product flavors replace it because the
bundled distribution has a different promise and size:
| Flavor | Contents | versionName |
|---|---|---|
standard | proot for both ABIs; rootfs and code-server downloaded | 2026.10.0 |
raccoonArm64 | proot, Ubuntu 26.04 LTS arm64 and code-server arm64 | 2026.10.0-Raccoon |
raccoonX64 | same for x86_64 | 2026.10.0-Raccoon |
Assets are not split by ABI automatically, so each Raccoon flavor carries only its own rootfs
and code-server. The shared applicationId means both distributions cannot be installed at the
same time.
Artifacts
artifacts.toml pins the URL, SHA-256 and size of every artifact. tools/bundle_artifacts.py
downloads to a local cache, verifies the checksum and copies the rootfs and code-server into the
flavor assets. Raccoon APKs are about 287 MB per ABI; the standard APK is about 13 MB.
Downloads
Downloads use the system DownloadManager instead of a custom foreground service. It provides
background progress, notifications, cancellation and resume without a service of our own. The
app verifies the SHA-256 after the download and aborts on mismatch, so integrity does not depend
on the system.
The trade-off is that the notification is owned by the system and the artifact lands in the app-specific external storage before it is used.
Storage layout
- Rootfs:
filesDir/environments/<id>/rootfs, extracted with hardlinks converted to symlinks. - code-server:
filesDir/environments/<id>/rootfs/opt/code-server, versioned by re-extracting the directory. - Projects bridge:
getExternalFilesDir()/projects, bind-mounted at~/projects_bridgeby proot path translation. - Registry:
filesDir/environments.jsonwith the distro, arch, code-server version, presets, size and creation time.
Catalog
catalog.json is versioned, validated and served from raw.githubusercontent.com with a cached
copy and a bundled asset as fallback. The catalog refresh is triggered when the environment
wizard opens and from the manual refresh action; failures fall back silently.
Kernel requirement
apt needs kernel 4.11 or newer (see the proot spike), while proot itself works from API 26. The wizard does not block old kernels, but package management will fail there.
Consequences
- Releases need three APKs or a matrix build; the release workflow will be updated when the distributions are published.
- The Raccoon flavor has a large APK and duplicates the rootfs and code-server per ABI.
- 16 KB page validation remains open (#16).
- proot is GPL-2.0 and code-server is MIT; the
NOTICEfile covers attribution and source locations.
ADR 0006: terminal
Context
Issue #6 needs an interactive terminal per environment, multiple sessions, background persistence and configurable extra keys.
Decisions
- Vendored Termux
terminal-emulatorandterminal-view(GPL-3.0) inmobile/lib, built with the project SDK and NDK.termux-sharedis not vendored: it is the Termux app utility layer and brings unrelated dependencies (guava,markwon,hiddenapibypass,termux-am-library). - PTY:
TerminalSessioncreates the pseudo-terminal through the vendored JNI and runslibproot.sofromnativeLibraryDirwith/bin/bash -land the same arguments as the runtime. - Sessions: a process-wide
TerminalSessionManagerkeeps sessions per environment; aspecialUseforeground service keeps the process alive and shows a notification. - Tabs: a
TabRowwith create and close actions; sessions are namedTerminal Nfrom a per-environment counter. - Extra keys: Ctrl, Alt, Esc, Tab and arrows, configurable from a dialog and stored in shared preferences.
- External windows:
TerminalActivityis resizeable and launches withNEW_TASK | MULTIPLE_TASKand an empty task affinity, so Android can place it in a separate window (split screen, freeform, desktop). The task description shows the session name. - Cleanup: deleting an environment stops its sessions before removing its files.
Consequences
- Sessions survive the UI going to the background, but not process death: the manager and the counters live in memory.
- The foreground service requires the
specialUsetype andPOST_NOTIFICATIONSon Android 13+. - Vendored code keeps its GPL-3.0 headers and the
NOTICEentry.