diff --git a/app/src/main/kotlin/com/tutpro/baresip/Account.kt b/app/src/main/kotlin/com/tutpro/baresip/Account.kt index 3180e44f..6930feee 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Account.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Account.kt @@ -285,7 +285,7 @@ class Account(val accp: Long, virtualAor: String? = null) { BaresipService.filesPath + "/accounts", accounts.toByteArray(Charsets.UTF_8) ) - Log.d(TAG, "Saved accounts '${accounts}' to '${BaresipService.filesPath}/accounts'") + // Log.d(TAG, "Saved accounts '${accounts}' to '${BaresipService.filesPath}/accounts'") } fun ofAor(aor: String): Account? { diff --git a/app/src/main/kotlin/com/tutpro/baresip/AccountScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/AccountScreen.kt index e77cb5df..e3b9905d 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/AccountScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/AccountScreen.kt @@ -1677,7 +1677,7 @@ private fun checkOnClick(ctx: Context, viewModel: AccountViewModel, ua: UserAgen if (viewModel.defaultAccount.value) ua.makeDefault() - Api.account_debug(acc.accp) + // Api.account_debug(acc.accp) Account.saveAccounts() diff --git a/app/src/main/kotlin/com/tutpro/baresip/AudioScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/AudioScreen.kt index fe7df0e8..3583cd3f 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/AudioScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/AudioScreen.kt @@ -164,8 +164,6 @@ private var newOpusPacketLoss = oldOpusPacketLoss private var newAudioDelay = BaresipService.audioDelay.toString() private var newToneCountry = BaresipService.toneCountry private var newRingtoneUri = "" -private var oldTelecom = BaresipService.telecom -private var newTelecom = oldTelecom private var save = false @@ -178,8 +176,6 @@ private fun AudioContent(contentPadding: PaddingValues) { oldSpeakerPhone = Config.variable("speaker_phone") == "yes" newSpeakerPhone = oldSpeakerPhone - oldTelecom = Config.variable("telecom") == "yes" - newTelecom = oldTelecom oldAudioModules = Config.variables("module") oldOpusBitrate = Config.variable("opus_bitrate") oldOpusPacketLoss = Config.variable("opus_packet_loss") @@ -206,7 +202,6 @@ private fun AudioContent(contentPadding: PaddingValues) { .verticalScroll(state = scrollState), verticalArrangement = Arrangement.spacedBy(12.dp), ) { - Telecom() Ringtone() ToneCountry() SpeakerPhone() @@ -219,37 +214,6 @@ private fun AudioContent(contentPadding: PaddingValues) { } } -@Composable -private fun Telecom() { - Row( - Modifier - .fillMaxWidth() - .padding(end = 10.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Start - ) { - val telecomTitle = stringResource(R.string.telecom) - val telecomHelp = stringResource(R.string.telecom_help) - Text(text = telecomTitle, - modifier = Modifier - .weight(1f) - .clickable { - alertTitle.value = telecomTitle - alertMessage.value = telecomHelp - showAlert.value = true - }, - fontSize = 18.sp) - var telecom by remember { mutableStateOf(oldTelecom) } - Switch( - checked = telecom, - onCheckedChange = { - telecom = it - newTelecom = telecom - } - ) - } -} - @Composable private fun Ringtone() { val ringToneTitle = stringResource(R.string.ringtone) @@ -629,13 +593,6 @@ private fun checkOnClick(ctx: Context): Result { var restart = false - if (newTelecom != oldTelecom) { - Config.replaceVariable("telecom", if (newTelecom) "yes" else "no") - BaresipService.telecom = newTelecom - restart = true - save = true - } - if (Preferences(ctx).ringtoneUri != newRingtoneUri) { Preferences(ctx).ringtoneUri = newRingtoneUri BaresipService.rt = RingtoneManager.getRingtone(ctx, newRingtoneUri.toUri()) diff --git a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt index 299efba1..a12c6614 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt @@ -1,6 +1,5 @@ package com.tutpro.baresip -import android.Manifest import android.Manifest.permission.RECORD_AUDIO import android.annotation.SuppressLint import android.app.Notification @@ -61,7 +60,6 @@ import androidx.appcompat.app.AppCompatDelegate import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue -import androidx.core.app.ActivityCompat import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat.MessagingStyle import androidx.core.app.Person @@ -491,18 +489,7 @@ class BaresipService: Service() { activeNetwork = cm.activeNetwork Log.i(TAG, "Active network: $activeNetwork") - if (telecom) - registerPhoneAccount() - else - if (btAdapter != null) { - Log.i(TAG, "Registering bluetooth receiver") - val filter = IntentFilter() - filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED) - filter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED) - filter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED) - this.registerReceiver(bluetoothReceiver, filter) - bluetoothReceiverRegistered = true - } + registerPhoneAccount() Log.i(TAG, "AEC/AGC/NS available = $aecAvailable/$agcAvailable/$nsAvailable") @@ -551,8 +538,6 @@ class BaresipService: Service() { setCallVolume() proximitySensing(proximitySensing) call?.answer() - if (!telecom) - ensureCommunicationMode() } "Call Reject" -> { @@ -877,12 +862,10 @@ class BaresipService: Service() { proximitySensing(proximitySensing) } "call ringing" -> { - if (!telecom) ensureCommunicationMode() playRingBack() return } "call progress" -> { - if (!telecom) ensureCommunicationMode() if ((ev[1].toInt() and Api.SDP_RECVONLY) != 0) stopMediaPlayer() else { @@ -926,7 +909,6 @@ class BaresipService: Service() { ) if (resourceId != 0) { ensureCommunicationMode() - if (!telecom) playUnInterrupted(resourceId, 1) } else { Log.e(TAG, "Callwaiting tone $name.wav not found") } @@ -946,23 +928,14 @@ class BaresipService: Service() { Log.d(TAG, "Incoming call $uap/$callp/$peerUri") if (Call.ofCallp(callp) == null) Call(callp, ua, peerUri, "in", "incoming").add() - if (telecom) { - val extras = android.os.Bundle() - extras.putLong("uap", uap) - extras.putLong("callp", callp) - extras.putString("peerUri", peerUri) - try { - tm.addNewIncomingCall(getPhoneAccountHandle(this), extras) - } catch (e: Exception) { - Log.e(TAG, "Telecom addNewIncomingCall failed: ${e.message}") - } - } else { - if (!requestAudioFocus(applicationContext)) { - Log.w(TAG, "Audio focus denied for incoming call") - Api.ua_hangup(uap, callp, 486, "Busy Here") - return - } - handleIncomingCall(Call.ofCallp(callp)!!) + val extras = android.os.Bundle() + extras.putLong("uap", uap) + extras.putLong("callp", callp) + extras.putString("peerUri", peerUri) + try { + tm.addNewIncomingCall(getPhoneAccountHandle(this), extras) + } catch (e: Exception) { + Log.e(TAG, "Telecom addNewIncomingCall failed: ${e.message}") } return } @@ -1023,12 +996,6 @@ class BaresipService: Service() { if (call.state() == Api.CALL_STATE_EARLY) { if ((ev[1].toInt() and Api.SDP_RECVONLY) != 0) stopMediaPlayer() - else { - if (!telecom) { - ConnectionService.connections[callp]?.setRinging() - playRingBack() - } - } } if (call.status.value == "connected" && !call.held && !call.onhold) { if (call.callOnHold.value || call.showOnHoldNotice.value) { @@ -1160,13 +1127,8 @@ class BaresipService: Service() { val tone = ev[2] if (tone == "busy") playBusy() - else { - if (!telecom && !Call.inCall()) - abandonAudioFocus(applicationContext) + else ensureCommunicationMode() - } - if (!telecom && !Call.inCall()) - abandonAudioFocus(applicationContext) if (call.dir == "out") call.rejected = call.startTime == null && !reason.startsWith("408") && @@ -1500,7 +1462,7 @@ class BaresipService: Service() { fun started() { Log.d(TAG, "Received 'started' from baresip") isNativeReady = true - if (telecom) addMobileUserAgent() + addMobileUserAgent() Api.net_debug() postServiceEvent(ServiceEvent("started", arrayListOf(callActionUri), System.nanoTime())) callActionUri = "" @@ -1849,23 +1811,41 @@ class BaresipService: Service() { messageUpdate.postValue(System.currentTimeMillis()) } - private fun addMobileUserAgent() { - if (!telecom || Utils.pstnAccountHandle(this) == null) return + fun addMobileUserAgent() { + if (VERSION.SDK_INT < 29) return + + val mobileAccountHandle = Utils.pstnAccountHandle(this) + val existingMobileUa = uas.value.find { it.account.isMobile } + + // If mobile account should not exist (role lost or no SIM), remove it if it exists + if (mobileAccountHandle == null) { + if (existingMobileUa != null) { + Log.d(TAG, "Removing Mobile account (role lost or SIM missing)") + existingMobileUa.remove() + Account.saveAccounts() + } + return + } val userPart = Utils.getLine1Number(this) ?: "mobile" val mobileAor = "sip:$userPart@pstn" - val existingMobileUa = uas.value.find { it.account.isMobile } if (existingMobileUa != null) { - // Replace previous sip:mobile@pstn with real number if available + // Update AOR if it previously was sip:mobile@pstn but now a real number if (existingMobileUa.account.aor == "sip:mobile@pstn" && mobileAor != "sip:mobile@pstn") { - val updatedUas = uas.value.toMutableList() - updatedUas.remove(existingMobileUa) - uas.value = updatedUas.toList() - } else - return + Log.d(TAG, "Updating existing Mobile account AOR to $mobileAor") + val aorField = Account::class.java.getDeclaredField("aor") + aorField.isAccessible = true + aorField.set(existingMobileUa.account, mobileAor) + val luriField = Account::class.java.getDeclaredField("luri") + luriField.isAccessible = true + luriField.set(existingMobileUa.account, mobileAor) + Account.saveAccounts() + } + return } + Log.d(TAG, "Injecting new virtual Mobile account: $mobileAor") val account = Account(0L, mobileAor) account.isMobile = true account.nickName = "Mobile" @@ -2515,7 +2495,6 @@ class BaresipService: Service() { var isRecOn = false var toneCountry = "us" var proximitySensing = true - var telecom = true val uas = mutableStateOf(emptyList()) val uasStatus = mutableStateOf(emptyMap()) @@ -2609,8 +2588,6 @@ class BaresipService: Service() { ) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED ) { Log.d(TAG, "requestAudioFocus granted") - if (!telecom && isBluetoothHeadsetConnected(ctx)) - startBluetoothSco(ctx, 250L, 3) } else { Log.w(TAG, "requestAudioFocus denied") @@ -2619,39 +2596,6 @@ class BaresipService: Service() { return audioFocusRequest != null } - fun abandonAudioFocus(ctx: Context) { - val am = ctx.getSystemService(AUDIO_SERVICE) as AudioManager - if (audioFocusRequest != null) { - Log.d(TAG, "Abandoning audio focus") - if (androidx.media.AudioManagerCompat.abandonAudioFocusRequest( - am, - audioFocusRequest!! - ) == - AudioManager.AUDIOFOCUS_REQUEST_GRANTED - ) { - audioFocusRequest = null - if (!telecom && isBluetoothHeadsetConnected(ctx)) - stopBluetoothSco(ctx) - } - else - Log.e(TAG, "Failed to abandon audio focus") - } - am.mode = MODE_NORMAL - } - - private fun isBluetoothHeadsetConnected(ctx: Context): Boolean { - if (VERSION.SDK_INT >= 31 && - ActivityCompat.checkSelfPermission( - ctx, - Manifest.permission.BLUETOOTH_CONNECT - ) == PackageManager.PERMISSION_DENIED - ) - return false - return btAdapter != null && btAdapter!!.isEnabled && - btAdapter!!.getProfileConnectionState(BluetoothHeadset.HEADSET) == - BluetoothAdapter.STATE_CONNECTED - } - private fun isBluetoothScoOn(am: AudioManager): Boolean { return if (VERSION.SDK_INT < 31) @Suppress("DEPRECATION") diff --git a/app/src/main/kotlin/com/tutpro/baresip/Call.kt b/app/src/main/kotlin/com/tutpro/baresip/Call.kt index 637de941..8b59122d 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Call.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Call.kt @@ -151,15 +151,11 @@ open class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir } open fun hangup(code: Int, reason: String) { - if (BaresipService.telecom) { - val connection = ConnectionService.connections[callp] - if (connection != null) - connection.onDisconnect() - else - Api.ua_hangup(ua.uap, callp, code, reason) - } else { + val connection = ConnectionService.connections[callp] + if (connection != null) + connection.onDisconnect() + else Api.ua_hangup(ua.uap, callp, code, reason) - } } open fun answer() { diff --git a/app/src/main/kotlin/com/tutpro/baresip/Config.kt b/app/src/main/kotlin/com/tutpro/baresip/Config.kt index efbedab2..da95803a 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Config.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Config.kt @@ -226,13 +226,6 @@ object Config { BaresipService.toneCountry = toneCountry config = "${config}tone_country ${BaresipService.toneCountry}\n" - val telecom = previousVariable("telecom") - if (telecom != "") - BaresipService.telecom = telecom == "yes" - else - BaresipService.telecom = true - config = "${config}telecom ${if (BaresipService.telecom) "yes" else "no"}\n" - save() BaresipService.isConfigInitialized = true diff --git a/app/src/main/kotlin/com/tutpro/baresip/MainScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/MainScreen.kt index 07612197..dd2f5446 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/MainScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/MainScreen.kt @@ -2116,60 +2116,49 @@ private fun makeCall(ctx: Context, viewModel: ViewModel, uriText: String, else { viewModel.dialerState.callButtonsEnabled.value = false var error = "" - if (BaresipService.telecom) { - val tm = ctx.getSystemService(Context.TELECOM_SERVICE) as TelecomManager - if (ua.account.isMobile) { - val phoneAccountHandle = Utils.pstnAccountHandle(ctx) - if (phoneAccountHandle != null) { - val extras = Bundle().apply { - putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle) - } - val callExtras = Bundle() - callExtras.putBoolean("pstnCall", true) - callExtras.putString("aor", aor) - extras.putBundle(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, callExtras) - try { - Log.i(TAG, "Placing Telecom PSTN call to $uri with uap=${ua.uap}") - tm.placeCall(uri.toUri(), extras) - } catch (e: SecurityException) { - error = "placeCall failed: ${e.message}" - } + val tm = ctx.getSystemService(Context.TELECOM_SERVICE) as TelecomManager + if (ua.account.isMobile) { + val phoneAccountHandle = Utils.pstnAccountHandle(ctx) + if (phoneAccountHandle != null) { + val extras = Bundle().apply { + putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle) } - else - error = "no phone account" - } - else { - val extras = Bundle() - extras.putParcelable( - TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, - BaresipService.getPhoneAccountHandle(ctx) - ) val callExtras = Bundle() - callExtras.putBoolean("conferenceCall", dialerState.showCallConferenceButton.value) - callExtras.putLong("uap", ua.uap) - if (onHoldCallp != 0L) - callExtras.putLong("onHoldCallp", onHoldCallp) + callExtras.putBoolean("pstnCall", true) + callExtras.putString("aor", aor) extras.putBundle(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, callExtras) try { - Log.d(TAG, "Placing Telecom SIP call to $uri with uap=${ua.uap}") + Log.i(TAG, "Placing Telecom PSTN call to $uri with uap=${ua.uap}") tm.placeCall(uri.toUri(), extras) } catch (e: SecurityException) { error = "placeCall failed: ${e.message}" } } - if (error != "") { - Log.e(TAG, error) - viewModel.dialerState.callButtonsEnabled.value = true - } + else + error = "no phone account" } else { - val intent = Intent(ctx, BaresipService::class.java) - intent.action = "Start Call" - intent.putExtra("uap", ua.uap) - intent.putExtra("uri", uri) - intent.putExtra("conferenceCall", dialerState.showCallConferenceButton.value) - intent.putExtra("onHoldCallp", onHoldCallp) - ctx.startService(intent) + val extras = Bundle() + extras.putParcelable( + TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, + BaresipService.getPhoneAccountHandle(ctx) + ) + val callExtras = Bundle() + callExtras.putBoolean("conferenceCall", dialerState.showCallConferenceButton.value) + callExtras.putLong("uap", ua.uap) + if (onHoldCallp != 0L) + callExtras.putLong("onHoldCallp", onHoldCallp) + extras.putBundle(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, callExtras) + try { + Log.d(TAG, "Placing Telecom SIP call to $uri with uap=${ua.uap}") + tm.placeCall(uri.toUri(), extras) + } catch (e: SecurityException) { + error = "placeCall failed: ${e.message}" + } + } + if (error != "") { + Log.e(TAG, error) + viewModel.dialerState.callButtonsEnabled.value = true } } } @@ -2392,8 +2381,6 @@ fun handleServiceEvent(ctx: Context, viewModel: ViewModel, event: String, params when (ev[0]) { "call rejected" -> { - if (!BaresipService.telecom) - BaresipService.abandonAudioFocus(ctx) if (aor == viewModel.selectedAor.value) viewModel.triggerAccountUpdate() } diff --git a/app/src/main/kotlin/com/tutpro/baresip/SettingsScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/SettingsScreen.kt index 9b6b3cea..2434a3fa 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/SettingsScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/SettingsScreen.kt @@ -80,6 +80,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable +import com.tutpro.baresip.BaresipService.Companion.uas import com.tutpro.baresip.CustomElements.AlertDialog import com.tutpro.baresip.CustomElements.verticalScrollbar import com.tutpro.baresip.Utils.copyInputStreamToFile @@ -1031,6 +1032,7 @@ private fun SettingsContent( ) { result -> Log.d(TAG, "dialerRoleRequest result: $result") viewModel.defaultDialer.value = roleManager.isRoleHeld(RoleManager.ROLE_DIALER) + BaresipService.instance?.addMobileUserAgent() } Switch( checked = defaultDialer, @@ -1181,14 +1183,14 @@ private fun SettingsContent( UserAgent() UniqueContactUri() AudioSettings(navController) + if (VERSION.SDK_INT >= 29) + DefaultDialer() BatteryOptimizations() DarkTheme() if (VERSION.SDK_INT >= 31) DynamicColors() ColorBlind() ProximitySensing() - if (VERSION.SDK_INT >= 29) - DefaultDialer() Debug() SipTrace() Reset(onRestartApp) diff --git a/app/src/main/kotlin/com/tutpro/baresip/Utils.kt b/app/src/main/kotlin/com/tutpro/baresip/Utils.kt index cdceed46..3dd7de9b 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Utils.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Utils.kt @@ -4,8 +4,10 @@ import android.Manifest import android.annotation.SuppressLint import android.app.Activity import android.app.KeyguardManager +import android.app.role.RoleManager import android.content.ContentResolver import android.content.Context +import android.content.Context.ROLE_SERVICE import android.content.Intent import android.content.pm.PackageManager import android.content.res.Configuration @@ -1335,15 +1337,16 @@ object Utils { return file } + @RequiresApi(29) fun pstnAccountHandle(ctx: Context): PhoneAccountHandle? { + val roleManager = ctx.getSystemService(ROLE_SERVICE) as RoleManager if (ctx.checkSelfPermission(Manifest.permission.READ_PHONE_STATE) == - PackageManager.PERMISSION_GRANTED) { + PackageManager.PERMISSION_GRANTED && + roleManager.isRoleHeld(RoleManager.ROLE_DIALER)) { val tm = ctx.getSystemService(Context.TELECOM_SERVICE) as TelecomManager - if (Build.VERSION.SDK_INT >= 29) { - val preferredHandle: PhoneAccountHandle? = tm.userSelectedOutgoingPhoneAccount - if (preferredHandle != null) - return preferredHandle - } + val preferredHandle: PhoneAccountHandle? = tm.userSelectedOutgoingPhoneAccount + if (preferredHandle != null) + return preferredHandle val baresipHandle = BaresipService.getPhoneAccountHandle(ctx) val phoneAccounts = tm.callCapablePhoneAccounts.filter { it != baresipHandle } return if (phoneAccounts.isNotEmpty()) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ab7a16b0..4ff482af 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -321,8 +321,7 @@ to standby state. Default Phone App Dialer role is not available - If checked, baresip is the default phone app. Do not check - if your device may need to handle also other than SIP calls or messages. + If checked, baresip is the default phone app. Listen Address IP address and port of form \'address:port\' at which baresip listens for incoming SIP requests. If IP address is an IPv6 address, it must be written inside @@ -408,9 +407,6 @@ Use colorblind friendly registration status icons Proximity Sensing If checked, proximity sensing is active during calls. - Telecom Framework - Use Android Telecom framework for calls. If you experience audio - related issues, try if it helps when you turn Telecom Framework off. Video Frame Size Size of transmitted video frames (width x height) Video Frames Per Second