Skip to content
This repository was archived by the owner on May 4, 2023. It is now read-only.

Commit 97cd87c

Browse files
committed
Add compat flags for modules, fix Androidacy code typo.
1 parent 4ce22b2 commit 97cd87c

File tree

6 files changed

+165
-18
lines changed

6 files changed

+165
-18
lines changed

app/src/main/java/com/fox2code/mmm/AppUpdateManager.java

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,63 @@
22

33
import android.util.Log;
44

5+
import com.fox2code.mmm.utils.Files;
56
import com.fox2code.mmm.utils.Http;
67

78
import org.json.JSONArray;
89
import org.json.JSONObject;
910

11+
import java.io.BufferedReader;
12+
import java.io.ByteArrayInputStream;
13+
import java.io.File;
14+
import java.io.FileInputStream;
15+
import java.io.IOException;
16+
import java.io.InputStream;
17+
import java.io.InputStreamReader;
1018
import java.nio.charset.StandardCharsets;
19+
import java.util.HashMap;
1120

1221
// See https://docs.github.com/en/rest/reference/repos#releases
1322
public class AppUpdateManager {
23+
public static int FLAG_COMPAT_LOW_QUALITY = 0x01;
24+
public static int FLAG_COMPAT_NO_EXT = 0x02;
25+
public static int FLAG_COMPAT_MAGISK_CMD = 0x04;
26+
public static int FLAG_COMPAT_NEED_32BIT = 0x08;
1427
private static final String TAG = "AppUpdateManager";
1528
private static final AppUpdateManager INSTANCE = new AppUpdateManager();
1629
private static final String RELEASES_API_URL =
1730
"https://api.github.com/repos/Fox2Code/FoxMagiskModuleManager/releases";
31+
private static final String COMPAT_API_URL =
32+
"https://api.github.com/repos/Fox2Code/FoxMagiskModuleManager/releases";
1833

1934
public static AppUpdateManager getAppUpdateManager() {
2035
return INSTANCE;
2136
}
2237

38+
private final HashMap<String, Integer> compatDataId = new HashMap<>();
2339
private final Object updateLock = new Object();
40+
private final File compatFile;
2441
private String latestRelease;
2542
private String latestPreRelease;
2643
private long lastChecked;
2744
private boolean preReleaseNewer;
2845
private boolean lastCheckSuccess;
2946

3047
private AppUpdateManager() {
48+
this.compatFile = new File(MainApplication.getINSTANCE().getFilesDir(), "compat.txt");
3149
this.latestRelease = MainApplication.getBootSharedPreferences()
3250
.getString("updater_latest_release", BuildConfig.VERSION_NAME);
3351
this.latestPreRelease = MainApplication.getBootSharedPreferences()
3452
.getString("updater_latest_pre_release", BuildConfig.VERSION_NAME);
3553
this.lastChecked = 0;
3654
this.preReleaseNewer = true;
55+
if (this.compatFile.isFile()) {
56+
try {
57+
this.parseCompatibilityFlags(new FileInputStream(this.compatFile));
58+
} catch (IOException e) {
59+
e.printStackTrace();
60+
}
61+
}
3762
}
3863

3964
// Return true if should show a notification
@@ -95,6 +120,31 @@ public boolean checkUpdate(boolean force) {
95120
return this.peekShouldUpdate();
96121
}
97122

123+
public void checkUpdateCompat() {
124+
if (this.compatFile.exists()) {
125+
long lastUpdate = this.compatFile.lastModified();
126+
if (lastUpdate <= System.currentTimeMillis() &&
127+
lastUpdate + 600_000L > System.currentTimeMillis()) {
128+
return; // Skip update
129+
}
130+
}
131+
try {
132+
JSONObject object = new JSONObject(new String(Http.doHttpGet(
133+
COMPAT_API_URL, false), StandardCharsets.UTF_8));
134+
if (object.isNull("body")) {
135+
compatDataId.clear();
136+
Files.write(compatFile, new byte[0]);
137+
return;
138+
}
139+
byte[] rawData = object.getString("body")
140+
.getBytes(StandardCharsets.UTF_8);
141+
this.parseCompatibilityFlags(new ByteArrayInputStream(rawData));
142+
Files.write(compatFile, rawData);
143+
} catch (Exception e) {
144+
Log.e("AppUpdateManager", "Failed to update compat list", e);
145+
}
146+
}
147+
98148
public boolean peekShouldUpdate() {
99149
return !(BuildConfig.VERSION_NAME.equals(this.latestRelease) ||
100150
(this.preReleaseNewer &&
@@ -109,4 +159,46 @@ public boolean peekHasUpdate() {
109159
public boolean isLastCheckSuccess() {
110160
return lastCheckSuccess;
111161
}
162+
163+
private void parseCompatibilityFlags(InputStream inputStream) throws IOException {
164+
compatDataId.clear();
165+
BufferedReader bufferedReader = new BufferedReader(
166+
new InputStreamReader(inputStream, StandardCharsets.UTF_8));
167+
String line;
168+
while ((line = bufferedReader.readLine()) != null) {
169+
line = line.trim();
170+
if (line.isEmpty() || line.startsWith("#")) continue;
171+
int i = line.indexOf('/');
172+
if (i == -1) continue;
173+
int value = 0;
174+
for (String arg : line.substring(i + 1).split(",")) {
175+
switch (arg) {
176+
default:
177+
break;
178+
case "lowQuality":
179+
value |= FLAG_COMPAT_LOW_QUALITY;
180+
break;
181+
case "noExt":
182+
value |= FLAG_COMPAT_NO_EXT;
183+
break;
184+
case "magiskCmd":
185+
value |= FLAG_COMPAT_MAGISK_CMD;
186+
break;
187+
case "need32bit":
188+
value |= FLAG_COMPAT_NEED_32BIT;
189+
break;
190+
}
191+
}
192+
compatDataId.put(line.substring(0, i), value);
193+
}
194+
}
195+
196+
public int getCompatibilityFlags(String moduleId) {
197+
Integer compatFlags = compatDataId.get(moduleId);
198+
return compatFlags == null ? 0 : compatFlags;
199+
}
200+
201+
public static int getFlagsForModule(String moduleId) {
202+
return INSTANCE.getCompatibilityFlags(moduleId);
203+
}
112204
}

app/src/main/java/com/fox2code/mmm/MainActivity.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@ public void commonNext() {
179179
} else {
180180
if (AppUpdateManager.getAppUpdateManager().checkUpdate(true))
181181
moduleViewListBuilder.addNotification(NotificationType.UPDATE_AVAILABLE);
182+
if (AppUpdateManager.getAppUpdateManager().isLastCheckSuccess())
183+
AppUpdateManager.getAppUpdateManager().checkUpdateCompat();
182184
if (max != 0) {
183185
int current = 0;
184186
for (LocalModuleInfo localModuleInfo :

app/src/main/java/com/fox2code/mmm/ModuleViewListBuilder.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public void appendRemoteModules() {
7676
RepoManager repoManager = RepoManager.getINSTANCE();
7777
repoManager.runAfterUpdate(() -> {
7878
Log.i(TAG, "A2: " + repoManager.getModules().size());
79+
boolean no32bitSupport = Build.SUPPORTED_32_BIT_ABIS.length == 0;
7980
for (RepoModule repoModule : repoManager.getModules().values()) {
8081
if (!repoModule.repoData.isEnabled()) continue;
8182
ModuleInfo moduleInfo = repoModule.moduleInfo;
@@ -84,9 +85,11 @@ public void appendRemoteModules() {
8485
// Only check Magisk compatibility if root is present
8586
(InstallerInitializer.peekMagiskPath() != null &&
8687
repoModule.moduleInfo.minMagisk >
87-
InstallerInitializer.peekMagiskVersion()
88-
)))
89-
continue; // Skip adding incompatible modules
88+
InstallerInitializer.peekMagiskVersion())) ||
89+
// If 64bit only system, skip 32bit only modules
90+
(no32bitSupport && (AppUpdateManager.getFlagsForModule(repoModule.id)
91+
& AppUpdateManager.FLAG_COMPAT_NEED_32BIT) != 0)
92+
) continue; // Skip adding incompatible modules
9093
ModuleHolder moduleHolder = this.mappedModuleHolders.get(repoModule.id);
9194
if (moduleHolder == null) {
9295
this.mappedModuleHolders.put(repoModule.id,

app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,9 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
8181
setActionBarBackground(null);
8282
this.setDisplayHomeAsUpEnabled(true);
8383
if (title == null || title.isEmpty()) {
84-
this.setTitle(title);
85-
} else {
8684
this.setTitle("Androidacy");
85+
} else {
86+
this.setTitle(title);
8787
}
8888
if (allowInstall || title == null || title.isEmpty()) {
8989
this.hideActionBar();

app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import androidx.recyclerview.widget.RecyclerView;
1717

1818
import com.fox2code.mmm.ActionButtonType;
19+
import com.fox2code.mmm.AppUpdateManager;
1920
import com.fox2code.mmm.BuildConfig;
2021
import com.fox2code.mmm.Constants;
2122
import com.fox2code.mmm.MainApplication;
@@ -26,6 +27,7 @@
2627
import com.fox2code.mmm.utils.Hashes;
2728
import com.fox2code.mmm.utils.Http;
2829
import com.fox2code.mmm.utils.IntentHelper;
30+
import com.fox2code.mmm.utils.PropUtils;
2931
import com.google.android.material.progressindicator.LinearProgressIndicator;
3032
import com.topjohnwu.superuser.CallbackList;
3133
import com.topjohnwu.superuser.Shell;
@@ -261,23 +263,39 @@ private void doInstall(File file,boolean noExtensions,boolean rootless) {
261263
.to(installerController, installerMonitor);
262264
} else {
263265
String arch32 = "true"; // Do nothing by default
266+
boolean needs32bit = false;
267+
String moduleId = null;
268+
try (ZipFile zipFile = new ZipFile(file)) {
269+
if (zipFile.getEntry( // Check if module hard require 32bit support
270+
"common/addon/Volume-Key-Selector/tools/arm64/keycheck") == null &&
271+
zipFile.getEntry(
272+
"common/addon/Volume-Key-Selector/install.sh") != null) {
273+
needs32bit = true;
274+
}
275+
moduleId = PropUtils.readModuleId(zipFile
276+
.getInputStream(zipFile.getEntry("module.prop")));
277+
} catch (IOException ignored) {}
278+
int compatFlags = AppUpdateManager.getFlagsForModule(moduleId);
279+
if ((compatFlags & AppUpdateManager.FLAG_COMPAT_NEED_32BIT) != 0)
280+
needs32bit = true;
281+
if ((compatFlags & AppUpdateManager.FLAG_COMPAT_NO_EXT) != 0)
282+
noExtensions = true;
283+
if (moduleId != null && (moduleId.isEmpty() ||
284+
moduleId.contains("/") || moduleId.contains("\0") ||
285+
(moduleId.startsWith(".") && moduleId.endsWith(".")))) {
286+
this.setInstallStateFinished(false,
287+
"! This module contain a dangerous moduleId",
288+
null);
289+
return;
290+
}
264291
if (Build.SUPPORTED_32_BIT_ABIS.length == 0) {
265-
boolean needs32bit = false;
266-
try (ZipFile zipFile = new ZipFile(file)) {
267-
if (zipFile.getEntry( // Check if module hard require 32bit support
268-
"common/addon/Volume-Key-Selector/tools/arm64/keycheck") == null &&
269-
zipFile.getEntry(
270-
"common/addon/Volume-Key-Selector/install.sh") != null) {
271-
needs32bit = true;
272-
}
273-
} catch (IOException ignored) {}
274292
if (needs32bit) {
275293
this.setInstallStateFinished(false,
276294
"! This module can't be installed on a 64bit only system",
277295
null);
278296
return;
279297
}
280-
} else {
298+
} else if (needs32bit || (compatFlags & AppUpdateManager.FLAG_COMPAT_NO_EXT) == 0) {
281299
// Restore Magisk legacy stuff for retro compatibility
282300
if (Build.SUPPORTED_32_BIT_ABIS[0].contains("arm"))
283301
arch32 = "export ARCH32=arm";
@@ -288,21 +306,23 @@ private void doInstall(File file,boolean noExtensions,boolean rootless) {
288306
File installExecutable;
289307
if (InstallerInitializer.peekMagiskVersion() >=
290308
Constants.MAGISK_VER_CODE_INSTALL_COMMAND &&
291-
(noExtensions || MainApplication.isUsingMagiskCommand())) {
309+
((compatFlags & AppUpdateManager.FLAG_COMPAT_MAGISK_CMD) != 0 ||
310+
noExtensions || MainApplication.isUsingMagiskCommand())) {
292311
installCommand = "magisk --install-module \"" + file.getAbsolutePath() + "\"";
293312
installExecutable = new File(InstallerInitializer.peekMagiskPath()
294313
.equals("/sbin") ? "/sbin/magisk" : "/system/bin/magisk");
295314
} else {
296315
installExecutable = this.extractInstallScript("module_installer_compat.sh");
297316
if (installExecutable == null) {
298317
this.setInstallStateFinished(false,
299-
"! Failed to extract module install script", "");
318+
"! Failed to extract module install script", null);
300319
return;
301320
}
302321
installCommand = "sh \"" + installExecutable.getAbsolutePath() + "\"" +
303322
" /dev/null 1 \"" + file.getAbsolutePath() + "\"";
304323
}
305324
installerMonitor = new InstallerMonitor(installExecutable);
325+
if (moduleId != null) installerMonitor.setForCleanUp(moduleId);
306326
if (noExtensions) {
307327
installJob = Shell.su(arch32, // No Extensions
308328
"cd \"" + this.moduleCache.getAbsolutePath() + "\"",
@@ -462,6 +482,7 @@ public static class InstallerMonitor extends CallbackList<String> {
462482
private static final String DEFAULT_ERR = "! Install failed";
463483
private final String installScriptErr;
464484
public String lastCommand = "";
485+
public String forCleanUp;
465486

466487
public InstallerMonitor(File installScript) {
467488
super(Runnable::run);
@@ -476,6 +497,10 @@ public void onAddElement(String s) {
476497
this.lastCommand = s;
477498
}
478499

500+
public void setForCleanUp(String forCleanUp) {
501+
this.forCleanUp = forCleanUp;
502+
}
503+
479504
private String doCleanUp() {
480505
String installScriptErr = this.installScriptErr;
481506
// This block is mainly to help fixing customize.sh syntax errors
@@ -490,6 +515,10 @@ private String doCleanUp() {
490515
Log.e(TAG, "Failed to delete failed update");
491516
return "Error: " + installScriptErr.substring(i + 1);
492517
}
518+
} else if (this.forCleanUp != null) {
519+
SuFile moduleUpdate = new SuFile("/data/adb/modules_update/" + this.forCleanUp);
520+
if (moduleUpdate.exists() && !moduleUpdate.deleteRecursive())
521+
Log.e(TAG, "Failed to delete failed update");
493522
}
494523
return DEFAULT_ERR;
495524
}

app/src/main/java/com/fox2code/mmm/utils/PropUtils.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
package com.fox2code.mmm.utils;
22

3+
import static com.fox2code.mmm.AppUpdateManager.FLAG_COMPAT_LOW_QUALITY;
4+
import static com.fox2code.mmm.AppUpdateManager.getFlagsForModule;
5+
36
import android.os.Build;
47
import android.text.TextUtils;
8+
import android.util.Log;
59

610
import com.fox2code.mmm.manager.ModuleInfo;
711
import com.topjohnwu.superuser.io.SuFileInputStream;
@@ -274,6 +278,22 @@ else if (moduleInfo.id.startsWith("riru_")
274278
}
275279
}
276280

281+
public static String readModuleId(InputStream inputStream) {
282+
String moduleId = null;
283+
try (BufferedReader bufferedReader = new BufferedReader(
284+
new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
285+
String line;
286+
while ((line = bufferedReader.readLine()) != null) {
287+
if (line.startsWith("id=")) {
288+
moduleId = line.substring(3).trim();
289+
}
290+
}
291+
} catch (IOException e) {
292+
Log.d("PropUtils", "Failed to get moduleId", e);
293+
}
294+
return moduleId;
295+
}
296+
277297
public static void applyFallbacks(ModuleInfo moduleInfo) {
278298
if (moduleInfo.support == null || moduleInfo.support.isEmpty()) {
279299
moduleInfo.support = moduleSupportsFallbacks.get(moduleInfo.id);
@@ -299,7 +319,8 @@ public static boolean isLowQualityModule(ModuleInfo moduleInfo) {
299319
|| moduleInfo.author == null || !TextUtils.isGraphic(moduleInfo.author)
300320
|| (description = moduleInfo.description) == null || !TextUtils.isGraphic(description)
301321
|| description.toLowerCase(Locale.ROOT).equals(moduleInfo.name.toLowerCase(Locale.ROOT))
302-
|| description.length() < Math.min(Math.max(moduleInfo.name.length() + 4, 16), 24);
322+
|| description.length() < Math.min(Math.max(moduleInfo.name.length() + 4, 16), 24)
323+
|| (getFlagsForModule(moduleInfo.id) & FLAG_COMPAT_LOW_QUALITY) != 0;
303324
}
304325

305326
private static boolean isInvalidValue(String name) {

0 commit comments

Comments
 (0)