fix(logi): recover receiver slots from late activity - #1013
Conversation
6c081ea to
b938b97
Compare
Caldis
left a comment
There was a problem hiding this comment.
Thanks for this — excellent PR. The bug is real and well characterized (this is exactly the "device doesn't emit 0x41 after resume" scenario the multi-device handoff design anticipated), and the passive inference approach is better than the register-write fallback we had sketched: zero additional outbound HID++ traffic, fully event-driven, red-line compliant. The real-device validation with logs is much appreciated.
Accepting the direction. Two changes requested before merge:
1. Route the inferred takeover through scheduleReceiverSlotTakeover instead of calling handleReceiverSlotTakeover directly.
The 0x41 connection path deliberately goes through a 0.4s debounce to protect against link flapping (rapid connect/disconnect cycles triggering repeated full-slot discovery — we've been bitten by this on real hardware). The inferred-connection path currently bypasses that: a flaky link alternating between 0x41-disconnect and peripheral bursts would trigger an immediate un-debounced discovery per cycle. Routing through the debounced scheduler makes inferred connections behave identically to 0x41 connections — including auto-cancel if a 0x41 disconnect arrives within the window — at the cost of only 0.4s takeover latency.
2. Move the table writes below the !wasConnected guard in handleReceiverPeripheralActivity.
As written, every peripheral report from an already-online slot (i.e. every button event and response on managed slots) performs the firstIndex scan plus isConnected = true; lastError = nil writes before bailing. Reordering to guard first keeps the hot path read-only for online slots, and also removes the side effect of lastError being cleared by arbitrary traffic.
Minor, non-blocking: the wasConnected parameter of receiverPeripheralActivityAction is always false on the production path (the caller already guards it). Fine as-is for testability — just noting it.
Merge coordination note (no action needed from you): a large internal refactor of LogiDeviceSession (component split: ReceiverEnumerator, request pipeline, environment injection) is about to land from a long-running quality branch. Your PR will merge first; we'll adapt it onto the new component boundaries on our side during that merge (receiverPairedDevices writes move behind ReceiverEnumerator, LogiCenter.shared.registry becomes an injected capability).
|
Thanks for the detailed review — the link-flapping context was especially helpful. Both requested changes make sense, and I’ve pushed them in The inferred connection path now goes through I left the I reran the Logi boundary lint, all 31 And thanks for the heads-up about the upcoming component split. I’ve kept this follow-up scoped to the current structure and won’t try to anticipate that refactor here. |
Motivation
When a receiver reconnects while a paired mouse is asleep, the initial slot scan can finish with no connected devices. Some devices then resume by sending normal peripheral traffic without a receiver
0x41connection notification, so Mos never enumerates or takes over that slot.This was reproduced with a Logitech M720 Triathlon through a Unifying receiver:
0/6connected slots;0x41;This is separate from #1012: that PR restores diversion for a slot Mos already manages, while this bug leaves the late device entirely unmanaged.
Reproduction
Ping complete: 0/6 slots connectedandNo devices found on receiver.Root cause
Receiver peripheral reports were routed only against the current and managed slot sets. Traffic from a slot that the startup scan had marked offline did not update
receiverPairedDevicesor enter the existing takeover queue. Slot 1 was especially easy to miss because it is also the temporary default routing cursor.What changed
The recovery is event-driven. It adds no polling or persistent timer and does not change BLE behavior or stored settings. On receivers that cannot report device type, an unknown slot may receive the same one-time discovery already used during normal startup; non-mouse controls are then excluded by the existing Left/Right CID check.
Validation
scripts/qa/lint-logi-boundary.shLogiReceiverConnectionStateTests: 31 passed, 0 failed0/6slots connected;0x41notification followed;Relevant log excerpt
The full raw logs contain unrelated paired-device traffic, so they are not committed to the repository. They can be provided to reviewers if needed.