From b00e5b7a3ccde96198c775e6735b7659b2a29344 Mon Sep 17 00:00:00 2001 From: Juha Heinanen Date: Wed, 13 May 2026 18:33:05 +0300 Subject: [PATCH] Solved call hangup issues --- .../com/tutpro/baresip/BaresipService.kt | 49 ++++++++----------- .../main/kotlin/com/tutpro/baresip/Call.kt | 42 ++++++++++------ .../com/tutpro/baresip/ConnectionService.kt | 19 +++---- 3 files changed, 54 insertions(+), 56 deletions(-) diff --git a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt index ed8fdc7f..621b70e5 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt @@ -1075,7 +1075,6 @@ class BaresipService: Service() { if (!Call.inCall()) proximitySensing(false) } - ConnectionService.lastDisconnectTime = System.currentTimeMillis() val connection = ConnectionService.connections[callp] if (connection != null) { val cause = when { @@ -1086,13 +1085,10 @@ class BaresipService: Service() { else -> DisconnectCause.REMOTE } connection.setDisconnected(DisconnectCause(cause)) - Handler(Looper.getMainLooper()).postDelayed({ - connection.destroy() - ConnectionService.connections.remove(callp) - updateStatusNotification() - }, 500) + connection.destroy() + ConnectionService.connections.remove(callp) } - nm.cancel(CALL_NOTIFICATION_ID) + updateStatusNotification() if (call != null) { stopRinging() stopMediaPlayer() @@ -1229,6 +1225,7 @@ class BaresipService: Service() { } } } + break } } @@ -1671,35 +1668,32 @@ class BaresipService: Service() { try { if (activeCall != null) { - if (VERSION.SDK_INT >= 29) - startForeground( - STATUS_NOTIFICATION_ID, - notification, - foregroundServiceType(activeCall) - ) + if (!isNotificationInCall) { + // Only call startForeground when the FIRST call starts. + if (VERSION.SDK_INT >= 29) + startForeground(STATUS_NOTIFICATION_ID, notification, foregroundServiceType(activeCall)) + else + startForeground(STATUS_NOTIFICATION_ID, notification) + isNotificationInCall = true + } else - startForeground(STATUS_NOTIFICATION_ID, notification) - isNotificationInCall = true + nm.notify(STATUS_NOTIFICATION_ID, notification) } else { - // If we are currently in a call notification mode, downgrade to standby FGS. - // We call stopForeground(REMOVE) to explicitly clear the system's telephony UI context - // that might be persisting the timer/peer info on the lock screen. if (isNotificationInCall) { stopForeground(STOP_FOREGROUND_REMOVE) + // Only call startForeground to drop the "Call" type when the LAST call ends. + if (VERSION.SDK_INT >= 29) + startForeground(STATUS_NOTIFICATION_ID, notification, foregroundServiceType()) + else + startForeground(STATUS_NOTIFICATION_ID, notification) isNotificationInCall = false } - if (VERSION.SDK_INT >= 29) - startForeground( - STATUS_NOTIFICATION_ID, - notification, - foregroundServiceType() - ) else - startForeground(STATUS_NOTIFICATION_ID, notification) + // Already in standby, just keep the notification current. + nm.notify(STATUS_NOTIFICATION_ID, notification) } } catch (e: Exception) { - Log.e(TAG, "Failed to update foreground notification: ${e.message}") - // Fallback to standard notification if promotion/update fails + Log.e(TAG, "Failed to update notification: ${e.message}") nm.notify(STATUS_NOTIFICATION_ID, notification) } } @@ -1847,7 +1841,6 @@ class BaresipService: Service() { fun handleExternalCallRemoved(telecomCall: android.telecom.Call) { val callp = telecomCall.hashCode().toLong() val call = calls.find { it.callp == callp } - nm.cancel(CALL_NOTIFICATION_ID) if (call != null) { stopRinging() stopMediaPlayer() diff --git a/app/src/main/kotlin/com/tutpro/baresip/Call.kt b/app/src/main/kotlin/com/tutpro/baresip/Call.kt index 8dce9f3f..67af81f5 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Call.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Call.kt @@ -232,36 +232,48 @@ open class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir companion object { fun calls(): ArrayList { - return BaresipService.calls + synchronized(BaresipService.calls) { + return ArrayList(BaresipService.calls) + } } fun ofCallp(callp: Long): Call? { - for (c in BaresipService.calls) - if (c.callp == callp) return c - return null + synchronized(BaresipService.calls) { + for (c in BaresipService.calls) + if (c.callp == callp) return c + return null + } } fun call(status: String): Call? { - for (c in BaresipService.calls) - if (c.status.value == status) return c - return null + synchronized(BaresipService.calls) { + for (c in BaresipService.calls) + if (c.status.value == status) return c + return null + } } fun inCall(): Boolean { - return BaresipService.calls.isNotEmpty() + synchronized(BaresipService.calls) { + return BaresipService.calls.isNotEmpty() + } } fun hasTelecomCall(): Boolean { - return BaresipService.calls.any { - it is ExternalCall || ConnectionService.connections.containsKey(it.callp) - } || ConnectionService.pendingOutgoingConnection != null + synchronized(BaresipService.calls) { + return BaresipService.calls.any { + it is ExternalCall || ConnectionService.connections.containsKey(it.callp) + } || ConnectionService.pendingOutgoingConnection != null + } } fun isAnyCallActive(ctx: Context): Boolean { - // Check if there exist SIP calls that are not onhold or held - if (BaresipService.calls.any { !it.onhold && !it.held }) return true - // MODE_IN_CALL indicates a PSTN call is active - return Utils.isAudioMode(ctx, AudioManager.MODE_IN_CALL) + synchronized(BaresipService.calls) { + // Check if there exist SIP calls that are not onhold or held + if (BaresipService.calls.any { !it.onhold && !it.held }) return true + // MODE_IN_CALL indicates a PSTN call is active + return Utils.isAudioMode(ctx, AudioManager.MODE_IN_CALL) + } } } } diff --git a/app/src/main/kotlin/com/tutpro/baresip/ConnectionService.kt b/app/src/main/kotlin/com/tutpro/baresip/ConnectionService.kt index 6275aa96..b85c655f 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/ConnectionService.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/ConnectionService.kt @@ -176,14 +176,11 @@ class ConnectionService : ConnectionService() { } override fun onDisconnect() { - if (isDisconnecting) { - Log.d(TAG, "onDisconnect already in progress for $callp") - return - } + val now = System.currentTimeMillis() + if (now - lastDisconnectTime < 500) return + lastDisconnectTime = now Log.d(TAG, "Telecom Connection onDisconnect $callp") - isDisconnecting = true - lastDisconnectTime = System.currentTimeMillis() if (callp == 0L) { pendingOutgoingConnection = null @@ -191,16 +188,12 @@ class ConnectionService : ConnectionService() { destroy() return } - val call = Call.ofCallp(callp) - if (call != null) - Api.ua_hangup(uap, callp, 0, "") + + Api.ua_hangup(uap, callp, 0, "") + setDisconnected(DisconnectCause(DisconnectCause.LOCAL)) connections.remove(callp) destroy() - // Allow other disconnects after a short period to prevent the "Telecom Cascade" effect - android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ - isDisconnecting = false - }, 500) } override fun onAbort() {