Add compat flags for modules, fix Androidacy code typo.

pull/89/head
Fox2Code 2 years ago
parent 4ce22b2938
commit 97cd87c13e

@ -2,25 +2,42 @@ package com.fox2code.mmm;
import android.util.Log;
import com.fox2code.mmm.utils.Files;
import com.fox2code.mmm.utils.Http;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
// See https://docs.github.com/en/rest/reference/repos#releases
public class AppUpdateManager {
public static int FLAG_COMPAT_LOW_QUALITY = 0x01;
public static int FLAG_COMPAT_NO_EXT = 0x02;
public static int FLAG_COMPAT_MAGISK_CMD = 0x04;
public static int FLAG_COMPAT_NEED_32BIT = 0x08;
private static final String TAG = "AppUpdateManager";
private static final AppUpdateManager INSTANCE = new AppUpdateManager();
private static final String RELEASES_API_URL =
"https://api.github.com/repos/Fox2Code/FoxMagiskModuleManager/releases";
private static final String COMPAT_API_URL =
"https://api.github.com/repos/Fox2Code/FoxMagiskModuleManager/releases";
public static AppUpdateManager getAppUpdateManager() {
return INSTANCE;
}
private final HashMap<String, Integer> compatDataId = new HashMap<>();
private final Object updateLock = new Object();
private final File compatFile;
private String latestRelease;
private String latestPreRelease;
private long lastChecked;
@ -28,12 +45,20 @@ public class AppUpdateManager {
private boolean lastCheckSuccess;
private AppUpdateManager() {
this.compatFile = new File(MainApplication.getINSTANCE().getFilesDir(), "compat.txt");
this.latestRelease = MainApplication.getBootSharedPreferences()
.getString("updater_latest_release", BuildConfig.VERSION_NAME);
this.latestPreRelease = MainApplication.getBootSharedPreferences()
.getString("updater_latest_pre_release", BuildConfig.VERSION_NAME);
this.lastChecked = 0;
this.preReleaseNewer = true;
if (this.compatFile.isFile()) {
try {
this.parseCompatibilityFlags(new FileInputStream(this.compatFile));
} catch (IOException e) {
e.printStackTrace();
}
}
}
// Return true if should show a notification
@ -95,6 +120,31 @@ public class AppUpdateManager {
return this.peekShouldUpdate();
}
public void checkUpdateCompat() {
if (this.compatFile.exists()) {
long lastUpdate = this.compatFile.lastModified();
if (lastUpdate <= System.currentTimeMillis() &&
lastUpdate + 600_000L > System.currentTimeMillis()) {
return; // Skip update
}
}
try {
JSONObject object = new JSONObject(new String(Http.doHttpGet(
COMPAT_API_URL, false), StandardCharsets.UTF_8));
if (object.isNull("body")) {
compatDataId.clear();
Files.write(compatFile, new byte[0]);
return;
}
byte[] rawData = object.getString("body")
.getBytes(StandardCharsets.UTF_8);
this.parseCompatibilityFlags(new ByteArrayInputStream(rawData));
Files.write(compatFile, rawData);
} catch (Exception e) {
Log.e("AppUpdateManager", "Failed to update compat list", e);
}
}
public boolean peekShouldUpdate() {
return !(BuildConfig.VERSION_NAME.equals(this.latestRelease) ||
(this.preReleaseNewer &&
@ -109,4 +159,46 @@ public class AppUpdateManager {
public boolean isLastCheckSuccess() {
return lastCheckSuccess;
}
private void parseCompatibilityFlags(InputStream inputStream) throws IOException {
compatDataId.clear();
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(inputStream, StandardCharsets.UTF_8));
String line;
while ((line = bufferedReader.readLine()) != null) {
line = line.trim();
if (line.isEmpty() || line.startsWith("#")) continue;
int i = line.indexOf('/');
if (i == -1) continue;
int value = 0;
for (String arg : line.substring(i + 1).split(",")) {
switch (arg) {
default:
break;
case "lowQuality":
value |= FLAG_COMPAT_LOW_QUALITY;
break;
case "noExt":
value |= FLAG_COMPAT_NO_EXT;
break;
case "magiskCmd":
value |= FLAG_COMPAT_MAGISK_CMD;
break;
case "need32bit":
value |= FLAG_COMPAT_NEED_32BIT;
break;
}
}
compatDataId.put(line.substring(0, i), value);
}
}
public int getCompatibilityFlags(String moduleId) {
Integer compatFlags = compatDataId.get(moduleId);
return compatFlags == null ? 0 : compatFlags;
}
public static int getFlagsForModule(String moduleId) {
return INSTANCE.getCompatibilityFlags(moduleId);
}
}

@ -179,6 +179,8 @@ public class MainActivity extends CompatActivity implements SwipeRefreshLayout.O
} else {
if (AppUpdateManager.getAppUpdateManager().checkUpdate(true))
moduleViewListBuilder.addNotification(NotificationType.UPDATE_AVAILABLE);
if (AppUpdateManager.getAppUpdateManager().isLastCheckSuccess())
AppUpdateManager.getAppUpdateManager().checkUpdateCompat();
if (max != 0) {
int current = 0;
for (LocalModuleInfo localModuleInfo :

@ -76,6 +76,7 @@ public class ModuleViewListBuilder {
RepoManager repoManager = RepoManager.getINSTANCE();
repoManager.runAfterUpdate(() -> {
Log.i(TAG, "A2: " + repoManager.getModules().size());
boolean no32bitSupport = Build.SUPPORTED_32_BIT_ABIS.length == 0;
for (RepoModule repoModule : repoManager.getModules().values()) {
if (!repoModule.repoData.isEnabled()) continue;
ModuleInfo moduleInfo = repoModule.moduleInfo;
@ -84,9 +85,11 @@ public class ModuleViewListBuilder {
// Only check Magisk compatibility if root is present
(InstallerInitializer.peekMagiskPath() != null &&
repoModule.moduleInfo.minMagisk >
InstallerInitializer.peekMagiskVersion()
)))
continue; // Skip adding incompatible modules
InstallerInitializer.peekMagiskVersion())) ||
// If 64bit only system, skip 32bit only modules
(no32bitSupport && (AppUpdateManager.getFlagsForModule(repoModule.id)
& AppUpdateManager.FLAG_COMPAT_NEED_32BIT) != 0)
) continue; // Skip adding incompatible modules
ModuleHolder moduleHolder = this.mappedModuleHolders.get(repoModule.id);
if (moduleHolder == null) {
this.mappedModuleHolders.put(repoModule.id,

@ -81,9 +81,9 @@ public class AndroidacyActivity extends CompatActivity {
setActionBarBackground(null);
this.setDisplayHomeAsUpEnabled(true);
if (title == null || title.isEmpty()) {
this.setTitle(title);
} else {
this.setTitle("Androidacy");
} else {
this.setTitle(title);
}
if (allowInstall || title == null || title.isEmpty()) {
this.hideActionBar();

@ -16,6 +16,7 @@ import android.widget.Toast;
import androidx.recyclerview.widget.RecyclerView;
import com.fox2code.mmm.ActionButtonType;
import com.fox2code.mmm.AppUpdateManager;
import com.fox2code.mmm.BuildConfig;
import com.fox2code.mmm.Constants;
import com.fox2code.mmm.MainApplication;
@ -26,6 +27,7 @@ import com.fox2code.mmm.utils.Files;
import com.fox2code.mmm.utils.Hashes;
import com.fox2code.mmm.utils.Http;
import com.fox2code.mmm.utils.IntentHelper;
import com.fox2code.mmm.utils.PropUtils;
import com.google.android.material.progressindicator.LinearProgressIndicator;
import com.topjohnwu.superuser.CallbackList;
import com.topjohnwu.superuser.Shell;
@ -261,23 +263,39 @@ public class InstallerActivity extends CompatActivity {
.to(installerController, installerMonitor);
} else {
String arch32 = "true"; // Do nothing by default
boolean needs32bit = false;
String moduleId = null;
try (ZipFile zipFile = new ZipFile(file)) {
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;
}
moduleId = PropUtils.readModuleId(zipFile
.getInputStream(zipFile.getEntry("module.prop")));
} 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);
return;
}
if (Build.SUPPORTED_32_BIT_ABIS.length == 0) {
boolean needs32bit = false;
try (ZipFile zipFile = new ZipFile(file)) {
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;
}
} catch (IOException ignored) {}
if (needs32bit) {
this.setInstallStateFinished(false,
"! This module can't be installed on a 64bit only system",
null);
return;
}
} else {
} else if (needs32bit || (compatFlags & AppUpdateManager.FLAG_COMPAT_NO_EXT) == 0) {
// Restore Magisk legacy stuff for retro compatibility
if (Build.SUPPORTED_32_BIT_ABIS[0].contains("arm"))
arch32 = "export ARCH32=arm";
@ -288,7 +306,8 @@ public class InstallerActivity extends CompatActivity {
File installExecutable;
if (InstallerInitializer.peekMagiskVersion() >=
Constants.MAGISK_VER_CODE_INSTALL_COMMAND &&
(noExtensions || MainApplication.isUsingMagiskCommand())) {
((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");
@ -296,13 +315,14 @@ public class InstallerActivity extends CompatActivity {
installExecutable = this.extractInstallScript("module_installer_compat.sh");
if (installExecutable == null) {
this.setInstallStateFinished(false,
"! Failed to extract module install script", "");
"! Failed to extract module install script", null);
return;
}
installCommand = "sh \"" + installExecutable.getAbsolutePath() + "\"" +
" /dev/null 1 \"" + file.getAbsolutePath() + "\"";
}
installerMonitor = new InstallerMonitor(installExecutable);
if (moduleId != null) installerMonitor.setForCleanUp(moduleId);
if (noExtensions) {
installJob = Shell.su(arch32, // No Extensions
"cd \"" + this.moduleCache.getAbsolutePath() + "\"",
@ -462,6 +482,7 @@ public class InstallerActivity extends CompatActivity {
private static final String DEFAULT_ERR = "! Install failed";
private final String installScriptErr;
public String lastCommand = "";
public String forCleanUp;
public InstallerMonitor(File installScript) {
super(Runnable::run);
@ -476,6 +497,10 @@ public class InstallerActivity extends CompatActivity {
this.lastCommand = s;
}
public void setForCleanUp(String forCleanUp) {
this.forCleanUp = forCleanUp;
}
private String doCleanUp() {
String installScriptErr = this.installScriptErr;
// This block is mainly to help fixing customize.sh syntax errors
@ -490,6 +515,10 @@ public class InstallerActivity extends CompatActivity {
Log.e(TAG, "Failed to delete failed update");
return "Error: " + installScriptErr.substring(i + 1);
}
} else if (this.forCleanUp != null) {
SuFile moduleUpdate = new SuFile("/data/adb/modules_update/" + this.forCleanUp);
if (moduleUpdate.exists() && !moduleUpdate.deleteRecursive())
Log.e(TAG, "Failed to delete failed update");
}
return DEFAULT_ERR;
}

@ -1,7 +1,11 @@
package com.fox2code.mmm.utils;
import static com.fox2code.mmm.AppUpdateManager.FLAG_COMPAT_LOW_QUALITY;
import static com.fox2code.mmm.AppUpdateManager.getFlagsForModule;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import com.fox2code.mmm.manager.ModuleInfo;
import com.topjohnwu.superuser.io.SuFileInputStream;
@ -274,6 +278,22 @@ public class PropUtils {
}
}
public static String readModuleId(InputStream inputStream) {
String moduleId = null;
try (BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
if (line.startsWith("id=")) {
moduleId = line.substring(3).trim();
}
}
} catch (IOException e) {
Log.d("PropUtils", "Failed to get moduleId", e);
}
return moduleId;
}
public static void applyFallbacks(ModuleInfo moduleInfo) {
if (moduleInfo.support == null || moduleInfo.support.isEmpty()) {
moduleInfo.support = moduleSupportsFallbacks.get(moduleInfo.id);
@ -299,7 +319,8 @@ public class PropUtils {
|| moduleInfo.author == null || !TextUtils.isGraphic(moduleInfo.author)
|| (description = moduleInfo.description) == null || !TextUtils.isGraphic(description)
|| description.toLowerCase(Locale.ROOT).equals(moduleInfo.name.toLowerCase(Locale.ROOT))
|| description.length() < Math.min(Math.max(moduleInfo.name.length() + 4, 16), 24);
|| description.length() < Math.min(Math.max(moduleInfo.name.length() + 4, 16), 24)
|| (getFlagsForModule(moduleInfo.id) & FLAG_COMPAT_LOW_QUALITY) != 0;
}
private static boolean isInvalidValue(String name) {

Loading…
Cancel
Save