Skip to content

Do not use reflective exception pre-handler on newer Androids, since … #1030

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 22, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 28 additions & 15 deletions ui/kotlinx-coroutines-android/src/AndroidExceptionPreHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,43 @@

package kotlinx.coroutines.android

import android.os.*
import android.support.annotation.*
import kotlinx.coroutines.*
import java.lang.reflect.*
import kotlin.coroutines.*

private val getter =
try {
@Keep
internal class AndroidExceptionPreHandler :
AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler, Function0<Method?> {

private val preHandler by lazy(this)

// Reflectively lookup pre-handler. Implement Function0 to avoid generating second class for lambda
override fun invoke(): Method? = try {
Thread::class.java.getDeclaredMethod("getUncaughtExceptionPreHandler").takeIf {
Modifier.isPublic(it.modifiers) && Modifier.isStatic(it.modifiers)
}
} catch (e: Throwable) {
null /* not found */
}
catch (e: Throwable) { null /* not found */ }

/**
* Uses Android's `Thread.getUncaughtExceptionPreHandler()` whose default behavior is to log exception.
* See
* [here](https://github.com/aosp-mirror/platform_frameworks_base/blob/2efbc7239f419c931784acf98960ed6abc38c3f2/core/java/com/android/internal/os/RuntimeInit.java#L142)
*/
@Keep
internal class AndroidExceptionPreHandler :
AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler
{
override fun handleException(context: CoroutineContext, exception: Throwable) {
(getter?.invoke(null) as? Thread.UncaughtExceptionHandler)
?.uncaughtException(Thread.currentThread(), exception)
/*
* If we are on old SDK, then use Android's `Thread.getUncaughtExceptionPreHandler()` that ensures that
* an exception is logged before crashing the application.
*
* Since Android Pie default uncaught exception handler always ensures that exception is logged without interfering with
* pre-handler, so reflection hack is no longer needed.
*
* See https://android-review.googlesource.com/c/platform/frameworks/base/+/654578/
*/
val thread = Thread.currentThread()
if (Build.VERSION.SDK_INT >= 28) {
thread.uncaughtExceptionHandler.uncaughtException(thread, exception)
} else {
(preHandler?.invoke(null) as? Thread.UncaughtExceptionHandler)
?.uncaughtException(thread, exception)
}
}
}
}