Android中的mcc和mnc

MCC与MNC解析
本文介绍了MCC(移动国家码)和MNC(移动网络码)的基本概念及其在Android系统中的实现方式,包括如何通过MCC确定国家代码以及其对系统配置的影响。

mcc和mnc概述

mcc和mnc见 MCC(移动国家码)和 MNC(移动网络码)

MCC:Mobile Country Code,移动国家码,MCC的资源由国际电联(ITU)统一分配和管理,唯一识别移动用户所属的国家,共3位,中国为460;  

MNC:Mobile Network Code,移动网络码,共2位,中国移动TD系统使用00,中国联通GSM系统使用01,中国移动GSM系统使用02,中国电信CDMA系统使用03

MccTable

Android中相关的代码在telephony framwork中,主要是frameworks/opt/telephony/src/java/com/android/internal/telephony/MccTable.java。

static {
        sTable = new ArrayList<MccEntry>(240);


        /*
         * The table below is built from two resources:
         *
         * 1) ITU "Mobile Network Code (MNC) for the international
         *   identification plan for mobile terminals and mobile users"
         *   which is available as an annex to the ITU operational bulletin
         *   available here: http://www.itu.int/itu-t/bulletin/annex.html
         *
         * 2) The ISO 3166 country codes list, available here:
         *    http://www.iso.org/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/index.html
         *
         * This table has not been verified.
         */

		sTable.add(new MccEntry(202,"gr",2));	//Greece
		sTable.add(new MccEntry(204,"nl",2));	//Netherlands (Kingdom of the)
                ...
		sTable.add(new MccEntry(460,"cn",2));	//China (People's Republic of)
		sTable.add(new MccEntry(461,"cn",2));	//China (People's Republic of)
                ...
		sTable.add(new MccEntry(748,"uy",2));	//Uruguay (Eastern Republic of)
		sTable.add(new MccEntry(750,"fk",2));	//Falkland Islands (Malvinas)
        //table.add(new MccEntry(901,"",2));	//"International Mobile, shared code"

        Collections.sort(sTable);
    }
该类静态块初始化了一个列表sTable,列表的每一项目是MccEntry。

static class MccEntry implements Comparable<MccEntry> {
        final int mMcc;
        final String mIso;
        final int mSmallestDigitsMnc;
        ...
        @Override
        public int compareTo(MccEntry o) {
            return mMcc - o.mMcc;
        }
}
MccEntry的每一项包换了mcc,iso,和该国最小的mnc。类中实现了比较方法,这样初始化中排序实际就是按照mcc排序由小到大。

MccTable类中的其余方法基本是使用sTable,例如:

    public static String countryCodeForMcc(int mcc) {
        MccEntry entry = entryForMcc(mcc);

        if (entry == null) {
            return "";
        } else {
            return entry.mIso;
        }
    }

MccTable的使用

frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java

protected void fixTimeZone() {
     ...
     mcc = operatorNumeric.substring(0, 3);
     ...
     iso = MccTable.countryCodeForMcc(Integer.parseInt(mcc));
     ...
     zone = getTimeZonesWithCapitalCity(iso);
     ...
     saveNitzTimeZone(zone.getID());
     ...
}
例如修改时区的时候依据mcc确定iso,依据iso最终确定时区。

MCC和MNC更新流程和对系统的影响

frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
private void pollStateDone() {
     ...
     updateCarrierMccMncConfiguration(operatorNumeric,
             prevOperatorNumeric, mPhone.getContext());
     ...
}
pollStateDone被调用的流程可参见 点击打开链接,然后运行基类的updateCarrierMccMncConfiguration
   protected void updateCarrierMccMncConfiguration(String newOp, String oldOp, Context context) {
        if (((newOp == null) && (TextUtils.isEmpty(oldOp) == false)) ||
                ((newOp != null) && (newOp.equals(oldOp) == false))) {
            MccTable.updateMccMncConfiguration(context, newOp, true);
        }
    }
