fix(linux): stop the takeover firing on non-events with two devices connected - #724
Open
RamfiAogusto wants to merge 2 commits into
Open
fix(linux): stop the takeover firing on non-events with two devices connected#724RamfiAogusto wants to merge 2 commits into
RamfiAogusto wants to merge 2 commits into
Conversation
Taking the connection back from another device makes the earbuds renegotiate, and the card drops out of the sound server for a moment while that happens. get_audio_device_index looked exactly once, so a takeover that landed in that window found nothing and gave up. The failure is worse than a missing profile: ownership has already been taken from the other device by then, so the audio ends up nowhere. Playback stops on the device that was working and never starts on this one. Poll for up to three seconds instead of looking once.
…tate Three places treated an unknown initial state as an observed one, and each made the takeover fire when nothing had happened. With a second device connected, that means pulling the audio away from a device the user was listening on. Ear detection: `old_all_out` is computed with `.all()` over the previous reading, which is vacuously true for an empty list. The first report after connecting therefore looks like the buds were just inserted even when they were never removed — LibrePods reinitialises on every reconnect, so a worn pair triggers a resume on each one. Record the first reading as a baseline instead. Playback: `is_playing` starts false as a placeholder. Anything already playing when the listener starts reads as playback that just began, so a reconnect while media is open takes the audio. Skip the first poll for the same reason. Self-inflicted pauses: losing ownership pauses local players and drops the audio profile, and players tend to resume once the profile returns. That resume was indistinguishable from the user pressing play, so it took ownership back from the device that had just claimed it — which pauses this side again, and the two devices trade the audio until neither is playing. `i_paused_the_media` already existed for this but was never set on that path. Together these stop the takeover from firing on reconnects and feedback loops, while a genuine play still takes the audio as before.
v4rgas
added a commit
to v4rgas/librepods
that referenced
this pull request
Aug 12, 2026
…onnected From librepods-org#724 by RamfiAogusto. Four defects that make the takeover fire when the user did nothing, which with a second device connected means pulling audio away from whatever they were listening on: - The audio card lookup did not wait. Taking the connection back makes the earbuds renegotiate and the card briefly leaves the sound server; a single lookup landing in that window gave up after ownership had already been taken, leaving the audio nowhere. Now polls for 3s. - Ear detection read "unknown" as "removed": old_all_out is vacuously true for the empty list every reconnect starts with, so an already-worn pair looked freshly inserted. First reading is now a baseline. - is_playing started false as a placeholder, so anything already playing when the listener started read as playback that just began. Also a baseline now. - Losing ownership pauses local players and drops the profile; players resume by themselves when it returns, which was indistinguishable from the user pressing play and took the audio back from the device that had just claimed it. i_paused_the_media existed for this and was never set on that path.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Found while using the DID hook with an iPhone and a Linux laptop connected to the same AirPods Pro. Four defects, all in
media_controller.rs, that make the takeover fire when the user did nothing — and with a second device connected, firing means pulling the audio away from a device they were listening on.1. The audio card lookup does not wait
Taking the connection back makes the earbuds renegotiate, and the card drops out of the sound server for a moment.
get_audio_device_indexlooked exactly once, so a takeover landing in that window found nothing and gave up.The failure mode is worse than a missing profile: ownership has already been taken from the other device by then, so the audio ends up nowhere. Playback stops where it was working and never starts here.
Now it polls for up to three seconds.
2. Ear detection treats "unknown" as "removed"
old_all_outis.all()over the previous reading, which is vacuously true for an empty list. LibrePods reinitialises on every reconnect, so the first report of an already-worn pair reads as buds just inserted:The buds were never removed. The first reading is now recorded as a baseline.
3. Playback state has the same shape of bug
is_playingstartsfalseas a placeholder, so anything already playing when the listener starts reads as playback that just began. A reconnect while media is open takes the audio. The first poll is now a baseline too.4. Losing ownership feeds back into taking it
Losing ownership pauses local players and drops the audio profile; players tend to resume once the profile returns. That resume was indistinguishable from the user pressing play, so it took ownership back from the device that had just claimed it — which pauses this side again:
The two devices trade the audio until neither is playing.
i_paused_the_mediaalready existed for exactly this but was never set on that path.Testing
Ubuntu 26.04, GNOME 50.1 Wayland, AirPods Pro 3, iPhone and laptop both connected via the DID hook (
DeviceID = bluetooth:004c:...in BlueZmain.conf).Before: restarting the service while the phone was playing took the audio within seconds, and the two devices traded it until both went silent.
After: a restart with media open leaves the phone playing untouched, and pressing play locally still takes the audio as intended.
One limitation is unchanged and out of scope here: if the other device is still actively playing, the earbuds may answer the hijack with
OwnsConnection: 00and keep it there. That is the earbuds arbitrating, not this code.