diff --git a/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java b/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java
index a2f555b18beba8d65160339efb9db45f07ff5727..1aeac072da1b731226eda111eadd0f70bf8b71c5 100644
--- a/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java
+++ b/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java
@@ -13,6 +13,7 @@ import org.briarproject.api.plugins.duplex.DuplexPluginConfig;
 import org.briarproject.api.plugins.duplex.DuplexPluginFactory;
 import org.briarproject.api.plugins.simplex.SimplexPluginConfig;
 import org.briarproject.api.plugins.simplex.SimplexPluginFactory;
+import org.briarproject.api.system.LocationUtils;
 import org.briarproject.plugins.droidtooth.DroidtoothPluginFactory;
 import org.briarproject.plugins.tcp.LanTcpPluginFactory;
 import org.briarproject.plugins.tor.TorPluginFactory;
@@ -39,12 +40,13 @@ public class AndroidPluginsModule extends AbstractModule {
 	DuplexPluginConfig getDuplexPluginConfig(
 			@PluginExecutor Executor pluginExecutor,
 			AndroidExecutor androidExecutor, Context appContext,
-			CryptoComponent crypto, ShutdownManager shutdownManager) {
+			CryptoComponent crypto, LocationUtils locationUtils,
+			ShutdownManager shutdownManager) {
 		DuplexPluginFactory droidtooth = new DroidtoothPluginFactory(
 				pluginExecutor, androidExecutor, appContext,
 				crypto.getSecureRandom());
 		DuplexPluginFactory tor = new TorPluginFactory(pluginExecutor,
-				appContext, shutdownManager);
+				appContext, locationUtils, shutdownManager);
 		DuplexPluginFactory lan = new LanTcpPluginFactory(pluginExecutor);
 		final Collection<DuplexPluginFactory> factories =
 				Arrays.asList(droidtooth, tor, lan);
diff --git a/briar-android/src/org/briarproject/plugins/tor/TorNetworkMetadata.java b/briar-android/src/org/briarproject/plugins/tor/TorNetworkMetadata.java
new file mode 100644
index 0000000000000000000000000000000000000000..a8d75b616d7c323aadd9fd658b3c71bb381a95eb
--- /dev/null
+++ b/briar-android/src/org/briarproject/plugins/tor/TorNetworkMetadata.java
@@ -0,0 +1,39 @@
+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
+	// 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"
+	));
+
+	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;
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java
index 6112889ffb7a654ff8743d64fca94047009387a7..99e92959233126a39be059c905bbb82eb4edbbfb 100644
--- a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java
+++ b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java
@@ -44,6 +44,7 @@ import org.briarproject.util.StringUtils;
 
 import socks.Socks5Proxy;
 import socks.SocksSocket;
+import android.annotation.SuppressLint;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -275,6 +276,7 @@ class TorPlugin implements DuplexPlugin, EventHandler {
 		out.close();
 	}
 
