89

An Android mobile actually does know quite well where it is - but is there a way of retrieving the country by something, like a country code or country name?

There isn't any need to know the exact GPS position - the country code or name is sufficient, and I am using this code:

 String locale = context.getResources().getConfiguration().locale.getCountry(Locale.getDefault());
 System.out.println("country = "+locale);

It gives me the code "US", but my device kept in India. Is there a way to find the device's current country code without using GPS or a network provider?

Because I am using a tablet.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Vikas Goyal
  • 1,019
  • 1
  • 9
  • 12

10 Answers10

122

You can simply use this code,

TelephonyManager tm = (TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE);
String countryCodeValue = tm.getNetworkCountryIso();

This will return 'US' if your current connected network is in the United States. This works without a SIM card even.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Kishath
  • 5,560
  • 4
  • 17
  • 22
  • Good answer. Seems to be more reliable than what's set in the configuration – Itai Hanski Nov 30 '14 at 10:31
  • The phone should some source of connection. Is WiFi on? – Kishath Jul 07 '15 at 07:22
  • 6
    It doesn't work for devices without SIM Cards or Tablet that works only in Wifi – Alberto Anderick Jr Dec 21 '15 at 22:48
  • 10
    all TelephonyManager required permissions https://developer.android.com/reference/android/telephony/TelephonyManager.html – Ben.Slama.Jihed Jan 22 '18 at 13:08
  • It doesn't work without the SIM card on Mate 10 Lite – Ahmed Awadallah Dec 02 '18 at 10:44
  • This will return "us" not "US" in my phone (Samsung Note 9). So be careful of it. – Sam Chen Jun 02 '20 at 22:27
  • 7
    @Ben.Slama.Jihed Not all. According to the doc you referred: "Note that access to some telephony information is permission-protected." And I think `getNetworkCountryIso()` does not require extra permission. – Twisted Lullaby Nov 06 '20 at 08:22
  • Original question mentions "device", not mobile phone. It's a flawed assumption that there is even a telephony subsystem. – Mike Macpherson Feb 19 '22 at 11:58
  • 4
    This doesn't require any permission, yet it requires the FEATURE_TELEPHONY_RADIO_ACCESS feature, which means-as noted by @AlbertoAnderickJr -that for this to work the device must have a SIM card. Accordingly, you can check if the device supports this feature using PackageManager.hasSystemFeature as noted here: https://developer.android.com/reference/android/telephony/TelephonyManager#getNetworkCountryIso() – Mohamed Salah Feb 22 '22 at 05:14
112

You shouldn't be passing anything in to getCountry(). Remove Locale.getDefault():

String locale = context.getResources().getConfiguration().locale.getCountry();
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Rawkode
  • 21,990
  • 5
  • 38
  • 45
  • 127
    This will return the country of the Locale that the user configured. If the user configures the device for India and then takes it on a plane to Germany, you'll still get the country code for India. Just thought I would clarify that. – David Wasser Jul 02 '12 at 16:27
  • thanks Rawkode and David for your quick reply.. actually i already used getCountry() but it still shows "US" but as David says about device configuration so tell me how can i configure my device i searched it in settings but i couldn't find it.. :( – Vikas Goyal Jul 03 '12 at 07:55
  • 5
    I live in Belgium and my device is in English/US, I am not sure this code will give the country ;-) – Waza_Be Oct 14 '14 at 10:19
  • 2
    Is there any chance, no matter how slim that that call returns "" or null? Not an actual country code? – Scorpio Jan 29 '15 at 14:47
  • im not able to get india country code plz help me out – Shikhar Jan 22 '16 at 04:44
  • I am in india, checking in mobile without sim , but it is showing US – Stephen Aug 12 '16 at 08:18
  • 11
    This has been deprecated in API level 24. – Egemen Hamutçu Jun 06 '17 at 14:16
  • 1
    @EgemenHamutçu you can use Configuration#getLocales, or more practically ConfigurationCompat#getLocales – androidguy Nov 11 '18 at 21:11
  • 1
    Always return empty string – M.SH Aug 04 '19 at 22:54
  • this doesn't return any country even the not deprecated version, like `context.resources.configuration.locales` returns: "" – Amir Aug 19 '21 at 00:32
  • How to get a Country code? For example, India has 91, the US has country code 1. Is there any API? – Rohit Singh Sep 27 '21 at 15:25
  • As mentioned, `locale` is deprecated. You can replace `context.resources.configuration.locale.country` with the following `context.resources.configuration.locales[0].country` – James Mar 30 '22 at 03:49
