diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ab014d2..06a7317 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -433,6 +433,9 @@ dependencies { implementation("androidx.room:room-ktx:2.5.2") implementation("pl.droidsonroids.gif:android-gif-drawable:1.2.28") + + // crash activity + implementation("cat.ereza:customactivityoncrash:2.4.0") } android { diff --git a/app/src/main/kotlin/com/fox2code/mmm/CrashHandler.kt b/app/src/main/kotlin/com/fox2code/mmm/CrashHandler.kt index 9c3f29c..d742675 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/CrashHandler.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/CrashHandler.kt @@ -13,17 +13,18 @@ import android.os.Bundle import android.view.View import android.widget.Toast import androidx.appcompat.app.AppCompatActivity +import cat.ereza.customactivityoncrash.CustomActivityOnCrash import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.textview.MaterialTextView import timber.log.Timber -import java.io.PrintWriter -import java.io.StringWriter class CrashHandler : AppCompatActivity() { - @Suppress("DEPRECATION") @SuppressLint("RestrictedApi") override fun onCreate(savedInstanceState: Bundle?) { - if (MainApplication.forceDebugLogging) Timber.i("CrashHandler.onCreate(%s)", savedInstanceState) + if (MainApplication.forceDebugLogging) Timber.i( + "CrashHandler.onCreate(%s)", + savedInstanceState + ) // log intent with extras if (MainApplication.forceDebugLogging) Timber.d("CrashHandler.onCreate: intent=%s", intent) super.onCreate(savedInstanceState) @@ -32,21 +33,7 @@ class CrashHandler : AppCompatActivity() { // convert stacktrace from array to string, and pretty print it (first line is the exception, the rest is the stacktrace, with each line indented by 4 spaces) val crashDetails = findViewById(R.id.crash_details) crashDetails.text = "" - // get the exception from the intent - val exceptionFromIntent = intent.getSerializableExtra("exception") as Throwable? - var exception: String? = null - // parse the exception from the intent into exception if it is not null - if (exceptionFromIntent != null) { - val stringWriter = StringWriter() - exceptionFromIntent.printStackTrace(PrintWriter(stringWriter)) - var stacktrace = stringWriter.toString() - stacktrace = stacktrace.replace(",", "\n ") - exception = stacktrace - } - val sharedPreferences = MainApplication.getPreferences("mmm") - if (exception == null && sharedPreferences != null) { - exception = sharedPreferences.getString("pref_crash_stacktrace", null) - } + val exception = CustomActivityOnCrash.getStackTraceFromIntent(intent) // if the exception is null, set the crash details to "Unknown" if (exception == null) { crashDetails.setText(R.string.crash_details) @@ -74,8 +61,6 @@ class CrashHandler : AppCompatActivity() { startActivity(intent) finish() } - // remove pref_crashed from shared preferences - sharedPreferences?.edit()?.remove("pref_crashed")?.apply() } fun copyCrashDetails(view: View) { diff --git a/app/src/main/kotlin/com/fox2code/mmm/MainActivity.kt b/app/src/main/kotlin/com/fox2code/mmm/MainActivity.kt index 47fc201..b7f40f4 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/MainActivity.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/MainActivity.kt @@ -624,12 +624,11 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper { moduleViewListBuilder.addNotification( NotificationType.MAGISK_OUTDATED ) - } else { - moduleViewListBuilder.addNotification( - NotificationType.KSU_EXPERIMENTAL - ) } } + if (InstallerInitializer.isKsu) { + moduleViewListBuilder.addNotification(NotificationType.KSU_EXPERIMENTAL) + } if (!MainApplication.isShowcaseMode) moduleViewListBuilder.addNotification( NotificationType.INSTALL_FROM_STORAGE ) diff --git a/app/src/main/kotlin/com/fox2code/mmm/MainApplication.kt b/app/src/main/kotlin/com/fox2code/mmm/MainApplication.kt index ac729f3..a830d21 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/MainApplication.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/MainApplication.kt @@ -4,14 +4,12 @@ package com.fox2code.mmm -import android.Manifest import android.annotation.SuppressLint import android.app.Activity import android.app.ActivityManager import android.app.ActivityManager.RunningAppProcessInfo import android.app.Application import android.app.Application.ActivityLifecycleCallbacks -import android.app.PendingIntent import android.content.Context import android.content.Intent import android.content.SharedPreferences @@ -24,14 +22,13 @@ import android.util.Log import androidx.annotation.StyleRes import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ContextThemeWrapper -import androidx.core.app.ActivityCompat -import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.emoji2.text.DefaultEmojiCompatConfig import androidx.emoji2.text.EmojiCompat import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.MasterKey import androidx.work.Configuration +import cat.ereza.customactivityoncrash.config.CaocConfig import com.fox2code.mmm.installer.InstallerInitializer import com.fox2code.mmm.installer.InstallerInitializer.Companion.peekMagiskVersion import com.fox2code.mmm.manager.LocalModuleInfo @@ -51,8 +48,6 @@ import ly.count.android.sdk.Countly import ly.count.android.sdk.CountlyConfig import timber.log.Timber import java.io.File -import java.io.PrintWriter -import java.io.StringWriter import java.security.SecureRandom import java.text.SimpleDateFormat import java.util.Date @@ -204,53 +199,15 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle get() = !this.isLightTheme override fun onCreate() { - - Thread.setDefaultUncaughtExceptionHandler { _: Thread?, throwable: Throwable -> - clearCachedSharedPrefs() - // send high importance notification with pending intent to open CrashHandler activity with stacktrace - val intent = Intent(this, CrashHandler::class.java) - intent.putExtra("exception", throwable) - intent.putExtra("isCrashing", true) - intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK - val pendingIntent = PendingIntent.getActivity( - this, 0, intent, - PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE - ) - // set pref - val sharedPreferences = getPreferences("mmm") - val editor = sharedPreferences!!.edit() - editor.putBoolean("pref_crashed", true) - val stringWriter = StringWriter() - throwable.printStackTrace(PrintWriter(stringWriter)) - val stacktrace = stringWriter.toString() - editor.putString("pref_crash_stacktrace", stacktrace) - editor.apply() - val crashreportingenabled = sharedPreferences.getBoolean( - "pref_crashreportingenabled", - true - ) - // send notification - val notificationManagerCompat = NotificationManagerCompat.from(this) - val notifBody = if (crashreportingenabled) getString(R.string.crash_notification_body) else getString( - R.string.crash_notification_body_noreport - ) - val notification = NotificationCompat.Builder(this, "crash") - .setSmallIcon(R.drawable.ic_baseline_error_24) - .setContentTitle(getString(R.string.crash_notification_title)) - .setContentText(notifBody) - .setPriority(NotificationCompat.PRIORITY_HIGH) - .setCategory(NotificationCompat.CATEGORY_ERROR) - .setContentIntent(pendingIntent) - .setAutoCancel(true) - .build() - if (ActivityCompat.checkSelfPermission( - this, - Manifest.permission.POST_NOTIFICATIONS - ) == PackageManager.PERMISSION_GRANTED - ) { - notificationManagerCompat.notify(0, notification) - } - } + super.onCreate() + CaocConfig.Builder.create() + .backgroundMode(CaocConfig.BACKGROUND_MODE_SILENT) //default: CaocConfig.BACKGROUND_MODE_SHOW_CUSTOM + .enabled(true) //default: true + .trackActivities(true) //default: false + .minTimeBetweenCrashesMs(2000) + .restartActivity(MainActivity::class.java) //default: null (your app's launch activity) + .errorActivity(CrashHandler::class.java) //default: null (default error activity) + .apply() supportedLocales.addAll( listOf( "ar", @@ -280,7 +237,6 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle ) if (INSTANCE == null) INSTANCE = this relPackageName = this.packageName - super.onCreate() var output = Shell.cmd("echo $(id -u)").exec().out[0] if (output != null) { output = output.trim { it <= ' ' } diff --git a/app/src/main/kotlin/com/fox2code/mmm/installer/InstallerInitializer.kt b/app/src/main/kotlin/com/fox2code/mmm/installer/InstallerInitializer.kt index 4a2de9a..b8fdbe3 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/installer/InstallerInitializer.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/installer/InstallerInitializer.kt @@ -128,20 +128,20 @@ class InstallerInitializer { } } try { - if (!Shell.cmd( - "if grep ' / ' /proc/mounts | grep -q '/dev/root' &> /dev/null; " + "then echo true; else echo false; fi", - "su -V", + if (Shell.cmd( + "if grep ' / ' /proc/mounts | grep -q '/dev/root' &> /dev/null; " + "then echo true; else echo false; fi" ).to(output).exec().isSuccess ) { - if (MainApplication.forceDebugLogging) { - Timber.i("Failed to search for ramdisk") - } if (output.size != 0) { hsRmdsk = "false" == output[0] || "true".equals( System.getProperty("ro.build.ab_update"), ignoreCase = true ) } Companion.hsRmdsk = hsRmdsk + } else { + if (MainApplication.forceDebugLogging) { + Timber.e("Failed to check for ramdisk") + } return null } if (MainApplication.forceDebugLogging) { @@ -165,34 +165,24 @@ class InstallerInitializer { } } else if (Shell.cmd( "if [ -d /data/adb/ksu ]; then echo true; else echo false; fi", - "su -V" + "/data/adb/ksud -V" ).to( output ).exec().isSuccess && "true" == output[0] ) { - // check su -v for kernelsu - val suVer: ArrayList = ArrayList() - Shell.cmd("su -v").to(suVer).exec() - if (suVer.size > 0 && suVer[0].contains("ksu") || suVer[0].contains( - "Kernelsu", - true - ) - ) { - if (MainApplication.forceDebugLogging) { - Timber.i("Kernelsu detected") - } - mgskPth = "/data/adb" - isKsu = true - // if analytics enabled, set breadcrumb for countly - if (MainApplication.analyticsAllowed()) { - Countly.sharedInstance().crashes().addCrashBreadcrumb("ksu detected") - } - } else { - if (MainApplication.forceDebugLogging) { - Timber.e("[ANOMALY] Kernelsu not detected but /data/adb/ksu exists") - } - return null + if (MainApplication.forceDebugLogging) { + Timber.i("Kernelsu detected") + } + mgskPth = "/data/adb" + isKsu = true + // if analytics enabled, set breadcrumb for countly + if (MainApplication.analyticsAllowed()) { + Countly.sharedInstance().crashes().addCrashBreadcrumb("ksu detected") + } + if (MainApplication.forceDebugLogging) { + Timber.e("[ANOMALY] Kernelsu not detected but /data/adb/ksu exists - maybe outdated?") } + return mgskPth } else { if (MainApplication.forceDebugLogging) { Timber.e("Failed to get Magisk path")