Android 意外的telephonyManager.getSimCountryIso()行为

Android 意外的telephonyManager.getSimCountryIso()行为,android,localization,Android,Localization,我正在创建一个应用程序,根据您的移动网络提供商来自哪个国家,它会显示来自同一国家的所有可选移动网络提供商的列表。为了实现这一点,我使用检索国家代码 android开发者的官方文档说:“返回SIM卡提供商国家代码的ISO国家代码”,因此,我希望国家代码始终保持不变,与设备位置无关。但事实并非如此! 例如我最近遇到过这种情况: 我有一个西班牙的android设备,带有SIM卡,属于一家西班牙网络提供商。因此,如果我在西班牙,telephonyManager.getSimCountryIso()返回“

我正在创建一个应用程序,根据您的移动网络提供商来自哪个国家,它会显示来自同一国家的所有可选移动网络提供商的列表。为了实现这一点,我使用检索国家代码

android开发者的官方文档说:“返回SIM卡提供商国家代码的ISO国家代码”,因此,我希望国家代码始终保持不变,与设备位置无关。但事实并非如此! 例如我最近遇到过这种情况: 我有一个西班牙的android设备,带有SIM卡,属于一家西班牙网络提供商。因此,如果我在西班牙,telephonyManager.getSimCountryIso()返回“es”。到那时一切都很好问题是,例如,当我去法国旅行时,我调试应用程序并找到电话管理员。getSimCountryIso()返回国家代码:“nl”(来自荷兰!?我在法国漫游,但使用的是同一张西班牙SIM卡!)。我使用的是与西班牙相同的设备和SIM卡,因此国家代码ISO应仍然为“es”

我的问题是这种方法实际上是如何工作的?如果我使用的是西班牙SIM卡,为什么会收到国家代码“nl”(荷兰)


提前感谢您的帮助

您可以使用MCCMNC获取SIM卡国家/地区,它是SIM卡配置的,与您所在的网络无关

Configuration config = getResources().getConfiguration();
int countryCode = config.mcc;
您可以在这里找到MCC列表

例如,西班牙是214,法国是208

MCC应在所有带有SIM卡的GSM设备上工作,但在CDMA网络上不可靠

对于CDMA设备,我发现

if(telephonyManager.getPhoneType()==telephonyManager.PHONE\u TYPE\u CDMA){
Class c=Class.forName(“android.os.SystemProperties”);
方法get=c.getMethod(“get”,String.class);
//中冶+跨国公司
字符串同源运算符=((字符串)get.invoke(c,“ro.cdma.home.operator.numeric”);
String country=homeOperator.substring(0,3);//最后三位是MNC
}否则{
配置配置=getResources().getConfiguration();
int countryCode=config.mcc;
}

您可以尝试从TelephonyManager(SIM或CDMA设备)获取国家/地区代码,如果不可用,请尝试从本地配置获取。这是一个完整的例子

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;
}
私有静态字符串getDeviceCountryCode(上下文){
字符串国家代码;
//尝试从TelephonyManager服务获取国家/地区代码
TelephonyManager tm=(TelephonyManager)context.getSystemService(context.TELEPHONY_服务);
如果(tm!=null){
//查询第一个getSimCountryIso()
countryCode=tm.getSimCountryIso();
if(countryCode!=null&&countryCode.length()=2)
返回countryCode.toLowerCase();
if(tm.getPhoneType()==TelephonyManager.PHONE\u TYPE\u CDMA){
//CDMA设备的特殊情况
countryCode=getCDMACountryIso();
}否则{
//对于3G设备(带SIM卡)查询getNetworkCountryIso()
countryCode=tm.getNetworkCountryIso();
}
if(countryCode!=null&&countryCode.length()=2)
返回countryCode.toLowerCase();
}
//如果网络国家/地区不可用(可能是平板电脑),请从Locale类获取国家/地区代码
if(Build.VERSION.SDK\u INT>=Build.VERSION\u code.N){
countryCode=context.getResources().getConfiguration().getLocales().get(0.getCountry();
}否则{
countryCode=context.getResources().getConfiguration().locale.getCountry();
}
if(countryCode!=null&&countryCode.length()=2)
返回countryCode.toLowerCase();
//向“我们”撤退
返回“我们”;
}
@SuppressLint(“PrivateApi”)
私有静态字符串getCDMACountryIso(){
试一试{
//尝试从SystemProperties私有类获取国家/地区代码
Class systemProperties=Class.forName(“android.os.systemProperties”);
方法get=systemProperties.getMethod(“get”,String.class);
//获取包含MCC+MNC的自动调节器
字符串同源运算符=((字符串)get.invoke(系统属性,
“ro.cdma.home.operator.numeric”);
//homeOperator的前3个字符(MCC)代表国家代码
intmcc=Integer.parseInt(homeOperator.substring(0,3));
//仅映射实际使用CDMA网络的国家
开关(mcc){
案例330:返回“PR”;
案例310:返回“我们”;
案例311:返回“我们”;
案例312:返回“我们”;
案例316:返回“我们”;
案例283:返回“AM”;
案例460:返回“CN”;
案例455:返回“MO”;
案例414:返回“MM”;
判例619:返回“SL”;
案例450:返回“KR”;
案例634:返回“SD”;
判例434:返回“UZ”;
案例232:返回“AT”;
案例204:返回“NL”;
案例262:返回“DE”;
案例247:返回“LV”;
案例255:返回“UA”;
}
}捕获(忽略ClassNotFoundException){
}捕获(忽略NoSuchMethodException){
}捕获(IllegalAccessException被忽略){
}捕获(忽略InvocationTargetException){
}捕获(忽略NullPointerException){
}
返回null;
}
另一个想法是尝试这样的API请求,或者使用


参考资料和

谢谢!只有一个问题,MCC是否总是在任何带有sim卡的设备中可用?你知道为什么telephonyManager.getSimCountryIso()有这种奇怪的行为吗?嗨!深入研究一下可行的MCC解决方案,我有几个疑问。config.mcc是否返回与?不同的mcc?。我也做了一些研究,发现可能并不总是可用的,我猜config.mcc可能只有在设备注册后才可靠
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;
}