Fix and Refactor both RepoManager and BackgroundUpdateChecker

pull/192/head
Fox2Code 2 years ago
parent 23c153a6ed
commit 248865d617

@ -37,7 +37,7 @@ android {
buildConfigField(
"java.util.List<String>",
"ENABLED_REPOS",
"java.util.Arrays.asList(\"magisk_alt_repo\", \"dg_magisk_repo\", \"androidacy_repo\")",
"java.util.Arrays.asList(\"magisk_alt_repo\", \"androidacy_repo\")",
)
}

@ -18,6 +18,8 @@
<!-- Supposed to fix bugs with old firmware, only requested on pre Marshmallow -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="22" />
<!-- Post background notifications -->
<uses-permission-sdk-23 android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:name=".MainApplication"

@ -16,6 +16,11 @@ import com.fox2code.mmm.repo.RepoManager;
*/
@Keep
public class XHooks {
@Keep
public static void onRepoManagerInitialize() {
// Call addXRepo here if you are an XPosed module
}
@Keep
public static void onRepoManagerInitialized() {}

@ -12,8 +12,9 @@ public class BackgroundBootListener extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (!BOOT_COMPLETED.equals(intent.getAction())) return;
if (!MainApplication.isBackgroundUpdateCheckEnabled()) return;
BackgroundUpdateChecker.onMainActivityCreate(context);
BackgroundUpdateChecker.doCheck(context);
synchronized (BackgroundUpdateChecker.lock) {
BackgroundUpdateChecker.onMainActivityCreate(context);
BackgroundUpdateChecker.doCheck(context);
}
}
}

