Update and open WebRtcAudioUtils

Opening the visibility of a few methods from this utils class to allow
it to be used by other implementations of `AudioDeviceModule`.

Also updating a few methods, like adding new audio device types from
recent Android SDKs, and updating the definition of an emulator.

Bug: b/287409066
Change-Id: I1473fa0342252347ce92ee2319380ebb14e9885b
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/339905
Commit-Queue: Zoé Lepaul <xalep@webrtc.org>
Reviewed-by: Ranveer Aggarwal‎ <ranvr@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#41778}
This commit is contained in:
Zoé Lepaul 2024-02-19 11:21:48 +01:00 committed by WebRTC LUCI CQ
parent 56d3cf0c6d
commit dc6a001b6b

View file

@ -23,106 +23,124 @@ import android.media.AudioFormat;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.MediaRecorder.AudioSource; import android.media.MediaRecorder.AudioSource;
import android.os.Build; import android.os.Build;
import java.lang.Thread; import android.os.Build.VERSION_CODES;
import java.util.Arrays; import java.util.Arrays;
import org.webrtc.Logging; import org.webrtc.Logging;
final class WebRtcAudioUtils { /** Utilities for implementations of {@code AudioDeviceModule}, mostly for logging. */
public final class WebRtcAudioUtils {
private static final String TAG = "WebRtcAudioUtilsExternal"; private static final String TAG = "WebRtcAudioUtilsExternal";
// Helper method for building a string of thread information. /** Helper method for building a string of thread information. */
public static String getThreadInfo() { public static String getThreadInfo() {
return "@[name=" + Thread.currentThread().getName() + ", id=" + Thread.currentThread().getId() Thread current = Thread.currentThread();
+ "]"; return "@[name=" + current.getName() + ", id=" + current.getId() + "]";
} }
// Returns true if we're running on emulator. /** Returns true if we're running on emulator. */
public static boolean runningOnEmulator() { public static boolean runningOnEmulator() {
return Build.HARDWARE.equals("goldfish") && Build.BRAND.startsWith("generic_"); // Hardware type of qemu1 is goldfish and qemu2 is ranchu.
return Build.HARDWARE.equals("goldfish") || Build.HARDWARE.equals("ranchu");
} }
// Information about the current build, taken from system properties. /** Information about the current build, taken from system properties. */
static void logDeviceInfo(String tag) { private static void logDeviceInfo(String tag) {
Logging.d(tag, Logging.d(
"Android SDK: " + Build.VERSION.SDK_INT + ", " tag,
+ "Release: " + Build.VERSION.RELEASE + ", " ("Android SDK: " + Build.VERSION.SDK_INT)
+ "Brand: " + Build.BRAND + ", " + (", Release: " + Build.VERSION.RELEASE)
+ "Device: " + Build.DEVICE + ", " + (", Brand: " + Build.BRAND)
+ "Id: " + Build.ID + ", " + (", Device: " + Build.DEVICE)
+ "Hardware: " + Build.HARDWARE + ", " + (", Id: " + Build.ID)
+ "Manufacturer: " + Build.MANUFACTURER + ", " + (", Hardware: " + Build.HARDWARE)
+ "Model: " + Build.MODEL + ", " + (", Manufacturer: " + Build.MANUFACTURER)
+ "Product: " + Build.PRODUCT); + (", Model: " + Build.MODEL)
+ (", Product: " + Build.PRODUCT));
} }
// Logs information about the current audio state. The idea is to call this /**
// method when errors are detected to log under what conditions the error * Logs information about the current audio state. The idea is to call this method when errors are
// occurred. Hopefully it will provide clues to what might be the root cause. * detected to log under what conditions the error occurred. Hopefully it will provide clues to
static void logAudioState(String tag, Context context, AudioManager audioManager) { * what might be the root cause.
*/
public static void logAudioState(String tag, Context context, AudioManager audioManager) {
logDeviceInfo(tag); logDeviceInfo(tag);
logAudioStateBasic(tag, context, audioManager); logAudioStateBasic(tag, context, audioManager);
logAudioStateVolume(tag, audioManager); logAudioStateVolume(tag, audioManager);
logAudioDeviceInfo(tag, audioManager); logAudioDeviceInfo(tag, audioManager);
} }
// Converts AudioDeviceInfo types to local string representation. /** Converts AudioDeviceInfo types to local string representation. */
static String deviceTypeToString(int type) { public static String deviceTypeToString(int type) {
switch (type) { switch (type) {
case AudioDeviceInfo.TYPE_UNKNOWN: case AudioDeviceInfo.TYPE_AUX_LINE:
return "TYPE_UNKNOWN"; return "TYPE_AUX_LINE";
case AudioDeviceInfo.TYPE_BUILTIN_EARPIECE: case AudioDeviceInfo.TYPE_BLE_BROADCAST:
return "TYPE_BUILTIN_EARPIECE"; return "TYPE_BLE_BROADCAST";
case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER: case AudioDeviceInfo.TYPE_BLE_HEADSET:
return "TYPE_BUILTIN_SPEAKER"; return "TYPE_BLE_HEADSET";
case AudioDeviceInfo.TYPE_WIRED_HEADSET: case AudioDeviceInfo.TYPE_BLE_SPEAKER:
return "TYPE_WIRED_HEADSET"; return "TYPE_BLE_SPEAKER";
case AudioDeviceInfo.TYPE_WIRED_HEADPHONES:
return "TYPE_WIRED_HEADPHONES";
case AudioDeviceInfo.TYPE_LINE_ANALOG:
return "TYPE_LINE_ANALOG";
case AudioDeviceInfo.TYPE_LINE_DIGITAL:
return "TYPE_LINE_DIGITAL";
case AudioDeviceInfo.TYPE_BLUETOOTH_SCO:
return "TYPE_BLUETOOTH_SCO";
case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP: case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP:
return "TYPE_BLUETOOTH_A2DP"; return "TYPE_BLUETOOTH_A2DP";
case AudioDeviceInfo.TYPE_BLUETOOTH_SCO:
return "TYPE_BLUETOOTH_SCO";
case AudioDeviceInfo.TYPE_BUILTIN_EARPIECE:
return "TYPE_BUILTIN_EARPIECE";
case AudioDeviceInfo.TYPE_BUILTIN_MIC:
return "TYPE_BUILTIN_MIC";
case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER:
return "TYPE_BUILTIN_SPEAKER";
case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER_SAFE:
return "TYPE_BUILTIN_SPEAKER_SAFE";
case AudioDeviceInfo.TYPE_BUS:
return "TYPE_BUS";
case AudioDeviceInfo.TYPE_DOCK:
return "TYPE_DOCK";
case AudioDeviceInfo.TYPE_DOCK_ANALOG:
return "TYPE_DOCK_ANALOG";
case AudioDeviceInfo.TYPE_FM:
return "TYPE_FM";
case AudioDeviceInfo.TYPE_FM_TUNER:
return "TYPE_FM_TUNER";
case AudioDeviceInfo.TYPE_HDMI: case AudioDeviceInfo.TYPE_HDMI:
return "TYPE_HDMI"; return "TYPE_HDMI";
case AudioDeviceInfo.TYPE_HDMI_ARC: case AudioDeviceInfo.TYPE_HDMI_ARC:
return "TYPE_HDMI_ARC"; return "TYPE_HDMI_ARC";
case AudioDeviceInfo.TYPE_USB_DEVICE: case AudioDeviceInfo.TYPE_HDMI_EARC:
return "TYPE_USB_DEVICE"; return "TYPE_HDMI_EARC";
case AudioDeviceInfo.TYPE_USB_ACCESSORY: case AudioDeviceInfo.TYPE_HEARING_AID:
return "TYPE_USB_ACCESSORY"; return "TYPE_HEARING_AID";
case AudioDeviceInfo.TYPE_DOCK:
return "TYPE_DOCK";
case AudioDeviceInfo.TYPE_FM:
return "TYPE_FM";
case AudioDeviceInfo.TYPE_BUILTIN_MIC:
return "TYPE_BUILTIN_MIC";
case AudioDeviceInfo.TYPE_FM_TUNER:
return "TYPE_FM_TUNER";
case AudioDeviceInfo.TYPE_TV_TUNER:
return "TYPE_TV_TUNER";
case AudioDeviceInfo.TYPE_TELEPHONY:
return "TYPE_TELEPHONY";
case AudioDeviceInfo.TYPE_AUX_LINE:
return "TYPE_AUX_LINE";
case AudioDeviceInfo.TYPE_IP: case AudioDeviceInfo.TYPE_IP:
return "TYPE_IP"; return "TYPE_IP";
case AudioDeviceInfo.TYPE_BUS: case AudioDeviceInfo.TYPE_LINE_ANALOG:
return "TYPE_BUS"; return "TYPE_LINE_ANALOG";
case AudioDeviceInfo.TYPE_LINE_DIGITAL:
return "TYPE_LINE_DIGITAL";
case AudioDeviceInfo.TYPE_REMOTE_SUBMIX:
return "TYPE_REMOTE_SUBMIX";
case AudioDeviceInfo.TYPE_TELEPHONY:
return "TYPE_TELEPHONY";
case AudioDeviceInfo.TYPE_TV_TUNER:
return "TYPE_TV_TUNER";
case AudioDeviceInfo.TYPE_UNKNOWN:
return "TYPE_UNKNOWN";
case AudioDeviceInfo.TYPE_USB_ACCESSORY:
return "TYPE_USB_ACCESSORY";
case AudioDeviceInfo.TYPE_USB_DEVICE:
return "TYPE_USB_DEVICE";
case AudioDeviceInfo.TYPE_USB_HEADSET: case AudioDeviceInfo.TYPE_USB_HEADSET:
return "TYPE_USB_HEADSET"; return "TYPE_USB_HEADSET";
case AudioDeviceInfo.TYPE_WIRED_HEADPHONES:
return "TYPE_WIRED_HEADPHONES";
case AudioDeviceInfo.TYPE_WIRED_HEADSET:
return "TYPE_WIRED_HEADSET";
default: default:
return "TYPE_UNKNOWN"; return "TYPE_UNKNOWN(" + type + ")";
} }
} }
@TargetApi(Build.VERSION_CODES.N)
public static String audioSourceToString(int source) { public static String audioSourceToString(int source) {
// AudioSource.UNPROCESSED requires API level 29. Use local define instead.
final int VOICE_PERFORMANCE = 10;
switch (source) { switch (source) {
case AudioSource.DEFAULT: case AudioSource.DEFAULT:
return "DEFAULT"; return "DEFAULT";
@ -142,7 +160,7 @@ final class WebRtcAudioUtils {
return "VOICE_COMMUNICATION"; return "VOICE_COMMUNICATION";
case AudioSource.UNPROCESSED: case AudioSource.UNPROCESSED:
return "UNPROCESSED"; return "UNPROCESSED";
case VOICE_PERFORMANCE: case AudioSource.VOICE_PERFORMANCE:
return "VOICE_PERFORMANCE"; return "VOICE_PERFORMANCE";
default: default:
return "INVALID"; return "INVALID";
@ -163,7 +181,7 @@ final class WebRtcAudioUtils {
} }
} }
@TargetApi(Build.VERSION_CODES.N) @TargetApi(VERSION_CODES.N)
public static String audioEncodingToString(int enc) { public static String audioEncodingToString(int enc) {
switch (enc) { switch (enc) {
case AudioFormat.ENCODING_INVALID: case AudioFormat.ENCODING_INVALID:
@ -189,23 +207,29 @@ final class WebRtcAudioUtils {
} }
} }
// Reports basic audio statistics. /** Reports basic audio statistics. */
private static void logAudioStateBasic(String tag, Context context, AudioManager audioManager) { private static void logAudioStateBasic(String tag, Context context, AudioManager audioManager) {
Logging.d(tag, Logging.d(
tag,
"Audio State: " "Audio State: "
+ "audio mode: " + modeToString(audioManager.getMode()) + ", " + ("audio mode: " + modeToString(audioManager.getMode()))
+ "has mic: " + hasMicrophone(context) + ", " + (", has mic: " + hasMicrophone(context))
+ "mic muted: " + audioManager.isMicrophoneMute() + ", " + (", mic muted: " + audioManager.isMicrophoneMute())
+ "music active: " + audioManager.isMusicActive() + ", " + (", music active: " + audioManager.isMusicActive())
+ "speakerphone: " + audioManager.isSpeakerphoneOn() + ", " + (", speakerphone: " + audioManager.isSpeakerphoneOn())
+ "BT SCO: " + audioManager.isBluetoothScoOn()); + (", BT SCO: " + audioManager.isBluetoothScoOn()));
} }
// Adds volume information for all possible stream types. /** Adds volume information for all possible stream types. */
private static void logAudioStateVolume(String tag, AudioManager audioManager) { private static void logAudioStateVolume(String tag, AudioManager audioManager) {
final int[] streams = {AudioManager.STREAM_VOICE_CALL, AudioManager.STREAM_MUSIC, final int[] streams = {
AudioManager.STREAM_RING, AudioManager.STREAM_ALARM, AudioManager.STREAM_NOTIFICATION, AudioManager.STREAM_VOICE_CALL,
AudioManager.STREAM_SYSTEM}; AudioManager.STREAM_MUSIC,
AudioManager.STREAM_RING,
AudioManager.STREAM_ALARM,
AudioManager.STREAM_NOTIFICATION,
AudioManager.STREAM_SYSTEM
};
Logging.d(tag, "Audio State: "); Logging.d(tag, "Audio State: ");
// Some devices may not have volume controls and might use a fixed volume. // Some devices may not have volume controls and might use a fixed volume.
boolean fixedVolume = audioManager.isVolumeFixed(); boolean fixedVolume = audioManager.isVolumeFixed();
@ -216,21 +240,16 @@ final class WebRtcAudioUtils {
info.append(" " + streamTypeToString(stream) + ": "); info.append(" " + streamTypeToString(stream) + ": ");
info.append("volume=").append(audioManager.getStreamVolume(stream)); info.append("volume=").append(audioManager.getStreamVolume(stream));
info.append(", max=").append(audioManager.getStreamMaxVolume(stream)); info.append(", max=").append(audioManager.getStreamMaxVolume(stream));
logIsStreamMute(tag, audioManager, stream, info); if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
info.append(", muted=").append(audioManager.isStreamMute(stream));
}
Logging.d(tag, info.toString()); Logging.d(tag, info.toString());
} }
} }
} }
private static void logIsStreamMute(
String tag, AudioManager audioManager, int stream, StringBuilder info) {
if (Build.VERSION.SDK_INT >= 23) {
info.append(", muted=").append(audioManager.isStreamMute(stream));
}
}
private static void logAudioDeviceInfo(String tag, AudioManager audioManager) { private static void logAudioDeviceInfo(String tag, AudioManager audioManager) {
if (Build.VERSION.SDK_INT < 23) { if (Build.VERSION.SDK_INT < VERSION_CODES.M) {
return; return;
} }
final AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_ALL); final AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_ALL);
@ -261,7 +280,7 @@ final class WebRtcAudioUtils {
} }
} }
// Converts media.AudioManager modes into local string representation. /** Converts media.AudioManager modes into local string representation. */
static String modeToString(int mode) { static String modeToString(int mode) {
switch (mode) { switch (mode) {
case MODE_IN_CALL: case MODE_IN_CALL:
@ -296,7 +315,7 @@ final class WebRtcAudioUtils {
} }
} }
// Returns true if the device can record audio via a microphone. /** Returns true if the device can record audio via a microphone. */
private static boolean hasMicrophone(Context context) { private static boolean hasMicrophone(Context context) {
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE); return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE);
} }