+	@SuppressLint("NewApi")
 	private boolean setExecutable(File f) {
 		if(Build.VERSION.SDK_INT >= 9) {
 			return f.setExecutable(true, true);
diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java b/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java
index 91720ff9f587270275d9997ac26f6f58edda6a70..e5ba67129a3b66338e26cf47d0b915bef9c47791 100644
--- a/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java
+++ b/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java
@@ -1,30 +1,39 @@
 package org.briarproject.plugins.tor;
 
 import java.util.concurrent.Executor;
+import java.util.logging.Logger;
 
 import org.briarproject.api.TransportId;
 import org.briarproject.api.lifecycle.ShutdownManager;
 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;
 
 public class TorPluginFactory implements DuplexPluginFactory {
 
+	private static final Logger LOG =
+			Logger.getLogger(TorPluginFactory.class.getName());
+
 	private static final int MAX_FRAME_LENGTH = 1024;
 	private static final long MAX_LATENCY = 60 * 1000; // 1 minute
 	private static final long POLLING_INTERVAL = 3 * 60 * 1000; // 3 minutes
 
 	private final Executor pluginExecutor;
 	private final Context appContext;
+	private final LocationUtils locationUtils;
 	private final ShutdownManager shutdownManager;
 
 	public TorPluginFactory(Executor pluginExecutor, Context appContext,
-			ShutdownManager shutdownManager) {
+			LocationUtils locationUtils, ShutdownManager shutdownManager) {
 		this.pluginExecutor = pluginExecutor;
 		this.appContext = appContext;
+		this.locationUtils = locationUtils;
 		this.shutdownManager = shutdownManager;
 	}
 
@@ -35,6 +44,11 @@ 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");
+			return null;
+		}
 		return new TorPlugin(pluginExecutor,appContext, shutdownManager,
 				callback, MAX_FRAME_LENGTH, MAX_LATENCY, POLLING_INTERVAL);
 	}
diff --git a/briar-android/src/org/briarproject/system/AndroidLocationUtils.java b/briar-android/src/org/briarproject/system/AndroidLocationUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..2f52682076164118e22478f53bdf93ae13a459b0
--- /dev/null
+++ b/briar-android/src/org/briarproject/system/AndroidLocationUtils.java
@@ -0,0 +1,126 @@
+package org.briarproject.system;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+import java.util.logging.Logger;
+
+import org.briarproject.api.system.LocationUtils;
+
+import roboguice.inject.ContextSingleton;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.location.Address;
+import android.location.Geocoder;
+import android.location.Location;
+import android.location.LocationManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+
+import com.google.inject.Inject;
+
+@ContextSingleton
+class AndroidLocationUtils implements LocationUtils {
+
+	private static final Logger LOG =
+			Logger.getLogger(AndroidLocationUtils.class.getName());
+
+	final Context context;
+	
+	@Inject
+	public AndroidLocationUtils(Context context) {
+		this.context = context;
+	}
+	
+	/**
+	 * 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>
+	 * <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>
+	 * <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>
+	 * </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
+	 * 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;
+		//}
+		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.");
+		return Locale.getDefault().getCountry();
+	}
+	
+	String getCountryFromPhoneNetwork() {
+		TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
+		return tm.getNetworkCountryIso();
+	}
+	
+	String getCountryFromSimCard() {
+		TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
+		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() {
+		Location location = getLastKnownLocation();
+		if (location == null) return null;
+		Geocoder code = new Geocoder(context);
+		try {
+			List<Address> addresses = code.getFromLocation(location.getLatitude(), location.getLongitude(), 1);
+			if (addresses.isEmpty()) return null;
+			return addresses.get(0).getCountryCode();
+		} 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
+	 */
+	Location getLastKnownLocation() {
+		LocationManager locationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
+		Location bestResult = null;
+		long bestTime = Long.MIN_VALUE;
+		for (String provider: locationManager.getAllProviders()) {
+			Location location = locationManager.getLastKnownLocation(provider);
+			if (location == null) continue;
+			long time = location.getTime();
+			if (time > bestTime) {
+				bestResult = location;
+				bestTime = time;
+			}
+		}
+		return bestResult;
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/system/AndroidSystemModule.java b/briar-android/src/org/briarproject/system/AndroidSystemModule.java
index 570d4fbbc79b0fba9f7ceff17c741de14f70b0b0..aa6769e59bf8bcf53723af9494985d1f38e36c6c 100644
--- a/briar-android/src/org/briarproject/system/AndroidSystemModule.java
+++ b/briar-android/src/org/briarproject/system/AndroidSystemModule.java
@@ -2,6 +2,7 @@ package org.briarproject.system;
 
 import org.briarproject.api.system.Clock;
 import org.briarproject.api.system.FileUtils;
+import org.briarproject.api.system.LocationUtils;
 import org.briarproject.api.system.SeedProvider;
 import org.briarproject.api.system.Timer;
 
@@ -14,5 +15,6 @@ public class AndroidSystemModule extends AbstractModule {
 		bind(Timer.class).to(SystemTimer.class);
 		bind(SeedProvider.class).to(AndroidSeedProvider.class);
 		bind(FileUtils.class).to(AndroidFileUtils.class);
+		bind(LocationUtils.class).to(AndroidLocationUtils.class);
 	}
 }
diff --git a/briar-api/src/org/briarproject/api/system/LocationUtils.java b/briar-api/src/org/briarproject/api/system/LocationUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..c0c82844db7e3ae67b9e1c8979b55a73c076c20f
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/system/LocationUtils.java
@@ -0,0 +1,13 @@
+package org.briarproject.api.system;
+
+public interface LocationUtils {
+	
+	/** 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="
+	 * https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha 2</a>.
+	 */
+	String getCurrentCountry();
+
+}