aboutsummaryrefslogtreecommitdiff
path: root/ui/kotlinx-coroutines-android/src/AndroidExceptionPreHandler.kt
blob: 7d06752cbaa6aa4cf202f915f0d9b0dc2733a6d5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/*
 * 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 androidx.annotation.*
import kotlinx.coroutines.*
import java.lang.reflect.*
import kotlin.coroutines.*

@Keep
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) {
        /*
         * 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)
        }
    }
}