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(); +}