APN 源码分析 --- 数据库

本文深入剖析Android中APN的实现,从apns-config.xml文件解析到TelephonyProvider,详细讲解如何将APN参数写入数据库。内容涵盖TelephonyProvider的启动、数据库创建过程,以及APN信息的加载与初始化。

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的相关流程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值