Developer docs
Build an app
SpringBoard is the compositor and system UI. Every app is its own process, drawing into shared memory and talking to the shell over a socket. Apps install from this store.
Specification stage. The split of the built-in apps into separate processes is being written against this spec. Details below can still change; the SDK sdk_version number rises on breaking changes.
Process model
- An app is one executable built for
aarch64-linux-androidwith theandroios-appSDK crate. - The shell starts it with
ANDROIOS_SOCK=<fd number>, an inherited end of asocketpair(AF_UNIX, SOCK_SEQPACKET), andANDROIOS_DATA=/data/local/springboard/appdata/<id>. - The shell keeps an app alive while it is in the switcher and may kill a background app at any time to free memory. Persist your state when you receive
Suspend. - A crashing app never takes the shell down. The shell shows the app’s last frame, relaunches from the launch screen on the next open and logs
app <id> exited <status>. - Settings, Terminal, Control Center and the other system apps stay inside the shell.
Frames
- Allocate two buffers with
memfd_create(on Android,ashmemas fallback), eachw * h * 4bytes of premultiplied RGBA8 packed as 32-bit pixels, stride equal tow. Send both file descriptors once withSCM_RIGHTSinBuffers. - Each frame: draw into the buffer the shell is not holding, send
Frame { buf, damage }, and do not touch that buffer until the shell returns it withRelease { buf }. - The shell sends
Configurebefore the first frame and on every rotation or resize. Reallocate and resendBufferswhen the size changes.
Configure { w, h, scale, safe_top, safe_bottom, dark, text_scale, reduce_motion }
Messages
One message per SOCK_SEQPACKET packet, little-endian, with a u32 tag first. Unknown tags are ignored, so newer shells and older apps keep working.
| Direction | Messages |
|---|---|
| Shell to app | Configure, Touch { kind: down|move|up|cancel, id, x, y, t_ms }, Key { text | keycode }, Release { buf }, Resume, Suspend, Quit, Open { url } |
| App to shell | Hello { sdk_version, app_id }, Buffers { w, h } (+ 2 fds), Frame { buf, damage: [x,y,w,h]* }, WantsKeyboard { on }, Haptic, OpenUrl { url }, Notify { title, body }, Log { text } |
The exact byte encoding lives in springboard/sdk/src/proto.rs, shared by the shell and the SDK, with round-trip tests on both sides.
Package format
An .aap (AndroiOS app package) is an uncompressed POSIX tar holding manifest.toml, the executable, the icon and an optional assets/ directory.
id = "is.olibuijr.calculator" # reverse DNS
name = "Calculator"
version = "1.0.0" # semver
exec = "bin/calculator" # path inside the package
icon = "icon.png" # 180x180 PNG
min_shell = "0.1.0"
summary = "A basic calculator."
category = "Utilities"
permissions = ["network", "audio", "camera", "storage"]
tar --format=posix -cf is.olibuijr.calculator-1.0.0.aap manifest.toml bin icon.png assets
On the tablet, an install unpacks to /data/local/springboard/apps/<id>/<version>/ and points a current symlink at it. The previous version is kept until the new one has launched once, so a broken update can roll back. The tablet verifies the sha256 of every download before installing it.
SDK
The androios-app crate (in the repository under springboard/sdk) wraps the socket, buffer allocation and message loop, so an app implements a few callbacks and draws pixels. Shape of an app, sketched from the protocol above; the crate API is not final.
// Cargo.toml: androios-app = "0.1"
// cargo build --release --target aarch64-linux-android
use androios_app::{run, App, Config, Event, Frame};
struct Hello;
impl App for Hello {
fn configure(&mut self, cfg: &Config) { /* resize your canvas */ }
fn event(&mut self, ev: Event) { /* Touch, Key, Resume, Suspend ... */ }
fn draw(&mut self, frame: &mut Frame) { /* write premultiplied RGBA8 */ }
}
fn main() { run(Hello) }
Store API
| Request | Returns |
|---|---|
GET /api/v1/apps.json | { "apps": [ { id, name, version, summary, category, size, sha256, icon, package, min_shell } ] } for the newest version of each app. |
GET /store/<id>/<file> | Package and icon files. Versioned .aap files are immutable and cached for a year. Range requests are supported. |
GET /ota/springboard/latest.json | Shell update manifest (version, build, sha256, size, url) plus the binary next to it. |
POST /diag/<name> | Diagnostics upload, accepted only on the local network and not exposed on this public site. |
icon and package are site-relative paths. Live data: /api/v1/apps.json.
Publishing
The store is a folder per app, data/store/<id>/, holding every .aap plus icon.png. The index reads the manifest out of the newest package; there is no database. Publishing is a copy, done by the maintainer:
tools/publish-app.sh is.olibuijr.calculator-1.0.0.aap
tools/publish-ota.sh springboard --version 0.1.0 --build 2e9d2423994a
The first command copies the package, extracts its icon and the index updates at once. A published version is immutable, so bump version for every change. The second command is run by tab publish for shell updates. Submissions from other developers are not open yet.