fix(audio): make the audio diagnostics visible in release builds
Every diagnostic line in the audio path used `dart:developer`'s `log()`. That function writes to the VM service, which a RELEASE build does not have — so in the only build that ever runs in a car, all eleven of them went nowhere. `debugPrint`/`print` do reach logcat in release; `log()` does not. That includes the two channels built specifically to end the guessing: - `registrarErrorAudioService`, which subscribes to `AudioService.asyncError` so the plugin's swallowed platform exceptions stop vanishing (b0271fa). It moved them from a dropped PublishSubject to a dropped log call. - `_trazarEstadoPublicado`, the published-state trace added in7054a4cto settle why the car's play button never becomes pause. So "no evidence" was never a quiet app. It was an app writing its evidence somewhere release builds discard. Several rounds of hypotheses were argued without data that the app was already producing. All eleven now use `debugPrint` with a `[PluriWave][Tag]` prefix, so one filter catches the audio path and the existing alarm lines together: adb logcat | grep PluriWave No behaviour changes. Tests: 1158, unchanged.
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer' as developer;
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
@@ -94,16 +93,18 @@ StreamSubscription<Object> observarErroresAudio(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Default [observarErroresAudio] logger: one `[PluriWave]`-prefixed
|
/// Default [observarErroresAudio] logger: one line per swallowed plugin
|
||||||
/// `developer.log` line per swallowed plugin exception, at the same
|
/// exception.
|
||||||
/// `level: 900` (SEVERE) that `servicio_audio.dart`'s existing error lines
|
///
|
||||||
/// use, so a single logcat/DevTools filter catches both.
|
/// Uses [debugPrint], NOT `dart:developer`'s `log`. That distinction is the
|
||||||
|
/// whole reason this channel existed for weeks without ever producing a
|
||||||
|
/// single line of evidence: `log()` writes to the VM service, which a
|
||||||
|
/// RELEASE build does not have, so every exception this was built to catch
|
||||||
|
/// was still being thrown away — just one layer further down than before.
|
||||||
|
/// `debugPrint` reaches logcat in release, which is the only build that ever
|
||||||
|
/// runs in the car.
|
||||||
void registrarErrorAudioService(Object error) {
|
void registrarErrorAudioService(Object error) {
|
||||||
developer.log(
|
debugPrint('[PluriWave][ArranqueAudio] AudioService.asyncError: $error');
|
||||||
'[PluriWave] AudioService.asyncError: $error',
|
|
||||||
name: 'ArranqueAudio',
|
|
||||||
level: 900,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Minimal branded bootstrap widget for the degraded path (Design "still
|
/// Minimal branded bootstrap widget for the degraded path (Design "still
|
||||||
|
|||||||
@@ -97,7 +97,5 @@ DecisionAvanceCola decidirAvanceCola({
|
|||||||
/// distinct `ColaLocal` during the async URI-resolve gap is correctly
|
/// distinct `ColaLocal` during the async URI-resolve gap is correctly
|
||||||
/// detected as stale and aborts the advance, instead of silently racing an
|
/// detected as stale and aborts the advance, instead of silently racing an
|
||||||
/// external play/stop.
|
/// external play/stop.
|
||||||
bool avanceEsValido(
|
bool avanceEsValido(ColaLocal? colaLocalActual, ColaLocal? siguienteEsperado) =>
|
||||||
ColaLocal? colaLocalActual,
|
identical(colaLocalActual, siguienteEsperado);
|
||||||
ColaLocal? siguienteEsperado,
|
|
||||||
) => identical(colaLocalActual, siguienteEsperado);
|
|
||||||
|
|||||||
@@ -61,9 +61,10 @@ String nombreCarpetaDesdeUri(String treeUri, {required String nombreGenerico}) {
|
|||||||
segmento = documentId.substring(ultimaBarra + 1);
|
segmento = documentId.substring(ultimaBarra + 1);
|
||||||
} else {
|
} else {
|
||||||
final ultimosDosPuntos = documentId.lastIndexOf(':');
|
final ultimosDosPuntos = documentId.lastIndexOf(':');
|
||||||
segmento = ultimosDosPuntos >= 0
|
segmento =
|
||||||
? documentId.substring(ultimosDosPuntos + 1)
|
ultimosDosPuntos >= 0
|
||||||
: documentId;
|
? documentId.substring(ultimosDosPuntos + 1)
|
||||||
|
: documentId;
|
||||||
}
|
}
|
||||||
|
|
||||||
final recortado = segmento.trim();
|
final recortado = segmento.trim();
|
||||||
@@ -200,10 +201,9 @@ class FuenteMusicaLocalAutoImpl implements FuenteMusicaLocalAuto {
|
|||||||
try {
|
try {
|
||||||
final uri = await _uriPersistida();
|
final uri = await _uriPersistida();
|
||||||
if (uri == null || uri.isEmpty) return false;
|
if (uri == null || uri.isEmpty) return false;
|
||||||
final valido = await _canal.invokeMethod<bool>(
|
final valido = await _canal.invokeMethod<bool>('hasPersistedPermission', {
|
||||||
'hasPersistedPermission',
|
'treeUri': uri,
|
||||||
{'treeUri': uri},
|
});
|
||||||
);
|
|
||||||
return valido ?? false;
|
return valido ?? false;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
// Cold-start / revoked-permission safety (Spec "Permission revoked or
|
// Cold-start / revoked-permission safety (Spec "Permission revoked or
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer' as developer;
|
|
||||||
import 'dart:ui' show Locale;
|
import 'dart:ui' show Locale;
|
||||||
|
|
||||||
import 'package:audio_service/audio_service.dart';
|
import 'package:audio_service/audio_service.dart';
|
||||||
import 'package:flutter/foundation.dart' show visibleForTesting;
|
import 'package:flutter/foundation.dart' show debugPrint, visibleForTesting;
|
||||||
import 'package:just_audio/just_audio.dart';
|
import 'package:just_audio/just_audio.dart';
|
||||||
|
|
||||||
import '../l10n/display_names.dart';
|
import '../l10n/display_names.dart';
|
||||||
@@ -836,7 +835,14 @@ class PluriWaveAudioHandler extends BaseAudioHandler
|
|||||||
/// `eqDisponible` rides along because the equalizer custom action is gated
|
/// `eqDisponible` rides along because the equalizer custom action is gated
|
||||||
/// on it and the flag is otherwise unobservable — one car session answers
|
/// on it and the flag is otherwise unobservable — one car session answers
|
||||||
/// both questions at once:
|
/// both questions at once:
|
||||||
/// adb logcat -s ServicioAudio
|
///
|
||||||
|
/// adb logcat | grep PluriWave
|
||||||
|
///
|
||||||
|
/// It uses [debugPrint] and NOT `dart:developer`'s `log`, and that is not a
|
||||||
|
/// style choice. `log()` writes to the VM service, which a RELEASE build
|
||||||
|
/// does not have — so this trace, and every error line in this file, was
|
||||||
|
/// invisible in the only build that ever runs in a car. Weeks of "no
|
||||||
|
/// evidence" were this, not a quiet app. Do not convert these back.
|
||||||
void _trazarEstadoPublicado() {
|
void _trazarEstadoPublicado() {
|
||||||
final s = playbackState.value;
|
final s = playbackState.value;
|
||||||
final traza =
|
final traza =
|
||||||
@@ -846,7 +852,7 @@ class PluriWaveAudioHandler extends BaseAudioHandler
|
|||||||
'controles=${s.controls.length}';
|
'controles=${s.controls.length}';
|
||||||
if (traza == _ultimaTrazaEstado) return;
|
if (traza == _ultimaTrazaEstado) return;
|
||||||
_ultimaTrazaEstado = traza;
|
_ultimaTrazaEstado = traza;
|
||||||
developer.log(traza, name: 'ServicioAudio');
|
debugPrint('[PluriWave][ServicioAudio] estado $traza');
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Binds [construirControlesTransporte] — which holds the whole contract,
|
/// Binds [construirControlesTransporte] — which holds the whole contract,
|
||||||
@@ -909,11 +915,7 @@ class PluriWaveAudioHandler extends BaseAudioHandler
|
|||||||
mensaje = _textos.audioErrorGeneric;
|
mensaje = _textos.audioErrorGeneric;
|
||||||
}
|
}
|
||||||
|
|
||||||
developer.log(
|
debugPrint('[PluriWave][ServicioAudio] Error reproducción: $codigoLog');
|
||||||
'[PluriWave] Error reproducción: $codigoLog',
|
|
||||||
name: 'ServicioAudio',
|
|
||||||
level: 900,
|
|
||||||
);
|
|
||||||
|
|
||||||
_detenerReconexion();
|
_detenerReconexion();
|
||||||
playbackState.add(
|
playbackState.add(
|
||||||
@@ -956,12 +958,10 @@ class PluriWaveAudioHandler extends BaseAudioHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
_reconectando = true;
|
_reconectando = true;
|
||||||
developer.log(
|
debugPrint(
|
||||||
'[PluriWave] Stall de red, reintento ${_reconexion.intentos}/'
|
'[PluriWave][ServicioAudio] Stall de red, reintento ${_reconexion.intentos}/'
|
||||||
'${_reconexion.maxReintentos} en '
|
'${_reconexion.maxReintentos} en '
|
||||||
'${_reconexion.retrasoParaIntento(_reconexion.intentos).inSeconds}s',
|
'${_reconexion.retrasoParaIntento(_reconexion.intentos).inSeconds}s',
|
||||||
name: 'ServicioAudio',
|
|
||||||
level: 800,
|
|
||||||
);
|
);
|
||||||
playbackState.add(
|
playbackState.add(
|
||||||
playbackState.value.copyWith(
|
playbackState.value.copyWith(
|
||||||
@@ -1207,11 +1207,8 @@ class PluriWaveAudioHandler extends BaseAudioHandler
|
|||||||
rethrow;
|
rethrow;
|
||||||
} on Exception catch (e, stackTrace) {
|
} on Exception catch (e, stackTrace) {
|
||||||
_cambiandoFuente = false;
|
_cambiandoFuente = false;
|
||||||
developer.log(
|
debugPrint(
|
||||||
'[PluriWave] Error inesperado en playMediaItem: $e',
|
'[PluriWave][ServicioAudio] Error inesperado en playMediaItem: $e',
|
||||||
name: 'ServicioAudio',
|
|
||||||
level: 900,
|
|
||||||
stackTrace: stackTrace,
|
|
||||||
);
|
);
|
||||||
if (revision == _revisionFuente) {
|
if (revision == _revisionFuente) {
|
||||||
playbackState.add(
|
playbackState.add(
|
||||||
@@ -1264,11 +1261,8 @@ class PluriWaveAudioHandler extends BaseAudioHandler
|
|||||||
void _iniciarPlaySinBloquear(MediaItem mediaItem, int revision) {
|
void _iniciarPlaySinBloquear(MediaItem mediaItem, int revision) {
|
||||||
unawaited(
|
unawaited(
|
||||||
_player.play().catchError((Object error, StackTrace stackTrace) {
|
_player.play().catchError((Object error, StackTrace stackTrace) {
|
||||||
developer.log(
|
debugPrint(
|
||||||
'[PluriWave] Error al iniciar ${mediaItem.title}: $error',
|
'[PluriWave][ServicioAudio] Error al iniciar ${mediaItem.title}: $error',
|
||||||
name: 'ServicioAudio',
|
|
||||||
level: 900,
|
|
||||||
stackTrace: stackTrace,
|
|
||||||
);
|
);
|
||||||
if (revision == _revisionFuente) {
|
if (revision == _revisionFuente) {
|
||||||
_gestionarErrorReproduccion(error);
|
_gestionarErrorReproduccion(error);
|
||||||
@@ -1553,11 +1547,7 @@ class PluriWaveAudioHandler extends BaseAudioHandler
|
|||||||
if (destino == null) return;
|
if (destino == null) return;
|
||||||
await playMediaItem(mediaItemParaEmisora(destino, l10n: _textos));
|
await playMediaItem(mediaItemParaEmisora(destino, l10n: _textos));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
developer.log(
|
debugPrint('[PluriWave][ServicioAudio] Error saltando de emisora: $e');
|
||||||
'[PluriWave] Error saltando de emisora: $e',
|
|
||||||
name: 'ServicioAudio',
|
|
||||||
level: 900,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1720,10 +1710,8 @@ class PluriWaveAudioHandler extends BaseAudioHandler
|
|||||||
if (emisora == null) return;
|
if (emisora == null) return;
|
||||||
await playMediaItem(mediaItemParaEmisora(emisora, l10n: _textos));
|
await playMediaItem(mediaItemParaEmisora(emisora, l10n: _textos));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
developer.log(
|
debugPrint(
|
||||||
'[PluriWave] Error en playFromSearch($query): $e',
|
'[PluriWave][ServicioAudio] Error en playFromSearch($query): $e',
|
||||||
name: 'ServicioAudio',
|
|
||||||
level: 900,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1793,10 +1781,8 @@ class PluriWaveAudioHandler extends BaseAudioHandler
|
|||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Spec "Unknown or stale media id": never propagate from the handler.
|
// Spec "Unknown or stale media id": never propagate from the handler.
|
||||||
developer.log(
|
debugPrint(
|
||||||
'[PluriWave] Error en playFromMediaId($mediaId): $e',
|
'[PluriWave][ServicioAudio] Error en playFromMediaId($mediaId): $e',
|
||||||
name: 'ServicioAudio',
|
|
||||||
level: 900,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer' as developer;
|
|
||||||
|
|
||||||
import 'package:audio_session/audio_session.dart';
|
import 'package:audio_session/audio_session.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
@@ -89,10 +88,8 @@ class ServicioAudioSession {
|
|||||||
(_) => unawaited(manejarDesconexionSalida()),
|
(_) => unawaited(manejarDesconexionSalida()),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
developer.log(
|
debugPrint(
|
||||||
'[PluriWave] No se pudo configurar la sesion de audio: $e',
|
'[PluriWave][ServicioAudioSession] No se pudo configurar la sesion de audio: $e',
|
||||||
name: 'ServicioAudioSession',
|
|
||||||
level: 900,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,7 +157,9 @@ class ServicioDispositivoAudioReal extends ServicioDispositivoAudio {
|
|||||||
@override
|
@override
|
||||||
Future<Map<String, String>> obtenerNombresEmparejados() async {
|
Future<Map<String, String>> obtenerNombresEmparejados() async {
|
||||||
try {
|
try {
|
||||||
final raw = await _methodChannel.invokeMethod<Map>('getBondedDeviceNames');
|
final raw = await _methodChannel.invokeMethod<Map>(
|
||||||
|
'getBondedDeviceNames',
|
||||||
|
);
|
||||||
if (raw == null) return const {};
|
if (raw == null) return const {};
|
||||||
return {
|
return {
|
||||||
for (final entry in raw.entries)
|
for (final entry in raw.entries)
|
||||||
|
|||||||
@@ -70,7 +70,10 @@ class ServicioEcualizador {
|
|||||||
final porEmisora = _leerPresetsPorEmisora(prefs);
|
final porEmisora = _leerPresetsPorEmisora(prefs);
|
||||||
final presetsDispositivo = _leerMapa(prefs, _keyPresetsPorDispositivo);
|
final presetsDispositivo = _leerMapa(prefs, _keyPresetsPorDispositivo);
|
||||||
final presetsMatriz = _leerMapa(prefs, _keyPresetsMatriz);
|
final presetsMatriz = _leerMapa(prefs, _keyPresetsMatriz);
|
||||||
final nombresDispositivos = _leerMapaStrings(prefs, _keyNombresDispositivos);
|
final nombresDispositivos = _leerMapaStrings(
|
||||||
|
prefs,
|
||||||
|
_keyNombresDispositivos,
|
||||||
|
);
|
||||||
return ConfiguracionEcualizador(
|
return ConfiguracionEcualizador(
|
||||||
principal: principal,
|
principal: principal,
|
||||||
porEmisora: porEmisora,
|
porEmisora: porEmisora,
|
||||||
@@ -124,21 +127,22 @@ class ServicioEcualizador {
|
|||||||
String prefijo,
|
String prefijo,
|
||||||
) async {
|
) async {
|
||||||
final presetsPorDispositivo = _leerMapa(prefs, _keyPresetsPorDispositivo);
|
final presetsPorDispositivo = _leerMapa(prefs, _keyPresetsPorDispositivo);
|
||||||
final dispositivos = presetsPorDispositivo.keys
|
final dispositivos =
|
||||||
.where((clave) => clave.startsWith(prefijo))
|
presetsPorDispositivo.keys
|
||||||
.toList();
|
.where((clave) => clave.startsWith(prefijo))
|
||||||
|
.toList();
|
||||||
|
|
||||||
final nombres = _leerMapaStrings(prefs, _keyNombresDispositivos);
|
final nombres = _leerMapaStrings(prefs, _keyNombresDispositivos);
|
||||||
final nombresAPurgar = nombres.keys
|
final nombresAPurgar =
|
||||||
.where((clave) => clave.startsWith(prefijo))
|
nombres.keys.where((clave) => clave.startsWith(prefijo)).toList();
|
||||||
.toList();
|
|
||||||
|
|
||||||
final matriz = _leerMapa(prefs, _keyPresetsMatriz);
|
final matriz = _leerMapa(prefs, _keyPresetsMatriz);
|
||||||
final matrizAPurgar = matriz.keys.where((clave) {
|
final matrizAPurgar =
|
||||||
final separador = clave.indexOf(':');
|
matriz.keys.where((clave) {
|
||||||
if (separador == -1) return false;
|
final separador = clave.indexOf(':');
|
||||||
return clave.substring(separador + 1).startsWith(prefijo);
|
if (separador == -1) return false;
|
||||||
}).toList();
|
return clave.substring(separador + 1).startsWith(prefijo);
|
||||||
|
}).toList();
|
||||||
|
|
||||||
for (final clave in dispositivos) {
|
for (final clave in dispositivos) {
|
||||||
presetsPorDispositivo.remove(clave);
|
presetsPorDispositivo.remove(clave);
|
||||||
@@ -183,11 +187,12 @@ class ServicioEcualizador {
|
|||||||
// (station UUIDs are RFC4122 and contain no colons — multi-device-eq
|
// (station UUIDs are RFC4122 and contain no colons — multi-device-eq
|
||||||
// ADR-3), since deviceId itself may contain colons (e.g. a MAC-based id).
|
// ADR-3), since deviceId itself may contain colons (e.g. a MAC-based id).
|
||||||
final presetsMatriz = _leerMapa(prefs, _keyPresetsMatriz);
|
final presetsMatriz = _leerMapa(prefs, _keyPresetsMatriz);
|
||||||
final clavesMatrizAPurgar = presetsMatriz.keys.where((clave) {
|
final clavesMatrizAPurgar =
|
||||||
final separador = clave.indexOf(':');
|
presetsMatriz.keys.where((clave) {
|
||||||
if (separador == -1) return false;
|
final separador = clave.indexOf(':');
|
||||||
return clave.substring(separador + 1) == deviceId;
|
if (separador == -1) return false;
|
||||||
}).toList();
|
return clave.substring(separador + 1) == deviceId;
|
||||||
|
}).toList();
|
||||||
if (clavesMatrizAPurgar.isNotEmpty) {
|
if (clavesMatrizAPurgar.isNotEmpty) {
|
||||||
for (final clave in clavesMatrizAPurgar) {
|
for (final clave in clavesMatrizAPurgar) {
|
||||||
presetsMatriz.remove(clave);
|
presetsMatriz.remove(clave);
|
||||||
|
|||||||
Reference in New Issue
Block a user