STATE.md was five days stale. It now carries what is on the phone (viewtop r76, souveraine r333) and the four defects fixed on the glass: per-window capture was unreachable because quickshell holds wlr handles and the ext capture source is named by an ext handle with nothing correlating them; titles were never republished on commit; the border was a filled slab that relied on the client covering its middle; capture came back flipped. Also the packaging hole, which was the important one — viewtop's PKGBUILD existed and appeared in none of packages.yml's build lists, so nothing ever built it. Half closed: it builds now, but a viewtop commit still does not trigger a rebuild. TASK-15 question 1 is answered and closed: dropping the accelerometer claim took iio-sensor-proxy from 15.3% to 1.1%, sensors overall 17.2% to 2.6% of a core. Consequence noted against 08(g) — confidence loses accel's +0.3. New: 54, 55, 56, and 57/58 (Pi dock head, SMB2 OTG boost) with the USB notes they point at.
140 lines
7 KiB
Markdown
140 lines
7 KiB
Markdown
# TASK 15 — Idle coordinator: freeze, doze, and scheduled wakeups
|
||
|
||
**Status:** open — design task. Carved 2026-07-21 from Casey's ask
|
||
("app freeze state for idle, the timeoffs and wakeups to check for
|
||
notifications") plus the measured idle burn below.
|
||
|
||
## Why (measured 2026-07-21, phone idle, screen locked)
|
||
|
||
- `culver` 9.8% CPU, 6:11 accumulated — the single biggest idle spinner
|
||
- `qs` (shell) 4.5%, `souveraine-player` 3.9%
|
||
- cluster1 peaked 54.9 °C; Casey feels it in-pocket
|
||
- The mic power pin (TASK-07) holds codec sysclk + MICB2 from
|
||
boot — invisible to `top`, real on the battery
|
||
|
||
A phone that is "off" should approach zero. Nothing on the device today
|
||
even attempts that.
|
||
|
||
## Re-measured 2026-08-04 — the biggest spinner is now OURS, and it is the sensors
|
||
|
||
41 minutes uptime, locked, screen off, viewtop session:
|
||
|
||
```
|
||
23.3% iio-sensor-proxy 9m35s of CPU in 41m of uptime
|
||
9.3% monitor-sensor --proximity --light --accel (parent: souveraine-sensord)
|
||
3-4% dbus-broker carrying the traffic between the two
|
||
```
|
||
|
||
~36% of one core, permanently, on a phone doing nothing. Dies 44–48 °C, battery
|
||
41 °C, and **the SoC is nowhere near a trip point** — the shallowest passive
|
||
trip on this device is the GPU's at 85 °C. So this heat is not a thermal-policy
|
||
problem, it is a spinning userspace, and no governor will touch it.
|
||
|
||
**The cause is architectural, not a bug in either program.** `souveraine-sensord`
|
||
spawns `monitor-sensor --proximity --light --accel` and holds it open forever.
|
||
Claiming the **accelerometer** is what makes iio-sensor-proxy poll continuously —
|
||
proximity and light are cheap and event-shaped, accel is polled to derive
|
||
orientation. And per `DEVICE-STATE-MACHINE.md` §4, `accel.moving` is evidence
|
||
the machine deliberately **stopped branching on**: "motion cannot tell a pocket
|
||
from an ear from a hand". We are burning a third of a core to keep producing a
|
||
reading no rule consumes.
|
||
|
||
**The prior art is the reason the SLPI exists.** Android's sensor HAL batches:
|
||
the sensor DSP accumulates samples into a hardware FIFO and wakes the AP once
|
||
per batch (`batch(sensor, sampling_period, max_report_latency)`), with wake-up
|
||
and non-wake-up variants so a screen-off phone is not woken per sample. Polling
|
||
from userspace on the application processor, through D-Bus, defeats the entire
|
||
point of having a sensor DSP — and this device *has* one (TASK-36, TASK-34).
|
||
|
||
Three questions to settle, in this order, before any freeze work:
|
||
|
||
1. **Does anything still need the accelerometer claimed?** — **ANSWERED AND
|
||
DONE 2026-08-04** (`6b67512`). Measured on the phone, idle, screen on, same
|
||
session, one flag apart:
|
||
|
||
| `monitor-sensor` flags | iio-sensor-proxy |
|
||
|---|---|
|
||
| `--proximity --light --accel` | **15.3%** |
|
||
| `--proximity --light` | **1.1%** |
|
||
|
||
Fourteen points of a core, continuously, for the reading §4 weights least
|
||
(+0.3) and §9 calls a weak signal. The claim is what makes iio-sensor-proxy
|
||
poll; nothing else here does. `monitor-sensor` itself measured 0.8% and
|
||
sensord 0.1% — the subprocess was never the cost, the *claim* was.
|
||
|
||
The accelerometer source is dropped from `SOURCES` in
|
||
`src/bin/souveraine-sensord.rs`, along with `parse_accel`, `accel_moving`
|
||
and `MOTION_WINDOW` — all three existed only to turn orientation into a
|
||
motion edge. The 2026-07-27 stuck-`Moving(true)` finding is kept in comment
|
||
because whatever reports motion next owes a decay too.
|
||
|
||
This is not "accel is unwanted": it is that a subprocess holding a
|
||
continuous claim is the wrong way to get it. See (2).
|
||
2. **If it is wanted, does it go through the SLPI with batching** rather than
|
||
iio-sensor-proxy polling? That is TASK-36's host, and it turns a continuous
|
||
poll into an interrupt per batch.
|
||
3. **Is `monitor-sensor` the right transport at all**, or does sensord read the
|
||
IIO character device with a buffer and a trigger directly? A subprocess and a
|
||
D-Bus round trip per reading is three context switches for one number.
|
||
|
||
This is the *before* number for this task now: 36% of a core and 45 °C at idle.
|
||
|
||
## Shape of the thing
|
||
|
||
One coordinator (substrate side, likely a sessiond sibling or sessiond
|
||
itself — it already owns lock state, which IS the idle signal) driving
|
||
three tiers:
|
||
|
||
1. **Screen-off, tethered to lock.** Lock → after a grace period,
|
||
freeze the app tier: `systemctl --user freeze app-*.scope` (cgroup
|
||
freezer; requires app launches to land in per-app scopes — they may
|
||
already via uwsm/exec rules, verify). The shell, sessiond, modem
|
||
stack, and the notification path stay unfrozen. Unlock → thaw
|
||
everything, instantly.
|
||
2. **Doze.** After N min locked: stop the polling herd (culver first —
|
||
find its idle loop regardless; 10% CPU while doing nothing is a bug
|
||
on its own), drop Wi-Fi to power-save, let UPower's percentage
|
||
polling coast. The lockscreen glance data (media, agent, notify
|
||
cards) must still update on wake — cards read on unlock, they don't
|
||
need background freshness.
|
||
3. **Suspend + scheduled wakeups — the end state.** s2idle with RTC
|
||
alarms (`CLOCK_BOOTTIME_ALARM` timers in the coordinator, not
|
||
rtcwake hacks): wake every M min → thaw the network fetchers for a
|
||
bounded window (check Matrix/mail/notifications) → banner/lock-card
|
||
anything that landed (NotifyEvents seam already exists) → re-freeze,
|
||
re-suspend. Calls/SMS never wait for a poll window: modem IRQs are
|
||
hardware wake sources. Blocked-by: TASK-09 FTS suspend race
|
||
(interim-fixed, kernel-side) and the TASK-07 unpin (a pinned codec
|
||
clock across suspend is either a wake veto or a lie).
|
||
|
||
## Design questions to settle before code
|
||
|
||
- Freeze unit granularity: per-app scopes vs one `apps.slice`. A slice
|
||
freeze is one command and no bookkeeping; per-app lets Chatty stay
|
||
live for SMS-adjacent work. Likely: freeze `apps.slice`, keep an
|
||
allowlist slice (`apps-wake.slice`) for the few that may run locked.
|
||
- Who is the notification fetcher during doze? If everything that polls
|
||
is frozen, the wake window must thaw → settle → fetch → freeze; the
|
||
coordinator needs a "fetch complete" signal, not a fixed sleep.
|
||
- culver: freeze it like any app, but ALSO find the spin — freezing a
|
||
busy-loop just defers the heat to unlock time.
|
||
- Wi-Fi during suspend: WoWLAN on ath10k_snoc is unproven on this
|
||
device; assume packets do NOT wake us, only RTC + modem do. Design
|
||
for that and be pleasantly surprised otherwise.
|
||
- Interaction with charging: plugged-in can skip tiers 2–3 (it's the
|
||
desk-clock case; also the only time `grim`/remote debugging works).
|
||
|
||
## Acceptance
|
||
|
||
Locked overnight on battery: no app-tier CPU time accumulated, wake
|
||
windows visible in the coordinator's log at the configured cadence, an
|
||
SMS arriving mid-doze rings within seconds, and morning battery drop
|
||
measurably better than the current baseline (capture tonight's number
|
||
first — it is the before).
|
||
|
||
## Connects to
|
||
|
||
TASK-08 device-state-manager (this IS its idle half — 08 keeps the
|
||
profile/governor side), TASK-09 (suspend blocker), TASK-07's
|
||
codec pin, NotifyEvents/LockNotifyCard (the landing surface for
|
||
wake-window arrivals).
|