Files
enricobuehler ec1e7be7dd feat(android): AAudio grants a rate or fails, so the fallback had to happen before the Hello
Work package H6, Android half — plus the near-miss margin in core, which the Apple leg
independently proved was measuring the wrong thing.

AAudio never substitutes a rate: an explicit request is granted or the open fails. So a
48 kHz rung sitting below a 96 kHz session on the open ladder could only ever produce
playback at 2x speed, or a resample the design forbids in as many words. The fallback
therefore runs BEFORE the handshake: a probe opens a stream at the requested rate, reads
back what was granted and closes it without ever starting it, so nothing routes and no
audio focus is taken. The ladder is 96 -> 48 keeping the depth -> the legacy pair, so a
device that will not grant 96 kHz still gets a 48/24 lossless session rather than
silence. Only 96 kHz is probed; a default session opens nothing and pays nothing.

The open ladder still gains a rate dimension, because a probe is one measurement at one
instant and the supervisor reopens across route changes. Rate is the outermost
dimension, and the last rung is AAUDIO_UNSPECIFIED for the HAL that refuses an explicit
request while already running at exactly the rate we wanted — `arm` still holds it to
the session's rate, so that rung can rescue a session but never mislabel one.

The conceal scratch is now sized per plane: Opus keeps its 120 ms legal maximum, PCM
uses the longest ladder rung. Sizing both from the Opus figure was 24x too large at
96 kHz, and sizing both from the PCM one would have been far too small for Opus.

The poll tick is one frame of the running plane rather than a fixed 5 ms, so on a 2 ms
session the drought-concealment arm keeps pace with playout instead of losing 3 ms in 5.

In core, the near-miss margin stops being a constant. Its own doc always said "less than
one protocol frame was left in hand", but it was frozen at 5 ms — which against a 2 ms
lossless frame means two and a half frames, so it grew the target on a ring that was
never close to starving, inverting exactly what a near-miss exists to detect. Identical
on every Opus session.

The Android CI lane's test filter is an allowlist and eleven classes sat outside it,
including the audio HUD ones, which had never run in CI at all. All eleven pass, so
nothing was hiding — but nothing was gating either. They are listed now, explicitly
rather than by glob, because the unfiltered task also drags in the screenshot scenes.
2026-08-16 09:55:21 +02:00
..