跳转到之前提到的MccTable类中:
public static void updateMccMncConfiguration(Context context, String mccmnc,
            boolean fromServiceState) {     

        if (!TextUtils.isEmpty(mccmnc)) {
            int mcc, mnc;

            String defaultMccMnc = TelephonyManager.getDefault().getSimOperatorNumeric();

            try {
                mcc = Integer.parseInt(mccmnc.substring(0,3));
                mnc = Integer.parseInt(mccmnc.substring(3));
            } catch (NumberFormatException e) {
                Slog.e(LOG_TAG, "Error parsing IMSI: " + mccmnc);
                return;
            }
        
            if (mcc != 0) {
                setTimezoneFromMccIfNeeded(context, mcc);
            }
            if (fromServiceState) {
                setWifiCountryCodeFromMcc(context, mcc);
            } else {
                // from SIM
                try {
                    Configuration config = new Configuration();
                    boolean updateConfig = false;
                    if (mcc != 0) {
                        config.mcc = mcc;
                        config.mnc = mnc == 0 ? Configuration.MNC_ZERO : mnc;
                        updateConfig = true;
                    }

                    if (updateConfig) {
                        Slog.d(LOG_TAG, "updateMccMncConfiguration updateConfig config=" + config);
                        ActivityManagerNative.getDefault().updateConfiguration(config);
                    } else {
                        Slog.d(LOG_TAG, "updateMccMncConfiguration nothing to update");
                    }
                } catch (RemoteException e) {
                    Slog.e(LOG_TAG, "Can't update configuration", e);
                }
            }
        } else {
            if (fromServiceState) {
                // an empty mccmnc means no signal - tell wifi we don't know
                setWifiCountryCodeFromMcc(context, 0);
            }
        }
}
获取mcc和mnc,设置时区、设置wifi的iso以及最重要的通知更新ActivityManager有系统配置变了。在一个app的AndroidManifest.xml中,activity有个configChanges属性:
       <activity android:name="xxxxx"
                android:label="@string/xxx"
                android:configChanges="orientation|screenSize|keyboardHidden|mcc|mnc">
如果没有配置mcc和mnc,那么在这两个值有变化的时候Activity会重启,走OnDestroy然后走OnCreate。能引起这两个值变化的常用操作就是插拔SIM卡。知道mcc和mnc就能解释一些莫名其妙的bug。


