diff --git a/onionwrapper-android/build.gradle b/onionwrapper-android/build.gradle
index a37abee070c477cf395d8d55b532f3e92ea9b1d0..d1dd271c13dc8629990583f70e95ab5347207dfd 100644
--- a/onionwrapper-android/build.gradle
+++ b/onionwrapper-android/build.gradle
@@ -28,8 +28,8 @@ android {
 }
 
 dependencies {
-    implementation project(':onionwrapper-core')
-    implementation 'org.briarproject:dont-kill-me-lib:0.2.6'
+    api project(':onionwrapper-core')
+    api 'org.briarproject:dont-kill-me-lib:0.2.7'
 }
 
 mavenPublishing {
diff --git a/onionwrapper-android/src/main/java/org/briarproject/onionwrapper/AndroidLocationUtils.java b/onionwrapper-android/src/main/java/org/briarproject/onionwrapper/AndroidLocationUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..23e8b190469563acd7d0506c95e9f77c52c1b344
--- /dev/null
+++ b/onionwrapper-android/src/main/java/org/briarproject/onionwrapper/AndroidLocationUtils.java
@@ -0,0 +1,71 @@
+package org.briarproject.onionwrapper;
+
+import static android.content.Context.TELEPHONY_SERVICE;
+
+import android.annotation.SuppressLint;
+import android.app.Application;
+import android.content.Context;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+
+import org.briarproject.nullsafety.NotNullByDefault;
+
+import java.util.Locale;
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+
+@NotNullByDefault
+class AndroidLocationUtils implements LocationUtils {
+
+    private static final Logger LOG =
+            Logger.getLogger(AndroidLocationUtils.class.getName());
+
+    private final Context appContext;
+
+    @Inject
+    AndroidLocationUtils(Application app) {
+        appContext = app.getApplicationContext();
+    }
+
+    /**
+     * 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>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}.
+     */
+    @Override
+    @SuppressLint("DefaultLocale")
+    public String getCurrentCountry() {
+        String countryCode = getCountryFromPhoneNetwork();
+        if (!TextUtils.isEmpty(countryCode)) return countryCode.toUpperCase();
+        LOG.info("Falling back to SIM card country");
+        countryCode = getCountryFromSimCard();
+        if (!TextUtils.isEmpty(countryCode)) return countryCode.toUpperCase();
+        LOG.info("Falling back to user-defined locale");
+        return Locale.getDefault().getCountry();
+    }
+
+    private String getCountryFromPhoneNetwork() {
+        Object o = appContext.getSystemService(TELEPHONY_SERVICE);
+        TelephonyManager tm = (TelephonyManager) o;
+        return tm == null ? "" : tm.getNetworkCountryIso();
+    }
+
+    private String getCountryFromSimCard() {
+        Object o = appContext.getSystemService(TELEPHONY_SERVICE);
+        TelephonyManager tm = (TelephonyManager) o;
+        return tm == null ? "" : tm.getSimCountryIso();
+    }
+}
diff --git a/onionwrapper-android/src/main/java/org/briarproject/onionwrapper/AndroidLocationUtilsFactory.java b/onionwrapper-android/src/main/java/org/briarproject/onionwrapper/AndroidLocationUtilsFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..0708ffa218a5e0ebf9f30c74cd36784f2134af25
--- /dev/null
+++ b/onionwrapper-android/src/main/java/org/briarproject/onionwrapper/AndroidLocationUtilsFactory.java
@@ -0,0 +1,14 @@
+package org.briarproject.onionwrapper;
+
+import android.app.Application;
+
+import org.briarproject.nullsafety.NotNullByDefault;
+
+@NotNullByDefault
+public class AndroidLocationUtilsFactory {
+
+    public static LocationUtils createAndroidLocationUtils(Application app) {
+        return new AndroidLocationUtils(app);
+    }
+
+}
diff --git a/onionwrapper-android/src/main/java/org/briarproject/onionwrapper/AndroidTorWrapper.java b/onionwrapper-android/src/main/java/org/briarproject/onionwrapper/AndroidTorWrapper.java
index 137d9eaf4524e3c3b5b512b6f82bfed337ecb487..9ddf657d08d3acdf6a3d4ce262ac706be2e438de 100644
--- a/onionwrapper-android/src/main/java/org/briarproject/onionwrapper/AndroidTorWrapper.java
+++ b/onionwrapper-android/src/main/java/org/briarproject/onionwrapper/AndroidTorWrapper.java
@@ -133,7 +133,7 @@ public class AndroidTorWrapper extends AbstractTorWrapper {
 	}
 
 	@Override
-	protected File getObfs4ExecutableFile() {
+    public File getObfs4ExecutableFile() {
 		return obfs4Lib.exists() ? obfs4Lib : super.getObfs4ExecutableFile();
 	}
 
diff --git a/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/AbstractTorWrapper.java b/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/AbstractTorWrapper.java
index fb7722287148d28e00e3a48731584be42f0faedf..fb5f1921ea28ee6302fd548f4cb1dd7e97e44463 100644
--- a/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/AbstractTorWrapper.java
+++ b/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/AbstractTorWrapper.java
@@ -107,7 +107,8 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
 		return new File(torDirectory, "tor");
 	}
 
-	protected File getObfs4ExecutableFile() {
+    @Override
+	public File getObfs4ExecutableFile() {
 		return new File(torDirectory, "obfs4proxy");
 	}
 
diff --git a/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/CircumventionProviderFactory.java b/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/CircumventionProviderFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..e568e43236976093ad5a4fc8cf3647b6febd611f
--- /dev/null
+++ b/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/CircumventionProviderFactory.java
@@ -0,0 +1,12 @@
+package org.briarproject.onionwrapper;
+
+import org.briarproject.nullsafety.NotNullByDefault;
+
+@NotNullByDefault
+public class CircumventionProviderFactory {
+
+    public static CircumventionProvider createCircumventionProvider() {
+        return new CircumventionProviderImpl();
+    }
+
+}
diff --git a/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/LocationUtils.java b/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/LocationUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..0e27df4e12e7f33f8850f9d14c0e1425c3ac3e65
--- /dev/null
+++ b/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/LocationUtils.java
@@ -0,0 +1,16 @@
+package org.briarproject.onionwrapper;
+
+import org.briarproject.nullsafety.NotNullByDefault;
+
+@NotNullByDefault
+public interface LocationUtils {
+
+    /**
+     * Get the country the device is currently located in, or "" if it cannot
+     * be determined.
+     * <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();
+}
diff --git a/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/TorWrapper.java b/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/TorWrapper.java
index d3c2efd9e13e65e553ab67cb44440248f37bbf18..21d05106b955359335bbee002b54888ccc046b18 100644
--- a/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/TorWrapper.java
+++ b/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/TorWrapper.java
@@ -2,6 +2,7 @@ package org.briarproject.onionwrapper;
 
 import org.briarproject.nullsafety.NotNullByDefault;
 
+import java.io.File;
 import java.io.IOException;
 import java.util.List;
 import java.util.logging.Logger;
@@ -16,7 +17,8 @@ public interface TorWrapper {
 	Logger LOG = getLogger(TorWrapper.class.getName());
 
 	/**
-	 * Starts the Tor process.
+	 * Starts the Tor process, but does not yet connect to the Tor Network.
+     * Call {@link #enableNetwork(boolean)} for this.
 	 * <p>
 	 * This method must only be called once. To restart the Tor process, stop
 	 * this wrapper instance and then create a new instance.
@@ -98,6 +100,11 @@ public interface TorWrapper {
 	 */
 	void enableIpv6(boolean ipv6Only) throws IOException;
 
+    /**
+     * Returns the Obfs4 executable as a File for use with Moat.
+     */
+    File getObfs4ExecutableFile();
+
 	/**
 	 * The state of the Tor wrapper.
 	 */
diff --git a/onionwrapper-java/build.gradle b/onionwrapper-java/build.gradle
index 1df23d95103b027d9d1ad27815fbfd779cea60e0..7c975e88bf20c569ecc1304ae7041507c83992a2 100644
--- a/onionwrapper-java/build.gradle
+++ b/onionwrapper-java/build.gradle
@@ -9,7 +9,7 @@ java {
 }
 
 dependencies {
-    implementation project(':onionwrapper-core')
+    api project(':onionwrapper-core')
     def jna_version = '4.5.2'
     implementation "net.java.dev.jna:jna:$jna_version"
     implementation "net.java.dev.jna:jna-platform:$jna_version"
diff --git a/onionwrapper-java/src/main/java/org/briarproject/onionwrapper/JavaLocationUtils.java b/onionwrapper-java/src/main/java/org/briarproject/onionwrapper/JavaLocationUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..033928a21e29e702335c42360146b5d968ac6dab
--- /dev/null
+++ b/onionwrapper-java/src/main/java/org/briarproject/onionwrapper/JavaLocationUtils.java
@@ -0,0 +1,26 @@
+package org.briarproject.onionwrapper;
+
+import org.briarproject.nullsafety.NotNullByDefault;
+
+import java.util.Locale;
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+
+@NotNullByDefault
+class JavaLocationUtils implements LocationUtils {
+
+    private static final Logger LOG =
+            Logger.getLogger(JavaLocationUtils.class.getName());
+
+    @Inject
+    JavaLocationUtils() {
+    }
+
+    @Override
+    public String getCurrentCountry() {
+        LOG.info("Using user-defined locale");
+        return Locale.getDefault().getCountry();
+    }
+
+}
diff --git a/onionwrapper-java/src/main/java/org/briarproject/onionwrapper/JavaLocationUtilsFactory.java b/onionwrapper-java/src/main/java/org/briarproject/onionwrapper/JavaLocationUtilsFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..34b5f16a182b10af61b526d84883f1dee1ae1808
--- /dev/null
+++ b/onionwrapper-java/src/main/java/org/briarproject/onionwrapper/JavaLocationUtilsFactory.java
@@ -0,0 +1,12 @@
+package org.briarproject.onionwrapper;
+
+import org.briarproject.nullsafety.NotNullByDefault;
+
+@NotNullByDefault
+public class JavaLocationUtilsFactory {
+
+    public static LocationUtils createJavaLocationUtils() {
+        return new JavaLocationUtils();
+    }
+
+}