Security fixes and optimizations

- All known hosts now have a hardcoded trusted root ca, because we're not just going to trust that the user has a-ok certs installed
- Remove some unused code
- Properly fix a couple NetworkOnMainThread errors

Signed-off-by: androidacy-user <opensource@androidacy.com>
pull/27/head
androidacy-user 3 years ago
parent 86c46de069
commit 66cb0b1813

@ -50,7 +50,6 @@ import com.fox2code.mmm.utils.BlurUtils;
import com.fox2code.mmm.utils.ExternalHelper; import com.fox2code.mmm.utils.ExternalHelper;
import com.fox2code.mmm.utils.Http; import com.fox2code.mmm.utils.Http;
import com.fox2code.mmm.utils.IntentHelper; import com.fox2code.mmm.utils.IntentHelper;
import com.fox2code.mmm.utils.NoodleDebug;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.progressindicator.LinearProgressIndicator; import com.google.android.material.progressindicator.LinearProgressIndicator;
@ -71,7 +70,6 @@ import io.sentry.Sentry;
public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRefreshListener, SearchView.OnQueryTextListener, SearchView.OnCloseListener, OverScrollManager.OverScrollHelper { public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRefreshListener, SearchView.OnQueryTextListener, SearchView.OnCloseListener, OverScrollManager.OverScrollHelper {
private static final String TAG = "MainActivity"; private static final String TAG = "MainActivity";
private static final int PRECISION = 10000; private static final int PRECISION = 10000;
public static boolean noodleDebugState = BuildConfig.DEBUG;
public final ModuleViewListBuilder moduleViewListBuilder; public final ModuleViewListBuilder moduleViewListBuilder;
public LinearProgressIndicator progressIndicator; public LinearProgressIndicator progressIndicator;
private ModuleViewAdapter moduleViewAdapter; private ModuleViewAdapter moduleViewAdapter;
@ -87,7 +85,6 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
private RecyclerView moduleList; private RecyclerView moduleList;
private CardView searchCard; private CardView searchCard;
private SearchView searchView; private SearchView searchView;
private NoodleDebug noodleDebug;
private boolean initMode; private boolean initMode;
public MainActivity() { public MainActivity() {
@ -104,7 +101,8 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
this.initMode = true; this.initMode = true;
noodleDebugState = MainApplication.isDeveloper(); // Ensure HTTP Cache directories are created
Http.ensureCacheDirs(this);
BackgroundUpdateChecker.onMainActivityCreate(this); BackgroundUpdateChecker.onMainActivityCreate(this);
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
this.setActionBarExtraMenuButton(R.drawable.ic_baseline_settings_24, v -> { this.setActionBarExtraMenuButton(R.drawable.ic_baseline_settings_24, v -> {
@ -132,7 +130,6 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
this.moduleList = findViewById(R.id.module_list); this.moduleList = findViewById(R.id.module_list);
this.searchCard = findViewById(R.id.search_card); this.searchCard = findViewById(R.id.search_card);
this.searchView = findViewById(R.id.search_bar); this.searchView = findViewById(R.id.search_bar);
this.noodleDebug = new NoodleDebug(this, R.id.noodle_debug);
this.moduleViewAdapter = new ModuleViewAdapter(); this.moduleViewAdapter = new ModuleViewAdapter();
this.moduleList.setAdapter(this.moduleViewAdapter); this.moduleList.setAdapter(this.moduleViewAdapter);
this.moduleList.setLayoutManager(new LinearLayoutManager(this)); this.moduleList.setLayoutManager(new LinearLayoutManager(this));
@ -290,6 +287,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
dialog.cancel(); dialog.cancel();
} }
preferences.edit().remove("lastEventId").apply(); preferences.edit().remove("lastEventId").apply();
preferences.edit().putString("lastExitReason", "").apply();
// Prevent strict mode violation // Prevent strict mode violation
new Thread(() -> { new Thread(() -> {
try { try {
@ -439,8 +437,6 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
@Override @Override
public void onFailure(int error) { public void onFailure(int error) {
moduleViewListBuilder.addNotification(InstallerInitializer.getErrorNotification()); moduleViewListBuilder.addNotification(InstallerInitializer.getErrorNotification());
noodleDebug.setEnabled(noodleDebugState);
noodleDebug.bind();
this.commonNext(); this.commonNext();
} }

@ -258,7 +258,7 @@ public final class AndroidacyActivity extends FoxActivity {
break; break;
} }
} }
return super.onConsoleMessage(consoleMessage); return true;
} }
@Override @Override

@ -13,10 +13,12 @@ public class BackgroundBootListener extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
if (!BOOT_COMPLETED.equals(intent.getAction())) return; if (!BOOT_COMPLETED.equals(intent.getAction())) return;
synchronized (BackgroundUpdateChecker.lock) { synchronized (BackgroundUpdateChecker.lock) {
BackgroundUpdateChecker.onMainActivityCreate(context); new Thread(() -> {
if (MainApplication.isBackgroundUpdateCheckEnabled()) { BackgroundUpdateChecker.onMainActivityCreate(context);
BackgroundUpdateChecker.doCheck(context); if (MainApplication.isBackgroundUpdateCheckEnabled()) {
} BackgroundUpdateChecker.doCheck(context);
}
}).start();
} }
} }
} }

@ -4,7 +4,6 @@ import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Build; import android.os.Build;
import android.os.StrictMode;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.app.NotificationChannelCompat; import androidx.core.app.NotificationChannelCompat;
@ -54,10 +53,7 @@ public class BackgroundUpdateChecker extends Worker {
} }
static void doCheck(Context context) { static void doCheck(Context context) {
// This is actually not recommended but it's the only way to do it Thread.currentThread().setPriority(2);
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
ModuleManager.getINSTANCE().scanAsync(); ModuleManager.getINSTANCE().scanAsync();
RepoManager.getINSTANCE().update(null); RepoManager.getINSTANCE().update(null);
ModuleManager.getINSTANCE().runAfterScan(() -> { ModuleManager.getINSTANCE().runAfterScan(() -> {

@ -8,8 +8,6 @@ import androidx.annotation.NonNull;
import com.fox2code.mmm.BuildConfig; import com.fox2code.mmm.BuildConfig;
import com.fox2code.mmm.MainApplication; import com.fox2code.mmm.MainApplication;
import com.fox2code.mmm.installer.InstallerInitializer; import com.fox2code.mmm.installer.InstallerInitializer;
import com.fox2code.mmm.utils.Http;
import com.fox2code.mmm.utils.NoodleDebug;
import com.fox2code.mmm.utils.PropUtils; import com.fox2code.mmm.utils.PropUtils;
import com.fox2code.mmm.utils.SyncManager; import com.fox2code.mmm.utils.SyncManager;
import com.topjohnwu.superuser.Shell; import com.topjohnwu.superuser.Shell;
@ -24,33 +22,34 @@ import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
public final class ModuleManager extends SyncManager { public final class ModuleManager extends SyncManager {
private static final String TAG = "ModuleManager";
// New method is not really effective, this flag force app to use old method // New method is not really effective, this flag force app to use old method
public static final boolean FORCE_NEED_FALLBACK = true; public static final boolean FORCE_NEED_FALLBACK = true;
private static final String TAG = "ModuleManager";
private static final int FLAG_MM_INVALID = ModuleInfo.FLAG_METADATA_INVALID; private static final int FLAG_MM_INVALID = ModuleInfo.FLAG_METADATA_INVALID;
private static final int FLAG_MM_UNPROCESSED = ModuleInfo.FLAG_CUSTOM_INTERNAL; private static final int FLAG_MM_UNPROCESSED = ModuleInfo.FLAG_CUSTOM_INTERNAL;
private static final int FLAGS_KEEP_INIT = FLAG_MM_UNPROCESSED | private static final int FLAGS_KEEP_INIT = FLAG_MM_UNPROCESSED |
ModuleInfo.FLAGS_MODULE_ACTIVE | ModuleInfo.FLAG_MODULE_UPDATING_ONLY; ModuleInfo.FLAGS_MODULE_ACTIVE | ModuleInfo.FLAG_MODULE_UPDATING_ONLY;
private static final int FLAGS_RESET_UPDATE = FLAG_MM_INVALID | FLAG_MM_UNPROCESSED; private static final int FLAGS_RESET_UPDATE = FLAG_MM_INVALID | FLAG_MM_UNPROCESSED;
private static final ModuleManager INSTANCE = new ModuleManager();
private final HashMap<String, LocalModuleInfo> moduleInfos; private final HashMap<String, LocalModuleInfo> moduleInfos;
private final SharedPreferences bootPrefs; private final SharedPreferences bootPrefs;
private int updatableModuleCount = 0; private int updatableModuleCount = 0;
private static final ModuleManager INSTANCE = new ModuleManager(); private ModuleManager() {
this.moduleInfos = new HashMap<>();
this.bootPrefs = MainApplication.getBootSharedPreferences();
}
public static ModuleManager getINSTANCE() { public static ModuleManager getINSTANCE() {
return INSTANCE; return INSTANCE;
} }
private ModuleManager() { public static boolean isModuleActive(String moduleId) {
this.moduleInfos = new HashMap<>(); ModuleInfo moduleInfo = ModuleManager.getINSTANCE().getModules().get(moduleId);
this.bootPrefs = MainApplication.getBootSharedPreferences(); return moduleInfo != null && (moduleInfo.flags & ModuleInfo.FLAGS_MODULE_ACTIVE) != 0;
} }
protected void scanInternal(@NonNull UpdateListener updateListener) { protected void scanInternal(@NonNull UpdateListener updateListener) {
NoodleDebug noodleDebug = NoodleDebug.getNoodleDebug();
noodleDebug.push("Initialize scan");
boolean firstScan = this.bootPrefs.getBoolean("mm_first_scan", true); boolean firstScan = this.bootPrefs.getBoolean("mm_first_scan", true);
SharedPreferences.Editor editor = firstScan ? this.bootPrefs.edit() : null; SharedPreferences.Editor editor = firstScan ? this.bootPrefs.edit() : null;
for (ModuleInfo v : this.moduleInfos.values()) { for (ModuleInfo v : this.moduleInfos.values()) {
@ -73,7 +72,6 @@ public final class ModuleManager extends SyncManager {
} }
if (BuildConfig.DEBUG) Log.d("NoodleDebug", "Scan"); if (BuildConfig.DEBUG) Log.d("NoodleDebug", "Scan");
if (modules != null) { if (modules != null) {
noodleDebug.push("");
for (String module : modules) { for (String module : modules) {
if (!new SuFile("/data/adb/modules/" + module).isDirectory()) if (!new SuFile("/data/adb/modules/" + module).isDirectory())
continue; // Ignore non directory files inside modules folder continue; // Ignore non directory files inside modules folder
@ -123,7 +121,6 @@ public final class ModuleManager extends SyncManager {
if (BuildConfig.DEBUG) Log.d("NoodleDebug", "Scan update"); if (BuildConfig.DEBUG) Log.d("NoodleDebug", "Scan update");
String[] modules_update = new SuFile("/data/adb/modules_update").list(); String[] modules_update = new SuFile("/data/adb/modules_update").list();
if (modules_update != null) { if (modules_update != null) {
noodleDebug.push("");
for (String module : modules_update) { for (String module : modules_update) {
if (!new SuFile("/data/adb/modules_update/" + module).isDirectory()) if (!new SuFile("/data/adb/modules_update/" + module).isDirectory())
continue; // Ignore non directory files inside modules folder continue; // Ignore non directory files inside modules folder
@ -148,7 +145,6 @@ public final class ModuleManager extends SyncManager {
this.updatableModuleCount = 0; this.updatableModuleCount = 0;
Iterator<LocalModuleInfo> moduleInfoIterator = Iterator<LocalModuleInfo> moduleInfoIterator =
this.moduleInfos.values().iterator(); this.moduleInfos.values().iterator();
noodleDebug.push("");
while (moduleInfoIterator.hasNext()) { while (moduleInfoIterator.hasNext()) {
LocalModuleInfo moduleInfo = moduleInfoIterator.next(); LocalModuleInfo moduleInfo = moduleInfoIterator.next();
if (BuildConfig.DEBUG) Log.d("NoodleDebug", moduleInfo.id); if (BuildConfig.DEBUG) Log.d("NoodleDebug", moduleInfo.id);
@ -232,7 +228,7 @@ public final class ModuleManager extends SyncManager {
try { // Check for module that declare having file outside their own folder. try { // Check for module that declare having file outside their own folder.
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader( try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
SuFileInputStream.open("/data/adb/modules/." + moduleInfo.id + "-files"), SuFileInputStream.open("/data/adb/modules/." + moduleInfo.id + "-files"),
StandardCharsets.UTF_8))) { StandardCharsets.UTF_8))) {
String line; String line;
while ((line = bufferedReader.readLine()) != null) { while ((line = bufferedReader.readLine()) != null) {
line = line.trim().replace(' ', '.'); line = line.trim().replace(' ', '.');
@ -245,16 +241,12 @@ public final class ModuleManager extends SyncManager {
Shell.cmd("rm -rf \"" + line + "\"").exec(); Shell.cmd("rm -rf \"" + line + "\"").exec();
} }
} }
} catch (IOException ignored) {} } catch (IOException ignored) {
}
Shell.cmd("rm -rf /data/adb/modules/" + escapedId + "/").exec(); Shell.cmd("rm -rf /data/adb/modules/" + escapedId + "/").exec();
Shell.cmd("rm -f /data/adb/modules/." + escapedId + "-files").exec(); Shell.cmd("rm -f /data/adb/modules/." + escapedId + "-files").exec();
Shell.cmd("rm -rf /data/adb/modules_update/" + escapedId + "/").exec(); Shell.cmd("rm -rf /data/adb/modules_update/" + escapedId + "/").exec();
moduleInfo.flags = ModuleInfo.FLAG_METADATA_INVALID; moduleInfo.flags = ModuleInfo.FLAG_METADATA_INVALID;
return true; return true;
} }
public static boolean isModuleActive(String moduleId) {
ModuleInfo moduleInfo = ModuleManager.getINSTANCE().getModules().get(moduleId);
return moduleInfo != null && (moduleInfo.flags & ModuleInfo.FLAGS_MODULE_ACTIVE) != 0;
}
} }

@ -537,6 +537,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
// Use MaterialAlertDialogBuilder // Use MaterialAlertDialogBuilder
new MaterialAlertDialogBuilder(this.requireContext()) new MaterialAlertDialogBuilder(this.requireContext())
.setTitle(R.string.warning) .setTitle(R.string.warning)
.setCancelable(false)
.setMessage(R.string.androidacy_test_mode_warning) .setMessage(R.string.androidacy_test_mode_warning)
.setPositiveButton(android.R.string.ok, (dialog, which) -> { .setPositiveButton(android.R.string.ok, (dialog, which) -> {
// User clicked OK button // User clicked OK button
@ -575,6 +576,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
// Show dialog to restart app with ok button // Show dialog to restart app with ok button
new MaterialAlertDialogBuilder(this.requireContext()) new MaterialAlertDialogBuilder(this.requireContext())
.setTitle(R.string.warning) .setTitle(R.string.warning)
.setCancelable(false)
.setMessage(R.string.androidacy_test_mode_disable_warning) .setMessage(R.string.androidacy_test_mode_disable_warning)
.setNeutralButton(android.R.string.ok, (dialog, which) -> { .setNeutralButton(android.R.string.ok, (dialog, which) -> {
// User clicked OK button // User clicked OK button
@ -609,6 +611,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
androidacyRepoEnabled.setOnPreferenceClickListener(preference -> { androidacyRepoEnabled.setOnPreferenceClickListener(preference -> {
new MaterialAlertDialogBuilder(this.requireContext()) new MaterialAlertDialogBuilder(this.requireContext())
.setTitle(R.string.androidacy_repo_disabled) .setTitle(R.string.androidacy_repo_disabled)
.setCancelable(false)
.setMessage(R.string.androidacy_repo_disabled_message) .setMessage(R.string.androidacy_repo_disabled_message)
.setPositiveButton(R.string.download_full_app, (dialog, which) -> { .setPositiveButton(R.string.download_full_app, (dialog, which) -> {
// User clicked OK button. Open GitHub releases page // User clicked OK button. Open GitHub releases page
@ -664,6 +667,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
// Show dialog to restart app with ok button // Show dialog to restart app with ok button
new MaterialAlertDialogBuilder(this.requireContext()) new MaterialAlertDialogBuilder(this.requireContext())
.setTitle(R.string.restart) .setTitle(R.string.restart)
.setCancelable(false)
.setMessage(R.string.api_key_restart) .setMessage(R.string.api_key_restart)
.setNeutralButton(android.R.string.ok, (dialog, which) -> { .setNeutralButton(android.R.string.ok, (dialog, which) -> {
// User clicked OK button // User clicked OK button
@ -721,6 +725,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
// Show dialog to restart app with ok button // Show dialog to restart app with ok button
new MaterialAlertDialogBuilder(this.requireContext()) new MaterialAlertDialogBuilder(this.requireContext())
.setTitle(R.string.restart) .setTitle(R.string.restart)
.setCancelable(false)
.setMessage(R.string.api_key_restart) .setMessage(R.string.api_key_restart)
.setNeutralButton(android.R.string.ok, (dialog, which) -> { .setNeutralButton(android.R.string.ok, (dialog, which) -> {
// User clicked OK button // User clicked OK button

@ -15,6 +15,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.fox2code.mmm.BuildConfig; import com.fox2code.mmm.BuildConfig;
import com.fox2code.mmm.MainActivity;
import com.fox2code.mmm.MainApplication; import com.fox2code.mmm.MainApplication;
import com.fox2code.mmm.androidacy.AndroidacyUtil; import com.fox2code.mmm.androidacy.AndroidacyUtil;
import com.fox2code.mmm.installer.InstallerInitializer; import com.fox2code.mmm.installer.InstallerInitializer;
@ -341,6 +342,29 @@ public class Http {
return hasWebView; return hasWebView;
} }
public static void ensureCacheDirs(MainActivity mainActivity) {
File cacheDir = mainActivity.getCacheDir();
File cacheDir2 = new File(cacheDir, "HTTP Cache");
if (!cacheDir2.exists()) {
if (!cacheDir2.mkdirs()) {
Log.e(TAG, "Failed to create cache dir");
}
}
// Ensure js and wasm cache dirs
File jsCacheDir = new File(cacheDir2, "js");
if (!jsCacheDir.exists()) {
if (!jsCacheDir.mkdirs()) {
Log.e(TAG, "Failed to create js cache dir");
}
}
File wasmCacheDir = new File(cacheDir2, "wasm");
if (!wasmCacheDir.exists()) {
if (!wasmCacheDir.mkdirs()) {
Log.e(TAG, "Failed to create wasm cache dir");
}
}
}
public interface ProgressListener { public interface ProgressListener {
void onUpdate(int downloaded, int total, boolean done); void onUpdate(int downloaded, int total, boolean done);
} }

@ -1,171 +0,0 @@
package com.fox2code.mmm.utils;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.util.Log;
import android.widget.TextView;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import java.lang.ref.WeakReference;
import java.util.LinkedList;
import java.util.Objects;
public class NoodleDebug {
private static final String TAG = "NoodleDebug";
private static final WeakReference<Thread> NULL_THREAD_REF = new WeakReference<>(null);
private static final ThreadLocal<NoodleDebug> THREAD_NOODLE = new ThreadLocal<>();
@SuppressLint("StaticFieldLeak") // <- Null initialized
private static final NoodleDebug NULL = new NoodleDebug() {
@Override
public NoodleDebug bind() {
getNoodleDebug().unbind();
THREAD_NOODLE.remove();
return this;
}
@Override
public void setEnabled(boolean enabled) {}
@Override
protected void markDirty() {}
};
private final Activity activity;
private final TextView textView;
private final LinkedList<String> tokens;
private final StringBuilder debug;
private WeakReference<Thread> thread;
private boolean enabled, updating;
private NoodleDebug() {
this.activity = null;
this.textView = null;
this.tokens = new LinkedList<>();
this.debug = new StringBuilder(0);
this.thread = NULL_THREAD_REF;
}
public NoodleDebug(Activity activity,@IdRes int textViewId) {
this(activity, activity.findViewById(textViewId));
}
public NoodleDebug(Activity activity, TextView textView) {
this.activity = Objects.requireNonNull(activity);
this.textView = Objects.requireNonNull(textView);
this.tokens = new LinkedList<>();
this.debug = new StringBuilder(64);
this.thread = NULL_THREAD_REF;
}
public NoodleDebug bind() {
synchronized (this.tokens) {
Thread thread;
if ((thread = this.thread.get()) != null) {
Log.e(TAG, "Trying to bind to thread \"" + Thread.currentThread().getName() +
"\" while already bound to \"" + thread.getName() + "\"");
return NULL;
}
this.tokens.clear();
}
if (this.enabled) {
this.thread = new WeakReference<>(Thread.currentThread());
THREAD_NOODLE.set(this);
} else {
this.thread = NULL_THREAD_REF;
THREAD_NOODLE.remove();
}
return this;
}
public void unbind() {
this.thread = NULL_THREAD_REF;
boolean markDirty;
synchronized (this.tokens) {
markDirty = !this.tokens.isEmpty();
this.tokens.clear();
}
if (markDirty) this.markDirty();
}
public boolean isBound() {
return this.thread.get() != null;
}
public void push(String token) {
if (!this.enabled) return;
synchronized (this.tokens) {
this.tokens.add(token);
}
if (!token.isEmpty())
this.markDirty();
}
public void pop() {
if (!this.enabled) return;
String last;
synchronized (this.tokens) {
last = this.tokens.removeLast();
}
if (!last.isEmpty())
this.markDirty();
}
public void replace(String token) {
if (!this.enabled) return;
String last;
synchronized (this.tokens) {
last = this.tokens.removeLast();
this.tokens.add(token);
}
if (!last.equals(token))
this.markDirty();
}
public void setEnabled(boolean enabled) {
if (this.enabled && !enabled) {
this.thread = NULL_THREAD_REF;
synchronized (this.tokens) {
this.tokens.clear();
}
this.markDirty();
}
this.enabled = enabled;
}
protected void markDirty() {
assert this.activity != null;
assert this.textView != null;
if (this.updating) return;
this.updating = true;
this.activity.runOnUiThread(() -> {
String debugText;
synchronized (this.tokens) {
StringBuilder debug = this.debug;
debug.setLength(0);
boolean first = true;
for (String text : this.tokens) {
if (text.isEmpty()) continue;
if (first) first = false;
else debug.append(" > ");
debug.append(text);
}
debugText = debug.toString();
}
this.updating = false;
this.textView.setText(debugText);
});
}
@NonNull
public static NoodleDebug getNoodleDebug() {
NoodleDebug noodleDebug = THREAD_NOODLE.get();
if (noodleDebug == null) return NULL;
if (noodleDebug.thread.get() != Thread.currentThread() ||
noodleDebug.activity.isDestroyed()) {
THREAD_NOODLE.remove();
return NULL;
}
return noodleDebug;
}
}

@ -15,12 +15,13 @@
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" > app:layout_constraintTop_toTopOf="parent">
<!-- FrameLayout is the best way to fix blurring --> <!-- FrameLayout is the best way to fix blurring -->
<FrameLayout <FrameLayout
android:id="@+id/blur_frame" android:id="@+id/blur_frame"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/module_list" android:id="@+id/module_list"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -36,6 +37,7 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<TextView <TextView
android:id="@+id/action_bar_padding" android:id="@+id/action_bar_padding"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -44,31 +46,31 @@
<com.google.android.material.progressindicator.LinearProgressIndicator <com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progress_bar" android:id="@+id/progress_bar"
android:layout_height="wrap_content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true" android:indeterminate="true"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/action_bar_blur" /> app:layout_constraintTop_toBottomOf="@+id/action_bar_blur" />
<TextView <!--<TextView
android:id="@+id/noodle_debug" android:id="@+id/noodle_debug"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/action_bar_blur" /> app:layout_constraintTop_toBottomOf="@+id/action_bar_blur" />-->
<LinearLayout <LinearLayout
android:id="@+id/search_container" android:id="@+id/search_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="12dp" android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginRight="12dp"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
android:gravity="right" android:gravity="right"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_fitsSystemWindowsInsets="bottom" app:layout_fitsSystemWindowsInsets="bottom"
tools:ignore="RtlHardcoded"> tools:ignore="RtlHardcoded">
<!-- <!--
@ -77,16 +79,17 @@
--> -->
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
android:id="@+id/search_card" android:id="@+id/search_card"
android:layout_gravity="center"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:shape="ring" android:layout_gravity="center"
android:background="@null" android:background="@null"
android:shape="ring"
app:cardCornerRadius="@dimen/card_corner_radius" app:cardCornerRadius="@dimen/card_corner_radius"
app:cardElevation="0dp"
app:cardPreventCornerOverlap="true" app:cardPreventCornerOverlap="true"
app:strokeColor="@android:color/transparent" app:strokeColor="@android:color/transparent"
app:cardElevation="0dp"
app:strokeWidth="0dp"> app:strokeWidth="0dp">
<androidx.appcompat.widget.SearchView <androidx.appcompat.widget.SearchView
android:id="@+id/search_bar" android:id="@+id/search_bar"
android:layout_width="match_parent" android:layout_width="match_parent"

@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
-----END CERTIFICATE-----

@ -205,13 +205,16 @@
<string name="transparent_theme_dialogue_message">Transparent themes may have some inconsistencies and may not work on all ROMs. In additon, monet and blur will be disabled. You can change back at any time.</string> <string name="transparent_theme_dialogue_message">Transparent themes may have some inconsistencies and may not work on all ROMs. In additon, monet and blur will be disabled. You can change back at any time.</string>
<string name="custom_repo_always_on">Custom repos are always on until you remove them.</string> <string name="custom_repo_always_on">Custom repos are always on until you remove them.</string>
<string name="sentry_dialogue_message">We encountered an error! Please help us improve the app <string name="sentry_dialogue_message">We encountered an error! Please help us improve the app
by adding some information about the error below. Name and email are optional.</string> by adding some information about the error below.\nName and email are optional but will
<string name="sentry_dialogue_title">Oops! Something went wrong.</string> allow us to contact you if needed for more information.</string>
<string name="sentry_dialogue_title">Oops! Looks like the app closed unexpectedly.</string>
<string name="name">Name</string> <string name="name">Name</string>
<string name="email">Email</string> <string name="email">Email</string>
<string name="additional_info">Tell us what happened</string> <string name="additional_info">Tell us what happened</string>
<string name="submit">Submit</string> <string name="submit">Submit</string>
<string name="sentry_dialogue_failed_toast">Could not submit feedback</string> <string name="sentry_dialogue_failed_toast">Could not submit feedback due to an error</string>
<string name="sentry_dialogue_success">Submitted feedback</string> <string name="sentry_dialogue_success">Submitted feedback successfully. We\'ll review it
<string name="sentry_dialogue_no_description">Please provide a description/comment for the issue</string> shortly</string>
<string name="sentry_dialogue_no_description">Could not submit feedback as no
description was provided</string>
</resources> </resources>

@ -6,4 +6,22 @@
<certificates src="@raw/androidacy_root_ca" /> <certificates src="@raw/androidacy_root_ca" />
</trust-anchors> </trust-anchors>
</domain-config> </domain-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">github.com</domain>
<trust-anchors>
<certificates src="@raw/gh_root_ca" />
</trust-anchors>
</domain-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">githubusercontent.com</domain>
<trust-anchors>
<certificates src="@raw/gh_root_ca" />
</trust-anchors>
</domain-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">gstatic.com</domain>
<trust-anchors>
<certificates src="@raw/gstatic_root_ca" />
</trust-anchors>
</domain-config>
</network-security-config> </network-security-config>

@ -3,16 +3,12 @@ package com.fox2code.mmm.sentry;
import static io.sentry.TypeCheckHint.SENTRY_TYPE_CHECK_HINT; import static io.sentry.TypeCheckHint.SENTRY_TYPE_CHECK_HINT;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.net.Uri; import android.net.Uri;
import android.util.Log; import android.util.Log;
import com.fox2code.mmm.BuildConfig; import com.fox2code.mmm.BuildConfig;
import com.fox2code.mmm.MainActivity;
import com.fox2code.mmm.MainApplication; import com.fox2code.mmm.MainApplication;
import com.fox2code.mmm.androidacy.AndroidacyUtil; import com.fox2code.mmm.androidacy.AndroidacyUtil;
@ -121,31 +117,10 @@ public class SentryMain {
// On uncaught exception, set the lastEventId in private sentry preferences // On uncaught exception, set the lastEventId in private sentry preferences
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> { Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
SentryId lastEventId = Sentry.captureException(throwable); SentryId lastEventId = Sentry.captureException(throwable);
SharedPreferences.Editor editor = mainApplication.getSharedPreferences("sentry", 0).edit(); SharedPreferences.Editor editor = mainApplication.getSharedPreferences(
"sentry", Context.MODE_PRIVATE).edit();
editor.putString("lastExitReason", "crash"); editor.putString("lastExitReason", "crash");
editor.apply(); editor.apply();
// Start a new instance of the main activity
// The intent flags ensure that the activity is started as a new task
// and that any existing task is cleared before the activity is started
// This ensures that the activity stack is cleared and the app is restarted
// from the root activity
Intent intent = new Intent(mainApplication, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
// Set an alarm to restart the app one second after it is killed
// This is necessary because the app is killed before the intent is started
// and the intent is ignored if the app is not running
PendingIntent pendingIntent;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
pendingIntent = PendingIntent.getActivity(mainApplication, 0,
intent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
} else {
pendingIntent = PendingIntent.getActivity(mainApplication, 0,
intent, PendingIntent.FLAG_CANCEL_CURRENT);
}
AlarmManager alarmManager = (AlarmManager) mainApplication.getSystemService(Context.ALARM_SERVICE);
if (alarmManager != null) {
alarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + 1000, pendingIntent);
}
// Kill the app // Kill the app
System.exit(2); System.exit(2);
}); });

Loading…
Cancel
Save