CarrierConfig配置使用和加载流程简介

背景描述

https://blog.csdn.net/a396604593/article/details/118668137
https://blog.csdn.net/a396604593/article/details/127279642
通过上面两篇文章,我们知道一些telephony的配置,但是这些配置为什么会生效,是哪里加载的这些配置,以什么样的命名规则去匹配这些文件。
本篇文章从这些问题出发,简单的看看CarrierConfig的使用和配置加载

关于CarrierConfig配置问题

以下分析基于android 12展讯um512平台

涉及到的模块:
frameworks\base\telephony
packages\providers\TelephonyProvider
packages\services\Telephony
packages\apps\CarrierConfig
从心出发

从上层应用角度出发,代码调用举例
1、packages\apps\Settings\src\com\android\settings\network\apn\ApnEditor.java

final CarrierConfigManager configManager = (CarrierConfigManager)
                getSystemService(Context.CARRIER_CONFIG_SERVICE);
if (configManager != null) {
    final PersistableBundle b = configManager.getConfigForSubId(mSubId);
    if (b != null) {//读取运营商配置
        hinddenXcapType = b.getBoolean(CarrierConfigManager.KEY_CARRIER_APN_XCAP_HIDDEN_BOOL, true);
    }    
}

2、frameworks\base\telephony\java\android\telephony\CarrierConfigManager.java

@Nullable
    public PersistableBundle getConfigForSubId(int subId) {
        try {
            ICarrierConfigLoader loader = getICarrierConfigLoader();
            if (loader == null) {
                Rlog.w(TAG, "Error getting config for subId " + subId
                        + " ICarrierConfigLoader is null");
                return null;
            }
            return loader.getConfigForSubIdWithFeature(subId, mContext.getOpPackageName(),
                    mContext.getAttributionTag());
        } catch (RemoteException ex) {
            Rlog.e(TAG, "Error getting config for subId " + subId + ": "
                    + ex.toString());
        }
        return null;
    }

3、packages\services\Telephony\src\com\android\phone\CarrierConfigLoader.java
先拿到defaultConfig,再判断是否有覆盖,最终返回PersistableBundle类型对象

    public PersistableBundle getConfigForSubIdWithFeature(int subscriptionId, String callingPackage,
            String callingFeatureId) {
        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subscriptionId,
                callingPackage, callingFeatureId, "getCarrierConfig")) {
            return new PersistableBundle();
        }

        int phoneId = SubscriptionManager.getPhoneId(subscriptionId);
        PersistableBundle retConfig = CarrierConfigManager.getDefaultConfig();
        if (SubscriptionManager.isValidPhoneId(phoneId)) {
            PersistableBundle config = mConfigFromDefaultApp[phoneId];
            if (config != null) {
                retConfig.putAll(config);
            }
            config = mConfigFromCarrierApp[phoneId];
            if (config != null) {
                retConfig.putAll(config);
            }
            config = mPersistentOverrideConfigs[phoneId];
            if (config != null) {
                retConfig.putAll(config);
            }
            config = mOverrideConfigs[phoneId];
            if (config != null) {
                retConfig.putAll(config);
            }
            // Ignore the theoretical case of the default app not being present since that won't
            // work in CarrierConfigLoader today.
            final boolean allConfigsApplied =
                    (mConfigFromCarrierApp[phoneId] != null
                        || getCarrierPackageForPhoneId(phoneId) == null)
                    && mConfigFromDefaultApp[phoneId] != null;
            retConfig.putBoolean(
                    CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, allConfigsApplied);
        } else {
            if (mNoSimConfig != null) {
                retConfig.putAll(mNoSimConfig);
            }
        }
        return retConfig;
    }

4、frameworks\base\core\java\android\os\PersistableBundle.java继承BaseBundle.java
维护一个map对象,可以实现get和put操作。

===================================================================
以上为一个完整的取值流程。
那么这些值是什么时候放进去的呢?

CarrierConfig数据加载

1、上面第三步

