From c9aab4067001712bfa0810de333887a94952cdee Mon Sep 17 00:00:00 2001 From: androidacy-user Date: Mon, 3 Apr 2023 21:30:20 -0400 Subject: [PATCH] misc fixes Signed-off-by: androidacy-user --- app/build.gradle | 7 + .../java/com/fox2code/mmm/MainActivity.java | 16 +- .../java/com/fox2code/mmm/SetupActivity.java | 9 +- .../background/BackgroundBootListener.java | 7 +- .../mmm/installer/InstallerActivity.java | 275 ++++++------------ .../com/fox2code/mmm/repo/RepoManager.java | 46 +-- .../com/fox2code/mmm/utils/io/net/Http.java | 42 +++ app/src/main/res/values/strings.xml | 1 + 8 files changed, 169 insertions(+), 234 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 26f9ab7..9603f4d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -223,9 +223,16 @@ android { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } + lint { disable 'MissingTranslation' } + + packagingOptions { + jniLibs { + useLegacyPackaging = true + } + } } aboutLibraries { diff --git a/app/src/main/java/com/fox2code/mmm/MainActivity.java b/app/src/main/java/com/fox2code/mmm/MainActivity.java index 9f09f3c..dcd00a2 100644 --- a/app/src/main/java/com/fox2code/mmm/MainActivity.java +++ b/app/src/main/java/com/fox2code/mmm/MainActivity.java @@ -103,7 +103,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe // Ensure HTTP Cache directories are created Http.ensureCacheDirs(this); if (!urlFactoryInstalled) { - try (HttpResponseCache cache = HttpResponseCache.getInstalled()) { + try (HttpResponseCache cache = HttpResponseCache.getInstalled()) { if (cache == null) { File cacheDir = new File(getCacheDir(), "http"); //noinspection ResultOfMethodCallIgnored @@ -281,8 +281,12 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe swipeRefreshBlocker = System.currentTimeMillis() + 5_000L; if (MainApplication.isShowcaseMode()) moduleViewListBuilder.addNotification(NotificationType.SHOWCASE_MODE); - if (!Http.hasWebView()) // Check Http for WebView availability + if (!Http.hasWebView()) { + // Check Http for WebView availability moduleViewListBuilder.addNotification(NotificationType.NO_WEB_VIEW); + // disable online tab + bottomNavigationView.getMenu().removeItem(R.id.online_menu_item); + } moduleViewListBuilder.applyTo(moduleList, moduleViewAdapter); runOnUiThread(() -> { progressIndicator.setIndeterminate(false); @@ -322,6 +326,14 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe if (!NotificationType.REPO_UPDATE_FAILED.shouldRemove()) { moduleViewListBuilder.addNotification(NotificationType.REPO_UPDATE_FAILED); } else { + if (!Http.hasWebView()) { + progressIndicator.setProgressCompat(PRECISION, true); + progressIndicator.setVisibility(View.GONE); + searchView.setEnabled(true); + setActionBarBackground(null); + updateScreenInsets(getResources().getConfiguration()); + return; + } // Compatibility data still needs to be updated AppUpdateManager appUpdateManager = AppUpdateManager.getAppUpdateManager(); if (BuildConfig.DEBUG) diff --git a/app/src/main/java/com/fox2code/mmm/SetupActivity.java b/app/src/main/java/com/fox2code/mmm/SetupActivity.java index 334b5e4..df194ca 100644 --- a/app/src/main/java/com/fox2code/mmm/SetupActivity.java +++ b/app/src/main/java/com/fox2code/mmm/SetupActivity.java @@ -13,6 +13,7 @@ import android.os.Bundle; import android.view.View; import android.view.WindowManager; import android.webkit.CookieManager; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; @@ -356,7 +357,13 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { public void createFiles() { // use cookiemanager to create the cookie database - CookieManager.getInstance(); + try { + CookieManager.getInstance(); + } catch (Exception e) { + Timber.e(e); + // show a toast + runOnUiThread(() -> Toast.makeText(this, R.string.error_creating_cookie_database, Toast.LENGTH_LONG).show()); + } // we literally only use these to create the http cache folders try { FileUtils.forceMkdir(new File(MainApplication.getINSTANCE().getDataDir() + "/cache/cronet")); diff --git a/app/src/main/java/com/fox2code/mmm/background/BackgroundBootListener.java b/app/src/main/java/com/fox2code/mmm/background/BackgroundBootListener.java index 72fd05d..b039089 100644 --- a/app/src/main/java/com/fox2code/mmm/background/BackgroundBootListener.java +++ b/app/src/main/java/com/fox2code/mmm/background/BackgroundBootListener.java @@ -5,6 +5,7 @@ import android.content.Context; import android.content.Intent; import com.fox2code.mmm.MainApplication; +import com.fox2code.mmm.utils.io.net.Http; public class BackgroundBootListener extends BroadcastReceiver { private static final String BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED"; @@ -12,14 +13,14 @@ public class BackgroundBootListener extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (!BOOT_COMPLETED.equals(intent.getAction())) return; + if (!MainApplication.isBackgroundUpdateCheckEnabled()) return; + if (!Http.hasConnectivity()) return; // clear boot shared prefs MainApplication.getBootSharedPreferences().edit().clear().apply(); synchronized (BackgroundUpdateChecker.lock) { new Thread(() -> { BackgroundUpdateChecker.onMainActivityCreate(context); - if (MainApplication.isBackgroundUpdateCheckEnabled()) { - BackgroundUpdateChecker.doCheck(context); - } + BackgroundUpdateChecker.doCheck(context); }).start(); } } diff --git a/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java b/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java index 80849bf..7f141f7 100644 --- a/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java +++ b/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java @@ -27,7 +27,6 @@ import com.fox2code.mmm.MainActivity; import com.fox2code.mmm.MainApplication; import com.fox2code.mmm.R; import com.fox2code.mmm.XHooks; -import com.fox2code.mmm.androidacy.AndroidacyUtil; import com.fox2code.mmm.module.ActionButtonType; import com.fox2code.mmm.utils.FastException; import com.fox2code.mmm.utils.IntentHelper; @@ -45,17 +44,18 @@ import com.topjohnwu.superuser.Shell; import com.topjohnwu.superuser.internal.UiThreadHandler; import com.topjohnwu.superuser.io.SuFile; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipFile; + import java.io.BufferedReader; -import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; +import java.util.Enumeration; import java.util.HashSet; import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; -import java.util.zip.ZipInputStream; import timber.log.Timber; @@ -131,12 +131,10 @@ public class InstallerActivity extends FoxActivity { getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); setTitle(name); this.textWrap = MainApplication.isTextWrapEnabled(); - setContentView(this.textWrap ? - R.layout.installer_wrap : R.layout.installer); + setContentView(this.textWrap ? R.layout.installer_wrap : R.layout.installer); int background; int foreground; - if (MainApplication.getINSTANCE().isLightTheme() && - !MainApplication.isForceDarkTerminal()) { + if (MainApplication.getINSTANCE().isLightTheme() && !MainApplication.isForceDarkTerminal()) { background = Color.WHITE; foreground = Color.BLACK; } else { @@ -147,17 +145,12 @@ public class InstallerActivity extends FoxActivity { RecyclerView installTerminal; this.progressIndicator = findViewById(R.id.progress_bar); this.rebootFloatingButton = findViewById(R.id.install_terminal_reboot_fab); - this.installerTerminal = new InstallerTerminal( - installTerminal = findViewById(R.id.install_terminal), - this.isLightTheme(), foreground, mmtReborn); - (horizontalScroller != null ? horizontalScroller : installTerminal) - .setBackground(new ColorDrawable(background)); + this.installerTerminal = new InstallerTerminal(installTerminal = findViewById(R.id.install_terminal), this.isLightTheme(), foreground, mmtReborn); + (horizontalScroller != null ? horizontalScroller : installTerminal).setBackground(new ColorDrawable(background)); installTerminal.setItemAnimator(null); this.progressIndicator.setVisibility(View.GONE); this.progressIndicator.setIndeterminate(true); - this.getWindow().setFlags( - WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, - WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // acquire wakelock PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Fox:Installer"); @@ -168,20 +161,16 @@ public class InstallerActivity extends FoxActivity { // ensure module cache is is in our cache dir if (urlMode && !moduleCache.getAbsolutePath().startsWith(MainApplication.getINSTANCE().getCacheDir().getAbsolutePath())) throw new SecurityException("Module cache is not in cache dir!"); - File moduleCache = this.toDelete = urlMode ? - new File(this.moduleCache, "module.zip") : new File(finalTarget); - if (urlMode && moduleCache.exists() && !moduleCache.delete() && - !new SuFile(moduleCache.getAbsolutePath()).delete()) + File moduleCache = this.toDelete = urlMode ? new File(this.moduleCache, "module.zip") : new File(finalTarget); + if (urlMode && moduleCache.exists() && !moduleCache.delete() && !new SuFile(moduleCache.getAbsolutePath()).delete()) Timber.e("Failed to delete module cache"); String errMessage = "Failed to download module zip"; // Set this to the error message if it's a HTTP error byte[] rawModule; - boolean androidacyBlame = false; try { Timber.i("%s%s", (urlMode ? "Downloading: " : "Loading: "), finalTarget); rawModule = urlMode ? Http.doHttpGet(finalTarget, (progress, max, done) -> { - if (max <= 0 && this.progressIndicator.isIndeterminate()) - return; + if (max <= 0 && this.progressIndicator.isIndeterminate()) return; this.runOnUiThread(() -> { this.progressIndicator.setIndeterminate(false); this.progressIndicator.setMax(max); @@ -193,14 +182,12 @@ public class InstallerActivity extends FoxActivity { this.progressIndicator.setIndeterminate(true); }); if (this.canceled) return; - androidacyBlame = urlMode && AndroidacyUtil.isAndroidacyFileUrl(finalTarget); if (checksum != null && !checksum.isEmpty()) { //noinspection UnnecessaryCallToStringValueOf Timber.i("Checking for checksum: %s", String.valueOf(checksum)); this.runOnUiThread(() -> this.installerTerminal.addLine("- Checking file integrity")); if (!Hashes.checkSumMatch(rawModule, checksum)) { - this.setInstallStateFinished(false, - "! File integrity check failed", ""); + this.setInstallStateFinished(false, "! File integrity check failed", ""); return; } } @@ -213,11 +200,22 @@ public class InstallerActivity extends FoxActivity { boolean isAnyKernel3 = false; boolean isInstallZipModule = false; errMessage = "File is not a valid zip file"; - try (ZipInputStream zipInputStream = new ZipInputStream( - new ByteArrayInputStream(rawModule))) { - ZipEntry zipEntry; - while ((zipEntry = zipInputStream.getNextEntry()) != null) { + // use apache commons to unzip the zip file, with a try-with-resources to ensure it's closed + // write the zip file to a temporary file + File zipFileTemp = new File(this.getCacheDir(), "module.zip"); + try (FileOutputStream fos = new FileOutputStream(zipFileTemp)) { + fos.write(rawModule); + } + try (ZipFile zipFile = new ZipFile(zipFileTemp)) { + // get the zip entries + Enumeration zipEntries = zipFile.getEntries(); + // iterate over the zip entries + while (zipEntries.hasMoreElements()) { + // get the next zip entry + ZipEntry zipEntry = zipEntries.nextElement(); + // get the name of the zip entry String entryName = zipEntry.getName(); + // check if the zip entry is a directory if (entryName.equals("tools/ak3-core.sh")) { noPatch = true; isAnyKernel3 = true; @@ -233,24 +231,21 @@ public class InstallerActivity extends FoxActivity { break; } else if (entryName.endsWith("/tools/ak3-core.sh")) { isAnyKernel3 = true; - } else if (entryName.endsWith( - "/META-INF/com/google/android/magisk/module.prop")) { + } else if (entryName.endsWith("/META-INF/com/google/android/update-binary")) { isInstallZipModule = true; } else if (entryName.endsWith("/module.prop")) { isModule = true; } } + } catch (IOException e) { + Timber.e(e, "Failed to read zip file"); + this.setInstallStateFinished(false, errMessage, ""); + return; } if (!isModule && !isAnyKernel3 && !isInstallZipModule) { - if (androidacyBlame) { - this.installerTerminal.addLine( - "! Note: The following error is probably an Androidacy backend error"); - } - this.setInstallStateFinished(false, - "! File is not a valid Magisk module or AnyKernel3 zip", ""); + this.setInstallStateFinished(false, "! File is not a valid Magisk module or AnyKernel3 zip", ""); return; } - androidacyBlame = false; if (noPatch) { if (urlMode) { errMessage = "Failed to save module zip"; @@ -275,9 +270,6 @@ public class InstallerActivity extends FoxActivity { this.doInstall(moduleCache, noExtensions, rootless); } catch (IOException e) { Timber.e(e); - if (androidacyBlame) { - errMessage += " (" + e.getLocalizedMessage() + ")"; - } this.setInstallStateFinished(false, errMessage, null); } catch (OutOfMemoryError e) { //noinspection UnusedAssignment (Important to avoid OutOfMemoryError) @@ -285,12 +277,12 @@ public class InstallerActivity extends FoxActivity { if ("Failed to install module zip".equals(errMessage)) throw e; // Ignore if in installation state. Timber.e(e); - this.setInstallStateFinished(false, - "! Module is too large to be loaded on this device", ""); + this.setInstallStateFinished(false, "! Module is too large to be loaded on this device", ""); } }, "Module install Thread").start(); } + @SuppressWarnings("SpellCheckingInspection") @Keep private void doInstall(File file, boolean noExtensions, boolean rootless) { if (this.canceled) return; @@ -299,25 +291,21 @@ public class InstallerActivity extends FoxActivity { this.setDisplayHomeAsUpEnabled(false); }); Timber.i("Installing: %s", moduleCache.getName()); - InstallerController installerController = new InstallerController( - this.progressIndicator, this.installerTerminal, - file.getAbsoluteFile(), noExtensions); + InstallerController installerController = new InstallerController(this.progressIndicator, this.installerTerminal, file.getAbsoluteFile(), noExtensions); InstallerMonitor installerMonitor; Shell.Job installJob; if (rootless) { // rootless is only used for debugging File installScript = this.extractInstallScript("module_installer_test.sh"); if (installScript == null) { - this.setInstallStateFinished(false, - "! Failed to extract test install script", ""); + this.setInstallStateFinished(false, "! Failed to extract test install script", ""); return; } this.installerTerminal.enableAnsi(); // Extract customize.sh manually in rootless mode because unzip might not exists try (ZipFile zipFile = new ZipFile(file)) { - ZipEntry zipEntry = zipFile.getEntry("customize.sh"); + ZipArchiveEntry zipEntry = zipFile.getEntry("customize.sh"); if (zipEntry != null) { - try (FileOutputStream fileOutputStream = new FileOutputStream( - new File(file.getParentFile(), "customize.sh"))) { + try (FileOutputStream fileOutputStream = new FileOutputStream(new File(file.getParentFile(), "customize.sh"))) { Files.copy(zipFile.getInputStream(zipEntry), fileOutputStream); } } @@ -325,16 +313,7 @@ public class InstallerActivity extends FoxActivity { Timber.i(e); } installerMonitor = new InstallerMonitor(installScript); - installJob = Shell.cmd("export MMM_EXT_SUPPORT=1", - "export MMM_USER_LANGUAGE=" + this.getResources() - .getConfiguration().getLocales().get(0).toLanguageTag(), - "export MMM_APP_VERSION=" + BuildConfig.VERSION_NAME, - "export MMM_TEXT_WRAP=" + (this.textWrap ? "1" : "0"), - AnsiConstants.ANSI_CMD_SUPPORT, - "cd \"" + this.moduleCache.getAbsolutePath() + "\"", - "sh \"" + installScript.getAbsolutePath() + "\"" + - " 3 0 \"" + file.getAbsolutePath() + "\"") - .to(installerController, installerMonitor); + installJob = Shell.cmd("export MMM_EXT_SUPPORT=1", "export MMM_USER_LANGUAGE=" + this.getResources().getConfiguration().getLocales().get(0).toLanguageTag(), "export MMM_APP_VERSION=" + BuildConfig.VERSION_NAME, "export MMM_TEXT_WRAP=" + (this.textWrap ? "1" : "0"), AnsiConstants.ANSI_CMD_SUPPORT, "cd \"" + this.moduleCache.getAbsolutePath() + "\"", "sh \"" + installScript.getAbsolutePath() + "\"" + " 3 0 \"" + file.getAbsolutePath() + "\"").to(installerController, installerMonitor); } else { String arch32 = "true"; // Do nothing by default boolean needs32bit = false; @@ -352,11 +331,9 @@ public class InstallerActivity extends FoxActivity { try (ZipFile zipFile = new ZipFile(file)) { // Check if module is AnyKernel module if (zipFile.getEntry("tools/ak3-core.sh") != null) { - ZipEntry updateBinary = zipFile.getEntry( - "META-INF/com/google/android/update-binary"); + ZipArchiveEntry updateBinary = zipFile.getEntry("META-INF/com/google/android/update-binary"); if (updateBinary != null) { - BufferedReader bufferedReader = new BufferedReader( - new InputStreamReader(zipFile.getInputStream(updateBinary))); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(zipFile.getInputStream(updateBinary))); String line; while ((line = bufferedReader.readLine()) != null) { if (line.contains("AnyKernel3")) { @@ -368,43 +345,30 @@ public class InstallerActivity extends FoxActivity { } } if ((zipFile.getEntry( // Check if module hard require 32bit support - "common/addon/Volume-Key-Selector/tools/arm64/keycheck") == null && - zipFile.getEntry("common/addon/Volume-Key-Selector/install.sh") != null) || - (zipFile.getEntry("META-INF/zbin/keycheck_arm64") == null && - zipFile.getEntry("META-INF/zbin/keycheck_arm") != null)) { + "common/addon/Volume-Key-Selector/tools/arm64/keycheck") == null && zipFile.getEntry("common/addon/Volume-Key-Selector/install.sh") != null) || (zipFile.getEntry("META-INF/zbin/keycheck_arm64") == null && zipFile.getEntry("META-INF/zbin/keycheck_arm") != null)) { needs32bit = true; } - ZipEntry moduleProp = zipFile.getEntry("module.prop"); + ZipArchiveEntry moduleProp = zipFile.getEntry("module.prop"); magiskModule = moduleProp != null; - if (zipFile.getEntry("install.sh") == null && - zipFile.getEntry("customize.sh") == null && - zipFile.getEntry("setup.sh") != null && magiskModule) { + if (zipFile.getEntry("install.sh") == null && zipFile.getEntry("customize.sh") == null && zipFile.getEntry("setup.sh") != null && magiskModule) { mmtReborn = true; // MMT-Reborn require a separate runtime } - if (!magiskModule && (moduleProp = zipFile.getEntry( - "META-INF/com/google/android/magisk/module.prop")) != null) { + if (!magiskModule && (moduleProp = zipFile.getEntry("META-INF/com/google/android/magisk/module.prop")) != null) { installZipMagiskModule = true; } moduleId = PropUtils.readModuleId(zipFile.getInputStream(moduleProp)); } catch (IOException ignored) { } int compatFlags = AppUpdateManager.getFlagsForModule(moduleId); - if ((compatFlags & AppUpdateManager.FLAG_COMPAT_NEED_32BIT) != 0) - needs32bit = true; - if ((compatFlags & AppUpdateManager.FLAG_COMPAT_NO_EXT) != 0) - noExtensions = true; - if (moduleId != null && (moduleId.isEmpty() || - moduleId.contains("/") || moduleId.contains("\0") || - (moduleId.startsWith(".") && moduleId.endsWith(".")))) { - this.setInstallStateFinished(false, - "! This module contain a dangerous moduleId", - null); + if ((compatFlags & AppUpdateManager.FLAG_COMPAT_NEED_32BIT) != 0) needs32bit = true; + if ((compatFlags & AppUpdateManager.FLAG_COMPAT_NO_EXT) != 0) noExtensions = true; + if (moduleId != null && (moduleId.isEmpty() || moduleId.contains("/") || moduleId.contains("\0") || (moduleId.startsWith(".") && moduleId.endsWith(".")))) { + this.setInstallStateFinished(false, "! This module contain a dangerous moduleId", null); return; } if (magiskModule && moduleId == null && !anyKernel3) { // Modules without module Ids are module installed by 3rd party software - this.setInstallStateFinished(false, - "! Magisk modules require a moduleId", null); + this.setInstallStateFinished(false, "! Magisk modules require a moduleId", null); return; } if (anyKernel3) { @@ -425,47 +389,32 @@ public class InstallerActivity extends FoxActivity { this.warnReboot = true; // We should probably re-flash magisk... installExecutable = this.extractInstallScript("anykernel3_installer.sh"); if (installExecutable == null) { - this.setInstallStateFinished(false, - "! Failed to extract AnyKernel3 install script", ""); + this.setInstallStateFinished(false, "! Failed to extract AnyKernel3 install script", ""); return; } // "unshare -m" is needed to force mount namespace isolation. // This allow AnyKernel to mess-up with mounts point without crashing the system! - installCommand = "unshare -m " + ASH + " \"" + - installExecutable.getAbsolutePath() + "\"" + - " 3 1 \"" + file.getAbsolutePath() + "\""; - } else if (installZipMagiskModule || - (compatFlags & AppUpdateManager.FLAG_COMPAT_ZIP_WRAPPER) != 0) { + installCommand = "unshare -m " + ASH + " \"" + installExecutable.getAbsolutePath() + "\"" + " 3 1 \"" + file.getAbsolutePath() + "\""; + } else if (installZipMagiskModule || (compatFlags & AppUpdateManager.FLAG_COMPAT_ZIP_WRAPPER) != 0) { installExecutable = this.extractInstallScript("module_installer_wrapper.sh"); if (installExecutable == null) { - this.setInstallStateFinished(false, - "! Failed to extract Magisk module wrapper script", ""); + this.setInstallStateFinished(false, "! Failed to extract Magisk module wrapper script", ""); return; } - installCommand = ASH + " \"" + - installExecutable.getAbsolutePath() + "\"" + - " 3 1 \"" + file.getAbsolutePath() + "\""; - } else if (InstallerInitializer.peekMagiskVersion() >= - Constants.MAGISK_VER_CODE_INSTALL_COMMAND && - ((compatFlags & AppUpdateManager.FLAG_COMPAT_MAGISK_CMD) != 0 || - noExtensions || MainApplication.isUsingMagiskCommand())) { + installCommand = ASH + " \"" + installExecutable.getAbsolutePath() + "\"" + " 3 1 \"" + file.getAbsolutePath() + "\""; + } else if (InstallerInitializer.peekMagiskVersion() >= Constants.MAGISK_VER_CODE_INSTALL_COMMAND && ((compatFlags & AppUpdateManager.FLAG_COMPAT_MAGISK_CMD) != 0 || noExtensions || MainApplication.isUsingMagiskCommand())) { installCommand = "magisk --install-module \"" + file.getAbsolutePath() + "\""; - installExecutable = new File(MAGISK_PATH.equals("/sbin") ? - "/sbin/magisk" : "/system/bin/magisk"); + installExecutable = new File(MAGISK_PATH.equals("/sbin") ? "/sbin/magisk" : "/system/bin/magisk"); magiskCmdLine = true; } else if (moduleId != null) { installExecutable = this.extractInstallScript("module_installer_compat.sh"); if (installExecutable == null) { - this.setInstallStateFinished(false, - "! Failed to extract Magisk module install script", ""); + this.setInstallStateFinished(false, "! Failed to extract Magisk module install script", ""); return; } - installCommand = ASH + " \"" + - installExecutable.getAbsolutePath() + "\"" + - " 3 1 \"" + file.getAbsolutePath() + "\""; + installCommand = ASH + " \"" + installExecutable.getAbsolutePath() + "\"" + " 3 1 \"" + file.getAbsolutePath() + "\""; } else { - this.setInstallStateFinished(false, - "! Zip file is not a valid Magisk module or AnyKernel3 zip!", ""); + this.setInstallStateFinished(false, "! Zip file is not a valid Magisk module or AnyKernel3 zip!", ""); return; } installerMonitor = new InstallerMonitor(installExecutable); @@ -475,26 +424,12 @@ public class InstallerActivity extends FoxActivity { this.installerTerminal.enableAnsi(); else this.installerTerminal.disableAnsi(); installJob = Shell.cmd(arch32, "export BOOTMODE=true", // No Extensions - this.installerTerminal.isAnsiEnabled() ? - AnsiConstants.ANSI_CMD_SUPPORT : "true", - "cd \"" + this.moduleCache.getAbsolutePath() + "\"", - installCommand).to(installerController, installerMonitor); + this.installerTerminal.isAnsiEnabled() ? AnsiConstants.ANSI_CMD_SUPPORT : "true", "cd \"" + this.moduleCache.getAbsolutePath() + "\"", installCommand).to(installerController, installerMonitor); } else { if ((compatFlags & AppUpdateManager.FLAG_COMPAT_NO_ANSI) != 0) this.installerTerminal.disableAnsi(); else this.installerTerminal.enableAnsi(); - installJob = Shell.cmd(arch32, "export MMM_EXT_SUPPORT=1", - "export MMM_USER_LANGUAGE=" + this.getResources() - .getConfiguration().getLocales().get(0).toLanguageTag(), - "export MMM_APP_VERSION=" + BuildConfig.VERSION_NAME, - "export MMM_TEXT_WRAP=" + (this.textWrap ? "1" : "0"), - this.installerTerminal.isAnsiEnabled() ? - AnsiConstants.ANSI_CMD_SUPPORT : "true", - mmtReborn ? "export MMM_MMT_REBORN=1" : "true", - "export BOOTMODE=true", anyKernel3 ? "export AK3TMPFS=" + - InstallerInitializer.peekMagiskPath() + "/ak3tmpfs" : - "cd \"" + this.moduleCache.getAbsolutePath() + "\"", - installCommand).to(installerController, installerMonitor); + installJob = Shell.cmd(arch32, "export MMM_EXT_SUPPORT=1", "export MMM_USER_LANGUAGE=" + this.getResources().getConfiguration().getLocales().get(0).toLanguageTag(), "export MMM_APP_VERSION=" + BuildConfig.VERSION_NAME, "export MMM_TEXT_WRAP=" + (this.textWrap ? "1" : "0"), this.installerTerminal.isAnsiEnabled() ? AnsiConstants.ANSI_CMD_SUPPORT : "true", mmtReborn ? "export MMM_MMT_REBORN=1" : "true", "export BOOTMODE=true", anyKernel3 ? "export AK3TMPFS=" + InstallerInitializer.peekMagiskPath() + "/ak3tmpfs" : "cd \"" + this.moduleCache.getAbsolutePath() + "\"", installCommand).to(installerController, installerMonitor); } // Note: Sentry only send this info on crash. if (MainApplication.isCrashReportingEnabled()) { @@ -505,8 +440,7 @@ public class InstallerActivity extends FoxActivity { breadcrumb.setData("isAnyKernel3", anyKernel3 ? "true" : "false"); breadcrumb.setData("noExtensions", noExtensions ? "true" : "false"); breadcrumb.setData("magiskCmdLine", magiskCmdLine ? "true" : "false"); - breadcrumb.setData("ansi", this.installerTerminal - .isAnsiEnabled() ? "enabled" : "disabled"); + breadcrumb.setData("ansi", this.installerTerminal.isAnsiEnabled() ? "enabled" : "disabled"); breadcrumb.setCategory("app.action.install"); SentryMain.addSentryBreadcrumb(breadcrumb); } @@ -528,21 +462,17 @@ public class InstallerActivity extends FoxActivity { message = installerMonitor.doCleanUp(); } } - this.setInstallStateFinished(success, message, - installerController.getSupportLink()); + this.setInstallStateFinished(success, message, installerController.getSupportLink()); } private File extractInstallScript(String script) { File compatInstallScript = new File(this.moduleCache, script); - if (!compatInstallScript.exists() || compatInstallScript.length() == 0 || - !extracted.contains(script)) { + if (!compatInstallScript.exists() || compatInstallScript.length() == 0 || !extracted.contains(script)) { try { - Files.write(compatInstallScript, Files.readAllBytes( - this.getAssets().open(script))); + Files.write(compatInstallScript, Files.readAllBytes(this.getAssets().open(script))); extracted.add(script); } catch (IOException e) { - if (compatInstallScript.delete()) - extracted.remove(script); + if (compatInstallScript.delete()) extracted.remove(script); Timber.e(e); return null; } @@ -553,8 +483,8 @@ public class InstallerActivity extends FoxActivity { @Override public boolean dispatchKeyEvent(KeyEvent event) { int keyCode = event.getKeyCode(); - if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || - keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) return true; + if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) + return true; return super.dispatchKeyEvent(event); } @@ -564,8 +494,7 @@ public class InstallerActivity extends FoxActivity { this.installerTerminal.disableAnsi(); if (success && toDelete != null && !toDelete.delete()) { SuFile suFile = new SuFile(toDelete.getAbsolutePath()); - if (suFile.exists() && !suFile.delete()) - Timber.w("Failed to delete zip file"); + if (suFile.exists() && !suFile.delete()) Timber.w("Failed to delete zip file"); else toDelete = null; } else toDelete = null; this.runOnUiThread(() -> { @@ -588,33 +517,23 @@ public class InstallerActivity extends FoxActivity { String reboot_cmd = "/system/bin/svc power reboot || /system/bin/reboot || setprop sys.powerctl reboot"; this.rebootFloatingButton.setOnClickListener(_view -> { if (this.warnReboot || MainApplication.shouldPreventReboot()) { - MaterialAlertDialogBuilder builder = - new MaterialAlertDialogBuilder(this); + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this); - builder - .setTitle(R.string.install_terminal_reboot_now) - .setMessage(R.string.install_terminal_reboot_now_message) - .setCancelable(false) - .setIcon(R.drawable.ic_reboot_24) - .setPositiveButton(R.string.ok, (x, y) -> Shell.cmd(reboot_cmd).submit()) - .setNegativeButton(R.string.no, (x, y) -> x.dismiss()).show(); + builder.setTitle(R.string.install_terminal_reboot_now).setMessage(R.string.install_terminal_reboot_now_message).setCancelable(false).setIcon(R.drawable.ic_reboot_24).setPositiveButton(R.string.ok, (x, y) -> Shell.cmd(reboot_cmd).submit()).setNegativeButton(R.string.no, (x, y) -> x.dismiss()).show(); } else { Shell.cmd(reboot_cmd).submit(); } }); this.rebootFloatingButton.setVisibility(View.VISIBLE); - if (message != null && !message.isEmpty()) - this.installerTerminal.addLine(message); + if (message != null && !message.isEmpty()) this.installerTerminal.addLine(message); if (optionalLink != null && !optionalLink.isEmpty()) { - this.setActionBarExtraMenuButton(ActionButtonType.supportIconForUrl(optionalLink), - menu -> { - IntentHelper.openUrl(this, optionalLink); - return true; - }); + this.setActionBarExtraMenuButton(ActionButtonType.supportIconForUrl(optionalLink), menu -> { + IntentHelper.openUrl(this, optionalLink); + return true; + }); } else if (success) { final Intent intent = this.getIntent(); - final String config = MainApplication.checkSecret(intent) ? - intent.getStringExtra(Constants.EXTRA_INSTALL_CONFIG) : null; + final String config = MainApplication.checkSecret(intent) ? intent.getStringExtra(Constants.EXTRA_INSTALL_CONFIG) : null; if (config != null && !config.isEmpty()) { String configPkg = IntentHelper.getPackageOfConfig(config); try { @@ -624,11 +543,8 @@ public class InstallerActivity extends FoxActivity { return true; }); } catch (PackageManager.NameNotFoundException e) { - Timber.w("Config package \"" + - configPkg + "\" missing for installer view"); - this.installerTerminal.addLine(String.format( - this.getString(R.string.install_terminal_config_missing), - configPkg)); + Timber.w("Config package \"" + configPkg + "\" missing for installer view"); + this.installerTerminal.addLine(String.format(this.getString(R.string.install_terminal_config_missing), configPkg)); } } } @@ -640,13 +556,10 @@ public class InstallerActivity extends FoxActivity { private final InstallerTerminal terminal; private final File moduleFile; private final boolean noExtension; - private boolean enabled, useExt, - useRecovery, isRecoveryBar; + private boolean enabled, useExt, useRecovery, isRecoveryBar; private String supportLink = ""; - private InstallerController(LinearProgressIndicator progressIndicator, - InstallerTerminal terminal, File moduleFile, - boolean noExtension) { + private InstallerController(LinearProgressIndicator progressIndicator, InstallerTerminal terminal, File moduleFile, boolean noExtension) { this.progressIndicator = progressIndicator; this.terminal = terminal; this.moduleFile = moduleFile; @@ -686,9 +599,7 @@ public class InstallerActivity extends FoxActivity { } catch (Exception ignored) { } } else { - this.terminal.addLine(s.replace( - this.moduleFile.getAbsolutePath(), - this.moduleFile.getName())); + this.terminal.addLine(s.replace(this.moduleFile.getAbsolutePath(), this.moduleFile.getName())); } } @@ -751,8 +662,7 @@ public class InstallerActivity extends FoxActivity { case "setLoading": this.isRecoveryBar = false; try { - this.progressIndicator.setProgressCompat( - Short.parseShort(arg), true); + this.progressIndicator.setProgressCompat(Short.parseShort(arg), true); } catch (Exception ignored) { } break; @@ -762,8 +672,7 @@ public class InstallerActivity extends FoxActivity { break; case "setSupportLink": // Only set link if valid - if (arg.isEmpty() || (arg.startsWith("https://") && - arg.indexOf('/', 8) > 8)) + if (arg.isEmpty() || (arg.startsWith("https://") && arg.indexOf('/', 8) > 8)) this.supportLink = arg; break; case "disableANSI": @@ -779,8 +688,7 @@ public class InstallerActivity extends FoxActivity { public void disable() { this.enabled = false; if (this.isRecoveryBar) { - UiThreadHandler.runAndWait(() -> - this.processCommand("setLoading 256")); + UiThreadHandler.runAndWait(() -> this.processCommand("setLoading 256")); } } @@ -797,9 +705,7 @@ public class InstallerActivity extends FoxActivity { public InstallerMonitor(File installScript) { super(Runnable::run); - this.installScriptErr = - installScript.getAbsolutePath() + - ": /data/adb/modules_update/"; + this.installScriptErr = installScript.getAbsolutePath() + ": /data/adb/modules_update/"; } @Override @@ -822,8 +728,7 @@ public class InstallerActivity extends FoxActivity { String module = installScriptErr.substring(0, i); SuFile moduleUpdate = new SuFile("/data/adb/modules_update/" + module); if (moduleUpdate.exists()) { - if (!moduleUpdate.deleteRecursive()) - Timber.e("Failed to delete failed update"); + if (!moduleUpdate.deleteRecursive()) Timber.e("Failed to delete failed update"); return "Error: " + installScriptErr.substring(i + 1); } } else if (this.forCleanUp != null) { diff --git a/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java b/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java index 9493459..658b12c 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java @@ -20,10 +20,9 @@ import com.fox2code.mmm.manager.ModuleInfo; import com.fox2code.mmm.utils.SyncManager; import com.fox2code.mmm.utils.io.Files; import com.fox2code.mmm.utils.io.Hashes; -import com.fox2code.mmm.utils.io.net.Http; import com.fox2code.mmm.utils.io.PropUtils; +import com.fox2code.mmm.utils.io.net.Http; import com.google.android.material.dialog.MaterialAlertDialogBuilder; -import com.google.android.material.snackbar.Snackbar; import java.io.File; import java.nio.charset.StandardCharsets; @@ -33,8 +32,6 @@ import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; -import javax.net.ssl.SSLException; - import timber.log.Timber; public final class RepoManager extends SyncManager { @@ -55,10 +52,9 @@ public final class RepoManager extends SyncManager { private final MainApplication mainApplication; private final LinkedHashMap repoData; private final HashMap modules; + public String repoLastErrorName = null; private AndroidacyRepoData androidacyRepoData; private CustomRepoManager customRepoManager; - public String repoLastErrorName = null; - private boolean hasInternet; private boolean initialized; private boolean repoLastSuccess; @@ -225,7 +221,6 @@ public final class RepoManager extends SyncManager { RepoData[] repoDatas = new LinkedHashSet<>(this.repoData.values()).toArray(new RepoData[0]); RepoUpdater[] repoUpdaters = new RepoUpdater[repoDatas.length]; int moduleToUpdate = 0; - this.checkConnection(); if (!this.hasConnectivity()) { updateListener.update(STEP3); return; @@ -334,40 +329,6 @@ public final class RepoManager extends SyncManager { updateListener.update(1D); } - private void checkConnection() { - this.hasInternet = false; - // Check if we have internet connection - // Attempt to contact connectivitycheck.gstatic.com/generate_204 - // If we can't, we don't have internet connection - Timber.d("Checking internet connection..."); - // this url is actually hosted by Cloudflare and is not dependent on Androidacy servers being up - byte[] resp; - try { - resp = Http.doHttpGet("https://production-api.androidacy.com/cdn-cgi/trace", false); - } catch (Exception e) { - Timber.e(e, "Failed to check internet connection. Assuming no internet connection."); - // check if it's a security or ssl exception - if (e instanceof SSLException || e instanceof SecurityException) { - // if it is, user installed a certificate that blocks the connection - // show a snackbar to inform the user - Activity context = MainApplication.getINSTANCE().getLastCompatActivity(); - new Handler(Looper.getMainLooper()).post(() -> { - if (context != null) { - Snackbar.make(context.findViewById(android.R.id.content), R.string.certificate_error, Snackbar.LENGTH_LONG).show(); - } - }); - } - this.hasInternet = false; - return; - } - // get the response body - String response = new String(resp, StandardCharsets.UTF_8); - // check if the response body contains "visit_scheme=https" and "http/" - // if it does, we have internet connection - this.hasInternet = response.contains("visit_scheme=https") && response.contains("http/"); - Timber.d("Internet connection: %s", this.hasInternet); - } - public void updateEnabledStates() { for (RepoData repoData : this.repoData.values()) { boolean wasEnabled = repoData.isEnabled(); @@ -384,8 +345,7 @@ public final class RepoManager extends SyncManager { } public boolean hasConnectivity() { - Timber.d("Has connectivity: %s", this.hasInternet); - return this.hasInternet; + return Http.hasConnectivity(); } private RepoData addRepoData(String url, String fallBackName) { diff --git a/app/src/main/java/com/fox2code/mmm/utils/io/net/Http.java b/app/src/main/java/com/fox2code/mmm/utils/io/net/Http.java index d3201b2..7413718 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/io/net/Http.java +++ b/app/src/main/java/com/fox2code/mmm/utils/io/net/Http.java @@ -1,14 +1,18 @@ package com.fox2code.mmm.utils.io.net; import android.annotation.SuppressLint; +import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.net.Uri; import android.os.Build; +import android.os.Handler; +import android.os.Looper; import android.system.ErrnoException; import android.system.Os; import android.webkit.CookieManager; import android.webkit.WebSettings; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -16,9 +20,11 @@ import androidx.annotation.Nullable; import com.fox2code.mmm.BuildConfig; import com.fox2code.mmm.MainActivity; import com.fox2code.mmm.MainApplication; +import com.fox2code.mmm.R; import com.fox2code.mmm.androidacy.AndroidacyUtil; import com.fox2code.mmm.installer.InstallerInitializer; import com.fox2code.mmm.utils.io.Files; +import com.google.android.material.snackbar.Snackbar; import com.google.net.cronet.okhttptransport.CronetInterceptor; import org.chromium.net.CronetEngine; @@ -41,6 +47,8 @@ import java.util.List; import java.util.Objects; import java.util.concurrent.TimeUnit; +import javax.net.ssl.SSLException; + import io.sentry.android.okhttp.SentryOkHttpInterceptor; import okhttp3.Cache; import okhttp3.Dns; @@ -93,6 +101,9 @@ public enum Http { Exception t) { cookieManager = null; Timber.e(t, "No WebView support!"); + // show a toast + Context context = mainApplication.getApplicationContext(); + MainActivity.getFoxActivity(context).runOnUiThread(() -> Toast.makeText(mainApplication, R.string.error_creating_cookie_database, Toast.LENGTH_LONG).show()); } hasWebView = cookieManager != null; OkHttpClient.Builder httpclientBuilder = new OkHttpClient.Builder(); @@ -426,6 +437,37 @@ public enum Http { } } + public static boolean hasConnectivity() { + // Check if we have internet connection + // Attempt to contact connectivitycheck.gstatic.com/generate_204 + // If we can't, we don't have internet connection + Timber.d("Checking internet connection..."); + // this url is actually hosted by Cloudflare and is not dependent on Androidacy servers being up + byte[] resp; + try { + resp = Http.doHttpGet("https://production-api.androidacy.com/cdn-cgi/trace", false); + } catch (Exception e) { + Timber.e(e, "Failed to check internet connection. Assuming no internet connection."); + // check if it's a security or ssl exception + if (e instanceof SSLException || e instanceof SecurityException) { + // if it is, user installed a certificate that blocks the connection + // show a snackbar to inform the user + Activity context = MainApplication.getINSTANCE().getLastCompatActivity(); + new Handler(Looper.getMainLooper()).post(() -> { + if (context != null) { + Snackbar.make(context.findViewById(android.R.id.content), R.string.certificate_error, Snackbar.LENGTH_LONG).show(); + } + }); + } + return false; + } + // get the response body + String response = new String(resp, StandardCharsets.UTF_8); + // check if the response body contains "visit_scheme=https" and "http/" + // if it does, we have internet connection + return response.contains("visit_scheme=https") && response.contains("http/"); + } + public interface ProgressListener { void onUpdate(int downloaded, int total, boolean done); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a9b3923..2bf4f0b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -392,4 +392,5 @@ Send additional information Send additional info in crash reports. This may include device identifiers and IP addresses. No data will be used for any other purpose besides analyzing crashes and improving performance. + Error accessing WebView. Functionality may be impacted.