【通信篇4】APN模块总结

1.APN简介

APN(Access Point Name)是通过手机上网必须配置的一个参数,用来决定手机通过哪种接入方式来访问网络。只要我们的手机插上sim卡之后就可以在手机的设置中查看当前sim卡内置的默认apn参数,一般的安卓智能机都可以在sim卡设置中找到“接入点名称(APN)”并可以查看和新增apn。

下面我们看看apn到底是怎么使用的,在启动Android手机或者启动Android虚拟设备后,所有的APN配置信息都会保存在telephony.db的SQLite数据库表名为carriers的表中。我们可以将此数据库文件pull到本地,然后可以查看carriers表的结构和其中的APN配置信息数据,命令如下:

adb pull /data/user_de/0/com.android.providers.telephony
如上可以将mmssms.db和telephony.db等数据都pull出来。
在Android系统中APN配置文件的路径:
1)vendor\qcom\proprietary\qrdplus\Extension\apps\etc\apns-config.xml
2)framework\base\core\res\xml\apn.xm
开机后,启动phone进程时,会加载运行在phone进程中的TelephonyProvider,TelephonyProvider负责解析apns-conf.xml文件,将其中定义的APN参数写入到数据库中。

1.1 APN配置关键字段
字段名称 描述
name APN配置名称,如CMNET
numeric 运营商编号,如46000
mcc 移动国家码,如460
mnc 移动网络码,如00
apn APN接入点,比如中国移动有两个接入点:cmwap和cmnet
user 用户名
server 服务器地址
password 密码
proxy 代理服务器地址,如10.0.0.172
port 端口号,如80
mmsproxy 彩信代理服务器地址,如10.0.0.172
mmsport 彩信代理服务器端口号,如80
mmsc 彩信接入服务器地址,如http://mmsc.monternet.com
type APN接入类型,如default,net,supl,xcap,不同类型用","分隔
current
protocol 连接该APN所用的协议,如IPV4IPV6
roaming_protocol 漫游时连接该APN所用的协议,如IPV4IPV6
carrier_enabled 用于标识APN是否可用
bearer 无线接入,如LTE和eHRPD
bearer_bitmask 无线接入技术位掩码,用于标明当前APN可以包含的RAT
network_type_bitmask
mvno_type 移动虚拟网络运营商(Mobile virtual network operator)的类型,可用的数据有spn,IMSI,GID(Group Identifier Level 1)
mvno_match_data MVNO_TYPE数据,这个值是和MVNO_TYPE对应的。例如:SPN:A MOBILE,BEN NL,IMSI:302720x94,2060188 GID:4E,33
sub_id 用于表明这个APN属于哪个subscription,此值从siminfo表获取
profile_id Profile id,profile是modem侧存储信息的方式,这个值将APN和modem侧的profile联系起来
modem_cognitive 用于表明这个APN是否会在modem侧设置
max_conns APN支持的最大连接数量
wait_time 使用该APN进行数据连接时,如果失败,retry要等待的时间
max_conns_time 限制APN最大连接的时间
mtu 使用该APN建立的连接,可以传输的最大单元
edited 表明该APN是否被用户或运营商添加、编译或删除的状态
user_visible APN是否对用户可见
user_editable 用户是否可以编辑APN
owned_by APN的拥有者,0或者1
apn_set_id APN集合id,如果用户或者框架选择了一个apn作为首选APN,那么所有与选中apn相同集合id的APN拥有更高的优先级
persistent
read_only 是否只读
ppp_number
sourcetype
csdnum
ipversion
1.2 Android支持的APN类型
类型 描述
default 默认数据连接、即浏览器、Email等手机上网数据连接
mms 发送和接收彩信使用的数据连接
supl 支持AGPS的数据连接
dun(dial-up-network) 拨号连接
hipri 扩展
ims
1.3 APN配置信息

apn配置在apns-conf.xml中,carriers表中的数据和此文件中的数据内容一致,在加载TelephonyProvider的时候,会调用其initDatabase方法,将apns-conf.xml配置文件内容加载到carriers表中。

2、APN设置

2.1 重置APN

1、在ApnSettings界面点击重置后,通过Uri: “content://telephony/carriers/restore"进行delete操作,删除完成后会重新fillList。
2、进行delete操作时,TelephonyProvider通过URL_RESTOREAPN进行删除操作,会删除carriers表,同时删除首选APN,获取preferred-full-apn的SP,如果SP包含version1,表示apn已经存储起来了,删除该subId对应的version1字段以及APN唯一字段与subId组合在一起的字段,删除完成,重新初始化加载carrier表,
3、在ApnSettings界面查询到APN后,会通过Uri:"content://telephony/carriers/
preferapn" 去设置首选APN。

