fix: guard foreground service start against missing RECORD_AUDIO permission

On SDK 36 starting a foreground service with type microphone requires
RECORD_AUDIO to be granted at the moment startForeground() is called.
When the permission isn't available (e.g. revoked between call start
and peer connection) the service now falls back to a generic foreground
type instead of crashing with a SecurityException.

https://claude.ai/code/session_01WafqMofFQfWCQA5TcLvHRb
This commit is contained in:
Claude
2026-04-02 23:56:04 +00:00
parent 5b7e47d87e
commit dca38f7e01
@@ -20,17 +20,23 @@
*/ */
package com.vitorpamplona.amethyst.service.call package com.vitorpamplona.amethyst.service.call
import android.Manifest
import android.app.Notification import android.app.Notification
import android.app.NotificationChannel import android.app.NotificationChannel
import android.app.NotificationManager import android.app.NotificationManager
import android.app.Service import android.app.Service
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ServiceInfo import android.content.pm.ServiceInfo
import android.os.Build import android.os.Build
import android.os.IBinder import android.os.IBinder
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.ServiceCompat import androidx.core.app.ServiceCompat
import androidx.core.content.ContextCompat
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.quartz.utils.Log
private const val TAG = "CallForegroundService"
class CallForegroundService : Service() { class CallForegroundService : Service() {
companion object { companion object {
@@ -57,16 +63,26 @@ class CallForegroundService : Service() {
ACTION_START -> { ACTION_START -> {
val peerName = intent.getStringExtra(EXTRA_PEER_NAME) ?: "Unknown" val peerName = intent.getStringExtra(EXTRA_PEER_NAME) ?: "Unknown"
val notification = buildNotification(peerName) val notification = buildNotification(peerName)
ServiceCompat.startForeground( val hasAudioPermission =
this, ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) ==
NOTIFICATION_ID, PackageManager.PERMISSION_GRANTED
notification, try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { val fgsType =
ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE if (hasAudioPermission && Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
} else { ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE
0 } else {
}, 0
) }
ServiceCompat.startForeground(this, NOTIFICATION_ID, notification, fgsType)
} catch (e: SecurityException) {
Log.e(TAG, "Cannot start microphone foreground service, falling back", e)
try {
ServiceCompat.startForeground(this, NOTIFICATION_ID, notification, 0)
} catch (e2: Exception) {
Log.e(TAG, "Foreground service start failed entirely", e2)
stopSelf()
}
}
} }
ACTION_STOP -> { ACTION_STOP -> {