diff --git a/app/src/main/java/org/fdroid/fdroid/IndexV1Updater.java b/app/src/main/java/org/fdroid/fdroid/IndexV1Updater.java index 08f16430b..63ee68901 100644 --- a/app/src/main/java/org/fdroid/fdroid/IndexV1Updater.java +++ b/app/src/main/java/org/fdroid/fdroid/IndexV1Updater.java @@ -62,8 +62,10 @@ import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.jar.JarEntry; @@ -284,7 +286,14 @@ public class IndexV1Updater extends IndexUpdater { repo.name = getStringRepoValue(repoMap, "name"); repo.icon = getStringRepoValue(repoMap, "icon"); repo.description = getStringRepoValue(repoMap, "description"); - repo.mirrors = getStringArrayRepoValue(repoMap, "mirrors"); + + // ensure the canonical URL is included in the "mirrors" list + List mirrorsList = getStringListRepoValue(repoMap, "mirrors"); + HashSet mirrors = new HashSet<>(mirrorsList.size() + 1); + mirrors.addAll(mirrorsList); + mirrors.add(repo.address); + repo.mirrors = mirrors.toArray(new String[mirrors.size()]); + // below are optional, can be default value repo.maxage = getIntRepoValue(repoMap, "maxage"); repo.version = getIntRepoValue(repoMap, "version"); @@ -372,13 +381,12 @@ public class IndexV1Updater extends IndexUpdater { } @SuppressWarnings("unchecked") - private String[] getStringArrayRepoValue(Map repoMap, String key) { + private List getStringListRepoValue(Map repoMap, String key) { Object value = repoMap.get(key); if (value != null && value instanceof ArrayList) { - ArrayList list = (ArrayList) value; - return list.toArray(new String[list.size()]); + return (List) value; } - return null; + return Collections.emptyList(); } private HashMap parseRepo(ObjectMapper mapper, JsonParser parser) throws IOException { diff --git a/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java b/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java index 9eaad2d44..e56a0d139 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java +++ b/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java @@ -1394,6 +1394,12 @@ public class DBHelper extends SQLiteOpenHelper { return exists; } + /** + * Insert a new repo into the database. This also initializes the list of + * "mirror" URLs. There should always be at least one URL there, since the + * logic in {@link org.fdroid.fdroid.FDroidApp#getMirror(String, Repo)} + * expects at least one entry in the mirrors list. + */ private void insertRepo(SQLiteDatabase db, String name, String address, String description, String version, String enabled, String priority, String pushRequests, String pubKey) { @@ -1410,6 +1416,9 @@ public class DBHelper extends SQLiteOpenHelper { values.put(RepoTable.Cols.LAST_ETAG, (String) null); values.put(RepoTable.Cols.TIMESTAMP, 0); + String[] initializeMirrors = {address}; + values.put(Schema.RepoTable.Cols.MIRRORS, Utils.serializeCommaSeparatedString(initializeMirrors)); + switch (pushRequests) { case "ignore": values.put(RepoTable.Cols.PUSH_REQUESTS, Repo.PUSH_REQUEST_IGNORE); diff --git a/app/src/main/java/org/fdroid/fdroid/data/Repo.java b/app/src/main/java/org/fdroid/fdroid/data/Repo.java index b3b35f4b7..1a9929789 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/Repo.java +++ b/app/src/main/java/org/fdroid/fdroid/data/Repo.java @@ -37,6 +37,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; +import java.util.HashSet; import java.util.List; @@ -338,20 +339,29 @@ public class Repo extends ValueObject { } } + /** + * The main repo URL is included in the mirror list, so it only makes + * sense to activate this logic if there are more than one entry in the + * mirror list. + */ public boolean hasMirrors() { return (mirrors != null && mirrors.length > 1) || (userMirrors != null && userMirrors.length > 0); } + /** + * @return {@link List} of valid URLs to reach this repo, including the canonical URL + */ public List getMirrorList() { - final ArrayList allMirrors = new ArrayList(); + final HashSet allMirrors = new HashSet<>(); if (userMirrors != null) { allMirrors.addAll(Arrays.asList(userMirrors)); } if (mirrors != null) { allMirrors.addAll(Arrays.asList(mirrors)); } - return allMirrors; + allMirrors.add(address); + return new ArrayList<>(allMirrors); } /** @@ -360,19 +370,26 @@ public class Repo extends ValueObject { public int getMirrorCount() { int count = 0; for (String m : getMirrorList()) { - if (!m.equals(address)) { - if (FDroidApp.isUsingTor()) { - count++; - } else { - if (!m.contains(".onion")) { - count++; - } - } + if (FDroidApp.isUsingTor()) { + count++; + } else if (!m.contains(".onion")) { + count++; } } return count; } + /** + * The mirror logic assumes that it has a mirrors list with at least once + * valid entry in it. In the index format as defined by {@code fdroid update}, + * there is always at least one valid URL: the canonical URL. That also means + * if there is only one item in the mirrors list, there are no other URLs to try. + *

+ * The initial state of the repos in the database also include the canonical + * URL in the mirrors list so the mirror logic works on the first index + * update. That makes it possible to do the first index update via SD Card + * or USB OTG drive. + */ public String getMirror(String lastWorkingMirror) { if (TextUtils.isEmpty(lastWorkingMirror)) { lastWorkingMirror = address; @@ -382,7 +399,7 @@ public class Repo extends ValueObject { if (shuffledMirrors.size() > 1) { for (String m : shuffledMirrors) { // Return a non default, and not last used mirror - if (!m.equals(address) && !m.equals(lastWorkingMirror)) { + if (!m.equals(lastWorkingMirror)) { if (FDroidApp.isUsingTor()) { return m; } else {