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..b010506f53dada8241dbfdcd17684600d2cb0071
--- /dev/null
+++ b/onionwrapper-android/src/main/java/org/briarproject/onionwrapper/AndroidLocationUtilsFactory.java
@@ -0,0 +1,11 @@
+package org.briarproject.onionwrapper;
+
+import android.app.Application;
+
+public class AndroidLocationUtilsFactory {
+
+    public static AndroidLocationUtils createAndroidLocationUtils(Application app) {
+        return new AndroidLocationUtils(app);
+    }
+
+}
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();
+}