diff options
author | cyBerta <cyberta@riseup.net> | 2021-11-23 14:51:33 +0100 |
---|---|---|
committer | cyBerta <cyberta@riseup.net> | 2021-11-23 14:51:33 +0100 |
commit | f29ee8ac7378408e070be1130c9cadc8e947ddb3 (patch) | |
tree | 5b6cf94738065434a41ca5a69be049d3b261d3cf | |
parent | 438b2b866b64f63dc1346ca89cf3e56847def6da (diff) |
sort locations by transport
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 |