From 28d2dee617c2a5c9feff8a60b16de94c00a2c0b7 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Fri, 17 Jul 2026 09:20:20 -0400 Subject: [PATCH] Potentially resolve bad service starts on BOOT_COMPLETED. --- .../securesms/service/KeyCachingService.java | 13 ++++++- .../signal/core/util/SafeForegroundService.kt | 34 ++++++++++++++++--- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java b/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java index 3535ffe9e4..d82d8bcf3a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java @@ -18,12 +18,14 @@ package org.thoughtcrime.securesms.service; import android.annotation.SuppressLint; import android.app.AlarmManager; +import android.app.ForegroundServiceStartNotAllowedException; import android.app.Notification; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.SystemClock; @@ -285,7 +287,16 @@ public class KeyCachingService extends Service { builder.setContentIntent(buildLaunchIntent()); stopForeground(true); - startForeground(SERVICE_RUNNING_ID, builder.build()); + + try { + startForeground(SERVICE_RUNNING_ID, builder.build()); + } catch (Exception e) { + if (Build.VERSION.SDK_INT >= 31 && e instanceof ForegroundServiceStartNotAllowedException) { + Log.w(TAG, "Not allowed to start foreground service.", e); + } else { + throw e; + } + } } private void broadcastNewSecret() { diff --git a/core/util/src/main/java/org/signal/core/util/SafeForegroundService.kt b/core/util/src/main/java/org/signal/core/util/SafeForegroundService.kt index d46ee471fe..412fe493ed 100644 --- a/core/util/src/main/java/org/signal/core/util/SafeForegroundService.kt +++ b/core/util/src/main/java/org/signal/core/util/SafeForegroundService.kt @@ -6,6 +6,7 @@ package org.signal.core.util import android.annotation.SuppressLint +import android.app.ForegroundServiceStartNotAllowedException import android.app.Notification import android.app.Service import android.content.Context @@ -183,7 +184,6 @@ abstract class SafeForegroundService : Service() { super.onCreate() } - @SuppressLint("WrongConstant") override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { checkNotNull(intent) { "Must have an intent!" } @@ -191,10 +191,11 @@ abstract class SafeForegroundService : Service() { if (intent.action == ACTION_TIMEOUT) { Log.i(TAG, "Time limit for foreground services has been met. Skipping starting a foreground.") - } else if (Build.VERSION.SDK_INT >= 30 && serviceType(intent) != 0) { - startForeground(notificationId, getForegroundNotification(intent), serviceType(intent)) - } else { - startForeground(notificationId, getForegroundNotification(intent)) + } else if (!postForegroundNotification(intent)) { + Log.w(tag, "[onStartCommand] Unable to enter the foreground. Stopping service. action: ${intent.action}") + stateLock.withLock { states[javaClass] = State.STOPPED } + stopSelf() + return START_NOT_STICKY } when (val action = intent.action) { @@ -216,6 +217,29 @@ abstract class SafeForegroundService : Service() { return START_NOT_STICKY } + /** + * Moves the service into the foreground. Returns false if the system prevented us from doing so, in which case the caller should abandon the start and stop + * the service. + */ + @SuppressLint("WrongConstant") + private fun postForegroundNotification(intent: Intent): Boolean { + return try { + if (Build.VERSION.SDK_INT >= 30 && serviceType(intent) != 0) { + startForeground(notificationId, getForegroundNotification(intent), serviceType(intent)) + } else { + startForeground(notificationId, getForegroundNotification(intent)) + } + true + } catch (e: Exception) { + if (Build.VERSION.SDK_INT >= 31 && e is ForegroundServiceStartNotAllowedException) { + Log.w(tag, "[postForegroundNotification] Not allowed to start foreground service.", e) + false + } else { + throw e + } + } + } + override fun onDestroy() { super.onDestroy()