63

Use the link http://ip-api.com/json. This will provide all the information as JSON. From this JSON content you can get the country easily. This site works using your current IP address. It automatically detects the IP address and sendback details.

Documentation

This is what I got:

{
"as": "AS55410 C48 Okhla Industrial Estate, New Delhi-110020",
"city": "Kochi",
"country": "India",
"countryCode": "IN",
"isp": "Vodafone India",
"lat": 9.9667,
"lon": 76.2333,
"org": "Vodafone India",
"query": "123.63.81.162",
"region": "KL",
"regionName": "Kerala",
"status": "success",
"timezone": "Asia/Kolkata",
"zip": ""
}

N.B. - As this is a third-party API, do not use it as the primary solution. And also I am not sure whether it's free or not.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
shine_joseph
  • 2,922
  • 4
  • 22
  • 44
  • @KhizarHayat using a simple restfull api call – Hamzeh Soboh Dec 18 '17 at 10:59
  • 2
    will this provide correct country while using vpn too ? – Vivek Mishra Apr 18 '18 at 11:01
  • 2
    Aha! I'm lying here on my sofa in Moscow, Russia, and it gives me {"as":"AS15169 Google LLC","city":"Ashburn","country":"United States","countryCode":"US","isp":"Google LLC","lat":39.0438,"lon":-77.4874,"org":"Google LLC","query":"66.102.9.142","region":"VA","regionName":"Virginia","status":"success","timezone":"America/New_York","zip":"20149"} – Eugene Kartoyev Sep 28 '19 at 21:59
17

The checked answer has deprecated code. You need to implement this:

String locale;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    locale = context.getResources().getConfiguration().getLocales().get(0).getCountry();
} else {
    locale = context.getResources().getConfiguration().locale.getCountry();
}
Egemen Hamutçu
  • 1,602
  • 3
  • 22
  • 34
17

I have created a utility function (tested once on a device where I was getting an incorrect country code based on locale).

Reference: CountryCodePicker.java

fun getDetectedCountry(context: Context, defaultCountryIsoCode: String): String {
    return detectSIMCountry(context)
        ?: detectNetworkCountry(context)
        ?: detectLocaleCountry(context)
        ?: defaultCountryIsoCode
}

private fun detectSIMCountry(context: Context): String? {
    try {
        val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
        Log.d(TAG, "detectSIMCountry: ${telephonyManager.simCountryIso}")
        return telephonyManager.simCountryIso
    }
    catch (e: Exception) {
        e.printStackTrace()
    }
    return null
}

private fun detectNetworkCountry(context: Context): String? {
    try {
        val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
        Log.d(TAG, "detectNetworkCountry: ${telephonyManager.networkCountryIso}")
        return telephonyManager.networkCountryIso
    }
    catch (e: Exception) {
        e.printStackTrace()
    }
    return null
}

private fun detectLocaleCountry(context: Context): String? {
    try {
        val localeCountryISO = context.resources.configuration.locales[0].country
        Log.d(TAG, "detectLocaleCountry: $localeCountryISO")
        return localeCountryISO
    }
    catch (e: Exception) {
        e.printStackTrace()
    }
    return null
}
James
  • 4,573
  • 29
  • 32
Shubham AgaRwal
  • 4,355
  • 8
  • 41
  • 62
12

Here is a complete example. It tries to get the country code from TelephonyManager (from SIM or CDMA devices), and if not available, tries to get it from the local configuration.

