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可以獲取到,也需要反射才行。

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