From dde539f0fd8917c6b26ccd0f2c9e5a9a6bfe17cb Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 20 May 2026 13:06:35 +0000 Subject: [PATCH] fix: catch ForegroundServiceStartNotAllowedException in PlaybackService Media3's MediaSessionService.onStartCommand calls stopSelfSafely() when delivered an intent (e.g. MEDIA_BUTTON from a headset) while no playback is active. stopSelfSafely() invokes startForeground() to detach the notification before stopping, which Android 12+ rejects when the app is backgrounded with ForegroundServiceStartNotAllowedException. Swallow that case in onStartCommand and stopSelf, matching the pattern already used in NotificationRelayService. https://claude.ai/code/session_01KTZDxK7zacrhFfqMHqt8dm --- .../playback/service/PlaybackService.kt | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/service/PlaybackService.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/service/PlaybackService.kt index 0c636f8a9a..c73b028646 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/service/PlaybackService.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/service/PlaybackService.kt @@ -20,7 +20,10 @@ */ package com.vitorpamplona.amethyst.service.playback.service +import android.app.ForegroundServiceStartNotAllowedException +import android.content.Intent import android.net.Uri +import android.os.Build import androidx.annotation.OptIn import androidx.core.net.toUri import androidx.media3.common.C @@ -137,6 +140,29 @@ class PlaybackService : MediaSessionService() { Log.d("PlaybackService", "PlaybackService.onCreate") } + override fun onStartCommand( + intent: Intent?, + flags: Int, + startId: Int, + ): Int = + try { + super.onStartCommand(intent, flags, startId) + } catch (e: IllegalStateException) { + // Media3's MediaSessionService.onStartCommand can call stopSelfSafely() when there is + // no active playback to handle a delivered intent (e.g. a MEDIA_BUTTON from a headset + // arriving while the app is backgrounded). stopSelfSafely() invokes startForeground() + // to detach the foreground notification before stopping, which Android 12+ rejects + // from the background with ForegroundServiceStartNotAllowedException. There is no + // playback to keep alive in this path, so swallow it and stop the service. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && e is ForegroundServiceStartNotAllowedException) { + Log.w("PlaybackService") { "Foreground service start not allowed; stopping PlaybackService" } + stopSelf() + START_NOT_STICKY + } else { + throw e + } + } + override fun onDestroy() { Log.d("PlaybackService", "PlaybackService.onDestroy")