In diesem Artikel werden einige Tipps und Tricks zum Debuggen von Android-Audio beschrieben.
T-Stück Waschbecken
Die „Tee-Senke“ ist eine AudioFlinger-Debugging-Funktion, die nur in benutzerdefinierten Builds verfügbar ist und dazu dient, ein kurzes Fragment aktueller Audiodaten für eine spätere Analyse aufzubewahren. Dies ermöglicht einen Vergleich zwischen dem, was tatsächlich abgespielt oder aufgenommen wurde, und dem, was erwartet wurde.
Aus Datenschutzgründen ist die Tee-Senke sowohl zur Kompilierungszeit als auch zur Laufzeit standardmäßig deaktiviert. Um die Tee-Senke verwenden zu können, müssen Sie sie durch erneutes Kompilieren und Festlegen einer Eigenschaft aktivieren. Stellen Sie sicher, dass Sie diese Funktion deaktivieren, nachdem Sie mit dem Debuggen fertig sind. Die Tee-Senke sollte in Produktions-Builds nicht aktiviert bleiben.
Die Anweisungen in diesem Abschnitt gelten für Android 7.x und höher. Ersetzen Sie für Android 5.x und 6.x /data/misc/audioserver
durch /data/misc/media
. Darüber hinaus müssen Sie einen Userdebug- oder Eng-Build verwenden. Wenn Sie einen Userdebug-Build verwenden, deaktivieren Sie Verity mit:
adb root && adb disable-verity && adb reboot
Einrichtung zur Kompilierungszeit
-
cd frameworks/av/services/audioflinger
- Bearbeiten Sie
Configuration.h
. - Kommentieren Sie
#define TEE_SINK
aus. - Erstellen Sie
libaudioflinger.so
neu. -
adb root
-
adb remount
- Übertragen oder synchronisieren Sie die neue
libaudioflinger.so
mit/system/lib
des Geräts.
Laufzeit-Setup
-
adb shell getprop | grep ro.debuggable
Bestätigen Sie, dass die Ausgabe lautet:[ro.debuggable]: [1]
-
adb shell
-
ls -ld /data/misc/audioserver
Bestätigen Sie, dass die Ausgabe wie folgt lautet:
drwx------ media media ... media
Wenn das Verzeichnis nicht existiert, erstellen Sie es wie folgt:
mkdir /data/misc/audioserver
chown media:media /data/misc/audioserver
-
echo af.tee=# > /data/local.prop
Dabei ist deraf.tee
Wert eine unten beschriebene Zahl. -
chmod 644 /data/local.prop
-
reboot
Werte für die af.tee-Eigenschaft
Der Wert von af.tee
ist eine Zahl zwischen 0 und 7 und drückt die Summe mehrerer Bits aus, eines pro Merkmal. Eine kurze Erklärung der einzelnen Bits finden Sie im Code unter AudioFlinger::AudioFlinger()
in AudioFlinger.cpp
:
- 1 = Eingabe
- 2 = FastMixer-Ausgang
- 4 = AudioRecord und AudioTrack pro Spur
Es gibt noch kein Bit für Deep Buffer oder Normal Mixer, aber mit „4“ können Sie ähnliche Ergebnisse erzielen.
Testen und erfassen Sie Daten
- Führen Sie Ihren Audiotest durch.
-
adb shell dumpsys media.audio_flinger
- Suchen Sie in der
dumpsys
Ausgabe nach einer Zeile wie dieser:
tee copied to /data/misc/audioserver/20131010101147_2.wav
Dies ist eine PCM-WAV-Datei. - Dann
adb pull
alle gewünschten/data/misc/audioserver/*.wav
ab; Beachten Sie, dass spurspezifische Dump-Dateinamen nicht in derdumpsys
Ausgabe erscheinen, aber beim Schließen des Tracks weiterhin in/data/misc/audioserver
gespeichert werden. - Überprüfen Sie die Dump-Dateien auf Datenschutzbedenken, bevor Sie sie mit anderen teilen.
Vorschläge
Probieren Sie diese Ideen aus, um nützlichere Ergebnisse zu erzielen:
- Deaktivieren Sie Berührungstöne und Tastenklicks, um Unterbrechungen bei der Testausgabe zu reduzieren.
- Maximieren Sie alle Lautstärken.
- Deaktivieren Sie Apps, die Ton erzeugen oder über das Mikrofon aufnehmen, wenn sie für Ihren Test nicht von Interesse sind.
- Trackspezifische Dumps werden nur gespeichert, wenn der Track geschlossen ist; Möglicherweise müssen Sie das Schließen einer App erzwingen, um deren spurspezifische Daten zu löschen
- Führen Sie die
dumpsys
sofort nach dem Test durch. Der verfügbare Aufnahmeplatz ist begrenzt. - Um sicherzustellen, dass Sie Ihre Dump-Dateien nicht verlieren, laden Sie sie regelmäßig auf Ihren Host hoch. Es bleibt nur eine begrenzte Anzahl von Dump-Dateien erhalten; Ältere Dumps werden entfernt, nachdem dieses Limit erreicht ist.
Wiederherstellen
Wie oben erwähnt, sollte die Tee-Sink-Funktion nicht aktiviert bleiben. Stellen Sie Ihren Build und Ihr Gerät wie folgt wieder her:
- Machen Sie die Quellcodeänderungen in
Configuration.h
rückgängig. - Erstellen Sie
libaudioflinger.so
neu. - Übertragen oder synchronisieren Sie die wiederhergestellte
libaudioflinger.so
mit/system/lib
des Geräts. -
adb shell
-
rm /data/local.prop
-
rm /data/misc/audioserver/*.wav
-
reboot
media.log
ALOGx-Makros
Die standardmäßige Java-Sprachprotokollierungs-API im Android SDK ist android.util.Log .
Die entsprechende C-Sprach-API im Android NDK ist __android_log_print
deklariert in <android/log.h>
.
Im nativen Teil des Android-Frameworks bevorzugen wir Makros mit den Namen ALOGE
, ALOGW
, ALOGI
, ALOGV
usw. Sie werden in <utils/Log.h>
deklariert und für die Zwecke dieses Artikels werden wir sie gemeinsam als ALOGx
bezeichnen .
Alle diese APIs sind einfach zu verwenden und gut verständlich, sodass sie auf der gesamten Android-Plattform verbreitet sind. Insbesondere der mediaserver
Prozess, der den AudioFlinger-Soundserver umfasst, nutzt ALOGx
in großem Umfang.
Dennoch gibt es einige Einschränkungen für ALOGx
und Co.:
- Sie sind anfällig für „Protokoll-Spam“: Der Protokollpuffer ist eine gemeinsam genutzte Ressource, sodass er aufgrund nicht zusammenhängender Protokolleinträge leicht überlaufen kann, was zu fehlenden Informationen führt. Die
ALOGV
Variante ist zur Kompilierungszeit standardmäßig deaktiviert. Aber selbst wenn es aktiviert ist, kann es natürlich zu Protokoll-Spam kommen. - Die zugrunde liegenden Kernel-Systemaufrufe könnten blockieren, was möglicherweise zu einer Prioritätsumkehr und damit zu Messstörungen und Ungenauigkeiten führen könnte. Dies ist insbesondere für zeitkritische Threads wie
FastMixer
undFastCapture
von Bedeutung. - Wenn ein bestimmtes Protokoll deaktiviert wird, um Protokoll-Spam zu reduzieren, gehen alle Informationen verloren, die von diesem Protokoll erfasst worden wären. Es ist nicht möglich, ein bestimmtes Protokoll nachträglich zu aktivieren, nachdem klar ist, dass das Protokoll interessant gewesen wäre.
NBLOG, media.log und MediaLogService
Die NBLOG
APIs und der zugehörige media.log
Prozess sowie MediaLogService
Dienst bilden zusammen ein neueres Protokollierungssystem für Medien und sind speziell für die Lösung der oben genannten Probleme konzipiert. Wir werden den Begriff „media.log“ grob verwenden, um alle drei zu bezeichnen, aber streng genommen ist NBLOG
die C++-Protokollierungs-API, media.log
ist ein Linux-Prozessname und MediaLogService
ist ein Android-Binderdienst zum Untersuchen der Protokolle.
Eine media.log
„Zeitleiste“ ist eine Reihe von Protokolleinträgen, deren relative Reihenfolge beibehalten wird. Konventionell sollte jeder Thread seine eigene Zeitleiste verwenden.
Vorteile
Die Vorteile des media.log
Systems bestehen darin, dass es:
- Versendet das Hauptprotokoll nur dann mit Spam, wenn dies erforderlich ist.
- Kann auch dann untersucht werden, wenn
mediaserver
abstürzt oder hängen bleibt. - Ist pro Zeitleiste nicht blockierend.
- Bietet weniger Leistungsbeeinträchtigungen. (Natürlich ist keine Form der Protokollierung völlig unaufdringlich.)
Die Architektur
Das folgende Diagramm zeigt die Beziehung zwischen dem mediaserver
Prozess und dem init
Prozess, bevor media.log
eingeführt wird:
![Architektur vor media.log](https://source.android.google.cn/static/docs/core/audio/images/medialog_before.png?authuser=6&hl=de)
Abbildung 1. Architektur vor media.log
Bemerkenswerte Punkte:
-
init
forks und execsmediaserver
. -
init
erkennt den Tod vonmediaserver
und führt bei Bedarf einen erneuten Fork durch. -
ALOGx
Protokollierung wird nicht angezeigt.
Das folgende Diagramm zeigt die neue Beziehung der Komponenten, nachdem media.log
zur Architektur hinzugefügt wurde:
![Architektur nach media.log](https://source.android.google.cn/static/docs/core/audio/images/medialog_after.png?authuser=6&hl=de)
Abbildung 2. Architektur nach media.log
Wichtige Änderungen:
- Clients verwenden
NBLOG
API, um Protokolleinträge zu erstellen und diese an einen Ringpuffer im gemeinsam genutzten Speicher anzuhängen. -
MediaLogService
kann den Inhalt des Ringpuffers jederzeit sichern. - Der Ringpuffer ist so konzipiert, dass eine Beschädigung des gemeinsam genutzten Speichers nicht zum Absturz
MediaLogService
führt und dennoch so viel Puffer ausgegeben werden kann, wie nicht von der Beschädigung betroffen ist. - Der Ringpuffer ist sowohl beim Schreiben neuer Einträge als auch beim Lesen vorhandener Einträge nicht blockierend und sperrenfrei.
- Es sind keine Kernel-Systemaufrufe erforderlich, um in den Ringpuffer zu schreiben oder daraus zu lesen (außer optionalen Zeitstempeln).
Wo zu verwenden
Ab Android 4.4 gibt es in AudioFlinger nur noch wenige Protokollpunkte, die das media.log
System verwenden. Obwohl die neuen APIs nicht so einfach zu verwenden sind wie ALOGx
, sind sie auch nicht besonders schwierig. Wir empfehlen Ihnen, sich mit dem neuen Protokollierungssystem vertraut zu machen, wenn es unentbehrlich ist. Dies wird insbesondere für AudioFlinger-Threads empfohlen, die häufig, regelmäßig und ohne Blockierung ausgeführt werden müssen, wie z. B. die Threads FastMixer
und FastCapture
.
Wie benutzt man
Protokolle hinzufügen
Zuerst müssen Sie Ihrem Code Protokolle hinzufügen.
Verwenden Sie in FastMixer
und FastCapture
Threads Code wie diesen:
logWriter->log("string"); logWriter->logf("format", parameters); logWriter->logTimestamp();
Da diese NBLog
Timeline nur von den FastMixer
und FastCapture
Threads verwendet wird, besteht keine Notwendigkeit für einen gegenseitigen Ausschluss.
Verwenden Sie in anderen AudioFlinger-Threads mNBLogWriter
:
mNBLogWriter->log("string"); mNBLogWriter->logf("format", parameters); mNBLogWriter->logTimestamp();
Für andere Threads als FastMixer
und FastCapture
kann die NBLog
Zeitleiste des Threads sowohl vom Thread selbst als auch von Binder-Vorgängen verwendet werden. NBLog::Writer
bietet keinen impliziten gegenseitigen Ausschluss pro Zeitleiste. Stellen Sie daher sicher, dass alle Protokolle in einem Kontext erfolgen, in dem der Mutex- mLock
des Threads gehalten wird.
Nachdem Sie die Protokolle hinzugefügt haben, erstellen Sie AudioFlinger neu.
Achtung: Pro Thread ist eine separate NBLog::Writer
Timeline erforderlich, um die Thread-Sicherheit zu gewährleisten, da Timelines von Natur aus Mutexe auslassen. Wenn Sie möchten, dass mehr als ein Thread dieselbe Zeitleiste verwendet, können Sie dies mit einem vorhandenen Mutex schützen (wie oben für mLock
beschrieben). Oder Sie können den Wrapper NBLog::LockedWriter
anstelle von NBLog::Writer
verwenden. Dies macht jedoch einen Hauptvorteil dieser API zunichte: ihr nicht blockierendes Verhalten.
Die vollständige NBLog
API finden Sie unter frameworks/av/include/media/nbaio/NBLog.h
.
Aktivieren Sie media.log
media.log
ist standardmäßig deaktiviert. Es ist nur aktiv, wenn die Eigenschaft ro.test_harness
1
ist. Sie können es aktivieren, indem Sie:
adb root
adb shell
echo ro.test_harness=1 > /data/local.prop
chmod 644 /data/local.prop
reboot
Die Verbindung geht während des Neustarts verloren, also:
adb shellDer Befehl
ps media
zeigt nun zwei Prozesse an:- media.log
- Medienserver
Notieren Sie sich die Prozess-ID des mediaserver
für später.
Zeigen Sie die Zeitleisten an
Sie können jederzeit manuell einen Protokoll-Dump anfordern. Dieser Befehl zeigt Protokolle aller aktiven und letzten Zeitleisten an und löscht sie anschließend:
dumpsys media.log
Beachten Sie, dass Zeitleisten konstruktionsbedingt unabhängig voneinander sind und es keine Möglichkeit gibt, Zeitleisten zusammenzuführen.
Stellen Sie Protokolle nach dem Tod des Medienservers wieder her
Versuchen Sie nun, den mediaserver
Prozess zu beenden: kill -9 #
, wobei # die Prozess-ID ist, die Sie zuvor notiert haben. Im Haupt- logcat
sollte ein Dump von media.log
angezeigt werden, der alle Protokolle enthält, die zum Absturz geführt haben.
dumpsys media.log