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

Commit 248865d

Browse files
committed
Fix and Refactor both RepoManager and BackgroundUpdateChecker
1 parent 23c153a commit 248865d

File tree

10 files changed

+124
-95
lines changed

10 files changed

+124
-95
lines changed

app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ android {
3737
buildConfigField(
3838
"java.util.List<String>",
3939
"ENABLED_REPOS",
40-
"java.util.Arrays.asList(\"magisk_alt_repo\", \"dg_magisk_repo\", \"androidacy_repo\")",
40+
"java.util.Arrays.asList(\"magisk_alt_repo\", \"androidacy_repo\")",
4141
)
4242
}
4343

app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
<!-- Supposed to fix bugs with old firmware, only requested on pre Marshmallow -->
1919
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
2020
android:maxSdkVersion="22" />
21+
<!-- Post background notifications -->
22+
<uses-permission-sdk-23 android:name="android.permission.POST_NOTIFICATIONS" />
2123

2224
<application
2325
android:name=".MainApplication"

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616
*/
1717
@Keep
1818
public class XHooks {
19+
@Keep
20+
public static void onRepoManagerInitialize() {
21+
// Call addXRepo here if you are an XPosed module
22+
}
23+
1924
@Keep
2025
public static void onRepoManagerInitialized() {}
2126

app/src/main/java/com/fox2code/mmm/background/BackgroundBootListener.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ public class BackgroundBootListener extends BroadcastReceiver {
1212
@Override
1313
public void onReceive(Context context, Intent intent) {
1414
if (!BOOT_COMPLETED.equals(intent.getAction())) return;
15-
if (!MainApplication.isBackgroundUpdateCheckEnabled()) return;
16-
BackgroundUpdateChecker.onMainActivityCreate(context);
17-
BackgroundUpdateChecker.doCheck(context);
15+
synchronized (BackgroundUpdateChecker.lock) {
16+
BackgroundUpdateChecker.onMainActivityCreate(context);
17+
BackgroundUpdateChecker.doCheck(context);
18+
}
1819
}
1920
}

app/src/main/java/com/fox2code/mmm/background/BackgroundUpdateChecker.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,15 @@
2424
import com.fox2code.mmm.manager.ModuleManager;
2525
import com.fox2code.mmm.repo.RepoManager;
2626
import com.fox2code.mmm.repo.RepoModule;
27+
import com.fox2code.mmm.utils.PropUtils;
2728

29+
import java.util.HashMap;
2830
import java.util.Random;
2931
import java.util.concurrent.TimeUnit;
3032

3133
public class BackgroundUpdateChecker extends Worker {
3234
private static boolean easterEggActive = false;
35+
static final Object lock = new Object(); // Avoid concurrency issues
3336
public static final String NOTIFICATION_CHANNEL_ID = "background_update";
3437
public static final int NOTIFICATION_ID = 1;
3538

@@ -43,28 +46,30 @@ public BackgroundUpdateChecker(@NonNull Context context,
4346
public Result doWork() {
4447
if (!NotificationManagerCompat.from(this.getApplicationContext()).areNotificationsEnabled()
4548
|| !MainApplication.isBackgroundUpdateCheckEnabled()) return Result.success();
46-
47-
doCheck(this.getApplicationContext());
48-
49+
synchronized (lock) {
50+
doCheck(this.getApplicationContext());
51+
}
4952
return Result.success();
5053
}
5154

5255
static void doCheck(Context context) {
5356
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
5457
RepoManager.getINSTANCE().update(null);
5558
ModuleManager.getINSTANCE().scan();
56-
ModuleManager.getINSTANCE().scan();
5759
int moduleUpdateCount = 0;
60+
HashMap<String, RepoModule> repoModules =
61+
RepoManager.getINSTANCE().getModules();
5862
for (LocalModuleInfo localModuleInfo :
5963
ModuleManager.getINSTANCE().getModules().values()) {
6064
if ("twrp-keep".equals(localModuleInfo.id)) continue;
61-
RepoModule repoModule = RepoManager.getINSTANCE()
62-
.getModules().get(localModuleInfo.id);
65+
RepoModule repoModule = repoModules.get(localModuleInfo.id);
6366
localModuleInfo.checkModuleUpdate();
64-
if (localModuleInfo.updateVersionCode > localModuleInfo.versionCode) {
67+
if (localModuleInfo.updateVersionCode > localModuleInfo.versionCode &&
68+
!PropUtils.isNullString(localModuleInfo.updateVersion)) {
6569
moduleUpdateCount++;
6670
} else if (repoModule != null &&
67-
repoModule.moduleInfo.versionCode > localModuleInfo.versionCode) {
71+
repoModule.moduleInfo.versionCode > localModuleInfo.versionCode &&
72+
!PropUtils.isNullString(repoModule.moduleInfo.version)) {
6873
moduleUpdateCount++;
6974
}
7075
}

app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java

Lines changed: 6 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@
33
import android.content.SharedPreferences;
44
import android.util.Log;
55

6+
import androidx.annotation.NonNull;
7+
68
import com.fox2code.mmm.MainApplication;
79
import com.fox2code.mmm.installer.InstallerInitializer;
10+
import com.fox2code.mmm.utils.Http;
811
import com.fox2code.mmm.utils.PropUtils;
12+
import com.fox2code.mmm.utils.SyncManager;
913
import com.topjohnwu.superuser.Shell;
1014
import com.topjohnwu.superuser.io.SuFile;
1115
import com.topjohnwu.superuser.io.SuFileInputStream;
@@ -17,7 +21,7 @@
1721
import java.util.HashMap;
1822
import java.util.Iterator;
1923

20-
public final class ModuleManager {
24+
public final class ModuleManager extends SyncManager {
2125
private static final String TAG = "ModuleManager";
2226

2327
private static final int FLAG_MM_INVALID = ModuleInfo.FLAG_METADATA_INVALID;
@@ -28,9 +32,7 @@ public final class ModuleManager {
2832
private static final int FLAGS_RESET_UPDATE = FLAG_MM_INVALID | FLAG_MM_UNPROCESSED;
2933
private final HashMap<String, LocalModuleInfo> moduleInfos;
3034
private final SharedPreferences bootPrefs;
31-
private final Object scanLock = new Object();
3235
private int updatableModuleCount = 0;
33-
private boolean scanning;
3436

3537
private static final ModuleManager INSTANCE = new ModuleManager();
3638

@@ -43,36 +45,7 @@ private ModuleManager() {
4345
this.bootPrefs = MainApplication.getBootSharedPreferences();
4446
}
4547

46-
// MultiThread friendly method
47-
public void scan() {
48-
if (!this.scanning) {
49-
// Do scan
50-
synchronized (scanLock) {
51-
this.scanning = true;
52-
try {
53-
this.scanInternal();
54-
} finally {
55-
this.scanning = false;
56-
}
57-
}
58-
} else {
59-
// Wait for current scan
60-
synchronized (scanLock) {}
61-
}
62-
}
63-
64-
// Pause execution until the scan is completed if one is currently running
65-
public void afterScan() {
66-
if (this.scanning) synchronized (this.scanLock) {}
67-
}
68-
69-
public void runAfterScan(Runnable runnable) {
70-
synchronized (this.scanLock) {
71-
runnable.run();
72-
}
73-
}
74-
75-
private void scanInternal() {
48+
protected void scanInternal(@NonNull UpdateListener updateListener) {
7649
boolean firstBoot = MainApplication.isFirstBoot();
7750
boolean firstScan = this.bootPrefs.getBoolean("mm_first_scan", true);
7851
SharedPreferences.Editor editor = firstScan ? this.bootPrefs.edit() : null;

app/src/main/java/com/fox2code/mmm/repo/RepoManager.java

Lines changed: 14 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import android.content.SharedPreferences;
55
import android.util.Log;
66

7+
import androidx.annotation.NonNull;
8+
79
import com.fox2code.mmm.MainApplication;
810
import com.fox2code.mmm.XHooks;
911
import com.fox2code.mmm.androidacy.AndroidacyRepoData;
@@ -12,14 +14,16 @@
1214
import com.fox2code.mmm.utils.Hashes;
1315
import com.fox2code.mmm.utils.Http;
1416
import com.fox2code.mmm.utils.PropUtils;
17+
import com.fox2code.mmm.utils.SyncManager;
1518

1619
import java.io.File;
1720
import java.nio.charset.StandardCharsets;
1821
import java.util.HashMap;
1922
import java.util.LinkedHashMap;
23+
import java.util.LinkedHashSet;
2024
import java.util.List;
2125

22-
public final class RepoManager {
26+
public final class RepoManager extends SyncManager {
2327
private static final String TAG = "RepoManager";
2428

2529
private static final String MAGISK_REPO_MANAGER =
@@ -75,6 +79,7 @@ public static RepoManager getINSTANCE() {
7579
private boolean initialized;
7680

7781
private RepoManager(MainApplication mainApplication) {
82+
INSTANCE = this; // Set early fox XHooks
7883
this.initialized = false;
7984
this.mainApplication = mainApplication;
8085
this.repoData = new LinkedHashMap<>();
@@ -90,6 +95,7 @@ private RepoManager(MainApplication mainApplication) {
9095
dgRepo.defaultWebsite = "https://dergoogler.com/repo";
9196
this.androidacyRepoData = this.addAndroidacyRepoData();
9297
this.customRepoManager = new CustomRepoManager(mainApplication, this);
98+
XHooks.onRepoManagerInitialize();
9399
// Populate default cache
94100
boolean x = false;
95101
for (RepoData repoData:this.repoData.values()) {
@@ -136,7 +142,7 @@ public RepoData addOrGet(String url, String fallBackName) {
136142
if (DG_MAGISK_REPO.equals(url))
137143
url = DG_MAGISK_REPO_GITHUB;
138144
RepoData repoData;
139-
synchronized (this.repoUpdateLock) {
145+
synchronized (this.syncLock) {
140146
repoData = this.repoData.get(url);
141147
if (repoData == null) {
142148
if (ANDROIDACY_TEST_MAGISK_REPO_ENDPOINT.equals(url) ||
@@ -152,57 +158,18 @@ public RepoData addOrGet(String url, String fallBackName) {
152158
return repoData;
153159
}
154160

155-
public interface UpdateListener {
156-
void update(double value);
157-
}
158-
159-
private final Object repoUpdateLock = new Object();
160-
private boolean repoUpdating;
161161
private boolean repoLastResult = true;
162162

163-
public boolean isRepoUpdating() {
164-
return this.repoUpdating;
165-
}
166-
167-
public void afterUpdate() {
168-
if (this.repoUpdating) synchronized (this.repoUpdateLock) {}
169-
}
170-
171-
public void runAfterUpdate(Runnable runnable) {
172-
synchronized (this.repoUpdateLock) {
173-
runnable.run();
174-
}
175-
}
176-
177-
// MultiThread friendly method
178-
public void update(UpdateListener updateListener) {
179-
if (updateListener == null)
180-
updateListener = value -> {};
181-
if (!this.repoUpdating) {
182-
// Do scan
183-
synchronized (this.repoUpdateLock) {
184-
this.repoUpdating = true;
185-
try {
186-
this.repoLastResult =
187-
this.scanInternal(updateListener);
188-
} finally {
189-
this.repoUpdating = false;
190-
}
191-
}
192-
} else {
193-
// Wait for current scan
194-
synchronized (this.repoUpdateLock) {}
195-
}
196-
}
197-
198163
private static final double STEP1 = 0.1D;
199164
private static final double STEP2 = 0.8D;
200165
private static final double STEP3 = 0.1D;
201166

202-
private boolean scanInternal(UpdateListener updateListener) {
167+
protected void scanInternal(@NonNull UpdateListener updateListener) {
203168
this.modules.clear();
204169
updateListener.update(0D);
205-
RepoData[] repoDatas = this.repoData.values().toArray(new RepoData[0]);
170+
// Using LinkedHashSet to deduplicate Androidacy entry.
171+
RepoData[] repoDatas = new LinkedHashSet<>(
172+
this.repoData.values()).toArray(new RepoData[0]);
206173
RepoUpdater[] repoUpdaters = new RepoUpdater[repoDatas.length];
207174
int moduleToUpdate = 0;
208175
for (int i = 0; i < repoDatas.length; i++) {
@@ -215,7 +182,7 @@ private boolean scanInternal(UpdateListener updateListener) {
215182
for (int i = 0; i < repoUpdaters.length; i++) {
216183
List<RepoModule> repoModules = repoUpdaters[i].toUpdate();
217184
RepoData repoData = repoDatas[i];
218-
Log.d(TAG, "Registering " + repoData.name);
185+
Log.d(TAG, "Registering " + repoData.getName());
219186
for (RepoModule repoModule:repoModules) {
220187
try {
221188
if (repoModule.propUrl != null &&
@@ -261,7 +228,7 @@ private boolean scanInternal(UpdateListener updateListener) {
261228
}
262229
Log.i(TAG, "Got " + this.modules.size() + " modules!");
263230
updateListener.update(1D);
264-
return hasInternet;
231+
this.repoLastResult = hasInternet;
265232
}
266233

267234
public void updateEnabledStates() {

app/src/main/java/com/fox2code/mmm/repo/RepoUpdater.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public int fetchIndex() {
4848
// Return repo to update
4949
return this.toUpdate.size();
5050
} catch (Exception e) {
51-
Log.e(TAG, "Failed to get manifest", e);
51+
Log.e(TAG, "Failed to get manifest of " + this.repoData.id, e);
5252
this.indexRaw = null;
5353
this.toUpdate = Collections.emptyList();
5454
this.toApply = Collections.emptySet();

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,10 +380,13 @@ public static String makeNameFromId(String moduleId) {
380380
moduleId.substring(1).replace('_', ' ');
381381
}
382382

383+
public static boolean isNullString(String string) {
384+
return string == null || string.isEmpty() || "null".equals(string);
385+
}
386+
383387
// Make versionName no longer than 16 charters to avoid UI overflow.
384388
public static String shortenVersionName(String versionName, long versionCode) {
385-
if (versionName == null || versionName.isEmpty() ||
386-
"null".equals(versionName)) return "v" + versionCode;
389+
if (isNullString(versionName)) return "v" + versionCode;
387390
if (versionName.length() <= 16) return versionName;
388391
int i = versionName.lastIndexOf('.');
389392
if (i != -1 && i <= 16 && versionName.indexOf('.') != i
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package com.fox2code.mmm.utils;
2+
3+
import androidx.annotation.NonNull;
4+
import androidx.annotation.Nullable;
5+
6+
/**
7+
* Manager that want both to be thread safe and not to worry about thread safety
8+
* {@link #scan()} and {@link #update(UpdateListener)} can be called from multiple
9+
* thread at the same time, {@link #scanInternal(UpdateListener)} will only be
10+
* called from one thread at a time only.
11+
*/
12+
public abstract class SyncManager {
13+
private static final UpdateListener NO_OP = value -> {};
14+
protected final Object syncLock = new Object();
15+
private boolean syncing;
16+
17+
public final void scan() {
18+
this.update(null);
19+
}
20+
21+
// MultiThread friendly method
22+
public final void update(@Nullable UpdateListener updateListener) {
23+
if (updateListener == null) updateListener = NO_OP;
24+
if (!this.syncing) {
25+
// Do scan
26+
synchronized (this.syncLock) {
27+
this.syncing = true;
28+
try {
29+
this.scanInternal(updateListener);
30+
} finally {
31+
this.syncing = false;
32+
}
33+
}
34+
} else {
35+
// Wait for current scan
36+
synchronized (this.syncLock) {
37+
Thread.yield();
38+
}
39+
}
40+
}
41+
42+
// Pause execution until the scan is completed if one is currently running
43+
public final void afterScan() {
44+
if (this.syncing) synchronized (this.syncLock) { Thread.yield(); }
45+
}
46+
47+
public final void runAfterScan(Runnable runnable) {
48+
synchronized (this.syncLock) {
49+
runnable.run();
50+
}
51+
}
52+
53+
public final boolean isRepoUpdating() {
54+
return this.syncing;
55+
}
56+
57+
public final void afterUpdate() {
58+
if (this.syncing) synchronized (this.syncLock) { Thread.yield(); }
59+
}
60+
61+
public final void runAfterUpdate(Runnable runnable) {
62+
synchronized (this.syncLock) {
63+
runnable.run();
64+
}
65+
}
66+
67+
// This method can't be called twice at the same time.
68+
protected abstract void scanInternal(@NonNull UpdateListener updateListener);
69+
70+
public interface UpdateListener {
71+
void update(double value);
72+
}
73+
}

0 commit comments

Comments
 (0)