PAF becomes saf/device (history kept), STATE.md dissolves into saf/state.md with the dated era archived, the substrate SAF moves up from souveraine, and every agreement points at saf/INDEX.md and nowhere else. one map, nothing to remember
156 lines
6.3 KiB
Markdown
156 lines
6.3 KiB
Markdown
---
|
|
task_id: souveraine-native-audio-001
|
|
title: "Souveraine audio and privacy surfaces"
|
|
status: in_progress
|
|
priority: high
|
|
phase: delivery
|
|
created: 2026-07-18
|
|
updated: 2026-08-07
|
|
references:
|
|
- saf/device/audio.md
|
|
- docs/tasks/07-mic-wcd9340-slim-tx.md
|
|
- docs/tasks/39-audio-levels-as-state.md
|
|
- surfaces/quickshell/services/Audio.qml
|
|
- surfaces/quickshell/modules/ii/sidebarRight/volumeMixer/VolumeDialogContent.qml
|
|
- surfaces/quickshell/modules/ii/bar/UtilButtons.qml
|
|
- /home/casey/Projects/Pixel3Arch/pkgs/souveraine-ucm-blueline
|
|
- /home/casey/Projects/Pixel3Arch/pkgs/blueline-callaudio
|
|
- /home/casey/Projects/Pixel3Arch/pkgs/souveraine-callaudiod
|
|
- /home/casey/Projects/Pixel3Arch/pkgs/q6voiced
|
|
---
|
|
|
|
# Souveraine audio and privacy surfaces
|
|
|
|
## Current contract — 2026-08-07
|
|
|
|
The phone's audio stack works. PipeWire 1.6.8 owns the graph,
|
|
WirePlumber 0.5.15 owns session policy, `pipewire-pulse` provides the Pulse
|
|
compatibility API, and ALSA UCM describes the board routes. Native PulseAudio
|
|
and the old ABI-provider shims are historical experiments, not the desired
|
|
architecture.
|
|
|
|
- HiFi exposes both CS35L36 speakers and clean mono WCD9340 capture through
|
|
DMIC4 → DEC5/TX5. Casey uses the microphone for STT.
|
|
- The Voice Call profile carries real two-way calls. The call packages manage
|
|
the UCM transition, modem PCM, and earpiece/speaker selection.
|
|
- WirePlumber restores device volume and selected profile correctly. TASK-39
|
|
owns the remaining role/default/lock-tier policy; it must not invent a
|
|
second persistence writer.
|
|
- TASK-07 owns the remaining hardware debt: the kernel holds codec sysclk and
|
|
MICB2 permanently. That is a power and suspend defect, not an audio
|
|
functionality defect.
|
|
|
|
The detailed hardware and lifecycle evidence lives in `saf/device/audio.md`. Do not
|
|
reopen microphone bring-up from this surface document.
|
|
|
|
## Authority
|
|
|
|
| Concern | Authority |
|
|
|---|---|
|
|
| Board verbs, devices, routes, and gains | `souveraine-ucm-blueline` |
|
|
| Runtime graph, profiles, streams, and volume restoration | PipeWire + WirePlumber |
|
|
| Pulse client compatibility | `pipewire-pulse` |
|
|
| Call start/end UCM transition | `souveraine-callaudio` |
|
|
| Modem voice PCM bridge | `souveraine-q6voiced` |
|
|
| In-call earpiece/speaker action | `souveraine-callaudiod` + callaudio hook |
|
|
| User-facing state, privacy truth, and guarded actions | Souveraine surfaces |
|
|
|
|
`pactl` remains a valid control and observation API because `pipewire-pulse`
|
|
implements it. Calling that interface does not make native PulseAudio the
|
|
authority.
|
|
|
|
## Surface model
|
|
|
|
The owned audio service projects one truthful model:
|
|
|
|
```text
|
|
serverReady
|
|
defaultSink / defaultSource
|
|
sinks[] / sources[]
|
|
playbackStreams[] / recordingStreams[]
|
|
activeProfile
|
|
outputActive / microphoneActive
|
|
lastError / generation
|
|
```
|
|
|
|
Each endpoint and stream needs a stable identifier, description,
|
|
availability, volume, mute, application identity, media role, and running or
|
|
corked state. Sink and source readiness are independent: loss of a microphone
|
|
must not disable speaker controls.
|
|
|
|
The current `pactl` snapshots are acceptable scaffolding. The durable service
|
|
should keep a long-lived `pactl subscribe` observer, debounce graph changes,
|
|
and refresh from machine-readable JSON. A PipeWire restart increments
|
|
`generation`, clears stale objects, and repopulates the model. Setters report
|
|
failure; the UI does not optimistically lie.
|
|
|
|
## Privacy truth
|
|
|
|
Privacy state comes from active clients and sessions, never from the appearance
|
|
of a toggle.
|
|
|
|
- `microphoneActive`: one or more recording streams exist; expose client
|
|
identity and whether each stream is running or corked.
|
|
- `cameraActive`: an active camera or portal session, not device presence.
|
|
- `screenCaptureActive`: an active portal screencast or Souveraine recorder.
|
|
|
|
Muting and revoking access are different actions. A muted source can still be
|
|
held by a recording client, so the privacy indicator remains visible until the
|
|
stream ends. Unknown clients are shown as unknown, not omitted.
|
|
|
|
Active capture always has a persistent indicator on the unlocked shell and a
|
|
safe ambient indication while locked. Details may name clients when unlocked;
|
|
the lock surface must not leak private application metadata.
|
|
|
|
## Owned surfaces and IPC
|
|
|
|
The present `modules/ii` files are working scaffolding. The destination is an
|
|
original Souveraine service and presentation layer, with the same semantics on
|
|
phone and laptop and form-factor-specific layout.
|
|
|
|
Expose JSON-over-string methods because Quickshell drops `var` returns:
|
|
|
|
```text
|
|
audio.state()
|
|
audio.devices(direction)
|
|
audio.streams(direction)
|
|
audio.setVolume(direction, value, reason)
|
|
audio.setMuted(direction, muted, reason)
|
|
audio.setDefault(direction, endpointId, reason)
|
|
audio.stopStream(streamId, reason)
|
|
```
|
|
|
|
Read-only state is ambient. Volume and mute are ordinary audited actions.
|
|
Stopping another application's recording is a privacy action and follows the
|
|
common settings/capability authority. Every method validates direction,
|
|
ranges, endpoint existence, graph generation, and lock state, then returns a
|
|
structured success or refusal.
|
|
|
|
## Verification
|
|
|
|
- Speaker, microphone, STT, and two-way calls remain functional through the
|
|
PipeWire/WirePlumber stack.
|
|
- Volume, mute, and profile changes round-trip and survive a service refresh;
|
|
WirePlumber remains the sole persistence writer.
|
|
- Starting and stopping a recording updates the privacy indicator promptly and
|
|
identifies the client when safe to do so.
|
|
- Muting during recording leaves the access indicator visible.
|
|
- A graph restart clears stale devices and streams without restarting the
|
|
shell.
|
|
- A missing source leaves output controls usable.
|
|
- Call entry, earpiece/speaker switching, hangup, and HiFi restoration all
|
|
preserve subsequent microphone capture.
|
|
- Lock transitions retain an ambient capture warning without exposing client
|
|
metadata.
|
|
- IPC cannot bypass settings, step-up, or lock authority.
|
|
|
|
## Done when
|
|
|
|
- [ ] Active audio and privacy services are original Souveraine AGPL code with
|
|
no borrowed widget dependency in their active graph.
|
|
- [ ] Real endpoints and streams are modeled eventfully and recover after a
|
|
graph restart.
|
|
- [ ] Capture indicators distinguish access, mute, and revocation.
|
|
- [ ] Phone and laptop share semantics without sharing inappropriate layout.
|
|
- [ ] Role defaults and lock-tier behaviour are settled in TASK-39.
|
|
- [ ] Agent IPC uses the common settings/capability policy.
|