getConfigForSubIdWithFeature获取默认配置
PersistableBundle retConfig = CarrierConfigManager.getDefaultConfig();
//frameworks\base\telephony\java\android\telephony\CarrierConfigManager.java
//调用到CarrierConfigManager里的默认map

后续的几个数组,通过打断点看到是mConfigFromDefaultApp覆盖了默认配置
2、packages\services\Telephony\src\com\android\phone\CarrierConfigLoader.java
读取xml

...
config = restoreConfigFromXml(mPlatformCarrierConfigPackage, "", phoneId);
mConfigFromDefaultApp[phoneId] = config;
...
mPlatformCarrierConfigPackage =
                mContext.getString(R.string.platform_carrier_config_package);
...
<string name="platform_carrier_config_package" translatable="false">com.android.carrierconfig</string>

当插入卡时CarrierConfigLoader的log如下

	Line 12160: M0186CC  10-22 02:46:41.633  1394  1812 D CarrierConfigLoader: mHandler: EVENT_DO_FETCH_DEFAULT phoneId: 0
	Line 12161: M0186CD  10-22 02:46:41.634  1394  1812 D CarrierConfigLoader: File path: carrierconfig-com.android.carrierconfig-override-00101210004466778894-1696.xml
	Line 12162: M0186CE  10-22 02:46:41.634  1394  1812 D CarrierConfigLoader: File not found: /data/user_de/0/com.android.phone/files/carrierconfig-com.android.carrierconfig-override-00101210004466778894-1696.xml
	Line 12163: M0186CF  10-22 02:46:41.635  1394  1812 D CarrierConfigLoader: File path: carrierconfig-com.android.carrierconfig-00101210004466778894-1696.xml
	Line 12164: M0186D0  10-22 02:46:41.635  1394  1812 D CarrierConfigLoader: restoredBundle: PersistableBundle[{allowed_initial_attach_apn_types_string_array=[ia, default], __carrier_config_package_version__=1, config_ims_package_override_string=com.google.android.ims, hide_enable_2g_bool=true, carrier_metered_roaming_apn_types_strings=[default, dun, supl], allow_hold_call_during_emergency_bool=false, hide_enhanced_4g_lte_bool=true, always_show_data_rat_icon_bool=false, carrier_default_wfc_ims_mode_int=1, carrier_volte_available_bool=false, wcdma_default_signal_strength_measurement_string=rscp, hide_lte_plus_data_icon_bool=false, carrier_metered_apn_types_strings=[default, dun, supl], carrier_name_string=Tele2, carrier_name_override_bool=true}]
	Line 12165: M0186D1  10-22 02:46:41.636  1394  1812 D CarrierConfigLoader: File path: /data/user_de/0/com.android.phone/files/carrierconfig-com.android.carrierconfig-00101210004466778894-1696.xml

发现加载的是/data/user_de/0/com.android.phone/files/carrierconfig-com.android.carrierconfig-00101210004466778894-1696.xml的值进行覆盖。
这个文件命名规则

/** Builds a canonical file name for a config file. */
    private static String getFilenameForConfig(
            @NonNull String packageName, @NonNull String extraString,
            @NonNull String iccid, int cid) {
        // the same carrier should have a single copy of XML file named after carrier id.
        // However, it's still possible that platform doesn't recognize the current sim carrier,
        // we will use iccid + carrierid as the canonical file name. carrierid can also handle the
        // cases SIM OTA resolves to different carrier while iccid remains the same.
        return "carrierconfig-" + packageName + extraString + "-" + iccid + "-" + cid + ".xml";
    }

3、xml是哪里写入的
packages\services\Telephony\src\com\android\phone\CarrierConfigLoader.java
插入卡时,调用到updateConfigForPhoneId,根据插入的卡信息更新配置文件

private void updateConfigForPhoneId(int phoneId) {
        mHandler.sendMessage(mHandler.obtainMessage(EVENT_DO_FETCH_DEFAULT, phoneId, -1));
    }

跟踪Handler EVENT_DO_FETCH_DEFAULT

