From 908c38a1a5084b4e458079d5d5dd52babcde49f7 Mon Sep 17 00:00:00 2001 From: Juha Heinanen Date: Sun, 31 May 2026 08:40:10 +0300 Subject: [PATCH] Improved mute handling so that Android system bar mic icon is not shown when call is muted --- app/src/main/cpp/baresip.c | 10 +++- .../com/tutpro/baresip/BaresipService.kt | 53 +++++++++++++------ .../com/tutpro/baresip/ConnectionService.kt | 12 ++++- .../kotlin/com/tutpro/baresip/MainScreen.kt | 10 ++-- 4 files changed, 59 insertions(+), 26 deletions(-) diff --git a/app/src/main/cpp/baresip.c b/app/src/main/cpp/baresip.c index 5bd72bce..3e15eb8f 100644 --- a/app/src/main/cpp/baresip.c +++ b/app/src/main/cpp/baresip.c @@ -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); break; case BEVENT_CALL_CLOSED: + audio_set_source(call_audio(call), "nil", NULL); tone = call_scode(call) ? translate_errorcode(call_scode(call)) : ""; len = re_snprintf(event_buf, sizeof event_buf, "call closed,%s,%s", prm, tone); break; @@ -1375,7 +1376,13 @@ JNIEXPORT void JNICALL Java_com_tutpro_baresip_Api_calls_1mute( const struct ua *ua = ua_le->data; for (call_le = list_head(ua_calls(ua)); call_le != NULL; call_le = call_le->next) { 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(); @@ -1909,6 +1916,7 @@ JNIEXPORT void JNICALL Java_com_tutpro_baresip_Api_AAudio_1close_1stream(JNIEnv *env, jobject obj) { if (AAudio_stream != NULL) { + AAudioStream_requestStop(AAudio_stream); AAudioStream_close(AAudio_stream); AAudio_stream = NULL; } diff --git a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt index d8c0e62a..bf600b86 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt @@ -1031,18 +1031,20 @@ class BaresipService: Service() { } updateStatusNotification() proximitySensing(proximitySensing) + if (isMicMuted) + setMicMute(true) } if (!isMainVisible) return } "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 { 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) { Log.d(TAG, "Call ${call.callp} un-held by peer.") call.onhold = false @@ -1173,6 +1175,7 @@ class BaresipService: Service() { else -> DisconnectCause.REMOTE } connection.setDisconnected(DisconnectCause(cause)) + connection.safeDestroy() ConnectionService.connections.remove(callp) } if (call != null) { @@ -1201,15 +1204,8 @@ class BaresipService: Service() { } val isConference = call.conferenceCall val hasOtherCalls = synchronized(calls) { calls.any { it.ua == ua } } - if (noMoreCalls) { - aec?.release() - aec = null - agc?.release() - agc = null - ns?.release() - ns = null - recorderSessionId = 0 - } + if (noMoreCalls) + releaseAudioEffects() updateStatusNotification() if (isConference && !hasOtherCalls) { Log.d(TAG, "Last conference call closed, scheduling mixminus unload") @@ -1236,9 +1232,9 @@ class BaresipService: Service() { !reason.startsWith("Connection reset by") 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) { + val completedElsewhere = missed && ev[2].startsWith("SIP") && + ev[2].contains(";cause=200") CoroutineScope(Dispatchers.IO).launch { val history = CallHistoryNew(aor, call.peerUri, call.dir) history.stopTime = GregorianCalendar() @@ -2512,7 +2508,7 @@ class BaresipService: Service() { ) } if (isMicMuted) { - isMicMuted = false + setMicMute(false) postServiceEvent( ServiceEvent( "mic muted,false", @@ -2771,6 +2767,7 @@ class BaresipService: Service() { cm.unregisterNetworkCallback(networkCallback) if (this::androidContactsObserver.isInitialized) contentResolver.unregisterContentObserver(androidContactsObserver) + releaseAudioEffects() isServiceClean = true } } @@ -2849,6 +2846,19 @@ class BaresipService: Service() { private var btAdapter: BluetoothAdapter? = 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 colorblind = false @@ -2873,6 +2883,15 @@ class BaresipService: Service() { 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) { synchronized(serviceEvents) { serviceEvents.add(event) diff --git a/app/src/main/kotlin/com/tutpro/baresip/ConnectionService.kt b/app/src/main/kotlin/com/tutpro/baresip/ConnectionService.kt index 9017352a..e6318928 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/ConnectionService.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/ConnectionService.kt @@ -33,6 +33,7 @@ class ConnectionService : ConnectionService() { fun onCallClosed(callp: Long) { connections[callp]?.let { it.setDisconnected(DisconnectCause(DisconnectCause.REMOTE)) + it.safeDestroy() connections.remove(callp) } } @@ -152,6 +153,14 @@ class ConnectionService : ConnectionService() { inner class BaresipConnection(val uap: Long, var callp: Long) : Connection() { var isDisconnecting = false + private var isDestroyed = false + + fun safeDestroy() { + if (!isDestroyed) { + isDestroyed = true + destroy() + } + } override fun onAnswer() { Log.d(TAG, "Telecom Connection onAnswer $callp") @@ -218,8 +227,7 @@ class ConnectionService : ConnectionService() { Log.d(TAG, "onCallAudioStateChanged: $state") state.let { if (BaresipService.isMicMuted != it.isMuted) { - BaresipService.isMicMuted = it.isMuted - Api.calls_mute(it.isMuted) + BaresipService.setMicMute(it.isMuted) BaresipService.postServiceEvent( ServiceEvent("mic muted,${it.isMuted}", arrayListOf(uap, callp), System.nanoTime()) diff --git a/app/src/main/kotlin/com/tutpro/baresip/MainScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/MainScreen.kt index ad3df21b..79d87769 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/MainScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/MainScreen.kt @@ -610,14 +610,12 @@ private fun TopAppBar( .combinedClickable( onClick = { if (Call.call("connected") != null) { - BaresipService.isMicMuted = !BaresipService.isMicMuted - if (BaresipService.isMicMuted) { + val newMuteState = !BaresipService.isMicMuted + BaresipService.setMicMute(newMuteState) + if (newMuteState) viewModel.updateMicIcon(Icons.Filled.MicOff) - Api.calls_mute(true) - } else { + else viewModel.updateMicIcon(Icons.Filled.Mic) - Api.calls_mute(false) - } } }, onLongClick = {