Skip to content
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

Fix multiple exception handler invocation in Android #3056

Merged
merged 1 commit into from Dec 6, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 12 additions & 11 deletions ui/kotlinx-coroutines-android/src/AndroidExceptionPreHandler.kt
Expand Up @@ -34,20 +34,21 @@ internal class AndroidExceptionPreHandler :

override fun handleException(context: CoroutineContext, exception: Throwable) {
/*
* If we are on old SDK, then use Android's `Thread.getUncaughtExceptionPreHandler()` that ensures that
* an exception is logged before crashing the application.
* 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.
*
* 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/
* We're fixing this by manually invoking the pre-handler using reflection, if running on an Android Oreo SDK
* version (26 and 27).
*/
val thread = Thread.currentThread()
if (Build.VERSION.SDK_INT >= 28) {
thread.uncaughtExceptionHandler.uncaughtException(thread, exception)
} else {
if (Build.VERSION.SDK_INT in 26..27) {
(preHandler()?.invoke(null) as? Thread.UncaughtExceptionHandler)
?.uncaughtException(thread, exception)
?.uncaughtException(Thread.currentThread(), exception)
}
}
}