CarrierConfig解析

本文代码基于Android9.0

概述

APN配置流程详解中可以看到Carrier的身影,那么这篇文章重点来学习CarrierConfig的相关内容。

CarrierConfig(运营商配置)首次出现在Android 6.0 Marshmallow版本中,此机制的出现就是为运营商配置定制的功能,使运营商配置得以弃用静态配置叠加层,并使运营商和原始设备制造商 (OEM) 能够通过指定接口向相应平台动态提供运营商配置。

提供以下配置信息以便于设置应用:

  • 漫游/非漫游网络
  • 可视语音信箱
  • 短信/彩信网络设置
  • VoLTE/即时通讯配置

运营商可以通过自己的应用进行配置以上信息,该应用必须使用证书进行签名,且所用证书的签名要与 SIM 卡上的签名一致,运营商授权可通过Google官方文档自行了解。

Android系统提供的运营商配置

Android平台为我们提供了一个运营上配置应用,该应用的目的是在未安装运营商应用的情况下提供一些基于运营商网络的配置,运营商/OEM 应在他们自己的映像中对配置只做很小的改动。不过,运营商应该提供单独的运营商应用以实现运营商定制,从而使更新可以通过诸如应用商店之类的渠道分发给用户。

CarrierConfig配置

Android系统提供的运营商配置应用路径是packages/apps/CarrierConfig,CarrierConfig应用只提供了一个服务DefaultCarrierConfigService.java,该服务继承自CarrierService,应用中的主要功能体现在onLoadConfig()方法中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
* 返回运营商配置的网络覆盖信息
*
* 通过从assets文件夹中的文件读取数据,返回适合给定网络的运营商配置包。
* 首先,我们查找以{@code id}的MCC + MNC命名的文件,然后我们读取res / xml / vendor.xml。
* 这两个文件可能包含多个包含过滤器的包。 所有匹配的包都被展开以返回一个运营商配置包。
*/
@Override
public PersistableBundle onLoadConfig(CarrierIdentifier id) {
Log.d(TAG, "Config being fetched");

if (id == null) {
return null;
}

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

XmlPullParser parser = mFactory.newPullParser();
String fileName = "carrier_config_" + id.getMcc() + id.getMnc() + ".xml";
parser.setInput(getApplicationContext().getAssets().open(fileName), "utf-8");
config = readConfigFromXml(parser, id);
}
catch (IOException | XmlPullParserException e) {
// 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);
config.putAll(vendorConfig);
}
catch (IOException | XmlPullParserException e) {
Log.e(TAG, e.toString());
}

return config;
}

当我们需要配置一个运营商信息的时候,则需要添加一个carrier_config_xxxxxx.xml文件到packages/apps/CarrierConfig/assets/中,此文件在onLoadConfig()中会被加载

以aosp中的第一个配置文件为例:

carrier_config_00101.xml

文件名称中的00101是mcc+mnc组成

1
2
3
4
5
6
7
<carrier_config_list>
<carrier_config>
<boolean name="show_apn_setting_cdma_bool" value="true" />
<boolean name="carrier_volte_available_bool" value="true" />
<boolean name="carrier_wfc_ims_available_bool" value="true" />
</carrier_config>
</carrier_config_list>

运营商配置的加载时机

当我们配置完成carrier_config之后,在如下情况,系统就会对运营商配置信息进行重新加载,并且更新telephony.db Carriers表

  • 加载 SIM 卡(启动或 SIM 卡热插拔)时
  • 运营商应用手动触发重新加载时
  • 运营商应用更新时

CarrierConfigManager

如上所述,通过CarrierConfig应用来加载所有的运营商配置信息,CarrierConfigManager则提供了对CarrierConfig的访问和控制等功能,此类放在framework Telephony中,因此所有应用都可以调用。

frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java

当添加一个运营商配置信息的时候,相应的也需要在CarrierConfigManager中添加查询配置,这样才能让其他的App正确获取运营商信息

1
2
3
4
5
6
7
// 添加运营商信息的name
public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
static {
sDefaults = new PersistableBundle();
// 添加运营商信息的值
sDefaults.putBoolean(KEY_WORLD_PHONE_BOOL, false);
}

CarrierConfig的信息获取

可以通过以下方式获取CarrierConfig信息

1
2
3
// 获取CarrierConfigManager对象,通过CarrierConfigManager对象可以查询所有的运营商信息,但获取前提必须是已经存在的信息
CarrierConfigManager configManager = (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
String number = configManager.getConfig().getString(CarrierConfigManager.KEY_GLOBAL_CONF_VOICEMAIL_NUMBER);