You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
FoxMagiskModuleManager/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java

224 lines
8.9 KiB
Java

package com.fox2code.mmm.manager;
import android.content.SharedPreferences;
import com.fox2code.mmm.MainApplication;
import com.fox2code.mmm.utils.PropUtils;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.io.SuFile;
import java.util.HashMap;
import java.util.Iterator;
public final class ModuleManager {
private static final int FLAG_MM_INVALID = ModuleInfo.FLAG_METADATA_INVALID;
private static final int FLAG_MM_UNPROCESSED = 0x40000000;
private static final int FLAGS_RESET_INIT = FLAG_MM_INVALID |
ModuleInfo.FLAG_MODULE_DISABLED | ModuleInfo.FLAG_MODULE_UPDATING |
ModuleInfo.FLAG_MODULE_UNINSTALLING | ModuleInfo.FLAG_MODULE_ACTIVE;
private static final int FLAGS_RESET_UPDATE = FLAG_MM_INVALID | FLAG_MM_UNPROCESSED;
private final HashMap<String, ModuleInfo> moduleInfos;
private final HashMap<String, ModuleInfo> invalidModules;
private final SharedPreferences bootPrefs;
private final Object scanLock = new Object();
private boolean scanning, lastScanResult;
private static final ModuleManager INSTANCE = new ModuleManager();
public static ModuleManager getINSTANCE() {
return INSTANCE;
}
private ModuleManager() {
this.moduleInfos = new HashMap<>();
this.invalidModules = new HashMap<>();
this.bootPrefs = MainApplication.getBootSharedPreferences();
}
// MultiThread friendly method
public final boolean scan() {
if (!this.scanning) {
// Do scan
synchronized (scanLock) {
this.scanning = true;
try {
this.lastScanResult =
this.scanInternal();
} finally {
this.scanning = false;
}
}
} else {
// Wait for current scan
synchronized (scanLock) {}
}
return this.lastScanResult;
}
// Pause execution until the scan is completed if one is currently running
public final void afterScan() {
if (this.scanning) synchronized (this.scanLock) {}
}
public final void runAfterScan(Runnable runnable) {
synchronized (this.scanLock) {
runnable.run();
}
}
private boolean scanInternal() {
boolean firstScan = this.bootPrefs.getBoolean("mm_first_scan", true);
boolean changed = false;
SharedPreferences.Editor editor = firstScan ? this.bootPrefs.edit() : null;
// Reset existing ModuleInfo
this.moduleInfos.putAll(this.invalidModules);
this.invalidModules.clear();
for (ModuleInfo v : this.moduleInfos.values()) {
v.flags |= FLAG_MM_UNPROCESSED;
v.flags &= ~FLAGS_RESET_INIT;
v.name = v.id;
v.version = null;
v.versionCode = 0;
v.author = null;
v.description = "No description found.";
v.support = null;
v.config = null;
}
String[] modules = new SuFile("/data/adb/modules").list();
if (modules != null) {
for (String module : modules) {
ModuleInfo moduleInfo = moduleInfos.get(module);
if (moduleInfo == null) {
moduleInfo = new ModuleInfo(module);
moduleInfos.put(module, moduleInfo);
changed = true;
// Shis should not really happen, but let's handles theses cases anyway
moduleInfo.flags |= ModuleInfo.FLAG_MODULE_UPDATING_ONLY;
}
moduleInfo.flags &= ~FLAGS_RESET_UPDATE;
boolean disabled = new SuFile(
"/data/adb/modules/" + module + "/disable").exists();
if (disabled) {
moduleInfo.flags |= ModuleInfo.FLAG_MODULE_DISABLED;
} else {
if (firstScan) {
moduleInfo.flags |= ModuleInfo.FLAG_MODULE_ACTIVE;
editor.putBoolean("module_" + moduleInfo.id + "_active", true);
} else if (bootPrefs.getBoolean("module_" + moduleInfo.id + "_active", false)) {
moduleInfo.flags |= ModuleInfo.FLAG_MODULE_ACTIVE;
}
}
boolean uninstalling = new SuFile(
"/data/adb/modules/" + module + "/remove").exists();
if (uninstalling) {
moduleInfo.flags |= ModuleInfo.FLAG_MODULE_UNINSTALLING;
}
try {
PropUtils.readProperties(moduleInfo,
"/data/adb/modules/" + module + "/module.prop");
} catch (Exception e) {
moduleInfo.flags |= FLAG_MM_INVALID;
}
}
}
String[] modules_update = new SuFile("/data/adb/modules_update").list();
if (modules_update != null) {
for (String module : modules_update) {
ModuleInfo moduleInfo = moduleInfos.get(module);
if (moduleInfo == null) {
moduleInfo = new ModuleInfo(module);
moduleInfos.put(module, moduleInfo);
changed = true;
}
moduleInfo.flags &= ~FLAGS_RESET_UPDATE;
moduleInfo.flags |= ModuleInfo.FLAG_MODULE_UPDATING;
try {
PropUtils.readProperties(moduleInfo,
"/data/adb/modules_update/" + module + "/module.prop");
} catch (Exception e) {
moduleInfo.flags |= FLAG_MM_INVALID;
}
}
}
Iterator<ModuleInfo> moduleInfoIterator =
this.moduleInfos.values().iterator();
while (moduleInfoIterator.hasNext()) {
ModuleInfo moduleInfo = moduleInfoIterator.next();
if ((moduleInfo.flags & FLAG_MM_UNPROCESSED) != 0) {
moduleInfoIterator.remove();
continue; // Don't process fallbacks if unreferenced
} else if ((moduleInfo.flags & FLAG_MM_INVALID) != 0) {
moduleInfo.flags &=~ FLAG_MM_INVALID;
this.invalidModules.put(moduleInfo.id, moduleInfo);
moduleInfoIterator.remove();
}
if (moduleInfo.name == null || (moduleInfo.name.equals(moduleInfo.id))) {
moduleInfo.name = Character.toUpperCase(moduleInfo.id.charAt(0)) +
moduleInfo.id.substring(1).replace('_', ' ');
}
if (moduleInfo.version == null) {
moduleInfo.version = "v" + moduleInfo.versionCode;
}
}
if (firstScan) {
editor.putBoolean("mm_first_scan", false);
editor.apply();
}
return changed;
}
public HashMap<String, ModuleInfo> getModules() {
this.afterScan();
return this.moduleInfos;
}
public HashMap<String, ModuleInfo> getInvalidModules() {
this.afterScan();
return invalidModules;
}
public boolean setEnabledState(ModuleInfo moduleInfo, boolean checked) {
if (moduleInfo.hasFlag(ModuleInfo.FLAG_MODULE_UPDATING) && !checked) return false;
SuFile disable = new SuFile("/data/adb/modules/" + moduleInfo.id + "/disable");
if (checked) {
if (disable.exists() && !disable.delete()) {
moduleInfo.flags |= ModuleInfo.FLAG_MODULE_DISABLED;
return false;
}
moduleInfo.flags &= ~ModuleInfo.FLAG_MODULE_DISABLED;
} else {
if (!disable.exists() && !disable.createNewFile()) {
return false;
}
moduleInfo.flags |= ModuleInfo.FLAG_MODULE_DISABLED;
}
return true;
}
public boolean setUninstallState(ModuleInfo moduleInfo, boolean checked) {
if (checked && moduleInfo.hasFlag(ModuleInfo.FLAG_MODULE_UPDATING)) return false;
SuFile disable = new SuFile("/data/adb/modules/" + moduleInfo.id + "/remove");
if (checked) {
if (!disable.exists() && !disable.createNewFile()) {
return false;
}
moduleInfo.flags |= ModuleInfo.FLAG_MODULE_UNINSTALLING;
} else {
if (disable.exists() && !disable.delete()) {
moduleInfo.flags |= ModuleInfo.FLAG_MODULE_UNINSTALLING;
return false;
}
moduleInfo.flags &= ~ModuleInfo.FLAG_MODULE_UNINSTALLING;
}
return true;
}
public boolean masterClear(ModuleInfo moduleInfo) {
if (moduleInfo.hasFlag(ModuleInfo.FLAG_MODULE_ACTIVE)) return false;
Shell.su("rm -rf /data/adb/modules/" + moduleInfo.id + "/").exec();
Shell.su("rm -rf /data/adb/modules_update/" + moduleInfo.id + "/").exec();
moduleInfo.flags = ModuleInfo.FLAG_METADATA_INVALID;
return true;
}
}