Audio-Debugging

In diesem Artikel finden Sie einige Tipps und Tricks zur Fehlerbehebung bei Android-Audio.

T-Senke

„Tee sink“ ist eine AudioFlinger-Debugging-Funktion, die nur in benutzerdefinierten Builds verfügbar ist. Damit wird ein kurzes Fragment der letzten Audioaufnahme für eine spätere Analyse beibehalten. So können Sie vergleichen, was tatsächlich abgespielt oder aufgezeichnet wurde, und was erwartet wurde.

Aus Gründen des Datenschutzes ist der Tee-Sink sowohl zur Kompilierungs- als auch zur Laufzeit standardmäßig deaktiviert. Wenn Sie die Tee-Senke verwenden möchten, müssen Sie sie aktivieren, indem Sie sie neu kompilieren und ein Attribut festlegen. Deaktivieren Sie diese Funktion, nachdem Sie mit der Fehlerbehebung fertig sind. Der Tee-Sink sollte in Produktionsbuilds nicht aktiviert bleiben.

Die Anleitung in diesem Abschnitt gilt für Android 7.x und höher. Ersetzen Sie unter Android 5.x und 6.x /data/misc/audioserver durch /data/misc/media. Außerdem müssen Sie eine userdebug- oder eng-Build verwenden. Wenn Sie einen Userdebug-Build verwenden, deaktivieren Sie die Verity-Funktion mit:

adb root && adb disable-verity && adb reboot

Einrichtung zur Kompilierungszeit

  1. cd frameworks/av/services/audioflinger
  2. Configuration.h bearbeiten
  3. Entfernen Sie den Kommentar zu #define TEE_SINK.
  4. Erstellen Sie libaudioflinger.so neu.
  5. adb root
  6. adb remount
  7. Übertragen oder synchronisieren Sie die neue libaudioflinger.so mit der /system/lib des Geräts.

Laufzeiteinrichtung

  1. adb shell getprop | grep ro.debuggable
    Prüfen Sie, ob die Ausgabe so aussieht: [ro.debuggable]: [1]
  2. adb shell
  3. ls -ld /data/misc/audioserver

    Prüfen Sie, ob die Ausgabe:

    drwx------ media media ... media
    

    Wenn das Verzeichnis nicht vorhanden ist, erstellen Sie es so:

    mkdir /data/misc/audioserver
    chown media:media /data/misc/audioserver
    
  4. echo af.tee=# > /data/local.prop
    Der Wert af.tee ist eine Zahl, die unten beschrieben wird.
  5. chmod 644 /data/local.prop
  6. reboot

Werte für das Attribut „af.tee“

Der Wert von af.tee ist eine Zahl zwischen 0 und 7, die die Summe mehrerer Bits ausdrückt, jeweils eines pro Merkmal. Eine Erklärung der einzelnen Bits findest du im Code bei AudioFlinger::AudioFlinger() in AudioFlinger.cpp. Hier eine kurze Zusammenfassung:

  • 1 = Eingabe
  • 2 = FastMixer-Ausgabe
  • 4 = AudioRecord und AudioTrack pro Spur

Es gibt noch kein Bit für Deep Buffer oder Normal Mixer, aber du kannst ähnliche Ergebnisse mit „4“ erzielen.

