1 /* 2 * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. 3 */ 4 5 package kotlinx.coroutines.android 6 7 import android.os.* 8 import kotlinx.coroutines.* 9 import java.lang.reflect.* 10 import kotlin.coroutines.* 11 12 internal class AndroidExceptionPreHandler : 13 AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler 14 { 15 @Volatile 16 private var _preHandler: Any? = this // uninitialized marker 17 18 // Reflectively lookup pre-handler. preHandlernull19 private fun preHandler(): Method? { 20 val current = _preHandler 21 if (current !== this) return current as Method? 22 val declared = try { 23 Thread::class.java.getDeclaredMethod("getUncaughtExceptionPreHandler").takeIf { 24 Modifier.isPublic(it.modifiers) && Modifier.isStatic(it.modifiers) 25 } 26 } catch (e: Throwable) { 27 null /* not found */ 28 } 29 _preHandler = declared 30 return declared 31 } 32 handleExceptionnull33 override fun handleException(context: CoroutineContext, exception: Throwable) { 34 /* 35 * Android Oreo introduced private API for a global pre-handler for uncaught exceptions, to ensure that the 36 * exceptions are logged even if the default uncaught exception handler is replaced by the app. The pre-handler 37 * is invoked from the Thread's private dispatchUncaughtException() method, so our manual invocation of the 38 * Thread's uncaught exception handler bypasses the pre-handler in Android Oreo, and uncaught coroutine 39 * exceptions are not logged. This issue was addressed in Android Pie, which added a check in the default 40 * uncaught exception handler to invoke the pre-handler if it was not invoked already (see 41 * https://android-review.googlesource.com/c/platform/frameworks/base/+/654578/). So the issue is present only 42 * in Android Oreo. 43 * 44 * We're fixing this by manually invoking the pre-handler using reflection, if running on an Android Oreo SDK 45 * version (26 and 27). 46 */ 47 if (Build.VERSION.SDK_INT in 26..27) { 48 (preHandler()?.invoke(null) as? Thread.UncaughtExceptionHandler) 49 ?.uncaughtException(Thread.currentThread(), exception) 50 } 51 } 52 } 53