初始化完成后,DcTracker监听到数据库变化执行onApnChanged,然后会设置偏好APN,此时会将oldApnSettings设置成首选APN,同时ApnSettings查询到APN list检测到如果没有偏好APN,会将第一个APN设置成偏好APN。APN如果配置了bearer_bitmask,且bearer_bitmask不为0,如果插入的卡网络类型识别的是unknown,查询出来的APN则会被过滤掉不显示。

2.2切换APN

1、在ApnSettings界面点击onPreferenceChange,执行setSelectedApnKey,设置首选APN
通过URI:content://telephony/carriers/preferapn 和APN_ID为选中的id进行update。
2、TelephonyProvider:获取subId,检查values中是否包含apn_id,如果包含apn_id,获取apn_id带过来的id值,将对应的apn_id保存为该subId的首选APN
3、DcTracker:监听到数据库变化,ApnChangeObserver的onChange函数将被调用,触发onApnChange函数,onApnChange的时候,底层会清除掉所有的连接。

2.3 新建APN

1、ApnEditor:布局初始化,获取相关控件,获取subId等intent传过来的参数,插入一条仅有id的信息,并根据插入信息返回uri,根据uri查询相应id的数据。
2、根据查询到的数据fillUi进行显示,如果是新建的,mvnoType显示有差异,mvnoMatchData显示有差异
3、移动定制版本,APN协议和APN漫游协议默认为IpV4V6、设置SIM卡相关的监听、APN变化的TextWatcher
4、在onResume中会注册phone状态、插拔卡等的监听
5、(1)点击保存:内置的APN则弹框提示是否保存,非内置的APN,直接验证并保存,如果APN相关有用信息为空,则toast提示,不保存返回,根据各项字段,通过uri更新数据库中数据。

2.4 编辑APN

1、ApnEditor:布局初始化,获取相关控件,获取subId等intent传过来的参数,获取传递过来的uri,uri以id结尾,如果uri为空或不满足要求则返回,关闭界面。
2、根据uri查询相应id的数据,根据查询到的数据fillUi进行显示,设置SIM卡相关的监听、APN变化的TextWatcher
3、在onResume中会注册phone状态、插拔卡等的监听,如果配置了ReadOnly字段,则不可保存、不可编辑preferene项
4、(1)点击保存:name、APN、mcc、mnc 四项不能为空,如果为空则返回不让保存,内置的APN则弹框提示是否保存,非内置的APN,直接验证并保存,如果APN相关有用信息为空,则toast提示,不保存返回,根据各项字段,通过uri更新数据库中数据。
(2)修改mcc,点击保存,此时发生冲突时,更新旧的那条数据,然后删除掉要更新的这条数据,此时要更新这条APN就从数据库中删除了,此时不会触发onApnChange,不会导致DcTracker重新触发连接的逻辑。重启手机后,此时通过numeric查询APN是无法查到的,因为数据库中的那条APN的mnc和numeric已经相对应的被修改了。

3 TelephonyProvider

3.1 APN升级

APN升级在TelephonyProvider中实现,先获取首选APN保存,然后根据条件删除数据库。重新根据xml插入APN,根据名称、APN、numeric、bearer四个属性恢复首选APN,若其中有属性变更,则不恢复。

4、 APN流程

4.1 卡加载后,监听APN数据库变化
public DcTracker(Phone phone) {
    .......
    //每个Phone对象有自己DcTracker
    //每个DcTracker加载各自卡可用的APN
    mPhone = phone;
    .......
    //1、监听卡载入
    mUiccController = UiccController.getInstance();
    mUiccController.registerForIccChanged(this, DctConstants.EVENT_ICC_CHANGED, null);
    .......
    //2、监听卡信息变化
    mSubscriptionManager = SubscriptionManager.from(mPhone.getContext());
    mSubscriptionManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
    .......
    //监听APN数据库变化
    mApnObserver = new ApnChangeObserver();
    phone.getContext().getContentResolver().registerContentObserver(
            Telephony.Carriers.CONTENT_URI, true, mApnObserver);
    .............
    //初始化不同APN类型对应的网络能力,后文介绍
    initApnContexts();
    .............
    // Add Emergency APN to APN setting list by default to support EPDN in sim absent cases
    initEmergencyApnSetting();
    addEmergencyApnSetting();
    ...............
}