@ -24,12 +24,15 @@ import com.fox2code.mmm.manager.LocalModuleInfo;
import com.fox2code.mmm.manager.ModuleManager;
import com.fox2code.mmm.repo.RepoManager;
import com.fox2code.mmm.repo.RepoModule;
import com.fox2code.mmm.utils.PropUtils;
import java.util.HashMap;
import java.util.Random;
import java.util.concurrent.TimeUnit;
public class BackgroundUpdateChecker extends Worker {
private static boolean easterEggActive = false;
static final Object lock = new Object(); // Avoid concurrency issues
public static final String NOTIFICATION_CHANNEL_ID = "background_update";
public static final int NOTIFICATION_ID = 1;
@ -43,9 +46,9 @@ public class BackgroundUpdateChecker extends Worker {
public Result doWork() {
if (!NotificationManagerCompat.from(this.getApplicationContext()).areNotificationsEnabled()
|| !MainApplication.isBackgroundUpdateCheckEnabled()) return Result.success();
doCheck(this.getApplicationContext());
synchronized (lock) {
doCheck(this.getApplicationContext());
}
return Result.success();
}
@ -53,18 +56,20 @@ public class BackgroundUpdateChecker extends Worker {
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
RepoManager.getINSTANCE().update(null);
ModuleManager.getINSTANCE().scan();
ModuleManager.getINSTANCE().scan();
int moduleUpdateCount = 0;
HashMap<String, RepoModule> repoModules =
RepoManager.getINSTANCE().getModules();
for (LocalModuleInfo localModuleInfo :
ModuleManager.getINSTANCE().getModules().values()) {
if ("twrp-keep".equals(localModuleInfo.id)) continue;
RepoModule repoModule = RepoManager.getINSTANCE()
.getModules().get(localModuleInfo.id);
RepoModule repoModule = repoModules.get(localModuleInfo.id);
localModuleInfo.checkModuleUpdate();
if (localModuleInfo.updateVersionCode > localModuleInfo.versionCode) {
if (localModuleInfo.updateVersionCode > localModuleInfo.versionCode &&
!PropUtils.isNullString(localModuleInfo.updateVersion)) {
moduleUpdateCount++;
} else if (repoModule != null &&
repoModule.moduleInfo.versionCode > localModuleInfo.versionCode) {
repoModule.moduleInfo.versionCode > localModuleInfo.versionCode &&
!PropUtils.isNullString(repoModule.moduleInfo.version)) {
moduleUpdateCount++;
}
}

@ -3,9 +3,13 @@ package com.fox2code.mmm.manager;
import android.content.SharedPreferences;
import android.util.Log;
import androidx.annotation.NonNull;
import com.fox2code.mmm.MainApplication;
import com.fox2code.mmm.installer.InstallerInitializer;
import com.fox2code.mmm.utils.Http;
import com.fox2code.mmm.utils.PropUtils;
import com.fox2code.mmm.utils.SyncManager;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.io.SuFile;
import com.topjohnwu.superuser.io.SuFileInputStream;
@ -17,7 +21,7 @@ import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Iterator;
public final class ModuleManager {
public final class ModuleManager extends SyncManager {
private static final String TAG = "ModuleManager";
private static final int FLAG_MM_INVALID = ModuleInfo.FLAG_METADATA_INVALID;
@ -28,9 +32,7 @@ public final class ModuleManager {
private static final int FLAGS_RESET_UPDATE = FLAG_MM_INVALID | FLAG_MM_UNPROCESSED;
private final HashMap<String, LocalModuleInfo> moduleInfos;
private final SharedPreferences bootPrefs;
private final Object scanLock = new Object();
private int updatableModuleCount = 0;
private boolean scanning;
private static final ModuleManager INSTANCE = new ModuleManager();
@ -43,36 +45,7 @@ public final class ModuleManager {
this.bootPrefs = MainApplication.getBootSharedPreferences();
}
// MultiThread friendly method
public void scan() {
if (!this.scanning) {
// Do scan
synchronized (scanLock) {
this.scanning = true;
try {
this.scanInternal();
} finally {
this.scanning = false;
}
}
} else {
// Wait for current scan
synchronized (scanLock) {}
}
}
// Pause execution until the scan is completed if one is currently running
public void afterScan() {
if (this.scanning) synchronized (this.scanLock) {}
}
public void runAfterScan(Runnable runnable) {
synchronized (this.scanLock) {
runnable.run();
}
}
private void scanInternal() {
protected void scanInternal(@NonNull UpdateListener updateListener) {
boolean firstBoot = MainApplication.isFirstBoot();
boolean firstScan = this.bootPrefs.getBoolean("mm_first_scan", true);
SharedPreferences.Editor editor = firstScan ? this.bootPrefs.edit() : null;

@ -4,6 +4,8 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import androidx.annotation.NonNull;
import com.fox2code.mmm.MainApplication;
import com.fox2code.mmm.XHooks;
import com.fox2code.mmm.androidacy.AndroidacyRepoData;
@ -12,14 +14,16 @@ import com.fox2code.mmm.utils.Files;
import com.fox2code.mmm.utils.Hashes;
import com.fox2code.mmm.utils.Http;
import com.fox2code.mmm.utils.PropUtils;
import com.fox2code.mmm.utils.SyncManager;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
public final class RepoManager {
public final class RepoManager extends SyncManager {
private static final String TAG = "RepoManager";
private static final String MAGISK_REPO_MANAGER =
@ -75,6 +79,7 @@ public final class RepoManager {
private boolean initialized;
private RepoManager(MainApplication mainApplication) {
INSTANCE = this; // Set early fox XHooks
this.initialized = false;
this.mainApplication = mainApplication;
this.repoData = new LinkedHashMap<>();
@ -90,6 +95,7 @@ public final class RepoManager {
dgRepo.defaultWebsite = "https://dergoogler.com/repo";
this.androidacyRepoData = this.addAndroidacyRepoData();
this.customRepoManager = new CustomRepoManager(mainApplication, this);
XHooks.onRepoManagerInitialize();
// Populate default cache
boolean x = false;
for (RepoData repoData:this.repoData.values()) {
@ -136,7 +142,7 @@ public final class RepoManager {
if (DG_MAGISK_REPO.equals(url))
url = DG_MAGISK_REPO_GITHUB;
RepoData repoData;
synchronized (this.repoUpdateLock) {
synchronized (this.syncLock) {
repoData = this.repoData.get(url);
if (repoData == null) {
if (ANDROIDACY_TEST_MAGISK_REPO_ENDPOINT.equals(url) ||
@ -152,57 +158,18 @@ public final class RepoManager {
return repoData;
}
public interface UpdateListener {
void update(double value);
}
private final Object repoUpdateLock = new Object();
private boolean repoUpdating;
private boolean repoLastResult = true;
public boolean isRepoUpdating() {
return this.repoUpdating;
}
public void afterUpdate() {
if (this.repoUpdating) synchronized (this.repoUpdateLock) {}
}
public void runAfterUpdate(Runnable runnable) {
synchronized (this.repoUpdateLock) {
runnable.run();
}
}
// MultiThread friendly method
public void update(UpdateListener updateListener) {
if (updateListener == null)
updateListener = value -> {};
if (!this.repoUpdating) {
// Do scan
synchronized (this.repoUpdateLock) {
this.repoUpdating = true;
try {
this.repoLastResult =
this.scanInternal(updateListener);
} finally {
this.repoUpdating = false;
}
}
} else {
// Wait for current scan
synchronized (this.repoUpdateLock) {}
}
}
private static final double STEP1 = 0.1D;
private static final double STEP2 = 0.8D;
private static final double STEP3 = 0.1D;
private boolean scanInternal(UpdateListener updateListener) {
protected void scanInternal(@NonNull UpdateListener updateListener) {
this.modules.clear();
updateListener.update(0D);
RepoData[] repoDatas = this.repoData.values().toArray(new RepoData[0]);
// Using LinkedHashSet to deduplicate Androidacy entry.
RepoData[] repoDatas = new LinkedHashSet<>(
this.repoData.values()).toArray(new RepoData[0]);
RepoUpdater[] repoUpdaters = new RepoUpdater[repoDatas.length];
int moduleToUpdate = 0;
for (int i = 0; i < repoDatas.length; i++) {
@ -215,7 +182,7 @@ public final class RepoManager {
for (int i = 0; i < repoUpdaters.length; i++) {
List<RepoModule> repoModules = repoUpdaters[i].toUpdate();
RepoData repoData = repoDatas[i];
Log.d(TAG, "Registering " + repoData.name);
Log.d(TAG, "Registering " + repoData.getName());
for (RepoModule repoModule:repoModules) {
try {
if (repoModule.propUrl != null &&
@ -261,7 +228,7 @@ public final class RepoManager {
}
Log.i(TAG, "Got " + this.modules.size() + " modules!");
updateListener.update(1D);
return hasInternet;
this.repoLastResult = hasInternet;
}
public void updateEnabledStates() {

@ -48,7 +48,7 @@ public class RepoUpdater {
// Return repo to update
return this.toUpdate.size();
} catch (Exception e) {
Log.e(TAG, "Failed to get manifest", e);
Log.e(TAG, "Failed to get manifest of " + this.repoData.id, e);
this.indexRaw = null;
this.toUpdate = Collections.emptyList();
this.toApply = Collections.emptySet();

@ -380,10 +380,13 @@ public class PropUtils {
moduleId.substring(1).replace('_', ' ');
}
public static boolean isNullString(String string) {
return string == null || string.isEmpty() || "null".equals(string);
}
// Make versionName no longer than 16 charters to avoid UI overflow.
public static String shortenVersionName(String versionName, long versionCode) {
if (versionName == null || versionName.isEmpty() ||
"null".equals(versionName)) return "v" + versionCode;
if (isNullString(versionName)) return "v" + versionCode;
if (versionName.length() <= 16) return versionName;
int i = versionName.lastIndexOf('.');
if (i != -1 && i <= 16 && versionName.indexOf('.') != i

@ -0,0 +1,73 @@
package com.fox2code.mmm.utils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* Manager that want both to be thread safe and not to worry about thread safety
* {@link #scan()} and {@link #update(UpdateListener)} can be called from multiple
* thread at the same time, {@link #scanInternal(UpdateListener)} will only be
* called from one thread at a time only.
*/
public abstract class SyncManager {
private static final UpdateListener NO_OP = value -> {};
protected final Object syncLock = new Object();
private boolean syncing;
public final void scan() {
this.update(null);
}
// MultiThread friendly method
public final void update(@Nullable UpdateListener updateListener) {
if (updateListener == null) updateListener = NO_OP;
if (!this.syncing) {
// Do scan
synchronized (this.syncLock) {
this.syncing = true;
try {
this.scanInternal(updateListener);
} finally {
this.syncing = false;
}
}
} else {
// Wait for current scan
synchronized (this.syncLock) {
Thread.yield();
}
}
}
// Pause execution until the scan is completed if one is currently running
public final void afterScan() {
if (this.syncing) synchronized (this.syncLock) { Thread.yield(); }
}
public final void runAfterScan(Runnable runnable) {
synchronized (this.syncLock) {
runnable.run();
}
}
public final boolean isRepoUpdating() {
return this.syncing;
}
public final void afterUpdate() {
if (this.syncing) synchronized (this.syncLock) { Thread.yield(); }
}
public final void runAfterUpdate(Runnable runnable) {
synchronized (this.syncLock) {
runnable.run();
}
}
// This method can't be called twice at the same time.
protected abstract void scanInternal(@NonNull UpdateListener updateListener);
public interface UpdateListener {
void update(double value);
}
}
Loading…
Cancel
Save