Add more AudioManager logging

This commit is contained in:
Jim Gustafson
2026-01-21 11:57:48 -08:00
committed by GitHub
parent c1da17df48
commit 4bbddf736c
2 changed files with 167 additions and 15 deletions

View File

@@ -34,7 +34,15 @@ public abstract class AudioManagerCompat {
@SuppressWarnings("CodeBlock2Expr")
protected final AudioManager.OnAudioFocusChangeListener onAudioFocusChangeListener = focusChange -> {
Log.i(TAG, "onAudioFocusChangeListener: " + focusChange);
String focusName;
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN: focusName = "GAIN"; break;
case AudioManager.AUDIOFOCUS_LOSS: focusName = "LOSS"; break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: focusName = "LOSS_TRANSIENT"; break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: focusName = "LOSS_TRANSIENT_CAN_DUCK"; break;
default: focusName = "UNKNOWN(" + focusChange + ")"; break;
}
Log.i(TAG, "onAudioFocusChangeListener: " + focusName);
hasFocus = focusChange == AudioManager.AUDIOFOCUS_GAIN;
};
@@ -180,6 +188,16 @@ public abstract class AudioManagerCompat {
audioManager.unregisterAudioDeviceCallback(deviceCallback);
}
@RequiresApi(24)
public void registerAudioRecordingCallback(@NonNull AudioManager.AudioRecordingCallback callback, @NonNull Handler handler) {
audioManager.registerAudioRecordingCallback(callback, handler);
}
@RequiresApi(24)
public void unregisterAudioRecordingCallback(@NonNull AudioManager.AudioRecordingCallback callback) {
audioManager.unregisterAudioRecordingCallback(callback);
}
@SuppressLint("WrongConstant")
public boolean isWiredHeadsetOn() {
AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_ALL);
@@ -220,13 +238,40 @@ public abstract class AudioManagerCompat {
abstract public void abandonCallAudioFocus();
public static AudioManagerCompat create(@NonNull Context context) {
if (Build.VERSION.SDK_INT >= 26) {
if (Build.VERSION.SDK_INT >= 31) {
return new Api31AudioManagerCompat(context);
} else if (Build.VERSION.SDK_INT >= 26) {
return new Api26AudioManagerCompat(context);
} else {
return new Api21AudioManagerCompat(context);
}
}
@RequiresApi(31)
static class Api31AudioManagerCompat extends Api26AudioManagerCompat {
private Api31AudioManagerCompat(@NonNull Context context) {
super(context);
}
public void addOnModeChangedListener(@NonNull java.util.concurrent.Executor executor, @NonNull AudioManager.OnModeChangedListener listener) {
audioManager.addOnModeChangedListener(executor, listener);
}
public void removeOnModeChangedListener(@NonNull AudioManager.OnModeChangedListener listener) {
audioManager.removeOnModeChangedListener(listener);
}
public void addOnCommunicationDeviceChangedListener(@NonNull java.util.concurrent.Executor executor, @NonNull AudioManager.OnCommunicationDeviceChangedListener listener) {
audioManager.addOnCommunicationDeviceChangedListener(executor, listener);
}
public void removeOnCommunicationDeviceChangedListener(@NonNull AudioManager.OnCommunicationDeviceChangedListener listener) {
audioManager.removeOnCommunicationDeviceChangedListener(listener);
}
}
@RequiresApi(26)
private static class Api26AudioManagerCompat extends AudioManagerCompat {

View File

@@ -4,6 +4,8 @@ import android.content.Context
import android.media.AudioDeviceCallback
import android.media.AudioDeviceInfo
import android.media.AudioManager
import android.media.AudioRecordingConfiguration
import android.media.MediaRecorder
import android.net.Uri
import androidx.annotation.RequiresApi
import org.signal.core.util.logging.Log
@@ -38,6 +40,34 @@ class FullSignalAudioManagerApi31(context: Context, eventListener: EventListener
}
}
private val communicationDeviceChangedListener = AudioManager.OnCommunicationDeviceChangedListener { device ->
if (device != null) {
Log.i(TAG, "OnCommunicationDeviceChangedListener: id: ${device.id} type: ${getDeviceTypeName(device.type)}")
} else {
Log.w(TAG, "OnCommunicationDeviceChangedListener: null")
}
}
private val modeChangedListener = AudioManager.OnModeChangedListener { mode ->
Log.i(TAG, "OnModeChangedListener: ${getModeName(mode)}")
if (state == State.RUNNING && mode != AudioManager.MODE_IN_COMMUNICATION) {
Log.w(TAG, "OnModeChangedListener: Not MODE_IN_COMMUNICATION during a call. state: $state")
}
}
private val audioRecordingCallback = object : AudioManager.AudioRecordingCallback() {
override fun onRecordingConfigChanged(configs: List<AudioRecordingConfiguration>) {
if (configs.isEmpty()) {
Log.i(TAG, "AudioRecordingCallback: no active recordings")
} else {
for (config in configs) {
val deviceName = config.audioDevice?.let { getDeviceTypeName(it.type) } ?: "null"
Log.i(TAG, "AudioRecordingCallback: silenced: ${config.isClientSilenced} source: ${getAudioSourceName(config.audioSource)} device: $deviceName")
}
}
}
}
override fun setDefaultAudioDevice(recipientId: RecipientId?, newDefaultDevice: AudioDevice, clearUserEarpieceSelection: Boolean) {
Log.d(TAG, "setDefaultAudioDevice(): currentDefault: $defaultAudioDevice device: $newDefaultDevice clearUser: $clearUserEarpieceSelection")
defaultAudioDevice = when (newDefaultDevice) {
@@ -69,15 +99,27 @@ class FullSignalAudioManagerApi31(context: Context, eventListener: EventListener
savedIsMicrophoneMute = androidAudioManager.isMicrophoneMute
hasWiredHeadset = androidAudioManager.isWiredHeadsetOn
val focusedGained = androidAudioManager.requestCallAudioFocus()
if (!focusedGained) {
handler.postDelayed({ androidAudioManager.requestCallAudioFocus() }, 500)
Log.i(TAG, "initialize: savedMode: ${getModeName(savedAudioMode)} savedSpeaker: $savedIsSpeakerPhoneOn savedMicMute: $savedIsMicrophoneMute wiredHeadset: $hasWiredHeadset")
val focusGained = androidAudioManager.requestCallAudioFocus()
if (!focusGained) {
Log.w(TAG, "initialize: audio focus request failed, scheduling retry")
handler.postDelayed({
val retryGained = androidAudioManager.requestCallAudioFocus()
Log.i(TAG, "initialize: audio focus retry result: $retryGained")
}, 500)
}
setMicrophoneMute(false)
updateAudioDeviceState()
androidAudioManager.registerAudioDeviceCallback(deviceCallback, handler)
androidAudioManager.registerAudioRecordingCallback(audioRecordingCallback, handler)
val api31AudioManager = androidAudioManager as AudioManagerCompat.Api31AudioManagerCompat
api31AudioManager.addOnModeChangedListener(handler::post, modeChangedListener)
api31AudioManager.addOnCommunicationDeviceChangedListener(handler::post, communicationDeviceChangedListener)
state = State.PREINITIALIZED
Log.d(TAG, "Initialized")
@@ -85,15 +127,22 @@ class FullSignalAudioManagerApi31(context: Context, eventListener: EventListener
}
override fun start() {
Log.i(TAG, "start: currentState: $state currentMode: ${getModeName(androidAudioManager.mode)}")
incomingRinger.stop()
outgoingRinger.stop()
val focusedGained = androidAudioManager.requestCallAudioFocus()
if (!focusedGained) {
handler.postDelayed({ androidAudioManager.requestCallAudioFocus() }, 500)
val focusGained = androidAudioManager.requestCallAudioFocus()
if (!focusGained) {
Log.w(TAG, "start: audio focus request failed, scheduling retry")
handler.postDelayed({
val retryGained = androidAudioManager.requestCallAudioFocus()
Log.i(TAG, "start: audio focus retry result: $retryGained")
}, 500)
}
state = State.RUNNING
Log.i(TAG, "start: setting mode to MODE_IN_COMMUNICATION")
androidAudioManager.mode = AudioManager.MODE_IN_COMMUNICATION
val volume: Float = androidAudioManager.ringVolumeWithMinimum()
soundPool.play(connectedSoundId, volume, volume, 0, 0, 1.0f)
@@ -102,6 +151,8 @@ class FullSignalAudioManagerApi31(context: Context, eventListener: EventListener
}
override fun stop(playDisconnect: Boolean) {
Log.i(TAG, "stop: playDisconnect: $playDisconnect currentState: $state")
incomingRinger.stop()
outgoingRinger.stop()
@@ -109,7 +160,14 @@ class FullSignalAudioManagerApi31(context: Context, eventListener: EventListener
val volume: Float = androidAudioManager.ringVolumeWithMinimum()
soundPool.play(disconnectedSoundId, volume, volume, 0, 0, 1.0f)
}
androidAudioManager.unregisterAudioDeviceCallback(deviceCallback)
if (state != State.UNINITIALIZED) {
androidAudioManager.unregisterAudioDeviceCallback(deviceCallback)
androidAudioManager.unregisterAudioRecordingCallback(audioRecordingCallback)
val api31AudioManager = androidAudioManager as AudioManagerCompat.Api31AudioManagerCompat
api31AudioManager.removeOnModeChangedListener(modeChangedListener)
api31AudioManager.removeOnCommunicationDeviceChangedListener(communicationDeviceChangedListener)
}
if (state == State.UNINITIALIZED && userSelectedAudioDevice != null) {
Log.d(
TAG,
@@ -118,6 +176,7 @@ class FullSignalAudioManagerApi31(context: Context, eventListener: EventListener
"Therefore skipping audio device reset."
)
} else {
Log.i(TAG, "stop: restoring mode to ${getModeName(savedAudioMode)}")
androidAudioManager.clearCommunicationDevice()
setSpeakerphoneOn(savedIsSpeakerPhoneOn)
setMicrophoneMute(savedIsMicrophoneMute)
@@ -143,7 +202,7 @@ class FullSignalAudioManagerApi31(context: Context, eventListener: EventListener
}
override fun startIncomingRinger(ringtoneUri: Uri?, vibrate: Boolean) {
Log.i(TAG, "startIncomingRinger(): uri: ${if (ringtoneUri != null) "present" else "null"} vibrate: $vibrate")
Log.i(TAG, "startIncomingRinger: uri: ${if (ringtoneUri != null) "present" else "null"} vibrate: $vibrate currentMode: ${getModeName(androidAudioManager.mode)}")
androidAudioManager.mode = AudioManager.MODE_RINGTONE
setMicrophoneMute(false)
setDefaultAudioDevice(recipientId = null, newDefaultDevice = AudioDevice.SPEAKER_PHONE, clearUserEarpieceSelection = false)
@@ -151,7 +210,7 @@ class FullSignalAudioManagerApi31(context: Context, eventListener: EventListener
}
override fun startOutgoingRinger() {
Log.i(TAG, "startOutgoingRinger(): currentDevice: $selectedAudioDevice")
Log.i(TAG, "startOutgoingRinger: currentDevice: $selectedAudioDevice currentMode: ${getModeName(androidAudioManager.mode)}")
androidAudioManager.mode = AudioManager.MODE_IN_COMMUNICATION
setMicrophoneMute(false)
outgoingRinger.start(OutgoingRinger.Type.RINGING)
@@ -181,7 +240,7 @@ class FullSignalAudioManagerApi31(context: Context, eventListener: EventListener
if (result) {
eventListener?.onAudioDeviceChanged(AudioDeviceMapping.fromPlatformType(candidate.type), availableCommunicationDevices.map { AudioDeviceMapping.fromPlatformType(it.type) }.toSet())
} else {
Log.w(TAG, "Failed to set ${candidate.id} of type ${candidate.type}as communication device.")
Log.w(TAG, "Failed to set ${candidate.id} of type ${getDeviceTypeName(candidate.type)} as communication device.")
}
} else {
val searchOrder: List<AudioDevice> = listOf(AudioDevice.BLUETOOTH, AudioDevice.WIRED_HEADSET, defaultAudioDevice, AudioDevice.EARPIECE, AudioDevice.SPEAKER_PHONE, AudioDevice.NONE).distinct()
@@ -194,14 +253,14 @@ class FullSignalAudioManagerApi31(context: Context, eventListener: EventListener
when (candidate) {
null -> {
Log.e(TAG, "Tried to switch audio devices but could not find suitable device in list of types: ${availableCommunicationDevices.map { it.type }.joinToString()}")
Log.e(TAG, "Tried to switch audio devices but could not find suitable device in list of types: ${availableCommunicationDevices.map { getDeviceTypeName(it.type) }.joinToString()}")
androidAudioManager.clearCommunicationDevice()
}
else -> {
Log.d(TAG, "Switching to new device of type ${candidate.type} from ${currentAudioDevice?.type}")
Log.d(TAG, "Switching to new device of type ${getDeviceTypeName(candidate.type)} from ${currentAudioDevice?.type?.let { getDeviceTypeName(it) }}")
val result = androidAudioManager.setCommunicationDevice(candidate)
if (result) {
Log.w(TAG, "Succeeded in setting ${candidate.id} (type: ${candidate.type}) as communication device.")
Log.w(TAG, "Succeeded in setting ${candidate.id} (type: ${getDeviceTypeName(candidate.type)}) as communication device.")
eventListener?.onAudioDeviceChanged(AudioDeviceMapping.fromPlatformType(candidate.type), availableCommunicationDevices.map { AudioDeviceMapping.fromPlatformType(it.type) }.toSet())
} else {
Log.w(TAG, "Failed to set ${candidate.id} as communication device.")
@@ -210,4 +269,52 @@ class FullSignalAudioManagerApi31(context: Context, eventListener: EventListener
}
}
}
private fun getModeName(mode: Int): String {
return when (mode) {
AudioManager.MODE_NORMAL -> "MODE_NORMAL"
AudioManager.MODE_RINGTONE -> "MODE_RINGTONE"
AudioManager.MODE_IN_CALL -> "MODE_IN_CALL"
AudioManager.MODE_IN_COMMUNICATION -> "MODE_IN_COMMUNICATION"
AudioManager.MODE_CALL_SCREENING -> "MODE_CALL_SCREENING"
else -> "UNKNOWN($mode)"
}
}
private fun getDeviceTypeName(type: Int): String {
return when (type) {
AudioDeviceInfo.TYPE_BUILTIN_EARPIECE -> "BUILTIN_EARPIECE"
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER -> "BUILTIN_SPEAKER"
AudioDeviceInfo.TYPE_BUILTIN_MIC -> "BUILTIN_MIC"
AudioDeviceInfo.TYPE_WIRED_HEADSET -> "WIRED_HEADSET"
AudioDeviceInfo.TYPE_WIRED_HEADPHONES -> "WIRED_HEADPHONES"
AudioDeviceInfo.TYPE_BLUETOOTH_SCO -> "BLUETOOTH_SCO"
AudioDeviceInfo.TYPE_BLUETOOTH_A2DP -> "BLUETOOTH_A2DP"
AudioDeviceInfo.TYPE_USB_DEVICE -> "USB_DEVICE"
AudioDeviceInfo.TYPE_USB_ACCESSORY -> "USB_ACCESSORY"
AudioDeviceInfo.TYPE_USB_HEADSET -> "USB_HEADSET"
AudioDeviceInfo.TYPE_TELEPHONY -> "TELEPHONY"
AudioDeviceInfo.TYPE_HEARING_AID -> "HEARING_AID"
AudioDeviceInfo.TYPE_BLE_HEADSET -> "BLE_HEADSET"
AudioDeviceInfo.TYPE_BLE_SPEAKER -> "BLE_SPEAKER"
AudioDeviceInfo.TYPE_BLE_BROADCAST -> "BLE_BROADCAST"
else -> "UNKNOWN($type)"
}
}
private fun getAudioSourceName(source: Int): String {
return when (source) {
MediaRecorder.AudioSource.DEFAULT -> "DEFAULT"
MediaRecorder.AudioSource.MIC -> "MIC"
MediaRecorder.AudioSource.VOICE_UPLINK -> "VOICE_UPLINK"
MediaRecorder.AudioSource.VOICE_DOWNLINK -> "VOICE_DOWNLINK"
MediaRecorder.AudioSource.VOICE_CALL -> "VOICE_CALL"
MediaRecorder.AudioSource.CAMCORDER -> "CAMCORDER"
MediaRecorder.AudioSource.VOICE_RECOGNITION -> "VOICE_RECOGNITION"
MediaRecorder.AudioSource.VOICE_COMMUNICATION -> "VOICE_COMMUNICATION"
MediaRecorder.AudioSource.UNPROCESSED -> "UNPROCESSED"
MediaRecorder.AudioSource.VOICE_PERFORMANCE -> "VOICE_PERFORMANCE"
else -> "UNKNOWN($source)"
}
}
}