Fixed several audio focus/routing/volume control issues

This commit is contained in:
Juha Heinanen
2023-04-06 13:29:41 +03:00
parent de8d0f01ef
commit b77b7b8342
3 changed files with 92 additions and 81 deletions

View File

@ -50,7 +50,7 @@ import kotlin.math.roundToInt
class BaresipService: Service() {
internal lateinit var intent: Intent
internal lateinit var am: AudioManager
private lateinit var am: AudioManager
private lateinit var nt: Ringtone
private lateinit var nm: NotificationManager
private lateinit var snb: NotificationCompat.Builder
@ -94,7 +94,7 @@ class BaresipService: Service() {
filesPath = filesDir.absolutePath
pName = packageName
am = getSystemService(Context.AUDIO_SERVICE) as AudioManager
am = applicationContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val ntUri = RingtoneManager.getActualDefaultRingtoneUri(applicationContext,
RingtoneManager.TYPE_NOTIFICATION)
@ -253,12 +253,12 @@ class BaresipService: Service() {
BluetoothHeadset.STATE_CONNECTED -> {
Log.d(TAG, "Bluetooth headset is connected")
if (audioFocusRequest != null)
startBluetoothSco(am, 1000L, 3)
startBluetoothSco(applicationContext, 1000L, 3)
}
BluetoothHeadset.STATE_DISCONNECTED -> {
Log.d(TAG, "Bluetooth headset is disconnected")
if (audioFocusRequest != null)
stopBluetoothSco(am)
stopBluetoothSco(applicationContext)
}
}
@ -287,6 +287,7 @@ class BaresipService: Service() {
}
AudioManager.SCO_AUDIO_STATE_DISCONNECTED -> {
Log.d(TAG, "Bluetooth headset SCO is disconnected")
resetCallVolume()
}
AudioManager.SCO_AUDIO_STATE_ERROR -> {
Log.d(TAG, "Bluetooth headset SCO state ERROR")
@ -465,7 +466,6 @@ class BaresipService: Service() {
val vidMode = intent.getIntExtra("video", Api.VIDMODE_OFF)
stopRinging()
stopMediaPlayer()
//requestAudioFocus(applicationContext, am, AudioAttributes.CONTENT_TYPE_SPEECH)
setCallVolume()
proximitySensing(true)
Api.ua_answer(uap, callp, vidMode)
@ -677,8 +677,15 @@ class BaresipService: Service() {
}
"call incoming" -> {
val peerUri = ev[1]
var missed = false
if (!Utils.checkPermissions(this, arrayOf(Manifest.permission.RECORD_AUDIO))) {
toast(getString(R.string.no_calls))
missed = true
} else if (!requestAudioFocus(applicationContext)) {
toast(getString(R.string.audio_focus_denied))
missed = true
}
if (missed) {
if (ua.account.callHistory) {
CallHistory.add(CallHistory(aor, peerUri, "in"))
CallHistory.save()
@ -889,8 +896,9 @@ class BaresipService: Service() {
call.remove()
if (!Call.inCall()) {
resetCallVolume()
abandonAudioFocus(am)
Utils.clearCommunicationDevice(am)
if (!abandonAudioFocus(applicationContext))
Log.e(TAG, "Failed to abandon audio focus")
am.mode = MODE_NORMAL
proximitySensing(false)
}
val missed = call.startTime == null && call.dir == "in" && !call.rejected
@ -1190,7 +1198,6 @@ class BaresipService: Service() {
}
private fun startRinging() {
requestAudioFocus(applicationContext, am, AudioAttributes.CONTENT_TYPE_MUSIC)
if (rt == null) {
val rtUri = RingtoneManager.getActualDefaultRingtoneUri(
applicationContext,
@ -1305,7 +1312,7 @@ class BaresipService: Service() {
}
private fun setCallVolume() {
if (callVolume != 0 && origVolume.isEmpty()) {
if (callVolume != 0)
for (streamType in listOf(AudioManager.STREAM_MUSIC, AudioManager.STREAM_VOICE_CALL)) {
origVolume[streamType] = am.getStreamVolume(streamType)
val maxVolume = am.getStreamMaxVolume(streamType)
@ -1313,15 +1320,14 @@ class BaresipService: Service() {
Log.d(TAG, "Orig/new/max $streamType volume is " +
"${origVolume[streamType]}/${am.getStreamVolume(streamType)}/$maxVolume")
}
}
}
private fun resetCallVolume() {
for ((streamType, streamVolume) in origVolume) {
am.setStreamVolume(streamType, streamVolume, 0)
Log.d(TAG, "Reset $streamType volume to ${am.getStreamVolume(streamType)}")
}
origVolume.clear()
if (callVolume != 0)
for ((streamType, streamVolume) in origVolume) {
am.setStreamVolume(streamType, streamVolume, 0)
Log.d(TAG, "Reset $streamType volume to ${am.getStreamVolume(streamType)}")
}
}
@SuppressLint("WakelockTimeout")
@ -1499,7 +1505,7 @@ class BaresipService: Service() {
this.unregisterReceiver(bluetoothReceiver)
this.unregisterReceiver(hotSpotReceiver)
stopRinging()
abandonAudioFocus(am)
abandonAudioFocus(applicationContext)
stopMediaPlayer()
uas.clear()
callHistory.clear()
@ -1562,53 +1568,44 @@ class BaresipService: Service() {
private var audioFocusRequest: AudioFocusRequestCompat? = null
private var btAdapter: BluetoothAdapter? = null
fun requestAudioFocus(ctx: Context, am: AudioManager, type: Int): Boolean {
Log.d(TAG, "Requesting audio focus of type $type")
fun requestAudioFocus(ctx: Context): Boolean {
Log.d(TAG, "Requesting audio focus")
if (audioFocusRequest != null) {
if (audioFocusRequest!!.audioAttributesCompat.contentType == type) {
Log.d(TAG, "Already focused")
am.mode = MODE_IN_COMMUNICATION
return true
} else {
abandonAudioFocus(am)
}
Log.d(TAG, "Already focused")
return true
}
val am = ctx.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val attributes = AudioAttributesCompat.Builder()
.setUsage(AudioAttributesCompat.USAGE_VOICE_COMMUNICATION)
.setContentType(type)
.setUsage(AudioAttributesCompat.USAGE_MEDIA)
.setContentType(AudioAttributesCompat.CONTENT_TYPE_SPEECH)
.build()
audioFocusRequest = AudioFocusRequestCompat.Builder(AudioManagerCompat.AUDIOFOCUS_GAIN_TRANSIENT)
audioFocusRequest = AudioFocusRequestCompat.Builder(AudioManagerCompat.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)
.setAudioAttributes(attributes)
.setOnAudioFocusChangeListener { }
.build()
if (AudioManagerCompat.requestAudioFocus(am, audioFocusRequest!!) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.d(TAG, "Audio focus granted")
isAudioFocused = true
if (isBluetoothHeadsetConnected(ctx) /* && !am.isBluetoothScoOn */) {
Log.d(TAG, "Starting Bluetooth SCO")
startBluetoothSco(am, 10L, 1)
}
am.mode = MODE_IN_COMMUNICATION
//if (type == AudioAttributes.CONTENT_TYPE_SPEECH || isBluetoothHeadsetConnected(ctx))
//am.mode = AudioManager.MODE_IN_COMMUNICATION
Log.d(TAG, "requestAudioFocus granted")
if (isBluetoothHeadsetConnected(ctx))
startBluetoothSco(ctx, 250L, 3)
} else {
Log.i(TAG, "Audio focus denied")
Log.w(TAG, "requestAudioFocus denied")
audioFocusRequest = null
isAudioFocused = false
}
return isAudioFocused
return audioFocusRequest != null
}
private fun abandonAudioFocus(am: AudioManager) {
fun abandonAudioFocus(ctx: Context): Boolean {
if (audioFocusRequest != null) {
Log.d(TAG, "Abandoning audio focus")
AudioManagerCompat.abandonAudioFocusRequest(am, audioFocusRequest!!)
audioFocusRequest = null
isAudioFocused = false
stopBluetoothSco(am)
Utils.clearCommunicationDevice(am)
am.mode = MODE_NORMAL
val am = ctx.getSystemService(Context.AUDIO_SERVICE) as AudioManager
if (AudioManagerCompat.abandonAudioFocusRequest(am, audioFocusRequest!!) ==
AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
audioFocusRequest = null
if (isBluetoothHeadsetConnected(ctx))
stopBluetoothSco(ctx)
}
}
return audioFocusRequest == null
}
@SuppressLint("MissingPermission")
@ -1631,22 +1628,33 @@ class BaresipService: Service() {
false
}
private fun startBluetoothSco(am: AudioManager, delay: Long, count: Int) {
private fun startBluetoothSco(ctx: Context, delay: Long, count: Int) {
val am = ctx.getSystemService(Context.AUDIO_SERVICE) as AudioManager
if (isBluetoothScoOn(am)) {
Log.d(TAG, "Bluetooth SCO is already on")
return
}
Log.d(TAG, "Starting Bluetooth SCO at count $count")
Handler(Looper.getMainLooper()).postDelayed({
am.isBluetoothScoOn = true
if (VERSION.SDK_INT < 31) {
am.startBluetoothSco()
} else {
Utils.setCommunicationDevice(am, AudioDeviceInfo.TYPE_BLUETOOTH_SCO)
}
if (!isBluetoothScoOn(am) && count > 1)
startBluetoothSco(am, delay, count - 1)
startBluetoothSco(ctx, delay, count - 1)
else
am.isBluetoothScoOn = true
}, delay)
}
private fun stopBluetoothSco(am: AudioManager) {
private fun stopBluetoothSco(ctx: Context) {
Log.d(TAG, "Stopping Bluetooth SCO")
val am = ctx.getSystemService(Context.AUDIO_SERVICE) as AudioManager
if (!isBluetoothScoOn(am)) {
Log.d(TAG, "Bluetooth SCO is already off")
return
}
Handler(Looper.getMainLooper()).postDelayed({
if (VERSION.SDK_INT < 31)
am.stopBluetoothSco()

View File

@ -10,7 +10,6 @@ import android.content.*
import android.content.Intent.ACTION_CALL
import android.content.pm.PackageManager
import android.content.res.Configuration.ORIENTATION_PORTRAIT
import android.media.AudioAttributes
import android.media.AudioManager
import android.net.Uri
import android.os.*
@ -169,7 +168,7 @@ class MainActivity : AppCompatActivity() {
am = getSystemService(AUDIO_SERVICE) as AudioManager
kgm = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
serviceEventObserver = Observer<Event<Long>> {
serviceEventObserver = Observer {
val event = it.getContentIfNotHandled()
Log.d(TAG, "Observed event $event")
if (event != null && BaresipService.serviceEvents.isNotEmpty()) {
@ -352,7 +351,7 @@ class MainActivity : AppCompatActivity() {
callButton.setOnClickListener {
if (aorSpinner.selectedItemPosition >= 0) {
if (Utils.checkPermissions(this, arrayOf(RECORD_AUDIO)))
makeCall()
makeCall()
else
Toast.makeText(applicationContext, R.string.no_calls, Toast.LENGTH_SHORT).show()
}
@ -364,6 +363,7 @@ class MainActivity : AppCompatActivity() {
if (callRunnable != null) {
callHandler.removeCallbacks(callRunnable!!)
callRunnable = null
BaresipService.abandonAudioFocus(applicationContext)
am.mode = AudioManager.MODE_NORMAL
showCall(ua)
return@setOnClickListener
@ -372,6 +372,7 @@ class MainActivity : AppCompatActivity() {
if (audioModeChangedListener != null) {
am.removeOnModeChangedListener(audioModeChangedListener!!)
audioModeChangedListener = null
BaresipService.abandonAudioFocus(applicationContext)
am.mode = AudioManager.MODE_NORMAL
showCall(ua)
return@setOnClickListener
@ -972,12 +973,12 @@ class MainActivity : AppCompatActivity() {
AudioManager.STREAM_MUSIC
when (keyCode) {
KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_UP -> {
Log.d(TAG, "Adjusting volume $keyCode of stream $stream")
am.adjustStreamVolume(stream,
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)
AudioManager.ADJUST_LOWER else
AudioManager.ADJUST_RAISE,
AudioManager.FLAG_SHOW_UI)
Log.d(TAG, "Adjusted volume $keyCode of stream $stream to ${am.getStreamVolume(stream)}")
return true
}
}
@ -1803,6 +1804,8 @@ class MainActivity : AppCompatActivity() {
onHoldCall.newCall = null
call.remove()
call.destroy()
if (!BaresipService.abandonAudioFocus(applicationContext))
Log.e(TAG, "Failed to abandon audio focus")
showCall(ua)
false
}
@ -1863,7 +1866,7 @@ class MainActivity : AppCompatActivity() {
val uri = if (Utils.isTelUri(uriText)) {
if (ua.account.telProvider == "") {
Utils.alertView(this, getString(R.string.notice),
String.format(getString(R.string.no_telephony_provider), aor))
String.format(getString(R.string.no_telephony_provider), aor))
return
}
Utils.telToSip(uriText, ua.account)
@ -1872,7 +1875,10 @@ class MainActivity : AppCompatActivity() {
}
if (!Utils.checkUri(uri)) {
Utils.alertView(this, getString(R.string.notice),
String.format(getString(R.string.invalid_sip_or_tel_uri), uri))
String.format(getString(R.string.invalid_sip_or_tel_uri), uri))
} else if (!BaresipService.requestAudioFocus(applicationContext)) {
Toast.makeText(applicationContext, R.string.audio_focus_denied,
Toast.LENGTH_SHORT).show()
} else {
callUri.isFocusable = false
uaAdapter.notifyDataSetChanged()
@ -1881,24 +1887,16 @@ class MainActivity : AppCompatActivity() {
hangupButton.visibility = View.VISIBLE
hangupButton.isEnabled = true
if (Build.VERSION.SDK_INT < 31) {
if (!BaresipService.requestAudioFocus(this, am, AudioAttributes.CONTENT_TYPE_SPEECH)) {
Toast.makeText(
applicationContext,
R.string.audio_focus_denied,
Toast.LENGTH_SHORT
).show()
} else {
callRunnable = Runnable {
callRunnable = null
if (!call(ua, uri)) {
callButton.visibility = View.VISIBLE
callButton.isEnabled = true
hangupButton.visibility = View.INVISIBLE
hangupButton.isEnabled = false
}
callRunnable = Runnable {
callRunnable = null
if (!call(ua, uri)) {
callButton.visibility = View.VISIBLE
callButton.isEnabled = true
hangupButton.visibility = View.INVISIBLE
hangupButton.isEnabled = false
}
callHandler.postDelayed(callRunnable!!, 1000)
}
callHandler.postDelayed(callRunnable!!, 1000)
} else {
audioModeChangedListener = AudioManager.OnModeChangedListener { mode ->
if (mode == AudioManager.MODE_IN_COMMUNICATION) {
@ -1919,13 +1917,18 @@ class MainActivity : AppCompatActivity() {
"device ${am.communicationDevice!!.type}")
}
}
am.addOnModeChangedListener(mainExecutor, audioModeChangedListener!!)
if (!BaresipService.requestAudioFocus(this, am, AudioAttributes.CONTENT_TYPE_SPEECH))
Toast.makeText(
applicationContext,
R.string.audio_focus_denied,
Toast.LENGTH_SHORT
).show()
if (am.mode == AudioManager.MODE_IN_COMMUNICATION) {
if (!call(ua, uri)) {
callButton.visibility = View.VISIBLE
callButton.isEnabled = true
hangupButton.visibility = View.INVISIBLE
hangupButton.isEnabled = false
}
} else {
am.addOnModeChangedListener(mainExecutor, audioModeChangedListener!!)
Log.d(TAG, "Setting audio mode to MODE_IN_COMMUNICATION")
am.mode = AudioManager.MODE_IN_COMMUNICATION
}
}
}
} else {

View File

@ -986,7 +986,7 @@ object Utils {
Log.d(TAG, "New com dev/mode ${am.communicationDevice!!.type}/${am.mode}")
}
fun clearCommunicationDevice(am: AudioManager) {
private fun clearCommunicationDevice(am: AudioManager) {
if (Build.VERSION.SDK_INT > 33) {
am.clearCommunicationDevice()
} else {