RadioOperationLock
Tracks which RadioOperations are in flight, so the rest of the app can keep out of their way.
Three consumers, which is why one lock replaced the single-purpose FirmwareMaintenanceLock it grew out of: SharedRadioInterfaceService reads suppressesTransport to stay off the device, MeshService reads holders to refuse to stop itself and to hold a wake lock, and the service notification reads it to say what is running.
Holders are tracked as individual leases rather than a set of operation types. The set was wrong: a caller that cancels a job and immediately starts a replacement — which FirmwareUpdateViewModel does, without joining — lets the cancelled job's finally run after the replacement has acquired, and a shared entry would be retired out from under the live holder. Two holders of the same operation are now independent.
Lives in :core:common because the parties sit in modules that cannot see each other: the flows that take an operation are in :feature:firmware and :feature:discovery, and the code that must respect it is in :core:service.
Process-local by design. A process death drops every lease, which is correct — nothing is in flight any more — but it means this narrows the window in which an interrupted flash can strand a device rather than closing it.