From a3009af4f585c010cd5018037123fefd644f8921 Mon Sep 17 00:00:00 2001 From: self-similarity <137652432+self-similarity@users.noreply.github.com> Date: Sat, 19 Aug 2023 19:48:10 +0000 Subject: [PATCH] Add Native Crash Handler (#565) * Add NativeCrashHandler * Safer init --- app/CMakeLists.txt | 6 +++ app/build.gradle.kts | 6 +++ app/src/main/cpp/native-lib.cpp | 28 ++++++++++++ .../lagradost/cloudstream3/AcraApplication.kt | 1 + .../cloudstream3/NativeCrashHandler.kt | 44 +++++++++++++++++++ 5 files changed, 85 insertions(+) create mode 100644 app/CMakeLists.txt create mode 100644 app/src/main/cpp/native-lib.cpp create mode 100644 app/src/main/java/com/lagradost/cloudstream3/NativeCrashHandler.kt diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt new file mode 100644 index 00000000..7f7fd14c --- /dev/null +++ b/app/CMakeLists.txt @@ -0,0 +1,6 @@ +# Set this to the minimum version your project supports. +cmake_minimum_required(VERSION 3.18) +project(CrashHandler) +find_library(log-lib log) +add_library(native-lib SHARED src/main/cpp/native-lib.cpp) +target_link_libraries(native-lib ${log-lib}) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 708a2083..d6515289 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -32,6 +32,12 @@ android { enable = true } + externalNativeBuild { + cmake { + path("CMakeLists.txt") + } + } + signingConfigs { create("prerelease") { if (prereleaseStoreFile != null) { diff --git a/app/src/main/cpp/native-lib.cpp b/app/src/main/cpp/native-lib.cpp new file mode 100644 index 00000000..f4cb531f --- /dev/null +++ b/app/src/main/cpp/native-lib.cpp @@ -0,0 +1,28 @@ +#include +#include +#include + +#define TAG "CloudStream Crash Handler" +volatile sig_atomic_t gSignalStatus = 0; +void handleNativeCrash(int signal) { + gSignalStatus = signal; +} + +extern "C" JNIEXPORT void JNICALL +Java_com_lagradost_cloudstream3_NativeCrashHandler_initNativeCrashHandler(JNIEnv *env, jobject) { + #define REGISTER_SIGNAL(X) signal(X, handleNativeCrash); + REGISTER_SIGNAL(SIGSEGV) + #undef REGISTER_SIGNAL +} + +//extern "C" JNIEXPORT void JNICALL +//Java_com_lagradost_cloudstream3_NativeCrashHandler_triggerNativeCrash(JNIEnv *env, jobject thiz) { +// int *p = nullptr; +// *p = 0; +//} + +extern "C" JNIEXPORT int JNICALL +Java_com_lagradost_cloudstream3_NativeCrashHandler_getSignalStatus(JNIEnv *env, jobject) { + //__android_log_print(ANDROID_LOG_INFO, TAG, "Got signal status %d", gSignalStatus); + return gSignalStatus; +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt b/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt index 61d467c4..32702657 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt @@ -106,6 +106,7 @@ class ExceptionHandler(val errorFile: File, val onError: (() -> Unit)) : class AcraApplication : Application() { override fun onCreate() { super.onCreate() + NativeCrashHandler.initCrashHandler() Thread.setDefaultUncaughtExceptionHandler(ExceptionHandler(filesDir.resolve("last_error")) { val intent = context!!.packageManager.getLaunchIntentForPackage(context!!.packageName) startActivity(Intent.makeRestartActivityTask(intent!!.component)) diff --git a/app/src/main/java/com/lagradost/cloudstream3/NativeCrashHandler.kt b/app/src/main/java/com/lagradost/cloudstream3/NativeCrashHandler.kt new file mode 100644 index 00000000..e5cb2702 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/NativeCrashHandler.kt @@ -0,0 +1,44 @@ +package com.lagradost.cloudstream3 + +import com.lagradost.cloudstream3.MainActivity.Companion.lastError +import com.lagradost.cloudstream3.mvvm.logError +import com.lagradost.cloudstream3.plugins.PluginManager.checkSafeModeFile +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +object NativeCrashHandler { + // external fun triggerNativeCrash() + private external fun initNativeCrashHandler() + private external fun getSignalStatus(): Int + + private fun initSignalPolling() = CoroutineScope(Dispatchers.IO).launch { + while (true) { + delay(10_000) + val signal = getSignalStatus() + // Signal is initialized to zero + if (signal == 0) continue + + // Do not crash in safe mode! + if (lastError != null) continue + if (checkSafeModeFile()) continue + + throw RuntimeException("Native crash with code: $signal. Try uninstalling extensions.\n") + } + } + + fun initCrashHandler() { + try { + System.loadLibrary("native-lib") + initNativeCrashHandler() + } catch (t: Throwable) { + // Make debug crash. + if (BuildConfig.DEBUG) throw t + logError(t) + return + } + + initSignalPolling() + } +} \ No newline at end of file