fix(eq): stop the phone's FM sink from posing as the active output
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:
@@ -81,10 +81,12 @@ void main() {
|
||||
expect(device.tipo, TipoDispositivo.bluetoothA2dp);
|
||||
});
|
||||
|
||||
test('obtenerDispositivoActual maps usb_headset (type=14)', () async {
|
||||
// AudioDeviceInfo.TYPE_USB_HEADSET is 22, not 14. This mapping used to say
|
||||
// 14, which is TYPE_FM — an output many phones expose permanently.
|
||||
test('obtenerDispositivoActual maps usb_headset (type=22)', () async {
|
||||
stubGetActiveDevice({
|
||||
'id': 'usb_headset:0001:0002',
|
||||
'type': 14,
|
||||
'type': 22,
|
||||
'name': 'USB Headset',
|
||||
});
|
||||
final device = await servicio.obtenerDispositivoActual();
|
||||
@@ -92,6 +94,17 @@ void main() {
|
||||
expect(device.tipo, TipoDispositivo.usbAudio);
|
||||
});
|
||||
|
||||
test('type 14 is FM, never USB audio', () async {
|
||||
stubGetActiveDevice({
|
||||
'id': 'other:14:4',
|
||||
'type': 14,
|
||||
'name': 'FM',
|
||||
});
|
||||
final device = await servicio.obtenerDispositivoActual();
|
||||
expect(device.tipo, isNot(TipoDispositivo.usbAudio));
|
||||
expect(device.tipo, TipoDispositivo.desconocido);
|
||||
});
|
||||
|
||||
test('obtenerDispositivoActual maps unknown type to desconocido', () async {
|
||||
stubGetActiveDevice({
|
||||
'id': 'unknown_device',
|
||||
|
||||
@@ -648,6 +648,37 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
// A build that ranked every unlisted AudioDeviceInfo type above the phone
|
||||
// speaker selected permanent internal sinks (TYPE_FM on Xiaomi) as the
|
||||
// active output and persisted `other:` rows for them.
|
||||
test('removes other: rows persisted for internal sinks', () async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final servicio = ServicioEcualizador(prefs: prefs);
|
||||
await servicio.guardarPresetDispositivo(
|
||||
'other:14:4',
|
||||
PresetEcualizador.jazz,
|
||||
);
|
||||
await servicio.guardarNombresDispositivos({'other:14:4': 'Omoda'});
|
||||
await servicio.guardarPresetMatriz(
|
||||
'station1:other:14:4',
|
||||
PresetEcualizador.pop,
|
||||
);
|
||||
await servicio.guardarPresetDispositivo(
|
||||
stableKey,
|
||||
PresetEcualizador.rock,
|
||||
);
|
||||
|
||||
final config = await servicio.cargar();
|
||||
|
||||
expect(config.presetsDispositivo.containsKey('other:14:4'), isFalse);
|
||||
expect(config.nombresDispositivos.containsKey('other:14:4'), isFalse);
|
||||
expect(config.presetsMatriz.containsKey('station1:other:14:4'), isFalse);
|
||||
expect(
|
||||
config.presetsDispositivo[stableKey],
|
||||
equals(PresetEcualizador.rock),
|
||||
);
|
||||
});
|
||||
|
||||
test('is guarded: a re-seeded base entry survives a second load', () async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final servicio = ServicioEcualizador(prefs: prefs);
|
||||
|
||||
Reference in New Issue
Block a user