fix(eq): stop the phone's FM sink from posing as the active output
Build & Deploy PluriWave / Análisis de código (push) Successful in 24s
Build & Deploy PluriWave / Build APK + AAB release (push) Successful in 2m23s

Regression from the previous commit. Ranking every AudioDeviceInfo type this
build does not name individually ABOVE the built-in speaker was meant to let
a car stereo on LE Audio or an automotive bus win. It also promoted the
internal sinks a phone exposes permanently: on the Xiaomi test device
AudioManager reports TYPE_FM (14) as an output, so getActiveAudioDevice
picked it over the real speaker with nothing connected at all. Confirmed on
device:

  audio_devices.onListen -> {id=other:14:4, type=14, name=2412DPC0AG}

It then reached Dart under an `other:14:4` id whose type is neither the base
speaker nor a known one, slipped past the collision guard and had a preset
row persisted for it -- reinstating the exact symptom this series set out to
kill: a permanent green active-output dot on a device that was not connected.

Replace the deny-by-omission ranking with an explicit allow list of outputs a
user actually connects. The built-in speaker sits below all of them and above
everything else, so any sink that physically exists but is never where media
plays (TYPE_FM, TYPE_BUILTIN_SPEAKER_SAFE, telephony, remote submix) can no
longer be selected. A one-time purge clears the `other:` rows the bad build
persisted; genuine ones re-register on their next connection.

Fix the USB type constant while here: TYPE_USB_HEADSET is 22, not 14, and 14
is TYPE_FM. The Kotlin USB branch hardcoded 14 and the Dart type table
mirrored the same mistake, so the two cancelled out for real USB headsets
while making a phone's own FM sink decode as USB audio. Both now use 22.

Verified with javap against android.jar (android-36) rather than trusting the
comment that introduced the error.
This commit is contained in:
2026-07-25 20:43:47 +02:00
parent c94bc3d770
commit 4042cf5ffd
5 changed files with 162 additions and 26 deletions
+13 -2
View File
@@ -22,6 +22,13 @@ const prefijoPlaceholderBtName = 'bt_a2dp:name:';
/// entry that then marked the wrong device as active forever.
const idAltavozInterno = 'builtin_speaker';
/// Id prefix for outputs the native layer does not name individually
/// (`other:<AudioDeviceInfo type>:<address>`): LE Audio stereos, car buses,
/// hearing aids, docks. Kept distinct from every other id namespace so such a
/// device can never collide with [idAltavozInterno], and so a bad batch of them
/// can be purged wholesale.
const prefijoDispositivoOtro = 'other:';
/// Abstract service for audio device detection.
///
/// Implementations:
@@ -79,7 +86,11 @@ abstract class ServicioDispositivoAudio {
/// 2 → altavozInterno (builtin_speaker)
/// 3 → auricularesCable (wired_headset)
/// 8 → bluetoothA2dp (`bt_a2dp:<MAC>`)
/// 14 → usbAudio (`usb_headset:<address>`)
/// 22 → usbAudio (`usb_headset:<address>`)
///
/// Values match `AudioDeviceInfo.TYPE_*`. Note 22, NOT 14: type 14 is
/// `TYPE_FM`, an output plenty of phones expose permanently, and mapping it to
/// USB audio made a phone's own FM sink look like a connected headset.
class ServicioDispositivoAudioReal extends ServicioDispositivoAudio {
static const _channelName = 'pluriwave/audio_devices';
@@ -189,7 +200,7 @@ class ServicioDispositivoAudioReal extends ServicioDispositivoAudio {
return TipoDispositivo.auricularesCable;
case 8:
return TipoDispositivo.bluetoothA2dp;
case 14:
case 22:
return TipoDispositivo.usbAudio;
default:
return TipoDispositivo.desconocido;