misc fixes

Signed-off-by: androidacy-user <opensource@androidacy.com>
pull/299/head
androidacy-user 1 year ago
parent babc002422
commit c9aab40670

@ -223,9 +223,16 @@ android {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
lint {
disable 'MissingTranslation'
}
packagingOptions {
jniLibs {
useLegacyPackaging = true
}
}
}
aboutLibraries {

@ -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)

@ -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"));

@ -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();
}
}

@ -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<? extends ZipEntry> 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) {

@ -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<String, RepoData> repoData;
private final HashMap<String, RepoModule> 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/<some number>"
// 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) {

@ -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/<some number>"
// 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);
}

@ -392,4 +392,5 @@
<string name="crash_reporting_pii">Send additional information</string>
<string name="setup_crash_reporting_pii">Send additional info in crash reports.</string>
<string name="setup_crash_reporting_pii_summary"> This may include device identifiers and IP addresses. No data will be used for any other purpose besides analyzing crashes and improving performance.</string>
<string name="error_creating_cookie_database">Error accessing WebView. Functionality may be impacted.</string>
</resources>

Loading…
Cancel
Save