4)插卡或卡发生变化后,就要创建当前卡可用的APN,同时设置初始时使用的APN

private void onRecordsLoadedOrSubIdChanged() {
    ..............
    //1、创建当前卡可用的APN
    createAllApnList();

    //2、设置初始使用的APN
    setInitialAttachApn();

    if (mPhone.mCi.getRadioState().isOn()) {
        if (DBG) log("onRecordsLoadedOrSubIdChanged: notifying data availability");
        notifyOffApnsOfAvailability(Phone.REASON_SIM_LOADED);
    }

    //卡变化也会触发拨号流程;不过若此时数据开关未开,那么拨号是不会成功的
    setupDataOnConnectableApns(Phone.REASON_SIM_LOADED);
}

创建卡对应的APN的过程:

private void createAllApnList() {
    //表示mvno是否匹配
    //mvno也是APN的一种属性,代表该APN适用于虚拟运营商,目前用的比较少
    mMvnoMatched = false;

    //用于保存结果
    mAllApnSettings = new ArrayList();

    //得到当前卡的信息
    IccRecords r = mIccRecords.get();
    //得到卡对应的MCC/MNC
    String operator = (r != null) ? r.getOperatorNumeric() : "";
    if (operator != null) {
        //构造SQL语句
        String selection = "numeric = '" + operator + "'";
        String orderBy = "_id";
        ...............
        //查询MCC/MNC对应的APN
        Cursor cursor = mPhone.getContext().getContentResolver().query(
                Telephony.Carriers.CONTENT_URI, null, selection, null, orderBy);

        if (cursor != null) {
            if (cursor.getCount() > 0) {
                //1、利用数据创建APN
                mAllApnSettings = createApnList(cursor);
            }
            cursor.close();
        }
    }

    //2、添加emergencyApnSettings
    addEmergencyApnSetting();

    //3、去除重复的APN
    dedupeApnSettings();

    if (mAllApnSettings.isEmpty()) {
        mPreferredApn = null;
    } else {
        //4、得到用户偏爱的APN (用户在UI界面主动选择的)
        mPreferredApn = getPreferredApn();
        if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator) {
            mPreferredApn = null;
            //用户偏爱的与当前卡不匹配,删除数据库中对应信息
            setPreferredApn(-1);
        }
    }

    //5、在需要的情况下,构造APN文件发送给modem
    setDataProfilesAsNeeded();
}

5.APN举例

1)以中国移动举例:其中carrier、apn、mcc、mnc几个字段是一个完整的apn一定要有的,不同的卡mnc会存在不同的情况如,00、02,设置错误的情况下会无法上网。

apn=""
mcc="460"
mnc="00"
type="ia"
protocol="IPV4V6"
roaming_protocol="IPV4V6"
/>
apn=""
mcc="460"
mnc="00"
type="fota"
/>
apn="cmnet"
mcc="460"
mnc="00"
type="default,net,supl"
preferred="true"
protocol="IPV4V6"
roaming_protocol="IPV4V6"
/>
apn="cmwap"
mcc="460"
mnc="00"
proxy="10.0.0.172"
port="80"
mmsproxy="10.0.0.172"
mmsport="80"
mmsc="http://mmsc.monternet.com"
type="mms"
protocol="IPV4V6"
roaming_protocol="IPV4V6"
/>
apn="cmwap"
mcc="460"
mnc="00"
proxy="10.0.0.172"
port="80"
type="supl"
protocol="IPV4V6"
roaming_protocol="IPV4V6"
/>
mcc="460"
mnc="00"
apn="ims"
type="ims"
protocol="IPV4V6"
roaming_protocol="IPV4V6"
profile_id="2"
modem_cognitive="true"
max_conns="1023"
max_conns_time="300"
/>

这里需要强调一下type和authtype,type字段可以有多个属性值,依次用逗号隔开,authtype在自己添加apn时可能给定的值是字符串,我们需要转换为相应的值。具体关系如下:

属性值 合入值
None 0
不写(默认值) -1
PAP 1
CHAP 2
PAP OR CHAP 3
2)上网分为wap和net两种方式,使用net手机就会直接连入互联网,而使用wap则会中间多了一个代理网关,移动联通均是10.0.0.172,端口80。
3)彩信APN

彩信apn中mmsproxy和mmsport两个字段在发彩信的apn中是必须的

6.Android支持的apn类型

Android中支持的apn类型(”default, mms, supl, dun, hipri, fota, ims…….”),其功能如下所示:

类型 描述
default 默认数据连接,即浏览器、Email等普通连接(internet、wap、web)
mms 接收和发送彩信使用的数据连接
supl 支持APGS的数据连接(gprs上网)
dun 拨号连接(wifi等上网类型,tethering)
hipri 扩展

此表中的数据优先级是由低到高的,即default数据连接的优先级最低,而hipri数据连接的优先级最高。比如在手机上网聊天时,将建立default数据连接;当手机收到一条彩信,因为彩信的数据连接是mms,这时会断开default数据连接而创建mms数据连接,从而能快速接收到此彩信,因为mms比default的数据连接优先级高。因此,在发送和接收彩信的同时不能上网。
APN分类
1、default

默认网络连接,当激活时所有数据传输都使用该连接,不能与其他网络连接同时使用

适用场合:绝大部分正常上网时可以使用

2、mms

彩信专用连接,此连接与default类似,用于与载体的多媒体信息服务器对话的应用程序,此连接能与default连接同时使用

适用场合:使用彩信服务时,必须有mms类型的接入点,不必选中,应用程序会自动使用此接入点

3、supl

是SecureUser Plane Location“安全用户面定位”的简写,此连接与default类似,用于帮助定位设备与载体的安全用户面定位服务器对话的应用程序,此连接能与default连接同时使用

4、dun

Dial UpNetworking拨号网络的简称,此连接与default连接类似,用于执行一个拨号网络网桥,使载体能知道拨号网络流量的应用程序,此连接能与default连接同时使用

适用场合:当我们使用自己的手机给别人做热点时使用,不管是USB 热点,wifi热点或则bluetooth热点。将他与default区别开来的主要目的一般是方面计费,国外很多运营商手机自己上网和做热点计费不同的。目前在国内三大运营商都没有区分,所以也就没有dun这个apn

5、hipri

高优先级网络,与default类似,但路由设置不同。使用较少。

6、ims

当ims发起激活请求时会使用这个apn连建立ims的专用承载.

7、FOTA

手机FOTA升级的时候使用

8.IA

IA的apn专用于LTE attach使用,在手机检测到sim卡后,便会加载这个attach apn. 不过很多运营商并没有严格规定attach apn,所以常常复用default类型的apn。 在attachapn 加载的时候它有一个优先级顺序,如下:

IaApn > PreferredApn > DefaultApn>FirstApn

IaApn : 类型为ia的apn,优先级最高。

 PreferredApn :选中的apn。比如在手机setting里面设置的那个apn

 DefaultApn :从apnlist里面查询到的第一个类型为“default”的apn

 FirstApn :apnlist中的第一个apn。

APN加载和过滤
在每次开机的时候系统回自动检查telephony.db是否存在,如果不存在则会创建数据库telephony.db,并利用apns-conf.xml中的内容生成表carriers,以后所有对apn的操作都会是直接针对表carriers,包括查询,创建,修改,删除等。

当插入一张卡后系统会根据卡的相关信息来匹配相应的apn,在apn list中主要涉及匹配的项有:mcc,mnc,mvno_type, mvno_match_data。mvno_type值决定mvno_match_data的值,android原生代码里mvno_type会有4个值,他们分别是“spn”,“imsi”,“gid”, “iccid”。所以,在apn 读取的时候,会先根据sim卡的mcc,mnc读取出相应的apn list,接着会判断apn list 中的每一个apn的mvno_type 的值,如果不为空,则会根据mvno_type 和mvno_match_data再一次对apn list进行过滤,一般情况下,mvno_type,mvno_match_data为空。

7、常见的APN问题

7.1 重置APN,显示为默认的APN接入点后又变为手动更改的接入点
D ApnSettings: --restoreDefaultApn--  ——开始重置
D TelephonyProvider: restoreDefaultAPN: where: owned_by!=0

