5,APN
为了访问网络,手机必须设置合适的APN参数。APN的英文全称是Access Point Name,全称接入点,是手机上网时必现配置的参数。
终端中有一个apns-config.xml文件,负责定义各个运营商规定的默认APN参数。开机后,终端启动Phone进程时,
会加载运行在Phone进程中的TelephonyProvider。 TelephonyProvider负责解析apns-config.xml文件,将其中定义的APN参数写入到数据库中。
5.1 TelephonyProvider
TelephonyProvider路径如下:
packages\providers\TelephonyProvider\src\com\android\providers\telephony
packages\providers\TelephonyProvider\编译完成之后是TelephonyProvider.apk
AndroidManifest.xml文件如下,
android:sharedUserId="android.uid.phone">
说明运行于phone进程中。
TelephonyProvider定义如下,
<provider android:name="TelephonyProvider"
android:authorities="telephony"
android:exported="true" //可以被其他进程使用
android:singleUser="true" //所有Provider 是同一个
android:multiprocess="false" /> //不允许多个Provider实例运行于不同进程中
因此, TelephonyProvider是运行在phone进程中的,同时其multiprocess的值为false,也就意味着若其它进程要访问TelephonyProvider,
必须使用IPC机制进行调用。
phone进程是开机就启动的,因此TelephonyProvider在开机的时候,就会运行,并且会被加载到AMS中。
TelephonyProvider继承于ContentProvider,
public class TelephonyProvider extends ContentProvider
在启动TelephonyProvider过程中,会调用其onCreate方法, onCreate也是TelephonyProvider的入口方法,主要逻辑如下,
1,首先构造DatabaseHelper对象,并创建数据库,
mOpenHelper = new DatabaseHelper(getContext());
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
2,根据build.id的值,判断是否更新旧版本的存储信息和更新数据库,
if (!TextUtils.isEmpty(newBuildId)) {
•••
updateApnDb();
如果build.id 值不同,则更新数据库。
分2部分论述,创建数据库的流程如下,
DatabaseHelper的构造方法如下,
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, getVersion(context));
mContext = context; //进程上下文
}
getVersion方法获取数据库的版本信息; DATABASE_NAME定义如下,
private static final String DATABASE_NAME = "telephony.db";
因此,apn对应的数据库的名字是telephony.db。
DatabaseHelper是TelephonyProvider的内部类,继承于SQLiteOpenHelper, SQLiteOpenHelper的构造方法如下,
mContext = context; //进程上下文
mName = name; //数据库名称
mFactory = factory; // 在此为null
mNewVersion = version; //数据库版本信息
mErrorHandler = errorHandler; //在此为null
构造方法就到此为止了,紧接着就调用DatabaseHelper的getReadableDatabase方法创建数据库,得到SQLiteDatabase 对象。
直接调用的是父类SQLiteOpenHelper的getReadableDatabase方法,该方法如下,
synchronized (this) { //同步
return getDatabaseLocked(false); //这是用于读数据库,因此参数为false
}
getDatabaseLocked方法的主要根据不同的情况分别调用子类DatabaseHelper的方法来创建,
try {
if (version == 0) { //如果版本号为0
onCreate(db);
} else {
if (version > mNewVersion) {// 当前版本较高
onDowngrade(db, version, mNewVersion);
} else {
onUpgrade(db, version, mNewVersion);
}
}
•••
onOpen(db);
DatabaseHelper的onCreate方法主要包含3个逻辑,
createSimInfoTable(db); // 创建SIM卡对应的table,对于单卡来说,只有一个默认APN
createCarriersTable(db, CARRIERS_TABLE); //创建所有运营商的APN对应的table
initDatabase(db); //初始化数据库
createSimInfoTable和createCarriersTable方法都是调用SQLiteDatabase的execSQL方法创建的。
initDatabase 方法调用流程图如下,
DatabaseHelper的initDatabase方法主要逻辑就是解析不同路径下的关于APN配置的xml文件,然后加载到数据库里。例如,
Resources r = mContext.getResources();
XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns);
int publicversion = -1;
try {
XmlUtils.beginDocument(parser, "apns");
publicversion = Integer.parseInt(parser.getAttributeValue(null, "version"));
loadApns(db, parser);
••
其实就是找到apns-config.xml文件,然后解析这个xml文件,然后调用loadApns将xml中定义的数据,
插入到TelephonyProvider底层的数据库中。
loadApns方法主要逻辑如下,
XmlUtils.nextElement(parser);
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
ContentValues row = getRow(parser);
if (row == null) {
throw new XmlPullParserException("Expected 'apn' tag", parser, null);
}
insertAddingDefaults(db, row);
XmlUtils.nextElement(parser);
}
db.setTransactionSuccessful();
首先调用getRow方法读取xml中的一个APN块的内容,并将这些内容以键值对的形式,插入到ContentValues中。
然后调用insertAddingDefaults方法插入到数据库中。依次循环,直到添加完xml中的所有APN。
getRow方法主要就是调用XmlPullParser的方法解析APN的详细内容,然后封装为ContentValues对象;
insertAddingDefaults方法将APN信息插入运营商对应的table中。
updateApnDb方法逻辑如下,
1,首先调用SQLiteOpenHelper的getWritableDatabase方法获取SQLiteDatabase对象,
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
获取过程和SQLiteDatabase的getReadableDatabase方法完全相同。
2,调用deletePreferredApnId方法删除设置的默认APN,当然这次读取和删除只是在SharedPreferences中进行,
deletePreferredApnId();
3,删除CARRIERS_TABLE 表单,然后初始化数据库,
try {
if (VDBG) log("updateApnDb: deleting edited=Telephony.Carriers.UNEDITED entries");
db.delete(CARRIERS_TABLE, "edited=" + Telephony.Carriers.UNEDITED, null);
} catch (SQLException e) {
loge("got exception when deleting to update: " + e);
}
mOpenHelper.initDatabase(db);
4,最后告知该数据库的变化,
getContext().getContentResolver().notifyChange(
Telephony.Carriers.CONTENT_URI, null, true, UserHandle.USER_ALL);
data/data 路径下的telephony包名如下,
其中就有telephony.db数据库,该数据库如下,
其中,carriers表单包含了所有运营商所有的APN,多达两三千个,
每个APN有30多项信息,部分信息如下,
siminfo表中只有一个APN,就是SIM卡默认的APN,
例如,当前SIM卡为电信卡时,表中的数据如下,
不同厂商的apns-conf.xml 文件存放路径不同, apns-conf.xml 中APN信息,每条APN信息都是由键值对组成的。
其中一个APN的信息如下,
<apn carrier="APN_NAME_CTNET"
apn="ctnet"
mcc="460"
mnc="03"
user="ctnet@mycdma.cn"
password="vnet.mobi"
type="default,hipri,ia"
authtype="3"
ppp_number="#777"
protocol="IPV4V6"
roaming_protocol="IPV4V6"
/>
在上面的整个初始化过程中,只是将apns-conf.xml文件中的APN信息写入了CARRIERS_TABLE 表单,
siminfo并保存默认的APN,那么siminfo表单是怎么回事呢???
现在TelephonyProvider对应的telephony.db数据库初始化完成了,下个章节介绍读卡后APN的相关流程。
本文深入剖析Android中APN的实现,从apns-config.xml文件解析到TelephonyProvider,详细讲解如何将APN参数写入数据库。内容涵盖TelephonyProvider的启动、数据库创建过程,以及APN信息的加载与初始化。

5408

被折叠的 条评论
为什么被折叠?



