diff --git a/briar-android/src/org/briarproject/plugins/tor/TorNetworkMetadata.java b/briar-android/src/org/briarproject/plugins/tor/TorNetworkMetadata.java index a8d75b616d7c323aadd9fd658b3c71bb381a95eb..ae238564720bd385d63ccd9829adbabb9cfe2081 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorNetworkMetadata.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorNetworkMetadata.java @@ -3,37 +3,16 @@ package org.briarproject.plugins.tor; import java.util.Arrays; import java.util.HashSet; import java.util.Set; -import java.util.logging.Logger; - -import org.briarproject.api.system.LocationUtils; public class TorNetworkMetadata { - private static final Logger LOG = - Logger.getLogger(TorNetworkMetadata.class.getName()); - - // for country codes see https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 - // below list from https://trac.torproject.org/projects/tor/wiki/doc/OONI/censorshipwiki + // See https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 + // and https://trac.torproject.org/projects/tor/wiki/doc/OONI/censorshipwiki // TODO: get a more complete list - public static final Set<String> BLOCKED_IN_COUNTRIES = new HashSet<String>(Arrays.asList( - "CN", - "IR", - "SY", - //"ET", // possibly lifted - https://metrics.torproject.org/users.html?graph=userstats-relay-country&start=2012-02-08&end=2014-02-06&country=et&events=off#userstats-relay-country - //"KZ", // unclear due to botnet - https://metrics.torproject.org/users.html?graph=userstats-relay-country&start=2012-02-08&end=2014-02-06&country=kz&events=off#userstats-relay-country - //"PH", // unclear due to botnet - https://metrics.torproject.org/users.html?graph=userstats-relay-country&start=2012-02-08&end=2014-02-06&country=ph&events=off#userstats-relay-country - //"AE", // unclear due to botnet - https://metrics.torproject.org/users.html?graph=userstats-relay-country&start=2012-02-08&end=2014-02-06&country=ae&events=off#userstats-relay-country - //"GB", // for testing - "ZZ" - )); + private static final Set<String> BLOCKED_IN_COUNTRIES = + new HashSet<String>(Arrays.asList("CN", "IR", "SY", "ZZ")); - public static boolean isTorProbablyBlocked(LocationUtils locationUtils) { - String countryCode = locationUtils.getCurrentCountry(); - if (BLOCKED_IN_COUNTRIES.contains(countryCode)) { - LOG.info("Tor is probably blocked in your country: " + countryCode); - return true; - } - return false; + public static boolean isTorProbablyBlocked(String countryCode) { + return BLOCKED_IN_COUNTRIES.contains(countryCode); } - } diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java b/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java index e5ba67129a3b66338e26cf47d0b915bef9c47791..57c7f04500e82570d822bef1f02f67f3592b3faf 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java @@ -9,8 +9,6 @@ import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexPluginCallback; import org.briarproject.api.plugins.duplex.DuplexPluginFactory; import org.briarproject.api.system.LocationUtils; -import org.briarproject.plugins.AndroidPluginsModule; -import org.briarproject.plugins.tor.TorNetworkMetadata; import android.content.Context; import android.os.Build; @@ -43,10 +41,14 @@ public class TorPluginFactory implements DuplexPluginFactory { public DuplexPlugin createPlugin(DuplexPluginCallback callback) { // Check that we have a Tor binary for this architecture - if(!Build.CPU_ABI.startsWith("armeabi")) return null; - // Check that we don't know that Tor is blocked here - if (TorNetworkMetadata.isTorProbablyBlocked(locationUtils)) { - LOG.info("Tor has been pre-emptively disabled since it is probably blocked"); + if(!Build.CPU_ABI.startsWith("armeabi")) { + LOG.info("Tor is not supported on this architecture"); + return null; + } + // Check whether we know that Tor is blocked in this country + String countryCode = locationUtils.getCurrentCountry(); + if(TorNetworkMetadata.isTorProbablyBlocked(countryCode)) { + LOG.info("Tor has been disabled since it is probably blocked"); return null; } return new TorPlugin(pluginExecutor,appContext, shutdownManager, diff --git a/briar-android/src/org/briarproject/system/AndroidLocationUtils.java b/briar-android/src/org/briarproject/system/AndroidLocationUtils.java index 2f52682076164118e22478f53bdf93ae13a459b0..787f57ea5da1854f1b8a96acdbbc931f9d3cedd7 100644 --- a/briar-android/src/org/briarproject/system/AndroidLocationUtils.java +++ b/briar-android/src/org/briarproject/system/AndroidLocationUtils.java @@ -1,5 +1,8 @@ package org.briarproject.system; +import static android.content.Context.LOCATION_SERVICE; +import static android.content.Context.TELEPHONY_SERVICE; + import java.io.IOException; import java.util.List; import java.util.Locale; @@ -25,102 +28,99 @@ class AndroidLocationUtils implements LocationUtils { private static final Logger LOG = Logger.getLogger(AndroidLocationUtils.class.getName()); - final Context context; - + private final Context ctx; + @Inject - public AndroidLocationUtils(Context context) { - this.context = context; + public AndroidLocationUtils(Context ctx) { + this.ctx = ctx; } - + /** * This guesses the current country from the first of these sources that * succeeds (also in order of likelihood of being correct): * * <ul> * <li>Phone network. This works even when no SIM card is inserted, or a - * foreign SIM card is inserted.</li> + * foreign SIM card is inserted.</li> * <li><del>Location service (GPS/WiFi/etc).</del> <em>This is disabled for - * now, until we figure out an offline method of converting a long/lat - * into a country code, that doesn't involve a network call.</em> + * now, until we figure out an offline method of converting a long/lat + * into a country code, that doesn't involve a network call.</em> * <li>SIM card. This is only an heuristic and assumes the user is not - * roaming.</li> - * <li>User Locale. This is an even worse heuristic.</li> + * roaming.</li> + * <li>User locale. This is an even worse heuristic.</li> * </ul> * * Note: this is very similar to <a href="https://android.googlesource.com/platform/frameworks/base/+/cd92588%5E/location/java/android/location/CountryDetector.java"> - * this API</a> except it seems that Google doesn't want us to use it for + * this API</a> except it seems that Google doesn't want us to useit for * some reason - both that class and {@code Context.COUNTRY_CODE} are * annotated {@code @hide}. */ @SuppressLint("DefaultLocale") - @Override public String getCurrentCountry() { - String countryCode; - countryCode = getCountryFromPhoneNetwork(); - if (!TextUtils.isEmpty(countryCode)) { - return countryCode.toUpperCase(); // android api gives lowercase for some reason - } - // When we enable this, we will need to add ACCESS_FINE_LOCATION - //countryCode = getCountryFromLocation(); - //if (!TextUtils.isEmpty(countryCode)) { - // return countryCode; - //} + String countryCode = getCountryFromPhoneNetwork(); + if(!TextUtils.isEmpty(countryCode)) return countryCode.toUpperCase(); + // Disabled because it involves a network call; requires + // ACCESS_FINE_LOCATION + // countryCode = getCountryFromLocation(); + // if(!TextUtils.isEmpty(countryCode)) return countryCode; + LOG.info("Falling back to SIM card country"); countryCode = getCountryFromSimCard(); - if (!TextUtils.isEmpty(countryCode)) { - LOG.info("Could not determine current country; fall back to SIM card country."); - return countryCode.toUpperCase(); // android api gives lowercase for some reason - } - LOG.info("Could not determine current country; fall back to user-defined locale."); + if(!TextUtils.isEmpty(countryCode)) return countryCode.toUpperCase(); + LOG.info("Falling back to user-defined locale"); return Locale.getDefault().getCountry(); } - - String getCountryFromPhoneNetwork() { - TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); + + private String getCountryFromPhoneNetwork() { + Object o = ctx.getSystemService(TELEPHONY_SERVICE); + TelephonyManager tm = (TelephonyManager) o; return tm.getNetworkCountryIso(); } - - String getCountryFromSimCard() { - TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); + + private String getCountryFromSimCard() { + Object o = ctx.getSystemService(TELEPHONY_SERVICE); + TelephonyManager tm = (TelephonyManager) o; return tm.getSimCountryIso(); } // TODO: this is not currently used, because it involves a network call // it should be possible to determine country just from the long/lat, but // this would involve something like tzdata for countries. - String getCountryFromLocation() { + private String getCountryFromLocation() { Location location = getLastKnownLocation(); - if (location == null) return null; - Geocoder code = new Geocoder(context); + if(location == null) return null; + Geocoder code = new Geocoder(ctx); try { - List<Address> addresses = code.getFromLocation(location.getLatitude(), location.getLongitude(), 1); - if (addresses.isEmpty()) return null; + double lat = location.getLatitude(); + double lon = location.getLongitude(); + List<Address> addresses = code.getFromLocation(lat, lon, 1); + if(addresses.isEmpty()) return null; return addresses.get(0).getCountryCode(); - } catch (IOException e) { + } catch(IOException e) { return null; } } /** - * Returns the last location from all location providers. - * Since we're only checking the country, we don't care about the accuracy. - * If we ever need the accuracy, we can do something like: - * https://code.google.com/p/android-protips-location/source/browse/trunk\ - * /src/com/radioactiveyak/location_best_practices/utils/GingerbreadLastLocationFinder.java + * Returns the last location from all location providers, or null if there + * is no such location. Since we're only checking the country, we don't + * care about the accuracy. If we ever need the accuracy, we can do + * something like <a href="https://code.google.com/p/android-protips-location/source/browse/trunk/src/com/radioactiveyak/location_best_practices/utils/GingerbreadLastLocationFinder.java"> + * this</a>. */ - Location getLastKnownLocation() { - LocationManager locationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE); + private Location getLastKnownLocation() { + Object o = ctx.getSystemService(LOCATION_SERVICE); + LocationManager locationManager = (LocationManager) o; Location bestResult = null; long bestTime = Long.MIN_VALUE; - for (String provider: locationManager.getAllProviders()) { + for(String provider : locationManager.getAllProviders()) { Location location = locationManager.getLastKnownLocation(provider); - if (location == null) continue; + if(location == null) continue; long time = location.getTime(); - if (time > bestTime) { + if(time > bestTime) { bestResult = location; bestTime = time; } } return bestResult; } - } diff --git a/briar-api/src/org/briarproject/api/system/LocationUtils.java b/briar-api/src/org/briarproject/api/system/LocationUtils.java index c0c82844db7e3ae67b9e1c8979b55a73c076c20f..33057e839b04c14ccf7b3d9be1816b28b71395c0 100644 --- a/briar-api/src/org/briarproject/api/system/LocationUtils.java +++ b/briar-api/src/org/briarproject/api/system/LocationUtils.java @@ -2,12 +2,12 @@ package org.briarproject.api.system; public interface LocationUtils { - /** Get the country the device is currently-located in, or "" if it cannot + /** + * Get the country the device is currently located in, or "" if it cannot * be determined. Should never return {@code null}. - * - * <p>The country codes are formatted upper-case and as per <a href=" + * <p> + * The country codes are formatted upper-case and as per <a href=" * https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha 2</a>. */ String getCurrentCountry(); - }