private static String getDeviceCountryCode(Context context) {
    String countryCode;

    // Try to get country code from TelephonyManager service
    TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
    if(tm != null) {
        // Query first getSimCountryIso()
        countryCode = tm.getSimCountryIso();
        if (countryCode != null && countryCode.length() == 2)
            return countryCode.toLowerCase();

        if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
            // Special case for CDMA Devices
            countryCode = getCDMACountryIso();
        }
        else {
            // For 3G devices (with SIM) query getNetworkCountryIso()
            countryCode = tm.getNetworkCountryIso();
        }

        if (countryCode != null && countryCode.length() == 2)
            return countryCode.toLowerCase();
    }

    // If network country not available (tablets maybe), get country code from Locale class
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        countryCode = context.getResources().getConfiguration().getLocales().get(0).getCountry();
    }
    else {
        countryCode = context.getResources().getConfiguration().locale.getCountry();
    }

    if (countryCode != null && countryCode.length() == 2)
        return  countryCode.toLowerCase();

    // General fallback to "us"
    return "us";
}

@SuppressLint("PrivateApi")
private static String getCDMACountryIso() {
    try {
        // Try to get country code from SystemProperties private class
        Class<?> systemProperties = Class.forName("android.os.SystemProperties");
        Method get = systemProperties.getMethod("get", String.class);

        // Get homeOperator that contain MCC + MNC
        String homeOperator = ((String) get.invoke(systemProperties,
                "ro.cdma.home.operator.numeric"));

        // First three characters (MCC) from homeOperator represents the country code
        int mcc = Integer.parseInt(homeOperator.substring(0, 3));

        // Mapping just countries that actually use CDMA networks
        switch (mcc) {
            case 330: return "PR";
            case 310: return "US";
            case 311: return "US";
            case 312: return "US";
            case 316: return "US";
            case 283: return "AM";
            case 460: return "CN";
            case 455: return "MO";
            case 414: return "MM";
            case 619: return "SL";
            case 450: return "KR";
            case 634: return "SD";
            case 434: return "UZ";
            case 232: return "AT";
            case 204: return "NL";
            case 262: return "DE";
            case 247: return "LV";
            case 255: return "UA";
        }
    }
    catch (ClassNotFoundException ignored) {
    }
    catch (NoSuchMethodException ignored) {
    }
    catch (IllegalAccessException ignored) {
    }
    catch (InvocationTargetException ignored) {
    }
    catch (NullPointerException ignored) {
    }

    return null;
}

Also another idea is to try an API request like in this answer.

References are here and here.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
radu_paun
  • 1,940
  • 21
  • 25
  • In case anyone wondered, like me, the data source of the worldwide CDMA networks, it was WIkipedia https://en.wikipedia.org/wiki/List_of_CDMA2000_networks – androidguy Nov 11 '18 at 21:24
9

For some devices, if the default language is set different (an Indian can set English (US)) then

context.getResources().getConfiguration().locale.getDisplayCountry();

will give the wrong value. So this method is not reliable.

Also, getNetworkCountryIso() method of TelephonyManager will not work on devices which don't have SIM card (Wi-Fi tablets).

If a device doesn't have a SIM card then we can use the time zone to get the country. For countries like India, this method will work.

Sample code used to check the country is India or not.

Use the below values in your constants file

(Constants.INDIA_TIME_ZONE_ID: "asia/calcutta", Constants.NETWORK_INDIA_CODE :"in")

And in your activity, add the following code:

