From b22200fde2b106da3a000fd9cdccd27e1fa7fa74 Mon Sep 17 00:00:00 2001 From: Matthew Bogner Date: Tue, 11 Feb 2025 18:05:59 +0000 Subject: [PATCH] deleting a repo now removes corresponding items from the dns cache --- .../fdroid/fdroid/net/DnsWithCacheTest.java | 26 +++--- .../java/org/fdroid/fdroid/FDroidApp.java | 2 + .../java/org/fdroid/fdroid/Preferences.java | 21 ----- .../java/org/fdroid/fdroid/net/DnsCache.java | 86 +++++++++++++++++++ .../org/fdroid/fdroid/net/DnsWithCache.java | 55 ++---------- .../views/repos/RepoDetailsViewModel.kt | 7 ++ 6 files changed, 118 insertions(+), 79 deletions(-) create mode 100644 app/src/main/java/org/fdroid/fdroid/net/DnsCache.java diff --git a/app/src/androidTest/java/org/fdroid/fdroid/net/DnsWithCacheTest.java b/app/src/androidTest/java/org/fdroid/fdroid/net/DnsWithCacheTest.java index ad0e07fcd..abbe40c98 100644 --- a/app/src/androidTest/java/org/fdroid/fdroid/net/DnsWithCacheTest.java +++ b/app/src/androidTest/java/org/fdroid/fdroid/net/DnsWithCacheTest.java @@ -1,6 +1,7 @@ package org.fdroid.fdroid.net; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import org.fdroid.fdroid.Preferences; import org.junit.Test; @@ -40,12 +41,12 @@ public class DnsWithCacheTest { // test setup Preferences prefs = Preferences.get(); prefs.setDnsCacheEnabledValue(true); - DnsWithCache testObject = new DnsWithCache(); + DnsCache testObject = DnsCache.get(); // populate cache - testObject.updateCacheAndPrefs(URL_1, LIST_1); - testObject.updateCacheAndPrefs(URL_2, LIST_2); - testObject.updateCacheAndPrefs(URL_3, LIST_3); + testObject.insert(URL_1, LIST_1); + testObject.insert(URL_2, LIST_2); + testObject.insert(URL_3, LIST_3); // check for cached lookup results List testList = testObject.lookup(URL_1); @@ -58,14 +59,8 @@ public class DnsWithCacheTest { prefs.setDnsCacheEnabledValue(false); // attempt non-cached lookup - boolean gotException = false; - try { - testList = testObject.lookup(URL_1); - } catch (UnknownHostException e) { - // test urls are not valid - gotException = true; - } - assertEquals(true, gotException); + testList = testObject.lookup(URL_1); + assertNull(testList); // toggle preference (true) prefs.setDnsCacheEnabledValue(true); @@ -74,5 +69,12 @@ public class DnsWithCacheTest { testList = testObject.lookup(URL_2); assertEquals(1, testList.size()); assertEquals(IP_2.getHostAddress(), testList.get(0).getHostAddress()); + + // test removal + testList = testObject.remove(URL_2); + assertEquals(1, testList.size()); + assertEquals(IP_2.getHostAddress(), testList.get(0).getHostAddress()); + testList = testObject.lookup(URL_2); + assertNull(testList); } } diff --git a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java index dc47b68bb..ce7190c2b 100644 --- a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java +++ b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java @@ -70,6 +70,7 @@ import org.fdroid.fdroid.nearby.PublicSourceDirProvider; import org.fdroid.fdroid.nearby.SDCardScannerService; import org.fdroid.fdroid.nearby.WifiStateChangeService; import org.fdroid.fdroid.net.ConnectivityMonitorService; +import org.fdroid.fdroid.net.DnsCache; import org.fdroid.fdroid.net.DownloaderFactory; import org.fdroid.fdroid.panic.HidingManager; import org.fdroid.fdroid.receiver.DeviceStorageReceiver; @@ -297,6 +298,7 @@ public class FDroidApp extends Application implements androidx.work.Configuratio .build()); } Preferences.setup(this); + DnsCache.setup(); Languages.setLanguage(this); Preferences preferences = Preferences.get(); diff --git a/app/src/main/java/org/fdroid/fdroid/Preferences.java b/app/src/main/java/org/fdroid/fdroid/Preferences.java index db2cad840..9fe1361e3 100644 --- a/app/src/main/java/org/fdroid/fdroid/Preferences.java +++ b/app/src/main/java/org/fdroid/fdroid/Preferences.java @@ -531,17 +531,6 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh return preferences.getBoolean(PREF_USE_DNS_CACHE, false); } - public void updateDnsCache(String urlString, List ipList) { - // existing list is replaced, so make sure new list has values - if (ipList == null || ipList.isEmpty()) { - return; - } else { - HashMap> dnsMap = getDnsCache(); - dnsMap.put(urlString, ipList); - setDnsCache(dnsMap); - } - } - public void setDnsCache(HashMap> dnsMap) { HashMap> stringMap = new HashMap>(); for (String url : dnsMap.keySet()) { @@ -554,16 +543,6 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh preferences.edit().putString(PREF_DNS_CACHE, listMapToString(stringMap)).apply(); } - public List queryDnsCache(String urlString) { - HashMap> dnsMap = getDnsCache(); - if (dnsMap.containsKey(urlString)) { - return dnsMap.get(urlString); - } else { - // returns empty list to avoid null issues - return new ArrayList(); - } - } - public HashMap> getDnsCache() { HashMap> dnsMap = new HashMap>(); String mapString = preferences.getString(PREF_DNS_CACHE, ""); diff --git a/app/src/main/java/org/fdroid/fdroid/net/DnsCache.java b/app/src/main/java/org/fdroid/fdroid/net/DnsCache.java new file mode 100644 index 000000000..6b541c493 --- /dev/null +++ b/app/src/main/java/org/fdroid/fdroid/net/DnsCache.java @@ -0,0 +1,86 @@ +package org.fdroid.fdroid.net; + +import android.util.Log; + +import androidx.annotation.NonNull; + +import org.fdroid.fdroid.Preferences; + +import java.net.InetAddress; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public final class DnsCache { + + private static final String TAG = "DnsCache"; + + private volatile HashMap> cache; + private static final int DELAY_TIME = 1; + private static final TimeUnit DELAY_UNIT = TimeUnit.SECONDS; + private volatile boolean writeScheduled = false; + + private final Runnable delayedWrite = () -> { + Preferences prefs = Preferences.get(); + prefs.setDnsCache(cache); + writeScheduled = false; + }; + + private final ScheduledExecutorService writeExecutor = Executors.newSingleThreadScheduledExecutor(); + + private static DnsCache instance; + + private DnsCache() { + Preferences prefs = Preferences.get(); + cache = prefs.getDnsCache(); + if (cache == null) { + cache = new HashMap<>(); + } + } + + public static void setup() { + if (instance != null) { + final String error = "DnsCache can only be initialized once"; + Log.e(TAG, error); + throw new RuntimeException(error); + } + instance = new DnsCache(); + } + + public static DnsCache get() { + if (instance == null) { + final String error = "DnsCache must be initialized first"; + Log.e(TAG, error); + throw new RuntimeException(error); + } + return instance; + } + + public void insert(@NonNull String url, @NonNull List ipList) { + cache.put(url, ipList); + if (!writeScheduled) { + writeScheduled = true; + writeExecutor.schedule(delayedWrite, DELAY_TIME, DELAY_UNIT); + } + } + + public List remove(@NonNull String url) { + List removed = cache.remove(url); + if (!writeScheduled && removed != null) { + writeScheduled = true; + writeExecutor.schedule(delayedWrite, DELAY_TIME, DELAY_UNIT); + } + return removed; + } + + public List lookup(@NonNull String url) { + Preferences prefs = Preferences.get(); + if (!prefs.isDnsCacheEnabled() || !cache.keySet().contains(url)) { + return null; + } else { + return cache.get(url); + } + } +} diff --git a/app/src/main/java/org/fdroid/fdroid/net/DnsWithCache.java b/app/src/main/java/org/fdroid/fdroid/net/DnsWithCache.java index eb6ee37f9..2ee9eef86 100644 --- a/app/src/main/java/org/fdroid/fdroid/net/DnsWithCache.java +++ b/app/src/main/java/org/fdroid/fdroid/net/DnsWithCache.java @@ -6,62 +6,25 @@ import org.fdroid.fdroid.Preferences; import java.net.InetAddress; import java.net.UnknownHostException; -import java.util.HashMap; import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; import okhttp3.Dns; public class DnsWithCache implements Dns { - private volatile HashMap> cache; - private static final int DELAY_TIME = 1; - private static final TimeUnit DELAY_UNIT = TimeUnit.SECONDS; - private volatile boolean writeScheduled = false; - private final Runnable delayedWrite = () -> { - Preferences prefs = Preferences.get(); - prefs.setDnsCache(cache); - writeScheduled = false; - }; - - private final ScheduledExecutorService writeExecutor = Executors.newSingleThreadScheduledExecutor(); - - public DnsWithCache() { - Preferences prefs = Preferences.get(); - cache = prefs.getDnsCache(); - } - - public void updateCacheAndPrefs(@NonNull String url, @NonNull List ipList) { - updateCache(url, ipList); - if (!writeScheduled) { - writeScheduled = true; - writeExecutor.schedule(delayedWrite, DELAY_TIME, DELAY_UNIT); - } - } - - public void updateCache(@NonNull String url, @NonNull List ipList) { - if (cache == null) { - cache = new HashMap>(); - } - cache.put(url, ipList); - } - @NonNull @Override public List lookup(@NonNull String url) throws UnknownHostException { Preferences prefs = Preferences.get(); - if (!prefs.isDnsCacheEnabled() - || cache == null - || !cache.keySet().contains(url)) { - // do dns lookup and cache ip list - List ipList = Dns.SYSTEM.lookup(url); - updateCacheAndPrefs(url, ipList); - return ipList; - } else { - // return cached ip list if available - return cache.get(url); + if (!prefs.isDnsCacheEnabled()) { + return Dns.SYSTEM.lookup(url); } + DnsCache cache = DnsCache.get(); + List list = cache.lookup(url); + if (list == null) { + list = Dns.SYSTEM.lookup(url); + cache.insert(url, list); + } + return list; } } diff --git a/app/src/main/java/org/fdroid/fdroid/views/repos/RepoDetailsViewModel.kt b/app/src/main/java/org/fdroid/fdroid/views/repos/RepoDetailsViewModel.kt index f7dc96cbd..44791114d 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/repos/RepoDetailsViewModel.kt +++ b/app/src/main/java/org/fdroid/fdroid/views/repos/RepoDetailsViewModel.kt @@ -29,6 +29,7 @@ import org.fdroid.fdroid.FDroidApp import org.fdroid.fdroid.R import org.fdroid.fdroid.data.DBHelper import org.fdroid.fdroid.generateQrBitmapKt +import org.fdroid.fdroid.net.DnsCache import org.fdroid.fdroid.work.RepoUpdateWorker enum class ArchiveState { @@ -60,6 +61,7 @@ class RepoDetailsViewModel( } private val repoId = initialRepo.repoId + private val repoMirrors = initialRepo.getMirrors() private val repoManager = FDroidApp.getRepoManager(app) private val appDao = DBHelper.getDb(app).getAppDao() @@ -105,6 +107,11 @@ class RepoDetailsViewModel( viewModelScope.launch(Dispatchers.IO) { repoManager.deleteRepository(repoId) } + // clean up dns cache + val cache = DnsCache.get() + for (mirror in repoMirrors) { + cache.remove(mirror.url.host) + } } fun updateUsernameAndPassword(username: String, password: String) {