summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcyBerta <cyberta@riseup.net>2021-11-23 14:51:33 +0100
committercyBerta <cyberta@riseup.net>2021-11-23 14:51:33 +0100
commitf29ee8ac7378408e070be1130c9cadc8e947ddb3 (patch)
tree5b6cf94738065434a41ca5a69be049d3b261d3cf
parent438b2b866b64f63dc1346ca89cf3e56847def6da (diff)
sort locations by transport
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java10
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/models/Location.java43
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java42
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java57
-rw-r--r--app/src/test/resources/v4/riseup_geoip_v4_bad_obfs4_gateway.json41
5 files changed, 160 insertions, 33 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java
index 51ebe359..e3845164 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java
@@ -117,7 +117,7 @@ public class GatewaySelectionFragment extends Fragment implements Observer, Loca
recyclerView.setHasFixedSize(true);
LinearLayoutManager layoutManager = new LinearLayoutManager(this.getContext());
recyclerView.setLayoutManager(layoutManager);
- locationListAdapter = new LocationListAdapter(gatewaysManager.getGatewayLocations(), this, selectedTransport);
+ locationListAdapter = new LocationListAdapter(gatewaysManager.getSortedGatewayLocations(selectedTransport), this, selectedTransport);
recyclerView.setAdapter(locationListAdapter);
recyclerView.setVisibility(VISIBLE);
}
@@ -203,14 +203,15 @@ public class GatewaySelectionFragment extends Fragment implements Observer, Loca
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(USE_BRIDGES)) {
selectedTransport = PreferenceHelper.getUseBridges(sharedPreferences) ? OBFS4 : OPENVPN;
- locationListAdapter.updateTransport(selectedTransport);
+ gatewaysManager.updateTransport(selectedTransport);
+ locationListAdapter.updateTransport(selectedTransport, gatewaysManager);
}
}
static class LocationListAdapter extends RecyclerView.Adapter<LocationListAdapter.ViewHolder> {
private static final String TAG = LocationListAdapter.class.getSimpleName();
private Connection.TransportType transport;
- private final List<Location> values;
+ private List<Location> values;
private final WeakReference<LocationListSelectionListener> callback;
static class ViewHolder extends RecyclerView.ViewHolder {
@@ -232,8 +233,9 @@ public class GatewaySelectionFragment extends Fragment implements Observer, Loca
notifyItemRemoved(position);
}
- public void updateTransport(Connection.TransportType transportType) {
+ public void updateTransport(Connection.TransportType transportType, GatewaysManager gatewaysManager) {
transport = transportType;
+ values = gatewaysManager.getSortedGatewayLocations(transportType);
notifyDataSetChanged();
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/models/Location.java b/app/src/main/java/se/leap/bitmaskclient/base/models/Location.java
index e0ce9e8b..064f25c0 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/models/Location.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/models/Location.java
@@ -19,22 +19,25 @@ package se.leap.bitmaskclient.base.models;
import androidx.annotation.NonNull;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.function.ToDoubleFunction;
import de.blinkt.openvpn.core.connection.Connection;
+import de.blinkt.openvpn.core.connection.Connection.TransportType;
public class Location implements Cloneable {
@NonNull private String name = "";
- @NonNull private HashMap<Connection.TransportType, Double> averageLoad = new HashMap<>();
- @NonNull private HashMap<Connection.TransportType, Integer> numberOfGateways = new HashMap<>();
+ @NonNull private HashMap<TransportType, Double> averageLoad = new HashMap<>();
+ @NonNull private HashMap<TransportType, Integer> numberOfGateways = new HashMap<>();
public boolean selected;
public Location() {}
public Location(@NonNull String name,
- @NonNull HashMap<Connection.TransportType, Double> averageLoad,
- @NonNull HashMap<Connection.TransportType, Integer> numberOfGateways,
+ @NonNull HashMap<TransportType, Double> averageLoad,
+ @NonNull HashMap<TransportType, Integer> numberOfGateways,
boolean selected) {
this.name = name;
this.averageLoad = averageLoad;
@@ -46,26 +49,26 @@ public class Location implements Cloneable {
return !numberOfGateways.isEmpty() && !averageLoad.isEmpty() && !name.isEmpty();
}
- public boolean supportsTransport(Connection.TransportType transportType) {
+ public boolean supportsTransport(TransportType transportType) {
return numberOfGateways.containsKey(transportType);
}
- public void setAverageLoad(Connection.TransportType transportType, double load) {
+ public void setAverageLoad(TransportType transportType, double load) {
averageLoad.put(transportType, load);
}
- public double getAverageLoad(Connection.TransportType transportType) {
+ public double getAverageLoad(TransportType transportType) {
if (averageLoad.containsKey(transportType)) {
return averageLoad.get(transportType);
}
return 0;
}
- public void setNumberOfGateways(Connection.TransportType transportType, int numbers) {
+ public void setNumberOfGateways(TransportType transportType, int numbers) {
numberOfGateways.put(transportType, numbers);
}
- public int getNumberOfGateways(Connection.TransportType transportType) {
+ public int getNumberOfGateways(TransportType transportType) {
if (numberOfGateways.containsKey(transportType)) {
return numberOfGateways.get(transportType);
}
@@ -101,8 +104,26 @@ public class Location implements Cloneable {
public Location clone() throws CloneNotSupportedException {
Location copy = (Location) super.clone();
copy.name = this.name;
- copy.numberOfGateways = (HashMap<Connection.TransportType, Integer>) this.numberOfGateways.clone();
- copy.averageLoad = (HashMap<Connection.TransportType, Double>) this.averageLoad.clone();
+ copy.numberOfGateways = (HashMap<TransportType, Integer>) this.numberOfGateways.clone();
+ copy.averageLoad = (HashMap<TransportType, Double>) this.averageLoad.clone();
return copy;
}
+
+ public static class SortByAverageLoad implements Comparator<Location> {
+ TransportType transportType;
+ public SortByAverageLoad(TransportType transportType) {
+ this.transportType = transportType;
+ }
+
+ @Override
+ public int compare(Location location1, Location location2) {
+ if (location1.supportsTransport(transportType) && location2.supportsTransport(transportType)) {
+ return (int) (location1.getAverageLoad(transportType) * 100) - (int) (location2.getAverageLoad(transportType) * 100);
+ } else if (location1.supportsTransport(transportType) && !location2.supportsTransport(transportType)) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+ }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
index a0867605..060e69f2 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
@@ -31,15 +31,16 @@ import org.json.JSONObject;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Random;
import de.blinkt.openvpn.VpnProfile;
import de.blinkt.openvpn.core.ConfigParser;
import de.blinkt.openvpn.core.VpnStatus;
import de.blinkt.openvpn.core.connection.Connection;
+import de.blinkt.openvpn.core.connection.Connection.TransportType;
import se.leap.bitmaskclient.base.models.Location;
import se.leap.bitmaskclient.base.models.Provider;
import se.leap.bitmaskclient.base.models.ProviderObservable;
@@ -98,6 +99,7 @@ public class GatewaysManager {
private final Type listType = new TypeToken<ArrayList<Gateway>>() {}.getType();
private final ArrayList<Gateway> presortedList = new ArrayList<>();
private ArrayList<Location> locations = new ArrayList<>();
+ private TransportType selectedTransport;
public GatewaysManager(Context context) {
this.context = context;
@@ -114,7 +116,7 @@ public class GatewaysManager {
}
public Gateway select(int nClosest, String city) {
- Connection.TransportType transportType = getUseBridges(context) ? OBFS4 : OPENVPN;
+ TransportType transportType = getUseBridges(context) ? OBFS4 : OPENVPN;
if (presortedList.size() > 0) {
return getGatewayFromPresortedList(nClosest, transportType, city);
}
@@ -122,25 +124,26 @@ public class GatewaysManager {
return getGatewayFromTimezoneCalculation(nClosest, transportType, city);
}
- public ArrayList<Gateway> getSortedGateways() {
- if (presortedList.size() > 0) {
- return presortedList;
- } else {
- GatewaySelector gatewaySelector = new GatewaySelector(new ArrayList<>(gateways.values()));
- return gatewaySelector.getGatewaysSortedByDistance();
+ public void updateTransport(TransportType transportType) {
+ if (this.selectedTransport == null || transportType != this.selectedTransport) {
+ this.selectedTransport = transportType;
+ locations.clear();
}
}
public List<Location> getGatewayLocations() {
+ return getSortedGatewayLocations(null);
+ }
+
+ public List<Location> getSortedGatewayLocations(@Nullable TransportType selectedTransport) {
if (locations.size() > 0) {
return locations;
}
HashMap<String, Integer> locationNames = new HashMap<>();
ArrayList<Location> locations = new ArrayList<>();
- ArrayList<Gateway> gateways = getSortedGateways();
String preferredCity = PreferenceHelper.getPreferredCity(context);
- for (Gateway gateway : gateways) {
+ for (Gateway gateway : gateways.values()) {
String name = gateway.getName();
if (name == null) {
Log.e(TAG, "Gateway without location name found. This should never happen. Provider misconfigured?");
@@ -159,13 +162,16 @@ public class GatewaysManager {
locations.set(index, location);
}
}
- this.locations = locations;
+ if (selectedTransport != null) {
+ Collections.sort(locations, new Location.SortByAverageLoad(selectedTransport));
+ this.locations = locations;
+ }
return locations;
}
private Location initLocation(String name, Gateway gateway, String preferredCity) {
- HashMap<Connection.TransportType, Double> averageLoadMap = new HashMap<>();
- HashMap<Connection.TransportType, Integer> numberOfGatewaysMap = new HashMap<>();
+ HashMap<TransportType, Double> averageLoadMap = new HashMap<>();
+ HashMap<TransportType, Integer> numberOfGatewaysMap = new HashMap<>();
if (gateway.getSupportedTransports().contains(OBFS4)) {
averageLoadMap.put(OBFS4, gateway.getFullness());
numberOfGatewaysMap.put(OBFS4, 1);
@@ -203,12 +209,12 @@ public class GatewaysManager {
return null;
}
- public Load getLoadForLocation(@Nullable String name, Connection.TransportType transportType) {
+ public Load getLoadForLocation(@Nullable String name, TransportType transportType) {
Location location = getLocation(name);
return Load.getLoadByValue(location.getAverageLoad(transportType));
}
- private Gateway getGatewayFromTimezoneCalculation(int nClosest, Connection.TransportType transportType, @Nullable String city) {
+ private Gateway getGatewayFromTimezoneCalculation(int nClosest, TransportType transportType, @Nullable String city) {
List<Gateway> list = new ArrayList<>(gateways.values());
GatewaySelector gatewaySelector = new GatewaySelector(list);
Gateway gateway;
@@ -227,7 +233,7 @@ public class GatewaysManager {
return null;
}
- private Gateway getGatewayFromPresortedList(int nClosest, Connection.TransportType transportType, @Nullable String city) {
+ private Gateway getGatewayFromPresortedList(int nClosest, TransportType transportType, @Nullable String city) {
int found = 0;
for (Gateway gateway : presortedList) {
if ((city == null && gateway.supportsTransport(transportType)) ||
@@ -255,7 +261,7 @@ public class GatewaysManager {
}
private int getPositionFromPresortedList(VpnProfile profile) {
- Connection.TransportType transportType = profile.mUsePluggableTransports ? OBFS4 : OPENVPN;
+ TransportType transportType = profile.mUsePluggableTransports ? OBFS4 : OPENVPN;
int nClosest = 0;
for (Gateway gateway : presortedList) {
if (gateway.supportsTransport(transportType)) {
@@ -269,7 +275,7 @@ public class GatewaysManager {
}
private int getPositionFromTimezoneCalculatedList(VpnProfile profile) {
- Connection.TransportType transportType = profile.mUsePluggableTransports ? OBFS4 : OPENVPN;
+ TransportType transportType = profile.mUsePluggableTransports ? OBFS4 : OPENVPN;
GatewaySelector gatewaySelector = new GatewaySelector(new ArrayList<>(gateways.values()));
Gateway gateway;
int nClosest = 0;
diff --git a/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java
index 33abfbed..43a6a496 100644
--- a/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java
+++ b/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java
@@ -451,6 +451,63 @@ public class GatewaysManagerTest {
}
}
+
+ @Test
+ public void testGetSortedLocations_openvpn() {
+ Provider provider = getProvider(null, null, null, null, null, null, "v4/riseup_eipservice_for_geoip_v4.json", "v4/riseup_geoip_v4_bad_obfs4_gateway.json");
+
+ MockHelper.mockProviderObservable(provider);
+ mockStatic(PreferenceHelper.class);
+ when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(false);
+ GatewaysManager gatewaysManager = new GatewaysManager(mockContext);
+ List<Location> locations = gatewaysManager.getSortedGatewayLocations(OPENVPN);
+
+ assertEquals(3, locations.size());
+
+ /** -> v4/riseup_geoip_v4_bad_obfs4_gateway.json OPENVPN
+ * Paris = 0.527
+ * 0.36 - zarapito
+ * 0.92 - hoazin
+ * 0.3 - mouette
+ *
+ * Montreal = 0.59
+ * 0.59 - yal
+ *
+ * Amsterdam = 0.8
+ * 0.8 - redshank
+ */
+ assertEquals("Paris", locations.get(0).getName());
+ assertEquals("Montreal", locations.get(1).getName());
+ assertEquals("Amsterdam", locations.get(2).getName());
+ }
+
+ @Test
+ public void testGetSortedLocations_obfs4() {
+ Provider provider = getProvider(null, null, null, null, null, null, "v4/riseup_eipservice_for_geoip_v4.json", "v4/riseup_geoip_v4_bad_obfs4_gateway.json");
+
+ MockHelper.mockProviderObservable(provider);
+ mockStatic(PreferenceHelper.class);
+ when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(false);
+ GatewaysManager gatewaysManager = new GatewaysManager(mockContext);
+ List<Location> locations = gatewaysManager.getSortedGatewayLocations(OBFS4);
+
+ assertEquals(3, locations.size());
+
+ /** -> v4/riseup_geoip_v4_bad_obfs4_gateway.json OBFS4
+ * Paris = 0.92
+ * 0.92 - hoazin
+ *
+ * Montreal = 0.59
+ * 0.59 - yal
+ *
+ * Amsterdam = 0.0 - no obfs4
+ * 0.0 - redshank
+ */
+ assertEquals("Montreal", locations.get(0).getName());
+ assertEquals("Paris", locations.get(1).getName());
+ assertEquals("Amsterdam", locations.get(2).getName());
+ }
+
private String getJsonStringFor(String filename) throws IOException {
return TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream(filename));
}
diff --git a/app/src/test/resources/v4/riseup_geoip_v4_bad_obfs4_gateway.json b/app/src/test/resources/v4/riseup_geoip_v4_bad_obfs4_gateway.json
new file mode 100644
index 00000000..fcbc0d85
--- /dev/null
+++ b/app/src/test/resources/v4/riseup_geoip_v4_bad_obfs4_gateway.json
@@ -0,0 +1,41 @@
+{
+ "ip":"51.158.144.32",
+ "cc":"FR",
+ "city":"Paris",
+ "lat":48.8628,
+ "lon":2.3292,
+ "gateways":[
+ "mouette.riseup.net",
+ "hoatzin.riseup.net",
+ "yal.riseup.net",
+ "redshank.riseup.net",
+ "zarapito.riseup.net"
+ ],
+ "sortedGateways": [
+ {
+ "host": "mouette.riseup.net",
+ "fullness": 0.3,
+ "overload": false
+ },
+ {
+ "host": "hoatzin.riseup.net",
+ "fullness": 0.92,
+ "overload": false
+ },
+ {
+ "host": "yal.riseup.net",
+ "fullness": 0.59,
+ "overload": false
+ },
+ {
+ "host": "redshank.riseup.net",
+ "fullness": 0.8,
+ "overload": false
+ },
+ {
+ "host": "zarapito.riseup.net",
+ "fullness": 0.36,
+ "overload": true
+ }
+ ]
+} \ No newline at end of file