private void checkCountry() {

    TelephonyManager telMgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
    if (telMgr == null)
        return;

    int simState = telMgr.getSimState();

    switch (simState) {

        // If a SIM card is not available then the
        // country is found using the timezone ID
        case TelephonyManager.SIM_STATE_ABSENT:
            TimeZone tz = TimeZone.getDefault();
            String timeZoneId = tz.getID();
            if (timeZoneId.equalsIgnoreCase(Constants.INDIA_TIME_ZONE_ID)) {
               // Do something
            }
            else {
               // Do something
            }
            break;

        // If a SIM card is available then the telephony
        // manager network country information is used
        case TelephonyManager.SIM_STATE_READY:

            if (telMgr != null) {
                String countryCodeValue = tm.getNetworkCountryIso();

                // Check if the network country code is "in"
                if (countryCodeValue.equalsIgnoreCase(Constants.NETWORK_INDIA_CODE)) {
                   // Do something
                }
                else {
                   // Do something
                }
            }
            break;
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
MarGin
  • 2,078
  • 1
  • 17
  • 28
4

If you wish to get the country code without asking for any permission, you can choose a tricky way.

The method simply uses an API to get the country code, and there aren't any third-party libraries to depend on. We can create one for us.

Here I have used Google Cloud Functions to write an API and it is so effortless.

Step 1: Create a Google Cloud Account, and set up billing (the free tier is enough)

Step 2: Create a cloud function to get the geo location

Copy this basic function to the code editor of index.js:

const cors = require('cors')

function _geolocation(req, res) {
  const data = {
    country_code: req.headers["x-appengine-country"],
    region: req.headers["x-appengine-region"],
    city: req.headers["x-appengine-city"],
    cityLatLong: req.headers["x-appengine-citylatlong"],
    userIP: req.headers["x-appengine-user-ip"]
  }

  res.json(data)
};

exports.geolocation = (req, res) => {
  const corsHandler = cors({ origin: true })

  return corsHandler(req, res, function() {
    return _geolocation(req, res);
  });
};

Also we need to copy the package.json definition:

{
  "name": "gfc-geolocation",
  "version": "0.0.1",
  "dependencies": {
    "cors": "^2.8.4"
  }
}

Step 3: finish, and get the URL similar to: "https://us-central1-geolocation-mods-sdde.cloudfunctions.net/geolocation"

Step 4: parse the JSON response and get the country code

The response will look like:

{
   "country": "IN",
   "region": "kl",
   "city": "kochi",
   "cityLatLong": "9.9312,76.2673",
   "userIP": "xx.xx.xx.xx"
}

Thanks and credits go to the Medium article: Free IP-based Geolocation with Google Cloud Functions

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Blue_Alien
  • 2,148
  • 2
  • 25
  • 29
3

There isn't any need to call any API. You can get the country code from your device where it is located. Just use this function:

 fun getUserCountry(context: Context): String? {
    try {
        val tm = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
        val simCountry = tm.simCountryIso
        if (simCountry != null && simCountry.length == 2) { // SIM country code is available
            return simCountry.toLowerCase(Locale.US)
        } 
        else if (tm.phoneType != TelephonyManager.PHONE_TYPE_CDMA) { // Device is not 3G (would be unreliable)
            val networkCountry = tm.networkCountryIso
            if (networkCountry != null && networkCountry.length == 2) { // network country code is available
                return networkCountry.toLowerCase(Locale.US)
            }
        }
    }
    catch (e: Exception) {
    }
    return null
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
pavel
  • 1,603
  • 22
  • 19
0

To get country code, we can also perform an HTTP call (Here, I use a third-party link, you can also buy the services to get extra information in JSON https://ipinfo.io/ ). Here, when we call the API service, they trace our IP and provide information accordingly.

<uses-permission android:name="android.permission.INTERNET" />

public String makeServiceCall() {
    String response = null;
    String reqUrl = "https://ipinfo.io/country";
    try {
        URL url = new URL(reqUrl);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        // read the response
        InputStream in = new BufferedInputStream(conn.getInputStream());
        response = convertStreamToString(in);
    } catch (MalformedURLException e) {
        Log.e(TAG, "MalformedURLException: " + e.getMessage());
    } catch (ProtocolException e) {
        Log.e(TAG, "ProtocolException: " + e.getMessage());
    } catch (IOException e) {
        Log.e(TAG, "IOException: " + e.getMessage());
    } catch (Exception e) {
        Log.e(TAG, "Exception: " + e.getMessage());
    }
    return response;
}
  • 1
    Please don't post only code as an answer, but also provide an explanation of what your code does and how it solves the problem of the question. Answers with an explanation are usually more helpful and of better quality, and are more likely to attract upvotes. – Ran Marciano Feb 19 '21 at 07:50
  • It's always risky to use third party online services. You don't have any control over them, what so ever (what if the service is permanently removed?), there may be legal issues consuming them (incompatible licenses etc), or even moral aspects (what if it goes viral that the owner of the service likes to kill kittens and puppies for fun? Is that something you want to be associated with?). I would be very careful with adding this and similar code snippets to any code base other than internal or dev/demo apps. – dbm May 20 '21 at 07:11
  • 1
    if user uses proxy then this is not correct – Amir Aug 19 '21 at 00:34
  • Are you seriously joking? What if there is no internet connection? – user1034912 Sep 10 '21 at 02:16