mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-14 18:26:11 -04:00
fix: Handle ForegroundServiceDidNotStartInTimeException crash
This commit is contained in:
@@ -1,12 +1,16 @@
|
||||
package eu.darken.capod
|
||||
|
||||
import android.app.Application
|
||||
import android.os.Looper
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import eu.darken.capod.common.coroutine.AppScope
|
||||
import eu.darken.capod.common.debug.Bugs
|
||||
import eu.darken.capod.common.debug.autoreport.AutomaticBugReporter
|
||||
import eu.darken.capod.common.debug.logging.LogCatLogger
|
||||
import eu.darken.capod.common.debug.logging.Logging
|
||||
import eu.darken.capod.common.debug.logging.asLog
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.ERROR
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.flow.throttleLatest
|
||||
@@ -23,6 +27,7 @@ import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
@HiltAndroidApp
|
||||
open class App : Application() {
|
||||
@@ -37,6 +42,20 @@ open class App : Application() {
|
||||
super.onCreate()
|
||||
if (BuildConfig.DEBUG) Logging.install(LogCatLogger())
|
||||
|
||||
var foregroundExceptionHandled = false
|
||||
val oldHandler = Thread.getDefaultUncaughtExceptionHandler()
|
||||
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
|
||||
if (throwable.isForegroundServiceTimingException() && !foregroundExceptionHandled) {
|
||||
foregroundExceptionHandled = true
|
||||
log(TAG, WARN) { "Suppressed foreground service timing exception: ${throwable.asLog()}" }
|
||||
Bugs.report(tag = TAG, "Foreground service timing exception suppressed", exception = throwable)
|
||||
Looper.loop()
|
||||
return@setDefaultUncaughtExceptionHandler
|
||||
}
|
||||
log(TAG, ERROR) { "UNCAUGHT EXCEPTION: ${throwable.asLog()}" }
|
||||
if (oldHandler != null) oldHandler.uncaughtException(thread, throwable) else exitProcess(1)
|
||||
}
|
||||
|
||||
autoReporting.setup(this)
|
||||
|
||||
log(TAG) { "onCreate() done! ${Exception().asLog()}" }
|
||||
@@ -64,5 +83,14 @@ open class App : Application() {
|
||||
|
||||
companion object {
|
||||
internal val TAG = logTag("CAP")
|
||||
|
||||
private fun Throwable.isForegroundServiceTimingException(): Boolean {
|
||||
var current: Throwable? = this
|
||||
while (current != null) {
|
||||
if (current.javaClass.simpleName == "ForegroundServiceDidNotStartInTimeException") return true
|
||||
current = current.cause
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@ class MonitorService : Service() {
|
||||
private var monitoringJob: Job? = null
|
||||
@Volatile private var monitorGeneration = 0
|
||||
private var foregroundStartFailed = false
|
||||
private var injectionComplete = false
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
private fun promoteToForeground(notification: Notification): Boolean {
|
||||
@@ -114,7 +115,12 @@ class MonitorService : Service() {
|
||||
if (!promoteToForeground(MonitorNotifications.createEarlyNotification(this))) {
|
||||
foregroundStartFailed = true
|
||||
stopSelf()
|
||||
super.onCreate()
|
||||
try {
|
||||
super.onCreate()
|
||||
} catch (e: Exception) {
|
||||
log(TAG, WARN) { "Hilt DI failed in onCreate() (foreground denied): ${e.asLog()}" }
|
||||
Bugs.report(tag = TAG, "Hilt DI failed in onCreate() (foreground denied)", exception = e)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -127,6 +133,7 @@ class MonitorService : Service() {
|
||||
stopSelf()
|
||||
return
|
||||
}
|
||||
injectionComplete = true
|
||||
log(TAG, VERBOSE) { "onCreate()" }
|
||||
|
||||
// Replace early notification with the full one from injected MonitorNotifications.
|
||||
@@ -302,12 +309,16 @@ class MonitorService : Service() {
|
||||
log(TAG, VERBOSE) { "onDestroy()" }
|
||||
monitorScope.cancel("Service destroyed")
|
||||
|
||||
if (generalSettings.useExtraMonitorNotification.valueBlocking && !generalSettings.keepConnectedNotificationAfterDisconnect.valueBlocking) {
|
||||
try {
|
||||
notificationManager.cancel(MonitorNotifications.NOTIFICATION_ID_CONNECTED)
|
||||
} catch (e: Exception) {
|
||||
log(TAG, WARN) { "Failed to cancel connected notification: ${e.message}" }
|
||||
if (injectionComplete) {
|
||||
if (generalSettings.useExtraMonitorNotification.valueBlocking && !generalSettings.keepConnectedNotificationAfterDisconnect.valueBlocking) {
|
||||
try {
|
||||
notificationManager.cancel(MonitorNotifications.NOTIFICATION_ID_CONNECTED)
|
||||
} catch (e: Exception) {
|
||||
log(TAG, WARN) { "Failed to cancel connected notification: ${e.message}" }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log(TAG, WARN) { "onDestroy: Skipping notification cleanup, injection was incomplete." }
|
||||
}
|
||||
|
||||
super.onDestroy()
|
||||
|
||||
Reference in New Issue
Block a user