Improved mute handling so that Android system bar mic icon is not shown when call is muted

This commit is contained in:
Juha Heinanen
2026-05-31 08:40:10 +03:00
parent 01fb30aa47
commit 908c38a1a5
4 changed files with 59 additions and 26 deletions

View File

@ -255,6 +255,7 @@ static void event_handler(enum bevent_ev ev, struct bevent *event, void *arg)
len = re_snprintf(event_buf, sizeof event_buf, "transfer failed,%s", prm); len = re_snprintf(event_buf, sizeof event_buf, "transfer failed,%s", prm);
break; break;
case BEVENT_CALL_CLOSED: case BEVENT_CALL_CLOSED:
audio_set_source(call_audio(call), "nil", NULL);
tone = call_scode(call) ? translate_errorcode(call_scode(call)) : ""; tone = call_scode(call) ? translate_errorcode(call_scode(call)) : "";
len = re_snprintf(event_buf, sizeof event_buf, "call closed,%s,%s", prm, tone); len = re_snprintf(event_buf, sizeof event_buf, "call closed,%s,%s", prm, tone);
break; break;
@ -1375,7 +1376,13 @@ JNIEXPORT void JNICALL Java_com_tutpro_baresip_Api_calls_1mute(
const struct ua *ua = ua_le->data; const struct ua *ua = ua_le->data;
for (call_le = list_head(ua_calls(ua)); call_le != NULL; call_le = call_le->next) { for (call_le = list_head(ua_calls(ua)); call_le != NULL; call_le = call_le->next) {
const struct call *call = call_le->data; const struct call *call = call_le->data;
audio_mute(call_audio(call), mute); struct audio *audio = call_audio(call);
audio_mute(audio, mute);
if (mute)
audio_set_source(audio, "nil", NULL);
else
audio_set_source(audio, conf_config()->audio.src_mod,
conf_config()->audio.src_dev);
} }
} }
re_thread_leave(); re_thread_leave();
@ -1909,6 +1916,7 @@ JNIEXPORT void JNICALL
Java_com_tutpro_baresip_Api_AAudio_1close_1stream(JNIEnv *env, jobject obj) Java_com_tutpro_baresip_Api_AAudio_1close_1stream(JNIEnv *env, jobject obj)
{ {
if (AAudio_stream != NULL) { if (AAudio_stream != NULL) {
AAudioStream_requestStop(AAudio_stream);
AAudioStream_close(AAudio_stream); AAudioStream_close(AAudio_stream);
AAudio_stream = NULL; AAudio_stream = NULL;
} }

View File

@ -1031,18 +1031,20 @@ class BaresipService: Service() {
} }
updateStatusNotification() updateStatusNotification()
proximitySensing(proximitySensing) proximitySensing(proximitySensing)
if (isMicMuted)
setMicMute(true)
} }
if (!isMainVisible) if (!isMainVisible)
return return
} }
"call update" -> { "call update" -> {
val newHeldState = when (ev[1].toInt()) {
Api.SDP_INACTIVE, Api.SDP_RECVONLY -> true
else -> false
}
val connection = ConnectionService.connections[callp]
Handler(Looper.getMainLooper()).post { Handler(Looper.getMainLooper()).post {
if (call != null) { if (call != null) {
val newHeldState = when (ev[1].toInt()) {
Api.SDP_INACTIVE, Api.SDP_RECVONLY -> true
else -> false
}
val connection = ConnectionService.connections[callp]
if (call.held && !newHeldState) { if (call.held && !newHeldState) {
Log.d(TAG, "Call ${call.callp} un-held by peer.") Log.d(TAG, "Call ${call.callp} un-held by peer.")
call.onhold = false call.onhold = false
@ -1173,6 +1175,7 @@ class BaresipService: Service() {
else -> DisconnectCause.REMOTE else -> DisconnectCause.REMOTE
} }
connection.setDisconnected(DisconnectCause(cause)) connection.setDisconnected(DisconnectCause(cause))
connection.safeDestroy()
ConnectionService.connections.remove(callp) ConnectionService.connections.remove(callp)
} }
if (call != null) { if (call != null) {
@ -1201,15 +1204,8 @@ class BaresipService: Service() {
} }
val isConference = call.conferenceCall val isConference = call.conferenceCall
val hasOtherCalls = synchronized(calls) { calls.any { it.ua == ua } } val hasOtherCalls = synchronized(calls) { calls.any { it.ua == ua } }
if (noMoreCalls) { if (noMoreCalls)
aec?.release() releaseAudioEffects()
aec = null
agc?.release()
agc = null
ns?.release()
ns = null
recorderSessionId = 0
}
updateStatusNotification() updateStatusNotification()
if (isConference && !hasOtherCalls) { if (isConference && !hasOtherCalls) {
Log.d(TAG, "Last conference call closed, scheduling mixminus unload") Log.d(TAG, "Last conference call closed, scheduling mixminus unload")
@ -1236,9 +1232,9 @@ class BaresipService: Service() {
!reason.startsWith("Connection reset by") !reason.startsWith("Connection reset by")
val missed = call.dir == "in" && call.startTime == null && !call.rejected val missed = call.dir == "in" && call.startTime == null && !call.rejected
val completedElsewhere = missed && ev[2].startsWith("SIP") &&
ev[2].contains(";cause=200")
if (ua.account.callHistory) { if (ua.account.callHistory) {
val completedElsewhere = missed && ev[2].startsWith("SIP") &&
ev[2].contains(";cause=200")
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
val history = CallHistoryNew(aor, call.peerUri, call.dir) val history = CallHistoryNew(aor, call.peerUri, call.dir)
history.stopTime = GregorianCalendar() history.stopTime = GregorianCalendar()
@ -2512,7 +2508,7 @@ class BaresipService: Service() {
) )
} }
if (isMicMuted) { if (isMicMuted) {
isMicMuted = false setMicMute(false)
postServiceEvent( postServiceEvent(
ServiceEvent( ServiceEvent(
"mic muted,false", "mic muted,false",
@ -2771,6 +2767,7 @@ class BaresipService: Service() {
cm.unregisterNetworkCallback(networkCallback) cm.unregisterNetworkCallback(networkCallback)
if (this::androidContactsObserver.isInitialized) if (this::androidContactsObserver.isInitialized)
contentResolver.unregisterContentObserver(androidContactsObserver) contentResolver.unregisterContentObserver(androidContactsObserver)
releaseAudioEffects()
isServiceClean = true isServiceClean = true
} }
} }
@ -2849,6 +2846,19 @@ class BaresipService: Service() {
private var btAdapter: BluetoothAdapter? = null private var btAdapter: BluetoothAdapter? = null
private var audioFocusRequest: AudioFocusRequest? = null private var audioFocusRequest: AudioFocusRequest? = null
private fun releaseAudioEffects() {
aec?.enabled = false
aec?.release()
aec = null
agc?.enabled = false
agc?.release()
agc = null
ns?.enabled = false
ns?.release()
ns = null
recorderSessionId = 0
}
var rt: Ringtone? = null var rt: Ringtone? = null
var colorblind = false var colorblind = false
@ -2873,6 +2883,15 @@ class BaresipService: Service() {
return PhoneAccountHandle(componentName, PHONE_ACCOUNT_ID) return PhoneAccountHandle(componentName, PHONE_ACCOUNT_ID)
} }
fun setMicMute(mute: Boolean) {
instance?.let {
val am = it.getSystemService(AUDIO_SERVICE) as AudioManager
am.isMicrophoneMute = mute
}
isMicMuted = mute
Api.calls_mute(mute)
}
fun postServiceEvent(event: ServiceEvent) { fun postServiceEvent(event: ServiceEvent) {
synchronized(serviceEvents) { synchronized(serviceEvents) {
serviceEvents.add(event) serviceEvents.add(event)

View File

@ -33,6 +33,7 @@ class ConnectionService : ConnectionService() {
fun onCallClosed(callp: Long) { fun onCallClosed(callp: Long) {
connections[callp]?.let { connections[callp]?.let {
it.setDisconnected(DisconnectCause(DisconnectCause.REMOTE)) it.setDisconnected(DisconnectCause(DisconnectCause.REMOTE))
it.safeDestroy()
connections.remove(callp) connections.remove(callp)
} }
} }
@ -152,6 +153,14 @@ class ConnectionService : ConnectionService() {
inner class BaresipConnection(val uap: Long, var callp: Long) : Connection() { inner class BaresipConnection(val uap: Long, var callp: Long) : Connection() {
var isDisconnecting = false var isDisconnecting = false
private var isDestroyed = false
fun safeDestroy() {
if (!isDestroyed) {
isDestroyed = true
destroy()
}
}
override fun onAnswer() { override fun onAnswer() {
Log.d(TAG, "Telecom Connection onAnswer $callp") Log.d(TAG, "Telecom Connection onAnswer $callp")
@ -218,8 +227,7 @@ class ConnectionService : ConnectionService() {
Log.d(TAG, "onCallAudioStateChanged: $state") Log.d(TAG, "onCallAudioStateChanged: $state")
state.let { state.let {
if (BaresipService.isMicMuted != it.isMuted) { if (BaresipService.isMicMuted != it.isMuted) {
BaresipService.isMicMuted = it.isMuted BaresipService.setMicMute(it.isMuted)
Api.calls_mute(it.isMuted)
BaresipService.postServiceEvent( BaresipService.postServiceEvent(
ServiceEvent("mic muted,${it.isMuted}", arrayListOf(uap, callp), ServiceEvent("mic muted,${it.isMuted}", arrayListOf(uap, callp),
System.nanoTime()) System.nanoTime())

View File

@ -610,14 +610,12 @@ private fun TopAppBar(
.combinedClickable( .combinedClickable(
onClick = { onClick = {
if (Call.call("connected") != null) { if (Call.call("connected") != null) {
BaresipService.isMicMuted = !BaresipService.isMicMuted val newMuteState = !BaresipService.isMicMuted
if (BaresipService.isMicMuted) { BaresipService.setMicMute(newMuteState)
if (newMuteState)
viewModel.updateMicIcon(Icons.Filled.MicOff) viewModel.updateMicIcon(Icons.Filled.MicOff)
Api.calls_mute(true) else
} else {
viewModel.updateMicIcon(Icons.Filled.Mic) viewModel.updateMicIcon(Icons.Filled.Mic)
Api.calls_mute(false)
}
} }
}, },
onLongClick = { onLongClick = {