Improve error reporting & fix some app crashes.

pull/202/head
Fox2Code 2 years ago
parent 22bfae45c0
commit 60d804ba49

1
.gitignore vendored

@ -1,5 +1,6 @@
*.iml
.gradle
/sentry.properties
/local.properties
/.idea/
.DS_Store

@ -1,6 +1,7 @@
plugins {
id 'com.android.application'
id 'com.mikepenz.aboutlibraries.plugin'
id "io.sentry.android.gradle" version "3.1.5"
}
android {
@ -78,8 +79,25 @@ aboutLibraries {
additionalLicenses = ["LGPL_3_0_only"]
}
sentry {
ignoredBuildTypes = ["debug"]
includeProguardMapping = true
autoUploadProguardMapping = isLocalSentry
tracingInstrumentation {
enabled = false
}
autoInstallation {
enabled = false
}
}
configurations {
implementation.exclude group: 'org.jetbrains' , module: 'annotations'
implementation.exclude group: 'io.sentry' , module: 'sentry-android-okhttp'
}
dependencies {
@ -109,8 +127,8 @@ dependencies {
implementation 'com.github.Fox2Code:AndroidANSI:1.0.1'
// Error reporting
implementation 'io.sentry:sentry-android:6.4.0'
implementation 'io.sentry:sentry-android-fragment:6.4.0'
implementation 'io.sentry:sentry-android:6.4.1'
implementation 'io.sentry:sentry-android-fragment:6.4.1'
// Markdown
implementation "io.noties.markwon:core:4.6.2"

@ -9,8 +9,6 @@ import android.content.res.Resources;
import android.graphics.Color;
import android.os.Build;
import android.os.SystemClock;
import android.system.ErrnoException;
import android.system.Os;
import android.text.SpannableStringBuilder;
import android.util.Log;
@ -50,8 +48,8 @@ import io.noties.prism4j.annotations.PrismBundle;
import io.sentry.JsonObjectWriter;
import io.sentry.NoOpLogger;
import io.sentry.TypeCheckHint;
import io.sentry.UncaughtExceptionHandlerIntegration;
import io.sentry.android.core.SentryAndroid;
import io.sentry.android.fragment.FragmentLifecycleIntegration;
import io.sentry.hints.DiskFlushNotification;
@PrismBundle(
@ -371,6 +369,7 @@ public class MainApplication extends FoxApplication
}
SentryAndroid.init(this, options -> {
options.addIntegration(new FragmentLifecycleIntegration(this, true, false));
// Note: Sentry library only take a screenshot of Fox Magisk Module Manager.
// The screen shot doesn't and cannot contain other applications (if in multi windows)
// status bar and notifications (even if notification shade is pulled down)
@ -381,6 +380,10 @@ public class MainApplication extends FoxApplication
options.setAttachScreenshot(true);
// User interaction tracing is not needed to get context of crash
options.setEnableUserInteractionTracing(false);
// Send client reports has nothing to do with error reporting
options.setSendClientReports(false);
// Auto session tracking has nothing to do with error reporting
options.setEnableAutoSessionTracking(false);
// Add a callback that will be used before the event is sent to Sentry.
// With this callback, you can modify the event or, when returning null, also discard the event.
options.setBeforeSend((event, hint) -> {

@ -50,11 +50,11 @@ public class XHooks {
@Keep
public static XRepo addXRepo(String url, String fallbackName) {
return RepoManager.getINSTANCE().addOrGet(url, fallbackName);
return RepoManager.getINSTANCE_UNSAFE().addOrGet(url, fallbackName);
}
@Keep
public static XRepo getXRepo(String url) {
return RepoManager.getINSTANCE().get(url);
return RepoManager.getINSTANCE_UNSAFE().get(url);
}
}

@ -49,6 +49,10 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import io.sentry.Breadcrumb;
import io.sentry.Sentry;
import io.sentry.SentryLevel;
public class InstallerActivity extends FoxActivity {
private static final String TAG = "InstallerActivity";
public LinearProgressIndicator progressIndicator;
@ -99,6 +103,17 @@ public class InstallerActivity extends FoxActivity {
return;
}
Log.i(TAG, "Install link: " + target);
// Note: Sentry only send this info on crash.
if (MainApplication.isCrashReportingEnabled()) {
Breadcrumb breadcrumb = new Breadcrumb();
breadcrumb.setType("install");
breadcrumb.setData("target", target);
breadcrumb.setData("name", name);
breadcrumb.setData("checksum", checksum);
breadcrumb.setCategory("app.action.preinstall");
breadcrumb.setLevel(SentryLevel.INFO);
Sentry.addBreadcrumb(breadcrumb);
}
boolean urlMode = target.startsWith("http://") || target.startsWith("https://");
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setTitle(name);
@ -138,9 +153,10 @@ public class InstallerActivity extends FoxActivity {
!new SuFile(moduleCache.getAbsolutePath()).delete())
Log.e(TAG, "Failed to delete module cache");
String errMessage = "Failed to download module zip";
byte[] rawModule;
try {
Log.i(TAG, (urlMode ? "Downloading: " : "Loading: ") + target);
byte[] rawModule = urlMode ? Http.doHttpGet(target, (progress, max, done) -> {
rawModule = urlMode ? Http.doHttpGet(target, (progress, max, done) -> {
if (max <= 0 && this.progressIndicator.isIndeterminate())
return;
this.runOnUiThread(() -> {
@ -227,6 +243,14 @@ public class InstallerActivity extends FoxActivity {
Log.e(TAG, errMessage, e);
this.setInstallStateFinished(false,
"! " + errMessage, "");
} catch (OutOfMemoryError e) {
//noinspection UnusedAssignment (Important to avoid OutOfMemoryError)
rawModule = null; // Because reference is kept when calling setInstallStateFinished
if ("Failed to install module zip".equals(errMessage))
throw e; // Ignore if in installation state.
Log.e(TAG, "Module too large", e);
this.setInstallStateFinished(false,
"! Module is too large to be loaded on this device", "");
}
}, "Module install Thread").start();
}
@ -416,6 +440,19 @@ public class InstallerActivity extends FoxActivity {
"cd \"" + this.moduleCache.getAbsolutePath() + "\"",
installCommand).to(installerController, installerMonitor);
}
// Note: Sentry only send this info on crash.
if (MainApplication.isCrashReportingEnabled()) {
Breadcrumb breadcrumb = new Breadcrumb();
breadcrumb.setType("install");
breadcrumb.setData("moduleId", moduleId == null ? "<null>" : moduleId);
breadcrumb.setData("isAnyKernel3", anyKernel3 ? "true" : "false");
breadcrumb.setData("noExtensions", noExtensions ? "true" : "false");
breadcrumb.setData("ansi", this.installerTerminal
.isAnsiEnabled() ? "enabled" : "disabled");
breadcrumb.setCategory("app.action.install");
breadcrumb.setLevel(SentryLevel.INFO);
Sentry.addBreadcrumb(breadcrumb);
}
}
boolean success = installJob.exec().isSuccess();
// Wait one UI cycle before disabling controller or processing results

@ -57,6 +57,23 @@ public final class RepoManager extends SyncManager {
private static volatile RepoManager INSTANCE;
public static RepoManager getINSTANCE() {
if (INSTANCE == null || !INSTANCE.initialized) {
synchronized (lock) {
if (INSTANCE == null) {
MainApplication mainApplication = MainApplication.getINSTANCE();
if (mainApplication != null) {
INSTANCE = new RepoManager(mainApplication);
XHooks.onRepoManagerInitialized();
} else {
throw new RuntimeException("Getting RepoManager too soon!");
}
}
}
}
return INSTANCE;
}
public static RepoManager getINSTANCE_UNSAFE() {
if (INSTANCE == null) {
synchronized (lock) {
if (INSTANCE == null) {

@ -2,6 +2,8 @@ package com.fox2code.mmm.utils;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Locale;
@ -88,6 +90,35 @@ public class Hashes {
return hash.equals(checksum.toLowerCase(Locale.ROOT));
}
/**
* Check if the checksum match a file by picking the correct
* hashing algorithm depending on the length of the checksum
*/
public static boolean checkSumMatch(InputStream data, String checksum) throws IOException {
String hash;
if (checksum == null) return false;
String checksumAlgorithm = checkSumName(checksum);
if (checksumAlgorithm == null) {
Log.e(TAG, "No hash algorithm for " +
checksum.length() * 8 + "bit checksums");
return false;
}
try {
MessageDigest md = MessageDigest.getInstance(checksumAlgorithm);
byte[] bytes = new byte[2048];
int nRead;
while ((nRead = data.read(bytes)) > 0) {
md.update(bytes, 0, nRead);
}
hash = bytesToHex(md.digest());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
Log.d(TAG, "Checksum result (data: " + hash + ",expected: " + checksum + ")");
return hash.equals(checksum.toLowerCase(Locale.ROOT));
}
public static boolean checkSumValid(String checksum) {
if (checksum == null) return false;
switch (checksum.length()) {

@ -6,6 +6,8 @@ buildscript {
gradlePluginPortal()
}
project.ext.latestAboutLibsRelease = "10.4.1-a01"
project.ext.isLocalSentry = new File(
rootProject.rootDir, "sentry.properties").exists()
dependencies {
classpath 'com.android.tools.build:gradle:7.3.0-rc01'
classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:${latestAboutLibsRelease}"

Loading…
Cancel
Save