case EVENT_DO_FETCH_DEFAULT: {
                    // Clear in-memory cache for carrier app config, so when carrier app gets
                    // uninstalled, no stale config is left.
                    if (mConfigFromCarrierApp[phoneId] != null
                            && getCarrierPackageForPhoneId(phoneId) == null) {
                        mConfigFromCarrierApp[phoneId] = null;
                    }
                    // Restore persistent override values.
                    PersistableBundle config = restoreConfigFromXml(
                            mPlatformCarrierConfigPackage, OVERRIDE_PACKAGE_ADDITION, phoneId);
                    if (config != null) {
                        logd("Loaded persistent override config from XML. package="
                                + mPlatformCarrierConfigPackage
                                + " phoneId=" + phoneId);
                        mPersistentOverrideConfigs[phoneId] = config;
                    }

                    config = restoreConfigFromXml(mPlatformCarrierConfigPackage, "", phoneId);
                    if (config != null) {
                        logd(
                                "Loaded config from XML. package="
                                        + mPlatformCarrierConfigPackage
                                        + " phoneId="
                                        + phoneId);
                        mConfigFromDefaultApp[phoneId] = config;
                        Message newMsg = obtainMessage(EVENT_FETCH_DEFAULT_DONE, phoneId, -1);
                        newMsg.getData().putBoolean("loaded_from_xml", true);
                        mHandler.sendMessage(newMsg);
                    } else {
                        // No cached config, so fetch it from the default app.
                        if (bindToConfigPackage(
                                mPlatformCarrierConfigPackage,
                                phoneId,
                                EVENT_CONNECTED_TO_DEFAULT)) {
                            sendMessageDelayed(
                                    obtainMessage(EVENT_BIND_DEFAULT_TIMEOUT, phoneId, -1 /*arg2*/,
                                            getMessageToken(phoneId)),
                                    BIND_TIMEOUT_MILLIS);
                        } else {
                            // Put a stub bundle in place so that the rest of the logic continues
                            // smoothly.
                            mConfigFromDefaultApp[phoneId] = new PersistableBundle();
                            // Send broadcast if bind fails.
                            notifySubscriptionInfoUpdater(phoneId);
                            // TODO: We *must* call unbindService even if bindService returns false.
                            // (And possibly if SecurityException was thrown.)
                            loge("binding to default app: "
                                    + mPlatformCarrierConfigPackage + " fails");
                        }
                    }
                    break;
                }

第一次插卡的时候,走bindToConfigPackage,服务器Service绑定成功后发消息获取配置信息。
EVENT_CONNECTED_TO_DEFAULT

case EVENT_CONNECTED_TO_DEFAULT: {
                    removeMessages(EVENT_BIND_DEFAULT_TIMEOUT, getMessageToken(phoneId));
                    final CarrierServiceConnection conn = (CarrierServiceConnection) msg.obj;
                    // If new service connection has been created, unbind.
                    if (mServiceConnection[phoneId] != conn || conn.service == null) {
                        unbindIfBound(mContext, conn, phoneId);
                        break;
                    }
                    final CarrierIdentifier carrierId = getCarrierIdentifierForPhoneId(phoneId);
                    // ResultReceiver callback will execute in this Handler's thread.
                    final ResultReceiver resultReceiver =
                            new ResultReceiver(this) {
                                @Override
                                public void onReceiveResult(int resultCode, Bundle resultData) {
                                    unbindIfBound(mContext, conn, phoneId);
                                    removeMessages(EVENT_FETCH_DEFAULT_TIMEOUT,
                                            getMessageToken(phoneId));
                                    // If new service connection has been created, this is stale.
                                    if (mServiceConnection[phoneId] != conn) {
                                        loge("Received response for stale request.");
                                        return;
                                    }
                                    if (resultCode == RESULT_ERROR || resultData == null) {
                                        // On error, abort config fetching.
                                        loge("Failed to get carrier config");
                                        notifySubscriptionInfoUpdater(phoneId);
                                        return;
                                    }
                                    PersistableBundle config =
                                            resultData.getParcelable(KEY_CONFIG_BUNDLE);
                                    saveConfigToXml(mPlatformCarrierConfigPackage, "", phoneId,
                                            carrierId, config);
                                    mConfigFromDefaultApp[phoneId] = config;
                                    sendMessage(
                                            obtainMessage(
                                                    EVENT_FETCH_DEFAULT_DONE, phoneId, -1));
                                }
                            };
                    // Now fetch the config asynchronously from the ICarrierService.
                    try {
                        ICarrierService carrierService =
                                ICarrierService.Stub.asInterface(conn.service);
                        carrierService.getCarrierConfig(carrierId, resultReceiver);
                        logdWithLocalLog("Fetch config for default app: "
                                + mPlatformCarrierConfigPackage
                                + " carrierid: " + carrierId.toString());
                    } catch (RemoteException e) {
                        loge("Failed to get carrier config from default app: " +
                                mPlatformCarrierConfigPackage + " err: " + e.toString());
                        unbindIfBound(mContext, conn, phoneId);
                        break; // So we don't set a timeout.
                    }
                    sendMessageDelayed(
                            obtainMessage(EVENT_FETCH_DEFAULT_TIMEOUT, phoneId, -1 /*arg2*/,
                                    getMessageToken(phoneId)),
                            BIND_TIMEOUT_MILLIS);
                    break;
                }

