From 3ca8138a25239f5f461f9a6f4eb03c84038d8162 Mon Sep 17 00:00:00 2001
From: Torsten Grote <t@grobox.de>
Date: Tue, 2 May 2023 15:07:48 -0300
Subject: [PATCH] Add LocationUtils interface and AndroidLocationUtils

---
 .../onionwrapper/AndroidLocationUtils.java    | 71 +++++++++++++++++++
 .../AndroidLocationUtilsFactory.java          | 11 +++
 .../onionwrapper/LocationUtils.java           | 16 +++++
 3 files changed, 98 insertions(+)
 create mode 100644 onionwrapper-android/src/main/java/org/briarproject/onionwrapper/AndroidLocationUtils.java
 create mode 100644 onionwrapper-android/src/main/java/org/briarproject/onionwrapper/AndroidLocationUtilsFactory.java
 create mode 100644 onionwrapper-core/src/main/java/org/briarproject/onionwrapper/LocationUtils.java

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 0000000..23e8b19
--- /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 0000000..b010506
--- /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 0000000..0e27df4
--- /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();
+}
-- 
GitLab