Bài viết này mô tả một số mẹo và thủ thuật để gỡ lỗi âm thanh Android.
Tee Sink
"Tách chìm" là một tính năng gỡ lỗi AudioFlinger, chỉ có sẵn trong các bản dựng tùy chỉnh, để giữ lại một đoạn ngắn của âm thanh gần đây để phân tích sau này. Điều này cho phép so sánh giữa những gì đã thực sự được phát hoặc được ghi lại với những gì được mong đợi.
Đối với sự riêng tư, bồn rửa tee bị tắt theo mặc định, ở cả thời gian biên dịch và thời gian chạy. Để sử dụng bồn rửa tee, bạn sẽ cần phải kích hoạt nó bằng cách biên dịch lại và cũng bằng cách thiết lập một thuộc tính. Đảm bảo tắt tính năng này sau khi bạn gỡ lỗi xong; không nên bật bồn rửa tee trong các phiên bản sản xuất.
Hướng dẫn trong phần này dành cho Android 7.x trở lên. Đối với Android 5.x và 6.x, thay thế /data/misc/audioserver
bằng /data/misc/media
. Ngoài ra, bạn phải sử dụng một bản dựng userdebug hoặc eng. Nếu bạn sử dụng bản dựng userdebug, thì hãy tắt tính năng xác thực bằng:
adb root && adb disable-verity && adb reboot
Thiết lập thời gian biên dịch
-
cd frameworks/av/services/audioflinger
- Chỉnh sửa
Configuration.h
- Uncomment
#define TEE_SINK
. - Xây dựng lại
libaudioflinger.so
. -
adb root
-
adb remount
- Đẩy hoặc đồng bộ
libaudioflinger.so
mới với/system/lib
của thiết bị.
Thiết lập thời gian chạy
-
adb shell getprop | grep ro.debuggable
Xác nhận rằng đầu ra là:[ro.debuggable]: [1]
-
adb shell
-
ls -ld /data/misc/audioserver
Xác nhận rằng đầu ra là:
drwx------ media media ... media
Nếu thư mục không tồn tại, hãy tạo nó như sau:
mkdir /data/misc/audioserver
chown media:media /data/misc/audioserver
-
echo af.tee=# > /data/local.prop
Trong đó giá trịaf.tee
là một số được mô tả bên dưới. -
chmod 644 /data/local.prop
-
reboot
Giá trị cho tài sản af.tee
Giá trị của af.tee
là một số từ 0 đến 7, thể hiện tổng của một số bit, mỗi bit cho mỗi đặc trưng. Xem mã tại AudioFlinger::AudioFlinger()
trong AudioFlinger.cpp
để biết giải thích về từng bit, nhưng ngắn gọn:
- 1 = đầu vào
- 2 = Đầu ra FastMixer
- 4 = AudioRecord và AudioTrack cho mỗi bản nhạc
Chưa có bit cho bộ đệm sâu hoặc bộ trộn thông thường, nhưng bạn có thể nhận được kết quả tương tự bằng cách sử dụng "4."
Kiểm tra và thu thập dữ liệu
- Chạy kiểm tra âm thanh của bạn.
-
adb shell dumpsys media.audio_flinger
- Tìm một dòng trong
dumpsys
ra của bãi chứa như sau:
tee copied to /data/misc/audioserver/20131010101147_2.wav
Đây là tệp .wav PCM. - Sau đó,
adb pull
bất kỳ tệp/data/misc/audioserver/*.wav
nào quan tâm; lưu ý rằng tên tệp kết xuất dành riêng cho bản nhạc không xuất hiện trong đầu ra kết xuất, nhưng vẫn được lưu vào/data/misc/audioserver
dumpsys
đóng bản nhạc. - Xem lại các tệp kết xuất để biết các mối quan tâm về quyền riêng tư trước khi chia sẻ với người khác.
Gợi ý
Hãy thử những ý tưởng sau để có thêm kết quả hữu ích:
- Tắt âm thanh cảm ứng và tiếng nhấp phím để giảm gián đoạn trong đầu ra thử nghiệm.
- Tối đa hóa tất cả các khối lượng.
- Tắt các ứng dụng tạo âm thanh hoặc ghi âm từ micrô, nếu chúng không quan tâm đến thử nghiệm của bạn.
- Bãi chứa dành riêng cho tuyến đường chỉ được lưu khi tuyến đường được đóng lại; bạn có thể cần phải buộc đóng một ứng dụng để kết xuất dữ liệu theo dõi cụ thể của nó
-
dumpsys
ngay sau khi kiểm tra; có một số lượng hạn chế của không gian ghi âm có sẵn. - Để đảm bảo bạn không bị mất các tệp kết xuất, hãy tải chúng lên máy chủ lưu trữ của bạn theo định kỳ. Chỉ một số tệp kết xuất hạn chế được lưu giữ; các bãi cũ hơn sẽ bị loại bỏ sau khi đạt đến giới hạn đó.
Khôi phục
Như đã lưu ý ở trên, không nên bật tính năng bồn rửa bóng. Khôi phục bản dựng và thiết bị của bạn như sau:
- Hoàn nguyên các thay đổi mã nguồn thành
Configuration.h
- Xây dựng lại
libaudioflinger.so
. - Đẩy hoặc đồng bộ
libaudioflinger.so
đã khôi phục vào/system/lib
của thiết bị. -
adb shell
-
rm /data/local.prop
-
rm /data/misc/audioserver/*.wav
-
reboot
media.log
Macro ALOGx
API ghi nhật ký ngôn ngữ Java tiêu chuẩn trong Android SDK là android.util.Log .
API ngôn ngữ C tương ứng trong Android NDK là __android_log_print
được khai báo trong <android/log.h>
.
Trong phần gốc của khuôn khổ Android, chúng tôi thích các macro có tên ALOGE
, ALOGW
, ALOGI
, ALOGV
, v.v. Chúng được khai báo trong <utils/Log.h>
và cho mục đích của bài viết này, chúng tôi sẽ gọi chung chúng là ALOGx
.
Tất cả các API này đều dễ sử dụng và dễ hiểu, vì vậy chúng có sức lan tỏa rộng khắp nền tảng Android. Đặc biệt là quy trình máy chủ trung gian, bao gồm máy chủ âm thanh mediaserver
, sử dụng rộng rãi ALOGx
.
Tuy nhiên, có một số hạn chế đối với ALOGx
và bạn bè:
- Họ dễ bị "spam nhật ký": bộ đệm nhật ký là tài nguyên dùng chung nên dễ bị tràn do các mục nhật ký không liên quan, dẫn đến bỏ sót thông tin. Biến thể
ALOGV
bị tắt tại thời điểm biên dịch theo mặc định. Nhưng tất nhiên, thậm chí nó có thể dẫn đến spam nhật ký nếu nó được bật. - Các lệnh gọi của hệ thống hạt nhân cơ bản có thể bị chặn, có thể dẫn đến đảo ngược ưu tiên và do đó là các phép đo nhiễu loạn và không chính xác. Đây là mối quan tâm đặc biệt đối với các luồng quan trọng về thời gian như
FastMixer
vàFastCapture
. - Nếu một nhật ký cụ thể bị vô hiệu hóa để giảm spam nhật ký, thì bất kỳ thông tin nào đã được nhật ký đó ghi lại sẽ bị mất. Không thể kích hoạt một nhật ký cụ thể trở về trước, sau khi rõ ràng rằng nhật ký đó sẽ rất thú vị.
NBLOG, media.log và MediaLogService
Các API NBLOG
và quy trình media.log
liên kết và dịch vụ MediaLogService
cùng nhau tạo thành một hệ thống ghi nhật ký mới hơn cho phương tiện và được thiết kế đặc biệt để giải quyết các vấn đề ở trên. Chúng tôi sẽ sử dụng một cách lỏng lẻo thuật ngữ "media.log" để chỉ cả ba, nhưng nói chính xác thì NBLOG
là API ghi nhật ký C ++, media.log
là tên quy trình Linux và MediaLogService
là một dịch vụ liên kết Android để kiểm tra nhật ký.
"Dòng thời gian" media.log
là một loạt các mục nhập nhật ký có thứ tự tương đối được giữ nguyên. Theo quy ước, mỗi luồng nên sử dụng dòng thời gian riêng của nó.
Lợi ích
Lợi ích của hệ thống media.log
là nó:
- Không spam nhật ký chính trừ khi và cho đến khi nó cần thiết.
- Có thể được kiểm tra ngay cả khi máy chủ trung gian
mediaserver
sự cố hoặc treo. - Không chặn trên mỗi dòng thời gian.
- Cung cấp ít xáo trộn hơn cho hiệu suất. (Tất nhiên không có hình thức ghi nhật ký nào là hoàn toàn không xâm phạm.)
Ngành kiến trúc
Sơ đồ dưới đây cho thấy mối quan hệ của quy trình trung gian và quy trình init
, trước media.log
mediaserver
giới thiệu:

Hình 1. Kiến trúc trước media.log
Điểm đáng chú ý:
-
init
mediaserver
và thực thi máy chủ trung gian. -
init
phát hiện cái chết củamediaserver
chủ trung gian và chuyển đổi lại các nhánh nếu cần. - Nhật ký
ALOGx
không được hiển thị.
Sơ đồ dưới đây cho thấy mối quan hệ mới của các thành phần, sau khi media.log
được thêm vào kiến trúc:

Hình 2. Kiến trúc sau media.log
Những thay đổi quan trọng:
- Khách hàng sử dụng
NBLOG
API để xây dựng các mục nhập nhật ký và nối chúng vào một bộ đệm tròn trong bộ nhớ dùng chung. -
MediaLogService
có thể kết xuất nội dung của bộ đệm tròn bất kỳ lúc nào. - Bộ đệm tròn được thiết kế theo cách sao cho bất kỳ lỗi nào của bộ nhớ dùng chung sẽ không làm hỏng
MediaLogService
và nó vẫn có thể kết xuất nhiều bộ đệm không bị ảnh hưởng bởi lỗi đó. - Bộ đệm tròn không chặn và không có khóa cho cả việc viết các mục nhập mới và đọc các mục nhập hiện có.
- Không có lệnh gọi hệ thống hạt nhân nào được yêu cầu để ghi vào hoặc đọc từ bộ đệm tròn (ngoại trừ dấu thời gian tùy chọn).
Sử dụng ở đâu
Kể từ Android 4.4, chỉ có một số điểm nhật ký trong AudioFlinger sử dụng hệ thống media.log
. Mặc dù các API mới không dễ sử dụng như ALOGx
, nhưng chúng cũng không quá khó. Chúng tôi khuyến khích bạn tìm hiểu hệ thống ghi nhật ký mới cho những trường hợp không thể thiếu nó. Đặc biệt, nó được khuyến nghị cho các luồng AudioFlinger phải chạy thường xuyên, định kỳ và không bị chặn như các FastMixer
và FastCapture
.
Cách sử dụng
Thêm nhật ký
Đầu tiên, bạn cần thêm nhật ký vào mã của mình.
Trong FastMixer
và FastCapture
, hãy sử dụng mã như sau:
logWriter->log("string"); logWriter->logf("format", parameters); logWriter->logTimestamp();
Vì dòng thời gian NBLog
này chỉ được sử dụng bởi các FastMixer
và FastCapture
, nên không cần loại trừ lẫn nhau.
Trong các chuỗi AudioFlinger khác, hãy sử dụng mNBLogWriter
:
mNBLogWriter->log("string"); mNBLogWriter->logf("format", parameters); mNBLogWriter->logTimestamp();
Đối với các luồng không phải FastMixer
và FastCapture
, dòng thời gian NBLog
của luồng có thể được sử dụng bởi cả chính luồng đó và bằng các hoạt động của chất kết dính. NBLog::Writer
không cung cấp bất kỳ loại trừ lẫn nhau ngầm nào trên mỗi dòng thời gian, vì vậy hãy đảm bảo rằng tất cả nhật ký xảy ra trong ngữ cảnh nơi lưu trữ mutex mLock
của chuỗi.
Sau khi bạn đã thêm nhật ký, hãy xây dựng lại AudioFlinger.
Thận trọng: Cần có dòng thời gian NBLog::Writer
riêng cho mỗi luồng, để đảm bảo an toàn cho luồng, vì các mốc thời gian bỏ qua các mutexes theo thiết kế. Nếu bạn muốn nhiều luồng sử dụng cùng một dòng thời gian, bạn có thể bảo vệ bằng mutex hiện có (như đã mô tả ở trên cho mLock
). Hoặc bạn có thể sử dụng trình bao bọc NBLog::LockedWriter
thay vì NBLog::Writer
. Tuy nhiên, điều này phủ nhận một lợi ích chính của API này: hành vi không chặn của nó.
API NBLog
đầy đủ có tại frameworks/av/include/media/nbaio/NBLog.h
.
Bật media.log
media.log
bị tắt theo mặc định. Nó chỉ hoạt động khi thuộc tính ro.test_harness
là 1
. Bạn có thể kích hoạt nó bằng cách:
adb root
adb shell
echo ro.test_harness=1 > /data/local.prop
chmod 644 /data/local.prop
reboot
Kết nối bị mất trong khi khởi động lại, vì vậy:
adb shellLệnh
ps media
bây giờ sẽ hiển thị hai quá trình:- media.log
- người trung gian
Lưu ý ID quy trình của máy chủ trung mediaserver
để sử dụng sau này.
Hiển thị các mốc thời gian
Bạn có thể yêu cầu kết xuất nhật ký theo cách thủ công bất cứ lúc nào. Lệnh này hiển thị nhật ký từ tất cả các mốc thời gian hoạt động và gần đây, sau đó xóa chúng:
dumpsys media.log
Lưu ý rằng theo thiết kế các mốc thời gian là độc lập và không có cơ sở để hợp nhất các mốc thời gian.
Khôi phục nhật ký sau cái chết của máy chủ trung gian
Bây giờ hãy thử giết mediaserver
trình của máy chủ trung gian: kill -9 #
, trong đó # là ID quy trình mà bạn đã lưu ý trước đó. Bạn sẽ thấy một kết xuất từ media.log
trong logcat
chính, hiển thị tất cả các nhật ký dẫn đến sự cố.
dumpsys media.log