mirror of
https://github.com/bitfireAT/davx5-ose
synced 2024-10-02 01:10:25 +00:00
Install uncaught exception handler in a separate startup plugin (bitfireAT/davx5#597)
This commit is contained in:
parent
3a38a06302
commit
50c13e5b6d
|
@ -5,13 +5,11 @@
|
|||
package at.bitfire.davdroid
|
||||
|
||||
import android.app.Application
|
||||
import android.os.StrictMode
|
||||
import androidx.hilt.work.HiltWorkerFactory
|
||||
import androidx.work.Configuration
|
||||
import at.bitfire.davdroid.log.LogManager
|
||||
import at.bitfire.davdroid.startup.StartupPlugin
|
||||
import at.bitfire.davdroid.sync.account.AccountsCleanupWorker
|
||||
import at.bitfire.davdroid.ui.DebugInfoActivity
|
||||
import at.bitfire.davdroid.ui.NotificationUtils
|
||||
import at.bitfire.davdroid.ui.UiUtils
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
|
@ -19,13 +17,11 @@ import kotlinx.coroutines.DelicateCoroutinesApi
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.logging.Level
|
||||
import java.util.logging.Logger
|
||||
import javax.inject.Inject
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
@HiltAndroidApp
|
||||
class App: Application(), Thread.UncaughtExceptionHandler, Configuration.Provider {
|
||||
class App: Application(), Configuration.Provider {
|
||||
|
||||
@Inject
|
||||
lateinit var logger: Logger
|
||||
|
@ -51,19 +47,7 @@ class App: Application(), Thread.UncaughtExceptionHandler, Configuration.Provide
|
|||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
if (BuildConfig.DEBUG)
|
||||
// debug builds
|
||||
StrictMode.setVmPolicy(StrictMode.VmPolicy.Builder()
|
||||
.detectActivityLeaks()
|
||||
.detectFileUriExposure()
|
||||
.detectLeakedClosableObjects()
|
||||
.detectLeakedRegistrationObjects()
|
||||
.detectLeakedSqlLiteObjects()
|
||||
.penaltyLog()
|
||||
.build())
|
||||
else // if (BuildConfig.FLAVOR == FLAVOR_STANDARD)
|
||||
// handle uncaught exceptions in non-debug standard flavor
|
||||
Thread.setDefaultUncaughtExceptionHandler(this)
|
||||
logger.fine("Logging using LogManager $logManager")
|
||||
|
||||
NotificationUtils.createChannels(this)
|
||||
|
||||
|
@ -94,16 +78,4 @@ class App: Application(), Thread.UncaughtExceptionHandler, Configuration.Provide
|
|||
}
|
||||
}
|
||||
|
||||
override fun uncaughtException(t: Thread, e: Throwable) {
|
||||
logger.log(Level.SEVERE, "Unhandled exception!", e)
|
||||
|
||||
val intent = DebugInfoActivity.IntentBuilder(this)
|
||||
.withCause(e)
|
||||
.newTask()
|
||||
.build()
|
||||
startActivity(intent)
|
||||
|
||||
exitProcess(1)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/***************************************************************************************************
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
**************************************************************************************************/
|
||||
|
||||
package at.bitfire.davdroid.startup
|
||||
|
||||
import android.content.Context
|
||||
import android.os.StrictMode
|
||||
import at.bitfire.davdroid.BuildConfig
|
||||
import at.bitfire.davdroid.startup.StartupPlugin.Companion.PRIORITY_DEFAULT
|
||||
import at.bitfire.davdroid.startup.StartupPlugin.Companion.PRIORITY_HIGHEST
|
||||
import dagger.Binds
|
||||
import dagger.BindsOptionalOf
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dagger.multibindings.IntoSet
|
||||
import java.util.Optional
|
||||
import java.util.logging.Logger
|
||||
import javax.inject.Inject
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
|
||||
/**
|
||||
* Sets up the uncaught exception (crash) handler and enables StrictMode in debug builds.
|
||||
*/
|
||||
class CrashHandlerSetup @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val logger: Logger,
|
||||
private val crashHandler: Optional<Thread.UncaughtExceptionHandler>
|
||||
): StartupPlugin {
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
interface CrashHandlerSetupModule {
|
||||
// allows to inject Optional<Thread.UncaughtExceptionHandler>
|
||||
@BindsOptionalOf
|
||||
fun optionalDebugInfoCrashHandler(): Thread.UncaughtExceptionHandler
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
fun crashHandlerSetup(impl: CrashHandlerSetup): StartupPlugin
|
||||
}
|
||||
|
||||
|
||||
override fun onAppCreate() {
|
||||
if (BuildConfig.DEBUG) {
|
||||
logger.info("Debug build, enabling StrictMode with logging")
|
||||
StrictMode.setVmPolicy(
|
||||
StrictMode.VmPolicy.Builder()
|
||||
.detectAll()
|
||||
.penaltyLog()
|
||||
.build()
|
||||
)
|
||||
}
|
||||
|
||||
val handler = crashHandler.getOrNull()
|
||||
if (handler != null) {
|
||||
logger.info("Setting uncaught exception handler: ${handler.javaClass.name}")
|
||||
Thread.setDefaultUncaughtExceptionHandler(handler)
|
||||
} else
|
||||
logger.info("Using default uncaught exception handler")
|
||||
}
|
||||
|
||||
override fun priority() = PRIORITY_HIGHEST
|
||||
|
||||
override suspend fun onAppCreateAsync() {
|
||||
}
|
||||
|
||||
override fun priorityAsync(): Int = PRIORITY_DEFAULT
|
||||
|
||||
}
|
|
@ -6,6 +6,11 @@ package at.bitfire.davdroid.startup
|
|||
|
||||
interface StartupPlugin {
|
||||
|
||||
companion object {
|
||||
const val PRIORITY_DEFAULT = 100
|
||||
const val PRIORITY_HIGHEST = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs synchronously during [at.bitfire.davdroid.App.onCreate]. Use only for tasks that must be completed before
|
||||
* the app can run. Causes the app to start slower.
|
||||
|
|
|
@ -5,42 +5,41 @@
|
|||
package at.bitfire.davdroid.startup
|
||||
|
||||
import android.content.Context
|
||||
import at.bitfire.davdroid.startup.StartupPlugin.Companion.PRIORITY_DEFAULT
|
||||
import at.bitfire.davdroid.util.TaskUtils
|
||||
import at.bitfire.davdroid.util.packageChangedFlow
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dagger.multibindings.IntoSet
|
||||
import java.util.logging.Logger
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Watches whether a tasks app has been installed or uninstalled and updates
|
||||
* the selected tasks app and task sync settings accordingly.
|
||||
*/
|
||||
class TasksAppWatcher private constructor(
|
||||
private val context: Context,
|
||||
class TasksAppWatcher @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val logger: Logger
|
||||
): StartupPlugin {
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
class TasksAppWatcherModule {
|
||||
@Provides
|
||||
interface TasksAppWatcherModule {
|
||||
@Binds
|
||||
@IntoSet
|
||||
fun tasksAppWatcher(
|
||||
@ApplicationContext context: Context,
|
||||
logger: Logger
|
||||
): StartupPlugin = TasksAppWatcher(context, logger)
|
||||
fun tasksAppWatcher(impl: TasksAppWatcher): StartupPlugin
|
||||
}
|
||||
|
||||
|
||||
override fun onAppCreate() {
|
||||
}
|
||||
|
||||
override fun priority() = 100
|
||||
override fun priority() = PRIORITY_DEFAULT
|
||||
|
||||
override suspend fun onAppCreateAsync() {
|
||||
logger.info("Watching for package changes in order to detect tasks app changes")
|
||||
|
@ -49,7 +48,7 @@ class TasksAppWatcher private constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun priorityAsync() = 100
|
||||
override fun priorityAsync() = PRIORITY_DEFAULT
|
||||
|
||||
|
||||
private fun onPackageChanged() {
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/***************************************************************************************************
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
**************************************************************************************************/
|
||||
|
||||
package at.bitfire.davdroid
|
||||
|
||||
import android.content.Context
|
||||
import at.bitfire.davdroid.ui.DebugInfoActivity
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import java.util.logging.Level
|
||||
import java.util.logging.Logger
|
||||
import javax.inject.Inject
|
||||
|
||||
class DebugInfoCrashHandler @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val logger: Logger
|
||||
): Thread.UncaughtExceptionHandler {
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
interface DebugInfoCrashHandlerModule {
|
||||
@Binds
|
||||
fun debugInfoCrashHandler(
|
||||
debugInfoCrashHandler: DebugInfoCrashHandler
|
||||
): Thread.UncaughtExceptionHandler
|
||||
}
|
||||
|
||||
// See https://developer.android.com/about/versions/oreo/android-8.0-changes#loue
|
||||
val originalCrashHandler = Thread.getDefaultUncaughtExceptionHandler()
|
||||
|
||||
|
||||
override fun uncaughtException(t: Thread, e: Throwable) {
|
||||
logger.log(Level.SEVERE, "Unhandled exception in thread ${t.id}!", e)
|
||||
|
||||
// start debug info activity with exception (will be started in a new process)
|
||||
val intent = DebugInfoActivity.IntentBuilder(context)
|
||||
.withCause(e)
|
||||
.newTask()
|
||||
.build()
|
||||
context.startActivity(intent)
|
||||
|
||||
// pass through to default handler to kill the process
|
||||
originalCrashHandler?.uncaughtException(t, e)
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue