一、APN的定义和作用
APN(Access Point Name)是Android 手机实现移动数据上网必须配置的参数,用来决定手机通过哪种接入方式来访问网络,其配置信息全部记在telephony.db数据库中,名为carriers的表。
在系统中,telephony数据库文件路径是:
/data/user_de/0/com.android.providers.telephony/databases/telephony.db
用Android Studio或者ADB工具可以拿到手机的telephony.db文件,在数据库工具(DB Browser for SQLite)中打开carriers:
从carriers表可以看出,APN包含非常多个配置参数,列几个比较重要的字段:
我们是通过修改配置文件“apns-conf.xml”来设置数据库表的字段内容,所以,关于APN重中之重就是学会如何编写这个配置文件。
掌握APN,可以理解成开发者根据不同营运商和手机支持的网络服务,去定制telephony的数据库。
二、APN配置文件
在设备首次开机,恢复出厂设置或重置默认APN时,会读取配置文件中的内容同时生成相应的数据库telephony.db。
该数据库中存放有三个表,其中carriers表就是根据APN配置文件中的内容生成的,表中每一个字段对应一列,每个字段值就是每一列对应的值。
在APN配置界面中进行APN的增加,删除以及修改等操作时,都是针对数据库进行操作的,在Settings的APN配置界面中的任何操作并不会影响APN配置文件中的内容。
注意点:首次生成数据库后,以后APN信息直接从数据库中读取,除非将数据库从手机中删除(手动删除,恢复出厂设置,重置默认APN),设备才重新从配置文件中加载数据库。
我们每次做好配置文件的修改刷入系统后,验证时只需要使用前面三种方法删除数据库重新读取配置文件查看效果。
在编译后刷入手机系统的位置是:/system/etc/apns-conf.xml
不同平台的项目配置文件的位置可能不同,展锐项目的APN配置文件的代码路径是:
vendor/sprd/telephony-res/apnsprd/overlay/apn/apns-conf.xml
读一读这个文件:
标签里面添加了很多这些就是我们需要根据运营商网络要求配置进文件的参数,以键值对的形式对应数据库的字段与记录,读配置文件时用Map存放。
mcc参数:商户类别码
mnc参数:移动网络号码
apn参数:apn类型
protocol参数:协议类型(特别注明一点,“IP”就是IPv4。其他参数值的就是字面意思)
roaming_protocol参数:漫游协议
proxy、sport参数:apn地址和端口
mmproxy、mmsport参数:彩信地址和端口
注意点:比较重要的type字段,它决定了该APN可以支持建立哪种类型的数据连接,对应业务功能如下表所示。
在Settings的APN设置界面中,显示的是当前SIM卡对应的APN列表。展锐平台手机,除部分运营商要求IMS APN必需对用户可见外,其他IMS类型的APN设置成默认不显示。在工程模式中的“IMS APN”开关可控制IMSAPN在界面的显示情况,默认关闭状态。
注意点:mcc、mnc就是区别运营商的参数。数据库中有很多apn的记录,我们手机插入SIM卡后就是根据这两个参数在数据库中查询符合的记录。
例如,中国移动的mcc=460,mnc=02。
在DB Browser for SQLite工具筛选出四条记录,根据这个设置看看手机显示的APN。
所以,我们对不同运营商修改APN时,就可以根据mcc、mnc参数在配置文件中精准定位到我们需要修改的记录。
我在读文件过程中还发现,apns-conf.xml配置的参数数量与carriers表字段不匹配,查阅了文档才发现那些未在apns-conf.xml配置的字段是在TelephonyProvider.Java类中设置的默认值中设置的。
三、TelephonyProvider.java。
了解了apns-conf.xml配置文件的内容和apns-conf.xml、telephony.db、TelephonyProvider.java三则者的关系后,我们来看看TelephonyProvider.java类。
从读取VPN文件内容创建数据库的开始,搞清楚这个类在什么时候被创建?创建后做了什么?对我们学习VPN非常重要。
譬如,他对数据库定义了什么规范填入了哪些数据,读取的是那个apns-conf.xml文件?了解了前面这些,我对APN的启动流程有了大概的构思,下面我们开始学习APN的加载流程。
1、创建数据库
《紫光展锐APN说明文档ForCustomer.pdf》文档中说到了谷歌在Android 6.0后给Carries表加入了UNIQUE约束,carriers表记录都是唯一的。
看了一下aosp源代码,对照文档发现了一些不同。在TelephonyProvider.java中的静态代码块中,唯一约束是:(numeric,mcc,mnc,apn, proxy, port, mmsproxy, mmsport, mmsc, carrier_enabled, bearer, mvno_type, mvno_match_data, profile_id,PROTOCOL,ROAMING_PROTOCOL,USER_EDITABLE,OWNED_BY,APN_SET_ID,CARRIER_ID)
在我曾经做个的一个项目中(Android 11)的项目对这个约束做了改变,在原来的基础上多加了六个字段。
由此可以看出,随着安卓版本的更新,这个约束会发生改变。
由于唯一约束的存在,如果插入的两个SIM卡的APN参数中,符合约束的参数值完全相同将无法插入数据库。所以,我们需要修改其中一个字段的参数使约束不成立才可以插入,一般策略是修改profile_id字段。
静态代码和构造器规定好约束、加载完资源后,开始执行TelephonyProvider的onCreate方法创建数据库。
数据库的Carriers表的建表语句都写在代码里(TelephonyProvider.java:359行)。表属性(表名,表字段名称)定义在android.provider.Telephony.Carriers类中。感兴趣可以去翻翻。
2、读取参数
初始化数据库后,通过路径参数找到apns-conf.xml文件,然后获取配置文件的apns参数,写入读取到数据库表中,最终创建telephony数据库后写入carriers表。
我框出来的是配置文件的读取逻辑,默认是读取 "etc/apns-conf.xml"文件,但是后面路径的文件流可能会则覆盖前面的文件流,这里可能会发生定制,厂商可能会根据自己的规则去读取指定的文件,最终返回我们要读取的APN配置文件流。
四、实践
1、增加apn
1)在apns_conf.xml文件中添加一个apn标签,填入参数
2)刷入系统
3)测试结果:
达到预期,数据库和APN界面都添加了。
2、删除apn
1)在apns_conf.xml文件中删除我Cheney的apn标签
2)刷入系统:
3)测试结果:
达到预期,数据库和APN界面都删除了
3、隐藏
1)修改参数,将默认为1(不隐藏)的user_visible参数设置为0
2)刷入系统;
3)测试结果:
达到预期,在配置文件添加了,数据库表中记录创建成功,设置界面没显示,隐藏成功
4、将隐藏的apn显示出来
将user_visible设置为1,测试流程同上
5、置灰apn
1)置灰应该就是不可修改的意思,数据库表里有个参数user_editable就是关于可修改的意思,尝试配置文件改动它。
2)刷入系统;
3)测试结果:
达到预期,数据库创建成功,APN设置界面显示但是被置灰,不能修改。
五、总结
学习APN关键是学会明白apns-conf.xml文件各个参数的意思,然后熟悉TelephonyProvider是如何读取文件创建数据库的,譬如数据库表的约束、文件的读写顺序等,还有读取后的ApnSetting的二次定制以及显示(例如屏蔽xcap等)。
虽然一般在项目中APN的需求很简单,就是按照客户提供的APN参数添加上配置,或者在显示屏蔽一些参数,但是项目维护的过程中APN的问题也是比较多的。(ps:Google全家桶的应用会校验APN,APN非法,如,代理参数乱填、端口参数值超过65535,应用会出现闪退,想不到吧?)
学习APN的加载、显示的源代码,可以更快帮助定位问题。
当问题出现时,无非就两种,要么是数据入库前出错,要么是数据入库后的显示或二次修改出错。我们从Log或直接在DUT中拿到Carriers表数据,如果数据库里的数据没问题,那么就出现在显示或者二次处理上,否则,就看读取过程或者配置文件是否错误。
本文章重点在APN的加载上。编程最终要落实到实际操作,理论知识学完,sim卡参数如何与Carriers表记录对应上?如何完成需求?遇到陌生参数怎么办?这都是需要去研究。
本文档的所有内容是基于Android 11。
参考资料:
(1)紫光展锐APN说明文档ForCustomer.pdf
(2)Android Telephony原理解析与开发指南_杨青平.pdf