Telemetry Protocol & FPS Recorder (GET_STATS)
Auriya exposes an internal performance snapshot that the manager app polls to render its telemetry cards (FPS, temps, battery, CPU/GPU clocks) and to drive per-game FPS recording.
The GET_STATS payload is an internal IPC communication format designed specifically between the Auriya Rust daemon and the Auriya Android manager app. It is not intended as a generic plug-and-play public API for external projects without adaptation.
Transport
Section titled “Transport”| Fact | Value |
|---|---|
| Command | GET_STATS (alias GETSTATS) |
| Channel | Unix socket /dev/socket/auriya.sock (see IPC protocol) |
| Response | one line of JSON, then the connection closes on QUIT |
| Cost | computed on request — the daemon accumulates nothing between polls |
From a root shell:
$ printf 'GET_STATS\nQUIT\n' | nc -U /dev/socket/auriya.sockOK AURIYA IPC{"fps":{"avg":118.0,...},"thermal":{...},...}BYEJSON schema (one group = one UI card)
Section titled “JSON schema (one group = one UI card)”{ "fps": { "avg": 118.0, "peak": 258.9, "low_1pct": 78.5, "jank": 2, "frames": 600 }, "thermal": { "cpu_c": 64.7, "gpu_c": null, "battery_c": 41.5 }, "battery": { "pct": 100, "current_ma": 573, "voltage_v": 4.23, "status": "Charging", "health": "Good" }, "cpu": { "load_pct": 60.0, "cores": [ { "id": 0, "khz": 1804800, "gov": "walt", "cluster": "Little", "online": true } ] }, "gpu": { "mhz": 580, "load_pct": null, "vendor": "kgsl" }, "session": { "pkg": "com.mobile.legends", "profile": "performance", "active": true }}Field reference
Section titled “Field reference”| Group | Field | Meaning |
|---|---|---|
fps |
avg |
Mean FPS over the window (1 / mean frametime). |
peak |
Fastest single frame (1 / min frametime). |
|
low_1pct |
Mean FPS of the worst 1% of frames — the stutter metric. | |
jank |
Frames slower than target × 1.5. |
|
frames |
Sample count the window was computed from. | |
thermal |
cpu_c / gpu_c / battery_c |
°C. battery_c lives here (it is a temperature), not in battery. |
battery |
pct |
Charge 0–100 %. |
current_ma |
Instantaneous current. Sign is device-specific — use status, not the sign, for direction. |
|
voltage_v |
Terminal voltage. | |
status / health |
e.g. Charging / Good. |
|
cpu |
load_pct |
Overall CPU load. |
cores[] |
Per-core id, khz (clock), gov (governor), cluster (Little/Big/Prime), online. |
|
gpu |
mhz / load_pct / vendor |
GPU clock, busy %, driver. |
session |
pkg |
Foreground package. |
profile |
Active profile (performance/balance/powersave). |
|
active |
true only when a whitelisted game with a live PID is running. When true, pkg is that game. This is the record trigger. |
Null rules (must handle)
Section titled “Null rules (must handle)”fpsisnullwhen no game is running (idle) — render the FPS card as “inactive”, not “0”.- Any field can be
nullwhen the device does not expose that node (e.g.gpu_c,gpu.load_pcton some SoCs). Each card renders independently and skips null fields — never crash on a null. session.active == falsemeans no managed game (could be another app in the foreground). Onlyactive == trueis a game session.
Recommended access method (for the UI)
Section titled “Recommended access method (for the UI)”The socket is root-only (app-uid cannot open it under SELinux), so poll it through
the app’s existing libsu root shell — the same pattern OverlayService already
uses. Do not write a raw LocalSocket client.
// Persistent root shell (libsu). Poll on a coroutine loop.fun fetchStats(): Stats? { val raw = RootShell.run("printf 'GET_STATS\\nQUIT\\n' | timeout 2 nc -U /dev/socket/auriya.sock") val json = raw?.lineSequence()?.firstOrNull { it.startsWith("{") } ?: return null return Json { ignoreUnknownKeys = true }.decodeFromString<Stats>(json) // kotlinx.serialization}- Poll cadence: reuse the existing
update_interval_mspref (default 1000 ms). ~1 Hz is the design point; higher is unnecessary and the daemon computes on-request anyway. - Parse with
ignoreUnknownKeys = trueso future daemon fields never break the UI.
FPS auto-record (app-side)
Section titled “FPS auto-record (app-side)”Auto-record is orchestrated in the manager app, not the daemon — GET_STATS
already provides everything needed:
- Enable/disable per whitelisted game — store as an app preference keyed by
package. Do not add a field to
gamelist.toml; the daemon does not consume it and it would be dead config. The whitelist itself isgamelist.toml. - Trigger — watch
session.active. Onfalse → true(and the game has auto-record enabled), start a recording buffer forsession.pkg; whiletrue, append each poll’sfps(plus any telemetry you want) with a timestamp; ontrue → false, finalize a session summary (avg, minlow_1pct, maxcpu_c, totaljank, duration). - Run it in a foreground service (like
OverlayService) so recording continues while the game — not the app UI — is in the foreground. - Store recordings in the app’s own sandbox (
filesDir/ Room / DataStore). Never write under/data/adb.
Resolution ceiling
Section titled “Resolution ceiling”Poll-based recording is ~1 sample/second (coarse) — right for a session FPS graph and summary. Per-frame traces are not available this way; that would require a new daemon streaming API (out of scope today). Flag it if the product needs frame-level detail.
See also
Section titled “See also”- IPC protocol — the socket and every command.
- FPS detection — where the FPS numbers come from.
- Performance tuning — FAS modes & values.