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
import android.Manifest
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ServiceInfo
import android.os.Build
import android.os.IBinder
import androidx.core.app.NotificationCompat
import androidx.core.app.ServiceCompat
import androidx.core.content.ContextCompat
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.quartz.utils.Log
private const val TAG = "CallForegroundService"
class CallForegroundService : Service() {
companion object {
@@ -57,16 +63,26 @@ class CallForegroundService : Service() {
ACTION_START -> {
val peerName = intent.getStringExtra(EXTRA_PEER_NAME) ?: "Unknown"
val notification = buildNotification(peerName)
ServiceCompat.startForeground(
this,
NOTIFICATION_ID,
notification,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE
} else {
0
},
)
val hasAudioPermission =
ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) ==
PackageManager.PERMISSION_GRANTED
try {
val fgsType =
if (hasAudioPermission && Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE
} 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 -> {