I want to play some audio with volume lvl adjusted to ear aka. "phone call mode". For this purpose, I'm using well-known and commonly advised
audioManager.setMode(audioNormalState ?
AudioManager.MODE_NORMAL : AudioManager.MODE_IN_COMMUNICATION);
The problem is that I'm not always playing audio just after mode switching, I have to wait and can't be sure how long, it may be even minutes. I've made some looped logging and MODE_IN_COMMUNICATION
mode is kept as long as the user is in "phone call mode" in my app on some Motorola with Android 9, but on Pixel 3 with Android 12 after 6 seconds mode is auto-switching back to MODE_NORMAL
when nothing is played. No additional code executed (like some listener), no additional (system) logs. When I start playing audio 1 sec after switching to MODE_IN_COMMUNICATION
mode it won't auto-switch as long as the audio is played (even more than 6 secs), but right after finishing mode gets also auto-switched to MODE_NORMAL
.
My app makes sort-of real-time voice calls (commands), but can also "beep" a few signals-patterns, and there is also a history feature providing all chronological sound-making actions to be played again in order. If this would be only voice then switching to MODE_IN_COMMUNICATION
and back only during a call might be sufficient, but what to do with highly-important SoundPool
jingles, do I have to switch mode also for them? (or for history play, which is a mix) AFAIK mode switching isn't fast (even a few secs on some devices), so I may apply some significant delay for short hundreds-of-ms signals (no way, every ms is crucial!) or I'm risking playing out loud signal/voice even in "phone call mode", when mode doesn't change "fast enough" (user won't be happy). I was relying on setting fixed (but configured according to app state and settings) MODE_IN_COMMUNICATION
, which was working till Android 12... (can confirm new/wrong behavior on Pixels and Samsungs)
currently used method for switching audio mode + configuration below, worth noting that setSpeakerphoneOn
method also doesn't work always on Android 12. At least not when on MODE_NORMAL
, which is default auto-switch-back-to mode now, also isSpeakerphoneOn
is false
on the very first start, but all my audio sources are in fact played loud...
// forceAudioNormalState = true only when app exit!
public static void resolveLoudState(AudioManager audioManager, boolean forceAudioNormalState) {
boolean silentPhoneCallMode = isPhoneCallModeEnabled(); // phone call GUI, only ear-friendly volume!!
boolean silentHeadset = HeadsetPlugReceiver.isHeadsetPlugged &&
!HeadsetPlugReceiver.forceSpeakerWhenHeadsetOn; // headset plugged, but "muted", force speaker
boolean silentBluetooth = BluetoothController.isAudioDeviceConnected() &&
!audioManager.isBluetoothScoOn(); // bt headset plugged, but "muted", force speaker
boolean loud = true; // by default
if (silentPhoneCallMode || silentHeadset || silentBluetooth) loud = false;
String log = String.format("resolveLoudState play loud: %s," +
" silentPhoneCallMode: %s, silentHeadset: %s, silentBluetooth: %s",
loud, silentPhoneCallMode, silentHeadset, silentBluetooth);
Timber.i(log);
audioManager.setMode(forceAudioNormalState ?
AudioManager.MODE_NORMAL : AudioManager.MODE_IN_COMMUNICATION);
audioManager.setSpeakerphoneOn(loud);
// even if deprecated this still works! fake wired headset on even for bt
audioManager.setWiredHeadsetOn(!loud && (silentHeadset || silentBluetooth));
// not loud and any headset connected and "muted"
}
note that in the above snippet there is no flag/information about the current playing state, only apps state/config
I want to manage these modes by myself and decide which audio output will be used or maybe there is any other way for forcing playing all audio (AudioTrack
, SoundPool
, MediaPlayer
, ExoPlayer
etc.) with ear-friendly adjusted volume?
edit: Just noticed when the mode auto-switches to MODE_NORMAL
and I will start playing STREAM_VOICE_CALL
it will auto switch to MODE_IN_COMMUNICATION
(with some small but significant delay) and reset back again just after finishing audio... This is some new undocumented behavior of system overall, become very unfriendly, bugged and unclear API...
edit2: this looks like related issue
PS. I've noticed that MediaSession
apps (e.g. music players) on Android 12 device got a new option straight on Notification
for picking speaker/headphones when wired/bt headset/earphones connected, but I'm not using session API at all. bonus question: is there an API for that?