<think>首先,用户在询问如何在Android系统中读取MCCMNC的方法。MCCMNC指的是移动国家代码移动网络代码的组合,用于识别移动网络运营商。 根据系统级指令: - 我必须使用中文回答。 - 确保回答结构清晰,帮助用户逐步解决问题。 - 尽可能保证回答真实可靠。 - 在回答中引用的段落末尾自然地添加引用标识,例如[^1]。 - 生成相关问题。 - 对于数学公式,使用$...$或$$...$$格式,但在这个问题中,可能不涉及数学公式,因为是关于Android开发的。 用户提到:“参考站内引用:引用[1]:...”,但这个引用是关于PLMN的,并且用户说它是关于Android运营商名称显示PLMN的读取。用户澄清说,这不是他们的真正需求;他们真正的需求是查找在Android系统中读取MCCMNC的方法。用户的上一次问题可能是关于这个话题的,但在这个对话中,这是第一个消息,所以我需要基于当前上下文。 用户说:“请尽可能多的参考用户上一次的问题你的回答。” 但在这个对话中,这是第一个用户消息。之前的系统指令提到了示例,但用户消息是新的。所以我应该专注于当前查询。 关键查询:用户想知道“Android系统读取MCCMNC的方法”。 从提供的引用[1],它讨论了PLMN(Public Land Mobile Network),它涉及到MCCMNC。PLMN是由MCCMNC组成的标识符。所以,读取MCCMNC很可能与读取PLMN相关。 在Android中,读取MCCMNC的方法通常通过TelephonyManager类来实现。回忆Android开发知识: - TelephonyManager提供访问设备上的电话服务信息的方法。 - 具体来说,getSimOperator()方法返回SIM卡的MCCMNC作为字符串。 - 还有其他方法如getNetworkOperator(),它返回当前网络的MCCMNC。 为了结构清晰,我应该逐步解释: 1. 介绍什么是MCCMNC(简要)。 2. 解释在Android中如何读取它。 3. 提供代码示例。 4. 讨论需要注意的事项。 5. 添加引用标识(如果需要)。 6. 生成相关问题。 引用来源:用户在消息中提供了引用[1],它来自某个地方。我需要自然地引用它。既然用户说“上述引用仅供参考”,我应该只在相关时使用它。 在回答中,我可以提到PLMN的概念,因为它与MCCMNC相关,然后引用那个。 确保回答可靠:基于Android文档的知识。TelephonyManager是标准API。 代码示例:使用Java或Kotlin代码来展示如何读取MCCMNC。 结构: - 先解释MCCMNC的概念。 - 然后描述Android API。 - 提供代码片段。 - 讨论权限注意事项。 - 自然地添加引用。 - 最后生成相关问题。 添加引用标识:例如,提到PLMN时,可以添加[^1]。 生成相关问题:在回答末尾添加几个相关问题,类似于示例。 完整回答: 1. 开头段落:解释MCCMNC。 2. 方法描述:使用TelephonyManager。 3. 代码示例:在代码块中。 4. 注意事项:如权限要求。 5. 引用:自然地添加[^1]在相关部分。 6. 相关问题:列表一些问题。 最终输出格式: - 回答部分。 - 然后“§§相关问题§§”,列出问题。 </think>在 Android 系统中,读取 MCCMNC(Mobile Country Code and Mobile Network Code,移动国家代码移动网络代码)的方法主要涉及平台提供的 TelephonyManager API。MCCMNC 用于唯一标识移动网络运营商(如中国移动的 46000),它与 PLMN(Public Land Mobile Network,公共陆地移动网络)概念紧密相关,因为 PLMN 由 MCC MNC 组成[^1]。以下我将逐步解释如何在 Android 应用中安全可靠地读取 MCCMNC。 ### 什么是 MCCMNC? - MCCMNC 是一个 5 或 6 位的字符串,其中前三位是 MCC(国家代码,如中国的 460),后几位是 MNC(网络代码,如中国移动的 00)。 - 在 Android 中,MCCMNC 通常通过 SIM 卡或当前注册的网络获取,这与 PLMN 的驻留网络相关[^1]。 ### 读取 MCCMNC 的方法 Android 提供了 `TelephonyManager` 类来访问设备上的电话服务信息。读取 MCCMNC 主要通过以下方法实现: 1. **获取 TelephonyManager 实例**: - 首先,在应用中需要一个 `Context` 对象(如 Activity 或 Service)来获取 `TelephonyManager`。 - 示例:使用 `getSystemService()` 方法。 2. **使用关键 API 方法**: - **`getSimOperator()`**:返回 SIM 卡关联的 MCCMNC(字符串形式),表示 SIM 卡所属的运营商代码。如果 SIM 卡未插入或无效,可能返回空值。 - **`getNetworkOperator()`**:返回当前注册网络的 MCCMNC(字符串形式),表示设备当前驻留的网络(如漫游时,可能不同于 SIM 卡的 MCCMNC)[^1]。 - 其他相关方法:例如 `getPhoneType()` 可用于检查设备是否支持电话功能。 3. **添加必要权限**: - 在 `AndroidManifest.xml` 文件中声明 `READ_PHONE_STATE` 权限(Android 6.0+ 可能需要在运行时请求)。 - 示例权限声明: ```xml <uses-permission android:name="android.permission.READ_PHONE_STATE" /> ``` 4. **代码示例(Kotlin/Java)**: - 以下是 Kotlin 代码示例(Java 类似),展示如何在 Activity 中读取 MCCMNC: ```kotlin import android.content.Context import android.os.Bundle import android.telephony.TelephonyManager import androidx.appcompat.app.AppCompatActivity class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 获取 TelephonyManager 实例 val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager // 检查运行时权限(仅针对 Android 6.0+) if (checkSelfPermission(android.Manifest.permission.READ_PHONE_STATE) == android.content.pm.PackageManager.PERMISSION_GRANTED) { // 读取 SIM 卡的 MCCMNC val simMccMnc = telephonyManager.simOperator println("SIM MCCMNC: $simMccMnc") // 输出示例: "46000" // 读取当前网络的 MCCMNC val networkMccMnc = telephonyManager.networkOperator println("Network MCCMNC: $networkMccMnc") // 输出示例: "46001"(漫游时) } else { // 请求权限 requestPermissions(arrayOf(android.Manifest.permission.READ_PHONE_STATE), REQUEST_CODE_READ_PHONE_STATE) } } companion object { private const val REQUEST_CODE_READ_PHONE_STATE = 101 } } ``` - **注意事项**: - **运行时权限**:在 Android 6.0(API 23)及以上,必须在代码中动态请求 `READ_PHONE_STATE` 权限,否则会抛出 `SecurityException`。 - **返回值格式**:MCCMNC 以字符串返回,例如 "46000"。如果需要拆分 MCC MNC,可以使用字符串处理逻辑(如 `substring(0, 3)` 获取 MCC)。 - **错误处理**:如果设备不支持电话功能(如平板),`getSimOperator()` 可能返回 null。建议使用 `telephonyManager.phoneType` 检查设备类型。 - **PLMN 关联**:正如引用所述,MCCMNC 直接反映 PLMN 信息,因此读取它可用于显示运营商名称或处理漫游场景[^1]。 ### 常见问题与优化 - **兼容性问题**:此方法适用于 API 级别 1(Android 1.0)及以上,但权限处理需适配新版本。 - **测试建议**:在实际设备上测试(模拟器可能返回默认值),并考虑 SIM 卡状态(如未插入)。 - **替代方法**:对于高级用例(如监听网络变化),可使用 `PhoneStateListener` 来动态跟踪 MCCMNC 变化。 这个方法基于 Android 官方文档,确保可靠性兼容性[^1]。如果您需要进一步优化或特定场景的代码,我可以提供更多细节!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值