Improved SIM card detection

Do not remove mobile account when role changes
This commit is contained in:
Juha Heinanen
2026-06-30 14:04:39 +03:00
parent 026e845d9b
commit 12e6f3eada

View File

@ -1,7 +1,6 @@
@file:Suppress("DEPRECATION")
package com.tutpro.baresip
import android.Manifest
import android.Manifest.permission.RECORD_AUDIO
import android.annotation.SuppressLint
import android.app.Notification
@ -117,6 +116,7 @@ class BaresipService: Service() {
private lateinit var bluetoothReceiver: BroadcastReceiver
private lateinit var hotSpotReceiver: BroadcastReceiver
private lateinit var airplaneModeReceiver: BroadcastReceiver
private lateinit var simStateReceiver: BroadcastReceiver
private lateinit var androidContactsObserver: ContentObserver
private lateinit var networkCallback: ConnectivityManager.NetworkCallback
private lateinit var stopState: String
@ -138,6 +138,7 @@ class BaresipService: Service() {
private var hotSpotReceiverRegistered = false
private var bluetoothReceiverRegistered = false
private var airplaneModeReceiverRegistered = false
private var simStateReceiverRegistered = false
private var telephonyCallbackRegistered = false
private var isNotificationInCall = false
private var isServiceClean = false
@ -329,6 +330,22 @@ class BaresipService: Service() {
)
airplaneModeReceiverRegistered = true
simStateReceiver = object : BroadcastReceiver() {
override fun onReceive(ctx: Context, intent: Intent) {
if (intent.action == "android.intent.action.SIM_STATE_CHANGED") {
Log.d(TAG, "SIM state changed")
updateMobileStatus()
}
}
}
ContextCompat.registerReceiver(
this,
simStateReceiver,
IntentFilter("android.intent.action.SIM_STATE_CHANGED"),
ContextCompat.RECEIVER_EXPORTED
)
simStateReceiverRegistered = true
tm = getSystemService(TELECOM_SERVICE) as TelecomManager
btm = getSystemService(BLUETOOTH_SERVICE) as BluetoothManager
@ -1738,6 +1755,7 @@ class BaresipService: Service() {
Handler(Looper.getMainLooper()).post {
addMobileUserAgent()
updateMobileStatus()
val mobileUa = uas.value.find { it.account.isMobile }
if (mobileUa != null)
@ -2244,22 +2262,24 @@ class BaresipService: Service() {
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()
CallHistoryNew.save()
Message.save()
updateStatusNotification()
}
return
}
val mobileAor = "sip:mobile@pstn"
val isAirplaneModeOn = Utils.isAirplaneModeOn(this)
val status = if (mobileAccountHandle == null || isAirplaneModeOn || !isSimReady())
R.drawable.circle_white
else if (existingMobileUa?.status == circleGreen.getValue(colorblind))
circleGreen.getValue(colorblind)
else
circleRed.getValue(colorblind)
if (existingMobileUa != null) {
if (existingMobileUa.status != status) {
Log.d(TAG, "Updating existing Mobile UA status to $status")
existingMobileUa.updateStatus(status)
updateStatusNotification()
if (isMainVisible)
registrationUpdate.postValue(System.currentTimeMillis())
}
return
}
@ -2270,13 +2290,7 @@ class BaresipService: Service() {
account.regint = 0
account.telProvider = ""
val mobileUa = UserAgent(0L, account)
val isAirplaneModeOn = Utils.isAirplaneModeOn(this)
val hasSimCard = hasSimCard()
mobileUa.status = if (isAirplaneModeOn || !hasSimCard)
R.drawable.circle_white
else
circleRed.getValue(colorblind)
updateMobileStatus(mobileUa.status)
mobileUa.status = status
val updatedUas = uas.value.toMutableList()
updatedUas.add(mobileUa)
@ -2298,12 +2312,8 @@ class BaresipService: Service() {
private fun updateMobileStatus(newStatus: Int? = null) {
uas.value.find { it.account.isMobile }?.let { ua ->
val isAirplaneModeOn = Utils.isAirplaneModeOn(this)
val hasSimCard = hasSimCard()
val status = newStatus ?: if (isAirplaneModeOn || !hasSimCard) {
if (ua.status == circleGreen.getValue(colorblind))
ua.status
else
R.drawable.circle_white
val status = newStatus ?: if (isAirplaneModeOn || !isSimReady()) {
R.drawable.circle_white
}
else if (ua.status == R.drawable.circle_white)
// Show "Red" (not yet in service)
@ -2324,10 +2334,10 @@ class BaresipService: Service() {
if (state == previousMobileServiceState) return
previousMobileServiceState = state
val isAirplaneModeOn = Utils.isAirplaneModeOn(this)
val hasSimCard = hasSimCard()
val status = if (state == ServiceState.STATE_IN_SERVICE)
val isSimReady = isSimReady()
val status = if (state == ServiceState.STATE_IN_SERVICE && isSimReady)
circleGreen.getValue(colorblind)
else if (isAirplaneModeOn || !hasSimCard)
else if (isAirplaneModeOn || !isSimReady())
R.drawable.circle_white
else
circleRed.getValue(colorblind)
@ -2939,73 +2949,83 @@ class BaresipService: Service() {
tm.registerPhoneAccount(phoneAccount)
}
fun isSimReady(): Boolean {
val tm = getSystemService(TELEPHONY_SERVICE) as TelephonyManager
return tm.simState == TelephonyManager.SIM_STATE_READY
}
private fun cleanService() {
if (!isServiceClean) {
if (hotSpotReceiverRegistered) {
try {
unregisterReceiver(hotSpotReceiver)
} catch (_: IllegalArgumentException) {}
hotSpotReceiverRegistered = false
}
if (bluetoothReceiverRegistered) {
try {
unregisterReceiver(bluetoothReceiver)
} catch (_: IllegalArgumentException) {}
bluetoothReceiverRegistered = false
}
if (airplaneModeReceiverRegistered) {
try {
unregisterReceiver(airplaneModeReceiver)
} catch (_: IllegalArgumentException) {}
airplaneModeReceiverRegistered = false
}
if (telephonyCallbackRegistered) {
if (VERSION.SDK_INT >= 31)
try {
telephonyManager.unregisterTelephonyCallback(telephonyCallback)
} catch (_: Exception) {}
else {
@Suppress("DEPRECATION")
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)
}
telephonyCallbackRegistered = false
}
val callps = ConnectionService.connections.keys.toList()
for (callp in callps)
ConnectionService.onCallClosed(callp)
ConnectionService.pendingOutgoingConnection?.let {
it.setDisconnected(DisconnectCause(DisconnectCause.CANCELED))
it.destroy()
ConnectionService.pendingOutgoingConnection = null
}
stopRinging()
stopMediaPlayer()
abandonAudioFocus(this)
uas.value = emptyList()
uasStatus.value = emptyMap()
callHistory.clear()
messages = emptyList()
if (this::nm.isInitialized)
nm.cancelAll()
if (this::partialWakeLock.isInitialized && partialWakeLock.isHeld)
partialWakeLock.release()
if (this::proximityWakeLock.isInitialized && proximityWakeLock.isHeld)
proximityWakeLock.release()
if (this::wifiLock.isInitialized)
wifiLock.release()
if (this::networkCallback.isInitialized)
try {
cm.unregisterNetworkCallback(networkCallback)
} catch (_: Exception) {}
if (androidContactsObserverRegistered) {
try {
contentResolver.unregisterContentObserver(androidContactsObserver)
} catch (_: Exception) {}
androidContactsObserverRegistered = false
}
releaseAudioEffects()
isServiceClean = true
if (isServiceClean) return
if (simStateReceiverRegistered) {
try {
unregisterReceiver(simStateReceiver)
} catch (_: IllegalArgumentException) {}
simStateReceiverRegistered = false
}
if (hotSpotReceiverRegistered) {
try {
unregisterReceiver(hotSpotReceiver)
} catch (_: IllegalArgumentException) {}
hotSpotReceiverRegistered = false
}
if (bluetoothReceiverRegistered) {
try {
unregisterReceiver(bluetoothReceiver)
} catch (_: IllegalArgumentException) {}
bluetoothReceiverRegistered = false
}
if (airplaneModeReceiverRegistered) {
try {
unregisterReceiver(airplaneModeReceiver)
} catch (_: IllegalArgumentException) {}
airplaneModeReceiverRegistered = false
}
if (telephonyCallbackRegistered) {
if (VERSION.SDK_INT >= 31)
try {
telephonyManager.unregisterTelephonyCallback(telephonyCallback)
} catch (_: Exception) {}
else {
@Suppress("DEPRECATION")
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)
}
telephonyCallbackRegistered = false
}
val callps = ConnectionService.connections.keys.toList()
for (callp in callps)
ConnectionService.onCallClosed(callp)
ConnectionService.pendingOutgoingConnection?.let {
it.setDisconnected(DisconnectCause(DisconnectCause.CANCELED))
it.destroy()
ConnectionService.pendingOutgoingConnection = null
}
stopRinging()
stopMediaPlayer()
abandonAudioFocus(this)
uas.value = emptyList()
uasStatus.value = emptyMap()
callHistory.clear()
messages = emptyList()
if (this::nm.isInitialized)
nm.cancelAll()
if (this::partialWakeLock.isInitialized && partialWakeLock.isHeld)
partialWakeLock.release()
if (this::proximityWakeLock.isInitialized && proximityWakeLock.isHeld)
proximityWakeLock.release()
if (this::wifiLock.isInitialized)
wifiLock.release()
if (this::networkCallback.isInitialized)
try {
cm.unregisterNetworkCallback(networkCallback)
} catch (_: Exception) {}
if (androidContactsObserverRegistered) {
try {
contentResolver.unregisterContentObserver(androidContactsObserver)
} catch (_: Exception) {}
androidContactsObserverRegistered = false
}
releaseAudioEffects()
isServiceClean = true
}
private external fun baresipStart(
@ -3017,13 +3037,6 @@ class BaresipService: Service() {
external fun baresipStop(force: Boolean)
private fun hasSimCard(): Boolean {
if (checkSelfPermission(Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED)
return false
val sm = getSystemService(TELEPHONY_SUBSCRIPTION_SERVICE) as android.telephony.SubscriptionManager
return sm.activeSubscriptionInfoList?.isNotEmpty() ?: false
}
@SuppressLint("MutableCollectionMutableState")
companion object {