How can I get my Android device country code without using GPS?
Asked Answered
E

11

92

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.

Echolalia answered 2/7, 2012 at 12:24 Comment(1)
L
112

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

String locale = context.getResources().getConfiguration().locale.getCountry();
Lucila answered 2/7, 2012 at 12:28 Comment(12)
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.Cahilly
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.. :(Echolalia
I live in Belgium and my device is in English/US, I am not sure this code will give the country ;-)Dzungaria
Is there any chance, no matter how slim that that call returns "" or null? Not an actual country code?Nedrud
im not able to get india country code plz help me outState
I am in india, checking in mobile without sim , but it is showing USVeld
This has been deprecated in API level 24.Disencumber
@EgemenHamutçu you can use Configuration#getLocales, or more practically ConfigurationCompat#getLocalesNert
Always return empty stringGothard
this doesn't return any country even the not deprecated version, like context.resources.configuration.locales returns: ""Sporadic
How to get a Country code? For example, India has 91, the US has country code 1. Is there any API?Tiny
As mentioned, locale is deprecated. You can replace context.resources.configuration.locale.country with the following context.resources.configuration.locales[0].countryDelphina
M
127

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.

Mineralize answered 14/10, 2014 at 8:31 Comment(9)
Good answer. Seems to be more reliable than what's set in the configurationDunsany
The phone should some source of connection. Is WiFi on?Mineralize
It doesn't work for devices without SIM Cards or Tablet that works only in WifiBefuddle
all TelephonyManager required permissions developer.android.com/reference/android/telephony/…Pharmacopoeia
It doesn't work without the SIM card on Mate 10 LiteIrina
This will return "us" not "US" in my phone (Samsung Note 9). So be careful of it.Dietary
@Pharmacopoeia 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.Wastebasket
Original question mentions "device", not mobile phone. It's a flawed assumption that there is even a telephony subsystem.Evzone
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: developer.android.com/reference/android/telephony/…Eam
L
112

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

String locale = context.getResources().getConfiguration().locale.getCountry();
Lucila answered 2/7, 2012 at 12:28 Comment(12)
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.Cahilly
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.. :(Echolalia
I live in Belgium and my device is in English/US, I am not sure this code will give the country ;-)Dzungaria
Is there any chance, no matter how slim that that call returns "" or null? Not an actual country code?Nedrud
im not able to get india country code plz help me outState
I am in india, checking in mobile without sim , but it is showing USVeld
This has been deprecated in API level 24.Disencumber
@EgemenHamutçu you can use Configuration#getLocales, or more practically ConfigurationCompat#getLocalesNert
Always return empty stringGothard
this doesn't return any country even the not deprecated version, like context.resources.configuration.locales returns: ""Sporadic
How to get a Country code? For example, India has 91, the US has country code 1. Is there any API?Tiny
As mentioned, locale is deprecated. You can replace context.resources.configuration.locale.country with the following context.resources.configuration.locales[0].countryDelphina
S
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.

Sequel answered 5/11, 2014 at 9:30 Comment(3)
@KhizarHayat using a simple restfull api callLiebermann
will this provide correct country while using vpn too ?Scevor
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"}Charterhouse
S
18

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
}
Sperling answered 3/7, 2018 at 7:8 Comment(0)
M
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();
}
Mccollum answered 6/6, 2017 at 14:19 Comment(2)
This always return USBiosphere
Jip, I live in South Africa, all my devices are defaulted to USFalzetta
U
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.

Unprincipled answered 14/9, 2018 at 8:9 Comment(1)
In case anyone wondered, like me, the data source of the worldwide CDMA networks, it was WIkipedia en.wikipedia.org/wiki/List_of_CDMA2000_networksNert
P
10

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;
    }
}
Pigsty answered 26/10, 2018 at 11:44 Comment(4)
What need to import for Constants? I am not getting NETWORK_INDIA_CODE after importing all available Constants .Hesta
@PrantikMondal that is actually a class created by me, NETWORK_INDIA_CODE is not an android default constant. You can directly use the value "in"Pigsty
why do you get TelephonyManager twice? Once should be enoughFalzetta
GetTelephony won't work as well if there are 2 sim card slots!Birkle
F
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

Famine answered 29/5, 2020 at 6:40 Comment(0)
W
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
}
Whether answered 23/10, 2019 at 10:41 Comment(3)
Does this always work? Are there cases that even this fails? What does "networkCountryIso" return, for example?Optimism
@Famine ya probablyWhether
2021 this is now very restricted due to Privacy Law ChangesBirkle
O
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;
}
Operation answered 19/2, 2021 at 7:23 Comment(4)
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.Loiret
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.Hick
if user uses proxy then this is not correctSporadic
Are you seriously joking? What if there is no internet connection?Birkle
P
0

Both @Kishath and @Rawkode wrote good answers but I think TelephonyManager is a better choice since it's more accurate. Unfortunately it's not always reliable. For example if you put your device in airplane mode, you don't have a SIM, or for some reason you don't get cell phone reception, well tm.networkCountryIso will return an empty string which is not ideal. Since resources will always returns something, I use that as backup option. It works pretty well for my use case.

  /**
   * Retrieves the country code of the current network the device is connected to.
   * If the network country ISO code is not available, it retrieves the country code from the device's locale.
   *
   * @return A string representing the ISO country code. The country code is converted to uppercase.
   */
  private fun getCountryCode() : String {
    // Get the TelephonyManager from the system services
    val tm = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
    
    // Get the network country ISO code and convert it to uppercase
    val code = tm.networkCountryIso.uppercase()

    // If the country code is empty, retrieve the country code from the device's locale
    if (code.isEmpty()) {
      return context.resources.configuration.locales.get(0).country.uppercase()
    }
    return code
  }
Philan answered 26/3, 2024 at 16:27 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.