Android双卡设备 如何正确获取上网卡运营商类型

关于Android获取运营商的文章已经很多,大部分都是没有深入测试,在双卡设备上存在读取错误信息的问题,这里给出总结。

首先是读取SIM卡权限的问题

注册

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

动态获取 读SIM卡的权限

获取权限的提示框

在动态获取SIM卡权限时,这里的权限弹窗提示特别让人恶心,总是听人吐槽:一个视频播放软件,要什么拨打电话权限!!!需要一个友好的引导提示。

判断是否拥有读取SIM卡权限:

if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {}

存在误导的API

如果你百度获取SIM卡的运营商时,一般都会看到这个getNetworkOperator函数。从函数名上来看,确认了名称,注定要入坑。如果在双卡设备上反复测试,你就会怀疑这是什么鬼函数名,不按预期变化。事实上,这里的Network是蜂窝移动网络,不是万维网的数据流量。毕竟在手机出现之初期,只有打电话的能力,而且全部的移动设备本身就组织成了一个复杂的通话网络。所以,这里的getNetworkOperator是获取默认通话的运营商而已。

getNetworkOperator获取默认通话的运营商

较为靠谱API

getSimOperator能获取到上网卡的运营商。
从函数名上,很难想象出,这个函数是在读取上网卡信息,能准确获取到上网卡的运营商。

getSimOperator获取上网卡的运营商

android-20 源码

    /**
     * Returns the MCC+MNC (mobile country code + mobile network code) of the
     * provider of the SIM. 5 or 6 decimal digits.
     * <p>
     * Availability: SIM state must be {@link #SIM_STATE_READY}
     *
     * @see #getSimState
     */
    public String getSimOperator() {
        return SystemProperties.get(TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC);
    }

android-21 源码

    /**
     * Returns the MCC+MNC (mobile country code + mobile network code) of the
     * provider of the SIM. 5 or 6 decimal digits.
     * <p>
     * Availability: SIM state must be {@link #SIM_STATE_READY}
     *
     * @see #getSimState
     */
    public String getSimOperator() {
        long subId = getDefaultSubscription();
        Rlog.d(TAG, "getSimOperator(): default subId=" + subId);
        return getSimOperator(subId);
    }

利用getSimOperator获取上网卡运营商的调用如下:


    public static CarrierType getCarrierTypeAsFarAsPossible() {
        Context context = getApplication();
        if (context != null) {
            TelephonyManager telManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
            if (telManager == null) {
                return CarrierType.UNKNOWN;
            }
            String operator = telManager.getSimOperator();
            if (operator != null) {
                return parseOperatorCode(operator);
            }
        }
        return CarrierType.UNKNOWN;
    }

    public static CarrierType parseOperatorCode(String operatorCode) {
        if (operatorCode == null || "".equals(operatorCode)) return UNKNOWN;
        switch (operatorCode) {
            case "46000":
            case "46002":
            case "46007":
            case "46008":
                return MOBILE;
            case "46001":
            case "46006":
            case "46009":
                return UNICOM;
            case "46003":
            case "46005":
            case "46011":
                return TELECOM;
        }
        return UNKNOWN;
    }

另一个存在误导的API

在上述的getSimOperator中,成功的获取了运营的operatorCode,这里的code是5数字的字符串,并不是运营商直接给出的名称。
那什么是运营商给出的名称呢?看下图:

运营自己下发的名称

那如何获取这个运营商下发的名称呢?有一个API是getSimOperatorName,又是一个确认过名称,注定要入坑的API。这个函数看着像是获取上网卡的运营商名称,实际获取的是默认打电话的运营商名称,与getNetworkOperator变化是同样的规律。

比较推荐的方式是getSimOperator获取到operatorCode,再判断移动联通电信。
运营商直接给出的名称会非常的乱,以联通为例,可能会有CUCC、UNICOM、中国联通、中国联通 3G、中国联通 4G等等,非常个性,不可预测,取决于当地运营商大佬想干嘛就干嘛。

如何精确获取上网卡运营商

分系统版本来看待双卡

官方文档只支持5.1及其之后的系统提供双卡API。对于之前的系统版本,目前的市场占比已经很少了。采用的策略是放弃不支持的机器,因为这边对精度要求非常高,可以判断不出来运营商,但不能判断出错。

5.0及其之前 android.os.Build.VERSION.SDK_INT<=21

这部分放弃,将其归入没有获取到系统权限的一样,当做读不出运营商看待。

5.1及其之后 android.os.Build.VERSION.SDK_INT>=22

精准获取上网卡运营商的调用如下:

    public static CarrierType getCurrentCarrierType() {
        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
            return UNKNOWN;
        }
        Context context = getApplication();
        if (context == null) {
            return UNKNOWN;
        }
        TelephonyManager telMag = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        if (telMag == null) {
            return UNKNOWN;
        }
        return parseOperatorCode(telMag.getSimOperator());
    }
    
    public static CarrierType parseOperatorCode(String operatorCode) {
        if (operatorCode == null || "".equals(operatorCode)) return UNKNOWN;
        switch (operatorCode) {
            case "46000":
            case "46002":
            case "46007":
            case "46008":
                return MOBILE;
            case "46001":
            case "46006":
            case "46009":
                return UNICOM;
            case "46003":
            case "46005":
            case "46011":
                return TELECOM;
        }
        return UNKNOWN;
    }

注意

getNetworkOperator不是获取上网卡运营商
getSimOperatorName不是获取上网卡运营商的名称

获取默认通话、默认信息卡

关于获取默认通话的SIM卡

获取默认通话的SIM卡,基本上,大量API都是获取默认通话SIM卡的。在Android5.1之前的版本中,只能获取到默认通话SIM,在Android5.1及其之后,可以用getDefaultVoiceSubscriptionInfo获取到默认通话SIM卡信息。

关于获取默认发信息的SIM卡

在Android5.1及其之后,用getDefaultSmsSubscriptionInfo可以获取到。同样,这是个被隐藏的方法,需要反射。

默认上网卡信息

在5.1及其之后,用getDefaultDataSubscriptionInfo可以获取到,也需要反射才行。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章