Unexpected telephonyManager.getSimCountryIso() behaviour
Asked Answered
V

2

7

I am creating an app that depending on which country your mobile network provider is from, displays a list of all alternative mobile network providers from that same country. To achieve this, I was retrieving the country code using telephonyManager.getSimCountryIso().

Official android developer docs say : "Returns the ISO country code equivalent for the SIM provider's country code", so from this I was expecting country code will always be always the same independently from device location. But thats not how it actually works! For example I recently experienced this case: I have an android device with SIM card from Spain belonging to a spanish network provider. So if I am in Spain telephonyManager.getSimCountryIso() returns "es". Everything working fine to that point. The problem is when I travel to France for example I debug the app and find out the telephonyManager.getSimCountryIso() is returning country code: "nl" (from Netherlands!? and I am in France in roaming but with the same spanish SIM card!). I am using the same device and the same SIM card than in Spain so country code ISO should still be "es".

My question is How does this method actually work? why am I getting country code "nl" ( Netherlands) if I am using a spanish SIM card?

Thanks in advance for any help

Vivl answered 17/11, 2014 at 11:34 Comment(0)
D
4

You can use MCC MNC to get SIM country, it is SIM configured and has nothing to do with the network you are on.

Configuration config = getResources().getConfiguration();
int countryCode = config.mcc;

You can find MCC list here MccTable.java

For example Spain is 214 and France is 208

MCC should work on all GSM devices with SIM card but it is unreliable on CDMA networks

For CDMA devices I found the following solution

if (telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
    Class<?> c = Class.forName("android.os.SystemProperties");
    Method get = c.getMethod("get", String.class);

    // Gives MCC + MNC
    String homeOperator = ((String) get.invoke(c, "ro.cdma.home.operator.numeric")); 
    String country = homeOperator.substring(0, 3); // the last three digits is MNC 
} else {
    Configuration config = getResources().getConfiguration();
    int countryCode = config.mcc;
}
Deferred answered 17/11, 2014 at 13:25 Comment(6)
Thanks! Just one question, is MCC always available in any device with a sim card? Do you know why telephonyManager.getSimCountryIso() has this strange behavior?Vivl
Hi! looking a little bit deeper into the posible MCC solution I have several doubts. Does config.mcc return a different MCC than telephonyManager.getNetworkCountryIso ()?. I have also made a little bit of research and found out that mcc might not always be available and I am guessing that config.mcc might only be reliable if device is registered on gsm networks and not CDMA.Vivl
If I remember correctly outgoing MMSs had device phone number stored in the database, maybe you can look for it and try to figure out the country from it.Deferred
Same goes for getSimCountryIso, "Result may be unreliable on CDMA networks".Deferred
Thanks for the las update. Helps for CDMA networks, although I found out that you can't rely on any of the mentioned methods in my post or yours when the phone is on roaming status, becvasue SIM have different IMSI codes when they are on romaing networksVivl
Have you found any reliable way to find sim's original country? So that it would suit both GSM and CDMA and will stay unchanged while roaming?Cuesta
R
3

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

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 3 chars (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, or to use fine location.

References here and here

Ripp answered 14/9, 2018 at 8:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.