carrierService.getCarrierConfig获取运营商配置
resultReceiver回调中保存运营商缓存文件,更新mConfigFromDefaultApp。
mConfigFromDefaultApp也就是我们一开始看到的配置数组

frameworks\base\telephony\java\android\service\carrier\CarrierService.java
上面getCarrierConfig获取运营商配置

@Override
        public void getCarrierConfig(CarrierIdentifier id, ResultReceiver result) {
            try {
                Bundle data = new Bundle();
                data.putParcelable(KEY_CONFIG_BUNDLE, CarrierService.this.onLoadConfig(id));
                result.send(RESULT_OK, data);
            } catch (Exception e) {
                Log.e(LOG_TAG, "Error in onLoadConfig: " + e.getMessage(), e);
                result.send(RESULT_ERROR, null);
            }
        }

CarrierService.this.onLoadConfig加载运营商配置信息
packages\apps\CarrierConfig
packages\apps\CarrierConfig\src\com\android\carrierconfig\DefaultCarrierConfigService.java
集成CarrierService,重写onLoadConfig方法:

	public PersistableBundle onLoadConfig(@Nullable CarrierIdentifier id) {
        Log.d(TAG, "Config being fetched");

        try {
            synchronized (this) {
                if (mFactory == null) {
                    mFactory = XmlPullParserFactory.newInstance();
                }
            }

            XmlPullParser parser = mFactory.newPullParser();

            return loadConfig(parser, id);
        }
        catch (XmlPullParserException e) {
            Log.e(TAG, "Failed to load config", e);
            return new PersistableBundle();
        }
    }
    PersistableBundle loadConfig(XmlPullParser parser, @Nullable CarrierIdentifier id) {
        PersistableBundle config = new PersistableBundle();
        // OEM customizable filter for carrier requirements not related to hardware/vendor SKU.
        String sku = getApplicationContext().getResources().getString(R.string.sku_filter);

        if (id == null) {
            try {
                // Load no SIM config if carrier id is not set.
                parser.setInput(getApplicationContext().getAssets().open(
                        NO_SIM_CONFIG_FILE_NAME), "utf-8");
                config = readConfigFromXml(parser, null, sku);

                // Treat vendor_no_sim.xml as if it were appended to the no sim config file.
                XmlPullParser vendorInput =
                        getApplicationContext().getResources().getXml(R.xml.vendor_no_sim);
                PersistableBundle vendorConfig = readConfigFromXml(vendorInput, null, sku);
                config.putAll(vendorConfig);
            }
            catch (IOException|XmlPullParserException e) {
                Log.e(TAG, "Failed to load config for no SIM", e);
            }

            return config;
        }

        try {
            if (id.getCarrierId() != TelephonyManager.UNKNOWN_CARRIER_ID) {
                PersistableBundle configByCarrierId = new PersistableBundle();
                PersistableBundle configBySpecificCarrierId = new PersistableBundle();
                PersistableBundle configByMccMncFallBackCarrierId = new PersistableBundle();
                TelephonyManager telephonyManager = getApplicationContext()
                        .getSystemService(TelephonyManager.class);
                int mccmncCarrierId = telephonyManager
                        .getCarrierIdFromMccMnc(id.getMcc() + id.getMnc());
                //循环assets列表,匹配下面三个if的文件
                for (String file : getApplicationContext().getAssets().list("")) {
                    if (file.startsWith(CARRIER_ID_PREFIX + id.getSpecificCarrierId() + "_")) {
                        parser.setInput(getApplicationContext().getAssets().open(file), "utf-8");
                        configBySpecificCarrierId = readConfigFromXml(parser, null, sku);
                        break;
                    } else if (file.startsWith(CARRIER_ID_PREFIX + id.getCarrierId() + "_")) {
                        parser.setInput(getApplicationContext().getAssets().open(file), "utf-8");
                        configByCarrierId = readConfigFromXml(parser, null, sku);
                    } else if (file.startsWith(CARRIER_ID_PREFIX + mccmncCarrierId + "_")) {
                        parser.setInput(getApplicationContext().getAssets().open(file), "utf-8");
                        configByMccMncFallBackCarrierId = readConfigFromXml(parser, null, sku);
                    }
                }

                // priority: specific carrier id > carrier id > mccmnc fallback carrier id
                //配置文件优先级顺序
                if (!configBySpecificCarrierId.isEmpty()) {
                    config = configBySpecificCarrierId;
                } else if (!configByCarrierId.isEmpty()) {
                    config = configByCarrierId;
                } else if (!configByMccMncFallBackCarrierId.isEmpty()) {
                    config = configByMccMncFallBackCarrierId;
                }
            }
            if (config.isEmpty()) {
                // fallback to use mccmnc.xml when there is no carrier id named config found.
                parser.setInput(getApplicationContext().getAssets().open(
                        MCCMNC_PREFIX + id.getMcc() + id.getMnc() + ".xml"), "utf-8");
                config = readConfigFromXml(parser, id, sku);
            }
        }
        catch (IOException | XmlPullParserException e) {
            Log.d(TAG, e.toString());
            // We can return an empty config for unknown networks.
            config = new PersistableBundle();
        }

        // Treat vendor.xml as if it were appended to the carrier config file we read.
        XmlPullParser vendorInput = getApplicationContext().getResources().getXml(R.xml.vendor);
        try {
            PersistableBundle vendorConfig = readConfigFromXml(vendorInput, id, sku);
            config.putAll(vendorConfig);
        }
        catch (IOException | XmlPullParserException e) {
            Log.e(TAG, e.toString());
        }

        /**
         *  The vendor_ex.xml only exist when build overlay resource follow feature board.
         *  For example the CMCC product,if you need modify the a boolean value,add the value in the the vendor_ex.xml.
         *  path : vendor/sprd/feature_configs/carriers/cmcc/overlay/packages/apps/CarrierConfig/res/xml/vendor_ex.xml
         */
        try {
            Resources resource = getApplicationContext().getResources();
            int resId = resource.getIdentifier("vendor_ex", "xml", getPackageName());
            XmlPullParser vendorExInput =resource.getXml(resId);
            PersistableBundle vendorConfigEx = readConfigFromXml(vendorExInput, id, sku);
            config.putAll(vendorConfigEx);
            Log.d(TAG, "vendor ex for feature config");
        }
        catch (IOException | XmlPullParserException | Resources.NotFoundException e) {
            Log.e(TAG, e.toString());
        }

        return config;
    }

上述加载方法中,for循环遍历assets列表,三个if排列配置优先级和文件名称匹配规则。
readConfigFromXml读取配置文件
最后返回读取到的配置。
以上就是carrierConfig的文件如何根据不同的卡,读取不同文件的加载流程。

尾注

以前只知道一些功能配置什么开关,通过这篇完整,逐渐摸索配置文件的加载流程。
简单的做一个流程总结,希望能帮助到大家。

  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值