Daten testen und erfassen

  1. Führen Sie den Audiotest aus.
  2. adb shell dumpsys media.audio_flinger
  3. 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.
  4. Anschließend adb pull alle gewünschten /data/misc/audioserver/*.wav-Dateien. Beachte, dass trackspezifische Dumpdateinamen nicht in der dumpsys-Ausgabe erscheinen, aber beim Schließen des Tracks trotzdem in /data/misc/audioserver gespeichert werden.
  5. Prüfen Sie die Dumpdateien auf Datenschutzrisiken, bevor Sie sie für andere freigeben.

Vorschläge

Mit diesen Ideen erhalten Sie nützlichere Ergebnisse:

  • Deaktivieren Sie Berührungsgeräusche und Tastenklicks, um Unterbrechungen in der Testausgabe zu reduzieren.
  • Maximieren Sie alle Lautstärken.
  • Deaktivieren Sie Apps, die Töne ausgeben oder über das Mikrofon aufzeichnen, wenn sie für Ihren Test nicht relevant sind.
  • Daten zu einzelnen Tracks werden nur gespeichert, wenn der Track geschlossen ist. Möglicherweise müssen Sie eine App zum Dumpen der trackspezifischen Daten erzwingen.
  • Führen Sie die dumpsys unmittelbar nach dem Test durch. Es ist nur begrenzter Aufnahmespeicher verfügbar.
  • Damit Sie Ihre Dumpdateien nicht verlieren, sollten Sie sie regelmäßig auf Ihren Host hochladen. Es wird nur eine begrenzte Anzahl von Dumpdateien aufbewahrt. Ältere Dumps werden entfernt, sobald dieses Limit erreicht ist.

Wiederherstellen

Wie bereits erwähnt, sollte die Funktion „Tee-Senke“ nicht aktiviert bleiben. So stellen Sie die Build-Version und das Gerät wieder her:

  1. Nehmen Sie die Änderungen am Quellcode auf Configuration.h zurück.
  2. Erstellen Sie libaudioflinger.so neu.
  3. Pushen oder synchronisieren Sie die wiederhergestellte libaudioflinger.so mit der /system/lib des Geräts.
  4. adb shell
  5. rm /data/local.prop
  6. rm /data/misc/audioserver/*.wav
  7. reboot

media.log

ALOGx-Makros

Die standardmäßige Java-Logging API im Android SDK ist android.util.Log.

Die entsprechende C-Sprach-API in Android NDK ist __android_log_print und wird in <android/log.h> deklariert.

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 in diesem Artikel werden sie gemeinsam als ALOGx bezeichnet.

Alle diese APIs sind einfach zu verwenden und gut bekannt, daher werden sie auf der gesamten Android-Plattform eingesetzt. Insbesondere der Prozess mediaserver, der den AudioFlinger-Soundserver enthält, nutzt ALOGx intensiv.

Es gibt jedoch einige Einschränkungen für ALOGx und Freunde:

  • Sie sind anfällig für „Log-Spam“: Der Log-Puffer ist eine freigegebene Ressource und kann daher leicht aufgrund nicht verwandter Logeinträge überlaufen, was zu fehlenden Informationen führt. Die ALOGV-Variante ist standardmäßig zur Kompilierungszeit deaktiviert. Aber natürlich kann es auch zu Log-Spam kommen, wenn diese Option aktiviert ist.
  • Die zugrunde liegenden Kernelsystemaufrufe können blockieren, was zu einer Prioritätsumkehrung und infolgedessen zu Messstörungen und Ungenauigkeiten führen kann. Das ist besonders wichtig für zeitkritische Threads wie FastMixer und FastCapture.
  • Wenn ein bestimmtes Protokoll deaktiviert wird, um Protokollspam zu reduzieren, gehen alle Informationen verloren, die in diesem Protokoll erfasst worden wären. Es ist nicht möglich, ein bestimmtes Protokoll rückwirkend zu aktivieren, nachdem klar geworden ist, dass es interessant gewesen wäre.

NBLOG, media.log und MediaLogService

Die NBLOG APIs und der zugehörige media.log-Prozess und MediaLogService-Dienst bilden zusammen ein neueres Logging-System für Medien und wurden speziell entwickelt, um die oben genannten Probleme zu beheben. Wir verwenden den Begriff „media.log“ im weiteren Sinne für alle drei. Streng genommen ist NBLOG die C++-Logging-API, media.log ein Linux-Prozessname und MediaLogService ein Android-Binderdienst zum Prüfen der Protokolle.

Eine media.log-Zeitachse besteht aus einer Reihe von Logeinträgen, deren relative Reihenfolge beibehalten wird. Normalerweise sollte jeder Thread eine eigene Zeitachse haben.

Vorteile

Das media.log-System bietet folgende Vorteile:

  • Das Hauptprotokoll wird nur dann mit Informationen geflutet, wenn es erforderlich ist.
  • Kann auch dann geprüft werden, wenn mediaserver abstürzt oder hängt.
  • Ist nicht blockierend pro Zeitachse.
  • Sie beeinträchtigen die Leistung weniger. Natürlich ist keine Logging-Methode völlig unaufdringlich.

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

Abbildung 1: Architektur vor media.log

Wichtige Punkte:

  • initforks und execs mediaserver.
  • init erkennt den Tod von mediaserver und führt bei Bedarf eine neue Fork durch.
  • ALOGx Protokollierung wird nicht angezeigt.

Das folgende Diagramm zeigt die neue Beziehung der Komponenten, nachdem media.log der Architektur hinzugefügt wurde:

Architektur nach media.log

Abbildung 2: Architektur nach media.log

Wichtige Änderungen:

  • Clients verwenden die NBLOG API, um Protokolleinträge zu erstellen und an einen zyklischen Puffer im gemeinsamen Speicher anzuhängen.
  • MediaLogService kann den Inhalt des zyklischen Puffers jederzeit ausgeben.
  • Der zyklische Puffer ist so konzipiert, dass eine Beschädigung des gemeinsamen Speichers nicht zu einem Absturz von MediaLogService führt. Außerdem kann so viel wie möglich des Puffers, der nicht von der Beschädigung betroffen ist, gedumpt werden.
  • Der zyklische Puffer ist sowohl für das Schreiben neuer Einträge als auch für das Lesen vorhandener Einträge nicht blockierend und frei von Sperren.
  • Zum Schreiben in den zyklischen Puffer oder zum Lesen daraus sind keine Kernel-Systemaufrufe erforderlich (außer optionale Zeitstempel).

Verfügbarkeit

Ab Android 4.4 gibt es in AudioFlinger nur noch wenige Protokollpunkte, die das media.log-System verwenden. Die neuen APIs sind zwar nicht so einfach zu verwenden wie ALOGx, aber auch nicht extrem schwierig. Wir empfehlen Ihnen, sich mit dem neuen Logging-System vertraut zu machen, für den Fall, dass es unverzichtbar ist. Dies wird insbesondere für AudioFlinger-Threads empfohlen, die häufig, regelmäßig und ohne Blockierung ausgeführt werden müssen, z. B. die FastMixer- und FastCapture-Threads.

Verwendung

Protokolle hinzufügen

Dazu 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-Zeitachse nur von den Threads FastMixer und FastCapture verwendet wird, ist keine gegenseitige Ausschließung erforderlich.

In anderen AudioFlinger-Threads verwenden Sie mNBLogWriter:

mNBLogWriter->log("string");
mNBLogWriter->logf("format", parameters);
mNBLogWriter->logTimestamp();

Bei anderen Threads als FastMixer und FastCapture kann die NBLog-Zeitachse des Threads sowohl vom Thread selbst als auch von Binder-Vorgängen verwendet werden. NBLog::Writer bietet keinen impliziten gegenseitigen Ausschluss pro Zeitachse. Achten Sie daher darauf, dass alle Protokolle in einem Kontext auftreten, in dem der Mutex mLock des Threads gehalten wird.

Nachdem Sie die Protokolle hinzugefügt haben, bauen Sie AudioFlinger neu.

Achtung:Pro Thread ist eine separate NBLog::Writer-Zeitachse erforderlich, um die Threadsicherheit zu gewährleisten, da Zeitachsen standardmäßig keine Mutexe enthalten. Wenn mehrere Threads dieselbe Zeitachse verwenden sollen, können Sie sie mit einem vorhandenen Mutex schützen (wie oben für mLock beschrieben). Sie können auch den NBLog::LockedWriter-Wrapper anstelle von NBLog::Writer verwenden. Dadurch wird jedoch ein Hauptvorteil dieser API aufgehoben: ihr nicht blockierendes Verhalten.

Die vollständige NBLog API findest du unter frameworks/av/include/media/nbaio/NBLog.h.

media.log aktivieren

media.log ist standardmäßig deaktiviert. Sie ist nur aktiv, wenn das Attribut ro.test_harness den Wert 1 hat. So aktivieren Sie die Funktion:

adb root
adb shell
echo ro.test_harness=1 > /data/local.prop
chmod 644 /data/local.prop
reboot

Die Verbindung wird während des Neustarts unterbrochen. Gehen Sie so vor:

adb shell
Der Befehl ps media zeigt jetzt zwei Prozesse an:
  • media.log
  • mediaserver

Notieren Sie sich die Prozess-ID von mediaserver für später.

Zeitleisten anzeigen

Sie können jederzeit manuell einen Log-Dump anfordern. Mit diesem Befehl werden Protokolle aus allen aktiven und letzten Zeitachsen angezeigt und dann gelöscht:

dumpsys media.log

Zeitleisten sind unabhängig und können nicht zusammengeführt werden.

Protokolle nach einem Ausfall des Mediaservers wiederherstellen

Versuchen Sie jetzt, den Prozess mediaserver: kill -9 # zu beenden, wobei # die zuvor notierte Prozess-ID ist. Im Haupt-logcat sollte ein Dump von media.log angezeigt werden, der alle Logs enthält, die zum Absturz geführt haben.

dumpsys media.log