D TelephonyProvider: deletePreferredApn: for subId 1
D TelephonyProvider: deletePreferredApn: apn is stored. Deleting it now for subId 1
D TelephonyProvider: dbh.initDatabase:+ db=SQLiteDatabase: /data/user_de/0/com.android.providers.telephony/databases/telephony.db ——重新创建
D TelephonyProvider: dbh.initDatabase:- db=SQLiteDatabase: /data/user_de/0/com.android.providers.telephony/databases/telephony.db ——插入数据库完成
D TelephonyProvider: setPreferredApn: _id 2965 subId 1 ————设置偏好APN
 DCT     : [ApnContext:default] getApnSetting: apnSetting=[ApnSettingV5] CUWAP, 2966, 46001, 3gwap, 10.0.0.172, http://mmsc.myuni.com.cn, 10.0.0.172, 80, 80, -1, default | mms, IPV4V6, IPV4V6, true, 0, 0, 0, false, 0, 0, 0, 0, , , false, 0, 0
 DCT     : [ApnContext:default] getApnSetting: apnSetting=[ApnSettingV5] CUWAP, 2966, 46001, 3gwap, 10.0.0.172, http://mmsc.myuni.com.cn, 10.0.0.172, 80, 80, -1, default | mms, IPV4V6, IPV4V6, true, 0, 0, 0, false, 0, 0, 0, 0, , , false, 0, 0
 QtiDCT  : [0]buildWaitingApns: reset preferred APN to [ApnSettingV5] CUWAP, 2966, 46001, 3gwap, 10.0.0.172, http://mmsc.myuni.com.cn, 10.0.0.172, 80, 80, -1, default | mms, IPV4V6, IPV4V6, true, 0, 0, 0, false, 0, 0, 0, 0, , , false, 0, 0 ——设置偏好APN失败,重置到2966了
从log可知,上层设置2965 APN时未设置成功,底层重新设置了2966
//上层设置偏好APN 2965
Line 58117: 06-21 14:43:07.940  2883  3252 D TelephonyProvider: setPreferredApn: _id 2965 subId 1 
//framework设置偏好APN 2966
Line 4405: 06-21 14:43:07.939  2883  2883 D QtiDCT  : [0]buildWaitingApns: reset preferred APN to [ApnSettingV5] CUWAP, 2966, 46001, 3gwap, 10.0.0.172, http://mmsc.myuni.com.cn, 10.0.0.172, 80, 80, -1, default | mms, IPV4V6, IPV4V6, true, 0, 0, 0, false, 0, 0, 0, 0, , , false, 0, 0 ——设置偏好APN失败,重置到2966了
//framerok调用删除
Line 4406: 06-21 14:43:07.939  2883  2883 D QtiDCT  : [0]setPreferredApn: delete 
//OS写入完成,数据库执行完打印的log
Line 58119: 06-21 14:43:07.943  5337  5337 D _ApnSettings: set key to  2965
//framework开始写入偏好APN
Line 4407: 06-21 14:43:07.967  2883  2883 D QtiDCT  : [0]setPreferredApn: insert
//响应framework的操作,写入偏好APN 2966
Line 58161: 06-21 14:43:07.967  2883  2883 D TelephonyProvider: delete:match=12
Line 58162: 06-21 14:43:07.967  2883  2883 D TelephonyProvider: subIdString = 1 subId = 1
Line 58163: 06-21 14:43:07.967  2883  2883 D TelephonyProvider: deletePreferredApn: for subId 1
Line 58164: 06-21 14:43:07.967  2883  2883 D TelephonyProvider: deletePreferredApn: apn is stored. Deleting it now for subId 1
Line 58170: 06-21 14:43:07.972  2883  2883 D TelephonyProvider: subIdString = 1 subId = 1 ——此处log只有对应URL_PREFERAPN_NO_UPDATE_USING_SUBID 或者URL_PREFERAPN_USING_SUBID URI才会打印出来
Line 58171: 06-21 14:43:07.973  2883  2883 D TelephonyProvider: setPreferredApn: _id 2966 subId 1
//从上面流程可以看出,最后执行的是响应 framework的写入2966的操作

分析:在buildWaitingAPN()中增加的reset preferred APN的逻辑和上层冲突,导致preferred APN值不对,这段已经不需要。上层在restore APN时会设置default preferred APN
方案:删除reset prefered APN逻辑

7.2 升级后,SIM2的APN需要重新选择

分析:
(1)从log来看,卡2没有选择默认的APN,故会弹框通知设置APN
(2)对比升级前后的apn可知,升级后由于名称改变了,相当于APN变更了,故需要重新设置APN

7.3 手动配置APN

手动配置APN需要配置的元素,例如:
(1)name:ims
(2)APN:ims
(3)type:ims
(4)APN protocol & APN roaming protocol:IPV4V6

你可能感兴趣的:(【通信篇4】APN模块总结)