Merge branch 'master' into f2c-merge-patch

pull/107/head
Der_Googler 2 years ago committed by GitHub
commit 73c8baa662
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,5 +1,9 @@
# Contributor Covenant Code of Conduct
Note: The code of conduct are the rules that applies to everyone interacting
with the repo, regardless of acceptance, moderators of the repo are not forced
to apply it strictly, so be nice!
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
@ -24,6 +28,8 @@ community include:
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
* Respecting mistakes as something happening to everyone that is useful as
learning material
Examples of unacceptable behavior include:
@ -31,8 +37,8 @@ Examples of unacceptable behavior include:
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Publishing others' private information, such as a physical or email address,
with or without their explicit permission unless widely publicly available
* Other conduct which could reasonably be considered inappropriate in a
professional setting
@ -73,16 +79,16 @@ the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
harmful or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
behavior was inappropriate.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
of actions, or a behaviour that induce hatred in the community.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with

@ -10,8 +10,8 @@ android {
applicationId "com.fox2code.mmm"
minSdk 21
targetSdk 32
versionCode 39
versionName "0.4.4"
versionCode 40
versionName "0.4.5"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

@ -83,14 +83,16 @@ public enum NotificationType implements NotificationTypeCst {
try {
boolean needPatch;
try (ZipFile zipFile = new ZipFile(d)) {
needPatch = zipFile.getEntry("module.prop") == null;
needPatch = zipFile.getEntry("module.prop") == null &&
zipFile.getEntry("anykernel.sh") == null;
}
if (needPatch) {
Files.patchModuleSimple(Files.read(d),
new FileOutputStream(d));
}
try (ZipFile zipFile = new ZipFile(d)) {
needPatch = zipFile.getEntry("module.prop") == null;
needPatch = zipFile.getEntry("module.prop") == null &&
zipFile.getEntry("anykernel.sh") == null;
}
if (needPatch) {
if (d.exists() && !d.delete())

@ -37,11 +37,14 @@ import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.internal.UiThreadHandler;
import com.topjohnwu.superuser.io.SuFile;
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.io.PrintStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
@ -123,127 +126,106 @@ public class InstallerActivity extends CompatActivity {
this.getWindow().setFlags( // Note: Doesn't require WAKELOCK permission
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
if (urlMode) {
this.progressIndicator.setVisibility(View.VISIBLE);
this.installerTerminal.addLine("- Downloading " + name);
new Thread(() -> {
File moduleCache = this.toDelete =
new File(this.moduleCache, "module.zip");
if (moduleCache.exists() && !moduleCache.delete() &&
!new SuFile(moduleCache.getAbsolutePath()).delete())
Log.e(TAG, "Failed to delete module cache");
String errMessage = "Failed to download module zip";
try {
Log.i(TAG, "Downloading: " + target);
byte[] rawModule = Http.doHttpGet(target, (progress, max, done) -> {
if (max <= 0 && this.progressIndicator.isIndeterminate())
return;
this.runOnUiThread(() -> {
this.progressIndicator.setIndeterminate(false);
this.progressIndicator.setMax(max);
this.progressIndicator.setProgressCompat(progress, true);
});
this.progressIndicator.setVisibility(View.VISIBLE);
if (urlMode) this.installerTerminal.addLine("- Downloading " + name);
new Thread(() -> {
File moduleCache = this.toDelete = urlMode ?
new File(this.moduleCache, "module.zip") : new File(target);
if (urlMode && moduleCache.exists() && !moduleCache.delete() &&
!new SuFile(moduleCache.getAbsolutePath()).delete())
Log.e(TAG, "Failed to delete module cache");
String errMessage = "Failed to download module zip";
try {
Log.i(TAG, (urlMode ? "Downloading: " : "Loading: ") + target);
byte[] rawModule = urlMode ? Http.doHttpGet(target, (progress, max, done) -> {
if (max <= 0 && this.progressIndicator.isIndeterminate())
return;
this.runOnUiThread(() -> {
this.progressIndicator.setIndeterminate(false);
this.progressIndicator.setMax(max);
this.progressIndicator.setProgressCompat(progress, true);
});
}) : Files.readSU(moduleCache);
this.runOnUiThread(() -> {
this.progressIndicator.setVisibility(View.GONE);
this.progressIndicator.setIndeterminate(true);
});
if (this.canceled) return;
if (checksum != null && !checksum.isEmpty()) {
Log.d(TAG, "Checking for checksum: " + checksum);
this.runOnUiThread(() -> {
this.progressIndicator.setVisibility(View.GONE);
this.progressIndicator.setIndeterminate(true);
this.installerTerminal.addLine("- Checking file integrity");
});
if (this.canceled) return;
if (checksum != null && !checksum.isEmpty()) {
Log.d(TAG, "Checking for checksum: " + checksum);
this.runOnUiThread(() -> {
this.installerTerminal.addLine("- Checking file integrity");
});
if (!Hashes.checkSumMatch(rawModule, checksum)) {
this.setInstallStateFinished(false,
"! File integrity check failed", "");
return;
}
if (!Hashes.checkSumMatch(rawModule, checksum)) {
this.setInstallStateFinished(false,
"! File integrity check failed", "");
return;
}
if (this.canceled) return;
Files.fixJavaZipHax(rawModule);
boolean noPatch = false;
boolean isModule = false;
boolean isAnyKernel = false;
errMessage = "File is not a valid zip file";
try (ZipInputStream zipInputStream = new ZipInputStream(
new ByteArrayInputStream(rawModule))) {
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
String entryName = zipEntry.getName();
if (entryName.equals("anykernel.sh")) {
isAnyKernel = true;
break;
} else if (entryName.equals("module.prop")) {
noPatch = true;
isModule = true;
break;
} else if (entryName.endsWith("/module.prop")) {
isModule = true;
}
}
if (this.canceled) return;
Files.fixJavaZipHax(rawModule);
boolean noPatch = false;
boolean isModule = false;
boolean isAnyKernel = false;
errMessage = "File is not a valid zip file";
try (ZipInputStream zipInputStream = new ZipInputStream(
new ByteArrayInputStream(rawModule))) {
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
String entryName = zipEntry.getName();
if (entryName.equals("anykernel.sh")) {
noPatch = true;
isAnyKernel = true;
break;
} else if (entryName.equals("module.prop")) {
noPatch = true;
isModule = true;
break;
} else if (entryName.endsWith("/anykernel.sh")) {
isAnyKernel = true;
} else if (entryName.endsWith("/module.prop")) {
isModule = true;
}
}
if (!isModule) {
this.setInstallStateFinished(false, isAnyKernel ?
"! AnyKernel modules can only be installed on recovery" :
"! File is not a valid magisk module", "");
return;
}
if (noPatch) {
}
if (!isModule && !isAnyKernel) {
this.setInstallStateFinished(false,
"! File is not a valid magisk module", "");
return;
}
if (noPatch) {
if (urlMode) {
errMessage = "Failed to save module zip";
try (OutputStream outputStream = new FileOutputStream(moduleCache)) {
outputStream.write(rawModule);
outputStream.flush();
}
} else {
errMessage = "Failed to patch module zip";
this.runOnUiThread(() -> {
this.installerTerminal.addLine("- Patching " + name);
});
Log.i(TAG, "Patching: " + moduleCache.getName());
try (OutputStream outputStream = new FileOutputStream(moduleCache)) {
Files.patchModuleSimple(rawModule, outputStream);
outputStream.flush();
}
}
if (this.canceled) return;
//noinspection UnusedAssignment (Important to avoid OutOfMemoryError)
rawModule = null; // Because reference is kept when calling doInstall
} else {
errMessage = "Failed to patch module zip";
this.runOnUiThread(() -> {
this.installerTerminal.addLine("- Installing " + name);
this.installerTerminal.addLine("- Patching " + name);
});
errMessage = "Failed to install module zip";
this.doInstall(moduleCache, noExtensions, rootless);
} catch (IOException e) {
Log.e(TAG, errMessage, e);
this.setInstallStateFinished(false,
"! " + errMessage, "");
}
}, "Module download Thread").start();
} else {
final File moduleFile = new File(target);
if (checksum != null && !checksum.isEmpty()) {
Log.d(TAG, "Checking for checksum: " + checksum);
this.installerTerminal.addLine("- Checking file integrity");
try {
if (!Hashes.checkSumMatch(Files.readSU(moduleFile), checksum)) {
this.setInstallStateFinished(false,
"! File integrity check failed", "");
return;
Log.i(TAG, "Patching: " + moduleCache.getName());
try (OutputStream outputStream = new FileOutputStream(moduleCache)) {
Files.patchModuleSimple(rawModule, outputStream);
outputStream.flush();
}
} catch (IOException e) {
Log.e(TAG, "Failed to read file for checksum check", e);
this.setInstallStateFinished(false,
"! File integrity check failed", "");
return;
}
//noinspection UnusedAssignment (Important to avoid OutOfMemoryError)
rawModule = null; // Because reference is kept when calling doInstall
if (this.canceled) return;
this.runOnUiThread(() -> {
this.installerTerminal.addLine("- Installing " + name);
});
errMessage = "Failed to install module zip";
this.doInstall(moduleCache, noExtensions, rootless);
} catch (IOException e) {
Log.e(TAG, errMessage, e);
this.setInstallStateFinished(false,
"! " + errMessage, "");
}
this.installerTerminal.addLine("- Installing " + name);
new Thread(() -> this.doInstall(
this.toDelete = moduleFile, noExtensions, rootless),
"Install Thread").start();
}
}, "Module install Thread").start();
}
@ -276,17 +258,67 @@ public class InstallerActivity extends CompatActivity {
String arch32 = "true"; // Do nothing by default
boolean needs32bit = false;
String moduleId = null;
boolean anyKernel = false;
boolean magiskModule = false;
boolean anyKernelSystemLess = false;
File anyKernelInstallScript = new File(this.moduleCache, "update-binary");
try (ZipFile zipFile = new ZipFile(file)) {
ZipEntry anyKernelSh = zipFile.getEntry("anykernel.sh");
if (anyKernelSh != null) { // Check if module is AnyKernel module
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(zipFile.getInputStream(anyKernelSh)));
String line;
// Check if AnyKernel module support system-less
while ((line = bufferedReader.readLine()) != null) {
String trimmedLine = line.trim();
if (trimmedLine.equals("do.modules=1"))
anyKernel = true;
if (trimmedLine.equals("do.systemless=1"))
anyKernelSystemLess = true;
}
bufferedReader.close();
if (anyKernelSystemLess && anyKernel) {
anyKernelSystemLess = false;
ZipEntry updateBinary = zipFile.getEntry(
"META-INF/com/google/android/update-binary");
if (updateBinary != null) {
bufferedReader = new BufferedReader(
new InputStreamReader(zipFile.getInputStream(updateBinary)));
PrintStream printStream = new PrintStream(
new FileOutputStream(anyKernelInstallScript));
while ((line = bufferedReader.readLine()) != null) {
String trimmedLine = line.trim();
if (trimmedLine.equals("mount_all;") ||
trimmedLine.equals("umount_all;"))
continue; // Do not mount anything
line = line.replace("/sbin/sh", "/system/bin/sh");
int prePatch = line.length();
line = line.replace("/data/adb/modules/ak3-helper",
"/data/adb/modules-update/ak3-helper");
if (prePatch != line.length()) anyKernelSystemLess = true;
printStream.println(line);
}
printStream.close();
bufferedReader.close();
if (!anyKernelSystemLess) anyKernelInstallScript.delete();
}
}
anyKernel = true;
}
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) {
needs32bit = true;
}
ZipEntry moduleProp = zipFile.getEntry("module.prop");
magiskModule = moduleProp != null;
moduleId = PropUtils.readModuleId(zipFile
.getInputStream(zipFile.getEntry("module.prop")));
} catch (IOException ignored) {
}
.getInputStream(moduleProp));
} catch (IOException ignored) {}
int compatFlags = AppUpdateManager.getFlagsForModule(moduleId);
if ((compatFlags & AppUpdateManager.FLAG_COMPAT_NEED_32BIT) != 0)
needs32bit = true;
@ -300,6 +332,12 @@ public class InstallerActivity extends CompatActivity {
null);
return;
}
if (magiskModule && moduleId == null && !anyKernel) {
// Modules without module Ids are module installed by 3rd party software
this.setInstallStateFinished(false,
"! Magisk modules require a moduleId", null);
return;
}
if (Build.SUPPORTED_32_BIT_ABIS.length == 0) {
if (needs32bit) {
this.setInstallStateFinished(false,
@ -316,14 +354,23 @@ public class InstallerActivity extends CompatActivity {
}
String installCommand;
File installExecutable;
if (InstallerInitializer.peekMagiskVersion() >=
if (anyKernel && moduleId == null) { // AnyKernel modules don't have a moduleId
if (!anyKernelSystemLess) {
this.setInstallStateFinished(false,
"! This AnyKernel module only support recovery install", null);
return;
}
installExecutable = anyKernelInstallScript;
installCommand = "sh \"" + installExecutable.getAbsolutePath() + "\"" +
" /dev/null 0 \"" + 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(InstallerInitializer.peekMagiskPath()
.equals("/sbin") ? "/sbin/magisk" : "/system/bin/magisk");
} else {
} else if (moduleId != null) {
installExecutable = this.extractInstallScript("module_installer_compat.sh");
if (installExecutable == null) {
this.setInstallStateFinished(false,
@ -331,7 +378,11 @@ public class InstallerActivity extends CompatActivity {
return;
}
installCommand = "sh \"" + installExecutable.getAbsolutePath() + "\"" +
" /dev/null 1 \"" + file.getAbsolutePath() + "\"";
" /dev/null 0 \"" + file.getAbsolutePath() + "\"";
} else {
this.setInstallStateFinished(false,
"! Zip file is not a valid Magisk or a AnyKernel module!", null);
return;
}
installerMonitor = new InstallerMonitor(installExecutable);
if (moduleId != null) installerMonitor.setForCleanUp(moduleId);

@ -1,6 +1,7 @@
package com.fox2code.mmm.utils;
import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
@ -45,6 +46,11 @@ public class Files {
}
public static byte[] readSU(File file) throws IOException {
if (file.isFile() && file.canRead()) {
try { // Read as app if su not required
return read(file);
} catch (IOException ignored) {}
}
try (InputStream inputStream = SuFileInputStream.open(file)) {
return readAllBytes(inputStream);
}

@ -371,6 +371,7 @@ public class IntentHelper {
}
outputStream = new FileOutputStream(destination);
Files.copy(inputStream, outputStream);
Log.d(TAG, "File saved at " + destination);
success = true;
} catch (Exception e) {
Log.e(TAG, "failed copy of " + uri, e);

@ -303,6 +303,7 @@ public class PropUtils {
}
public static String readModuleId(InputStream inputStream) {
if (inputStream == null) return null;
String moduleId = null;
try (BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {

@ -1,23 +1,23 @@
<resources>
<string name="app_name">Fox\'s Magisk Module Manager</string>
<string name="app_name_short">Fox\'s Mmm</string>
<string name="fail_root_magisk">Failed to get access to Root or Magisk</string>
<string name="fail_root_magisk">Could not access either Root or Magisk</string>
<string name="loading">Loading…</string>
<string name="updatable">Updatable</string>
<string name="updatable">Upgradable</string>
<string name="installed">Installed</string>
<string name="online_repo">Online Repo</string>
<string name="showcase_mode">The application is in lockdown mode</string>
<string name="failed_download">Failed to download file.</string>
<string name="showcase_mode">The app is in lockdown mode</string>
<string name="failed_download">Could not download the file</string>
<string name="slow_modules">Modules took too long to boot, consider disabling some modules</string>
<string name="fail_internet">Failed to connect to the internet</string>
<string name="no_web_view">Failed to get system WebView</string>
<string name="fail_internet">Could not connect to the Internet</string>
<string name="no_web_view">Could not open system WebView</string>
<string name="title_activity_settings">SettingsActivity</string>
<string name="app_update_available">Application update available</string>
<string name="app_update">Update</string>
<string name="app_update_available">A new version of the app is available</string>
<string name="app_update">Upgrade</string>
<string name="no_desc_found">No description found.</string>
<string name="download_module">Download module</string>
<string name="install_module">Install module</string>
<string name="update_module">Update module</string>
<string name="update_module">Upgrade module</string>
<string name="changelog">Changelog</string>
<string name="website">Website</string>
<string name="support">Support</string>
@ -29,7 +29,7 @@
<string name="no">No</string>
<!-- Module section translation -->
<string name="module_last_update">Last update:</string>
<string name="module_last_update">Last version:</string>
<string name="module_repo">Repo:</string>
<string name="module_by">by</string>
<string name="module_downloads">Downloads:</string>
@ -42,65 +42,68 @@
<string name="showcase_mode_desc">Lockdown mode prevent manager to do action on modules</string>
<string name="prevent_reboot_pref">Prevent reboot</string>
<string name="prevent_reboot_desc">Prevents unexpected reboots</string>
<string name="showcase_mode_desc">Lockdown mode prevents the manager from carrying out actions on modules</string>
<string name="pref_category_settings">Settings</string>
<string name="pref_category_info">Info</string>
<string name="show_licenses">Show licenses</string>
<string name="licenses">Licences</string>
<string name="show_incompatible_pref">Show incompatible modules</string>
<string name="show_incompatible_desc">Show modules that are incompatible with your device based on their metadata</string>
<string name="magisk_outdated">Magisk is outdated!</string>
<string name="show_incompatible_desc">Show modules unlikely to work on your device based on their metadata</string>
<string name="magisk_outdated">There is a new version of Magisk to install!</string>
<string name="pref_category_repos">Repos</string>
<string name="pref_category_security">Security</string>
<string name="pref_category_appearance">Appearance</string>
<string name="pref_category_general">General</string>
<string name="repo_main_desc">The repository hosting Magisk Modules</string>
<string name="repo_main_alt">An alternative to Magisk-Modules-Repo with fewer restrictions.</string>
<string name="repo_main_desc">The repository hosting Magisk modules</string>
<string name="repo_main_alt">An alternative to the Magisk-Modules-Repo with fewer restrictions.</string>
<string name="master_delete">Delete the module files?</string>
<string name="master_delete_no">Keep files</string>
<string name="master_delete_yes">Delete files</string>
<string name="master_delete_fail">Failed to delete the module files</string>
<string name="master_delete_no">Keep</string>
<string name="master_delete_yes">Delete</string>
<string name="master_delete_fail">Could not delete the module files</string>
<string name="theme_pref">Theme</string>
<string name="theme_mode_pref">Theme mode</string>
<string name="module_id_prefix">Module id: </string>
<string name="module_id_prefix">Module ID: </string>
<string name="install_from_storage">Install module from storage</string>
<string name="invalid_format">The selected module is in an invalid format</string>
<string name="invalid_format">The selected module has an invalid format</string>
<string name="local_install_title">Local install</string>
<string name="source_code">Source code</string>
<string name="magisk_builtin_module">Magisk builtin module</string>
<string name="substratum_builtin_module">Substratum builtin module</string>
<string name="force_dark_terminal_title">Force dark mode terminal</string>
<string name="file_picker_failure">Your current file picker failed to give access to the file.</string>
<string name="magisk_builtin_module">Magisk built-in module</string>
<string name="substratum_builtin_module">Substratum built-in module</string>
<string name="force_dark_terminal_title">Dark terminal</string>
<string name="file_picker_failure">Your current file picker could not access the file.</string>
<string name="remote_install_title">Remote install</string>
<string name="file_picker_wierd">Your file picker returned a non standard response.</string>
<string name="use_magisk_install_command_pref">Use magisk module install command</string>
<string name="file_picker_wierd">Your file picker returned a non-standard response.</string>
<string name="use_magisk_install_command_pref">Use the \"magisk --install-module\" command</string>
<string name="use_magisk_install_command_desc">
During test it caused problems to the module install error diagnosis tool,
so I hid this option behind developer mode, enable this at your own risk!
During testing it caused problems to the module install error diagnosis tool,
so this option behind is hidden behind developer mode.\nTurn this on at your own risk!
</string>
<string name="dev_mode_enabled">Developer mode enabled</string>
<string name="force_english_pref">Force English language</string>
<string name="disable_low_quality_module_filter_pref">Disable low quality module filter</string>
<string name="dev_mode_enabled">Developer mode on</string>
<string name="force_english_pref">English app language</string>
<string name="disable_low_quality_module_filter_pref">Show low-quality modules</string>
<string name="disable_low_quality_module_filter_desc">
Some modules do not declare their metadata properly,causing visual glitches,
and/or indicating poor module quality, disable at your own risk!
Some modules do not declare their metadata properly, causing visual glitches,
and/or indicating poor module quality.\nTurn this off at your own risk!
</string>
<string name="dns_over_https_pref">Dns over https</string>
<string name="dns_over_https_pref">DNS over HTTPS</string>
<string name="dns_over_https_desc">
May fix connections issues in some cases.
(Does not apply to WebView)
(Does not apply to WebView.)
</string>
<string name="disable_extensions_pref">Disable extensions</string>
<string name="disable_extensions_pref">No Mmm</string>
<string name="disable_extensions_desc">
Disable Fox\'s Mmm extensions, this prevent modules from using
terminal extensions, useful if a module misuse Fox\'s Mmm extensions.
Turn off Fox\'s Mmm extensions, preventing modules from using
terminal extensions.\nUseful if a module misuses Fox\'s Mmm extensions.
</string>
<string name="wrap_text_pref">Wrap text</string>
<string name="wrap_text_pref">Text wrapping</string>
<string name="wrap_text_desc">
Wrap text to a new line instead of putting
all text on the same line when installing a module
Show text on multiple lines instead of putting
all text on the same line when installing a module.
</string>
<string name="enable_blur_pref">Enable blur</string>
<string name="repo_enabled">Repo enabled</string>
<string name="repo_disabled">Repo disabled</string>
<string name="androidacy_repo_info">Androidacy repo uses ads and trackers.</string>
<string name="enable_blur_pref">Blur</string>
<string name="repo_enabled">Repo on</string>
<string name="repo_disabled">Repo off</string>
<string name="androidacy_repo_info">The Androidacy repo has ads and trackers.</string>
</resources>

Loading…
Cancel
Save