blob: 0bc603ea1e1909930427c2cb00bc408659afa4da [file] [log] [blame]
/*
* Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines.android
import android.os.*
import kotlinx.coroutines.*
import java.lang.reflect.*
import kotlin.coroutines.*
internal class AndroidExceptionPreHandler :
AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler
{
@Volatile
private var _preHandler: Any? = this // uninitialized marker
// Reflectively lookup pre-handler.
private fun preHandler(): Method? {
val current = _preHandler
if (current !== this) return current as Method?
val declared = try {
Thread::class.java.getDeclaredMethod("getUncaughtExceptionPreHandler").takeIf {
Modifier.isPublic(it.modifiers) && Modifier.isStatic(it.modifiers)
}
} catch (e: Throwable) {
null /* not found */
}
_preHandler = declared
return declared
}
override fun handleException(context: CoroutineContext, exception: Throwable) {
/*
* Android Oreo introduced private API for a global pre-handler for uncaught exceptions, to ensure that the
* exceptions are logged even if the default uncaught exception handler is replaced by the app. The pre-handler
* is invoked from the Thread's private dispatchUncaughtException() method, so our manual invocation of the
* Thread's uncaught exception handler bypasses the pre-handler in Android Oreo, and uncaught coroutine
* exceptions are not logged. This issue was addressed in Android Pie, which added a check in the default
* uncaught exception handler to invoke the pre-handler if it was not invoked already (see
* https://android-review.googlesource.com/c/platform/frameworks/base/+/654578/). So the issue is present only
* in Android Oreo.
*
* We're fixing this by manually invoking the pre-handler using reflection, if running on an Android Oreo SDK
* version (26 and 27).
*/
if (Build.VERSION.SDK_INT in 26..27) {
(preHandler()?.invoke(null) as? Thread.UncaughtExceptionHandler)
?.uncaughtException(Thread.currentThread(), exception)
}
}
}