5.3 查询/增加/删除/修改 APN
在手机的设置里,每一张SIM卡对应一个APN界面,这个界面包含了APN信息,并且可以增加,修改,删除等操作。
例如,电信卡对应的APN界面如下,
第一个就是当前默认的APN或者已经连接的APN,第三个APN专门用于发送彩信的。
3.1查询
查询当前默认的APN信息,也就是查询telephony.db数据库的siminfo表
Uri curi = Uri.parse("content://telephony/carriers/preferapn");
Cursor ccr = mContext.getContentResolver().query(curi, null, null, null, null);
ccr.moveToFirst();
String idd = ccr.getString(ccr.getColumnIndex("_id"));
String named = ccr.getString(ccr.getColumnIndex("name"));
String apnd = ccr.getString(ccr.getColumnIndex("apn"));
String usercurrent = ccr.getString(ccr.getColumnIndex("user"));
String passwordcurrent = ccr.getString(ccr.getColumnIndex("password"));
String authtypecurrent = ccr.getString(ccr.getColumnIndex("authtype"));
String typecurrent = ccr.getString(ccr.getColumnIndex("type"));
查询当期SIM卡对应的APN信息,查询telephony.db数据库的carriers表单,
TelephonyManager iPhoneManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
Uri APN_URI = Uri.parse("content://telephony/carriers/");
String iNumeric = iPhoneManager.getSimOperator();
•••
Cursor apnCursor = mContext.getContentResolver().query(APN_URI, new String[] { "_id", "name", "numeric", "apn", "mcc", "mnc", "user", "password", "authtype", "type"}, "numeric=?", new String[]{iNumeric}, null);
if (apnCursor != null && apnCursor.moveToFirst()) {
while (!apnCursor.isAfterLast()) {
StringBuilder builder = new StringBuilder();
String user = apnCursor.getString(apnCursor.getColumnIndex("user"));
String password = apnCursor.getString(apnCursor.getColumnIndex("password"));
String authtype = apnCursor.getString(apnCursor.getColumnIndex("authtype"));
String type = apnCursor.getString(apnCursor.getColumnIndex("type"));
•••
apnCursor.moveToNext();
•••
apnCursor.close();
3.2增加
增加一个APN首先会往telephony.db数据库的carriers表单中插入一条APN数据,然后phone进程监听到数据库的改变之后,
会利用添加的APN发起拨号请求。往数据库里面添加一个APN主要逻辑如下,
1,将需要添加的APN信息封装成ContentValues 对象,
String[] strArray = strCommand.split("/"); //APN信息
ContentValues apnValues = new ContentValues();
apnValues.put("name",strArray[1]);
apnValues.put("apn",strArray[2]);
apnValues.put("mcc",iNumeric.substring(0,3));
apnValues.put("mnc",iNumeric.substring(3,5));
apnValues.put("user",strArray[3]);
apnValues.put("password",strArray[4]);
apnValues.put("authtype",strArray[5]);
apnValues.put("type",strArray[6]);
apnValues.put("numeric",iNumeric);
2,然后插入数据库,并更新
Uri APN_URI = Uri.parse("content://telephony/carriers/");
String PREFERAPN_NO_UPDATE_URI_USING_SUBID =
"content://telephony/carriers/preferapn/subId/";
int subId = SubscriptionManager.getSubId(0)[0];
Uri uri = Uri.parse(PREFERAPN_NO_UPDATE_URI_USING_SUBID + subId);
TelephonyManager iPhoneManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
String iNumeric = iPhoneManager.getSimOperator();
Uri k = mContext.getContentResolver().insert(APN_URI, apnValues);
Cursor c = mContext.getContentResolver().query(k, new String[]{"_id"},null,null,null);
Log.d(TAG, "apn add c = " + c);
if (c!=null && c.moveToFirst()) {
String id = c.getString(c.getColumnIndex("_id"));
Log.d(TAG, "apn add id = " + id);
ContentValues values = new ContentValues();
values.put("apn_id",id);
int kkk = mContext.getContentResolver().update(uri, values, null,null);
•••
c.close();
3.3更改默认APN
更改默认APN其实就是对telephony.db数据库的siminfo表单进行更新,
并且,只需要APN的id就可以,
String PREFERAPN_NO_UPDATE_URI_USING_SUBID =
"content://telephony/carriers/preferapn/subId/";
String apnid = ***; // APN的id
ContentResolver resolver = mContext.getContentResolver();
String APN_ID = "apn_id";
int subId = SubscriptionManager.getSubId(0)[0];
Uri uri = Uri.parse(PREFERAPN_NO_UPDATE_URI_USING_SUBID + subId);
ContentValues values = new ContentValues();
values.put(APN_ID, apnid);
resolver.update(uri, values, null, null); //更新
3.4重置(删除)
设置的APN界面有个重置的功能,重置具体流程如下,
1,首先删除数据库中的所有APN,然后重新解析加载apns-conf.xml 文件里面的APN信息。
2,然后重新发起拨号请求,设置默认的APN。
因此,重置就相当于删除非apns-conf.xml 文件里面定义的APN,也就是在设置界面手动添加上去的APN。
和前面的方法一样,重置是调用数据库的delete方法完成的,
TelephonyProvider的delete方法对重置操作处理如下,
case URL_RESTOREAPN: {
count = 1;
restoreDefaultAPN(subId);
break;
}
restoreDefaultAPN方法处理流程如下,
try {
db.delete(CARRIERS_TABLE, where, null); //删除数据库中的所有APN信息
} catch (SQLException e) {
loge("got exception when deleting to restore: " + e);
}
setPreferredApnId((long)-1, subId); //设置默认的APN
mOpenHelper.initDatabase(db); //重新加载apns-conf.xml 文件里面定义的APN
TelephonyProvider的四个方法, insert/delete/update/query 除了query方法不改变数据库之外,
其他三种方法最后都会引起数据库的改变,因此一般都会调用notifyChange方法进行通知,
getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null,
true, UserHandle.USER_ALL);
5.4 APN changed
根据5.2 小节的分析, 一旦telephony.db数据库发生变化,就会调用DcTracker的onApnChanged方法。
onApnChanged方法如下,
private void onApnChanged() {
if(DBG) log("onApnChanged: tryRestartDataConnections");
tryRestartDataConnections(Phone.REASON_APN_CHANGED); //原因:APN改变
}
tryRestartDataConnections方法主要逻辑如下,
1,调用createAllApnList方法创建当前SIM卡的APN,
createAllApnList();
2,调用setInitialAttachApn方法设置初始使用的APN,
setInitialAttachApn();
3,调用cleanUpConnectionsOnUpdatedApns方法断开当前的数据连接,
if (reason.equalsIgnoreCase(Phone.REASON_APN_CHANGED)) {
cleanUpConnectionsOnUpdatedApns(!isDisconnected);
} else {
cleanUpAllConnections(!isDisconnected, reason);
}
4,调用setupDataOnConnectableApns方法发起拨号请求,
setupDataOnConnectableApns(Phone.REASON_SIM_LOADED);
其中,第3个方法是清理并且断开网络连接的,第1,2,4个方法在5.2 章节中已经论述过了,在此就不赘述了。
还有一些其他原因导致调用setupDataOnConnectableApns方法重新发起拨号上网请求。
1,当数据注册状态变化时,rild守护进程会向RIL.java上报RIL_REQUEST_SETUP_DATA_CALL消息,
最后会调用DcTracker的handleMessage方法,如下,
case DctConstants.EVENT_DATA_RAT_CHANGED:
// When data rat changes we might need to load different
// set of apns (example, LTE->1x)
if (onUpdateIcc()) {
log("onUpdateIcc: tryRestartDataConnections " + Phone.REASON_NW_TYPE_CHANGED);
tryRestartDataConnections(Phone.REASON_NW_TYPE_CHANGED);
} else if (isNvSubscription()){
// If cdma subscription source changed to NV or data rat changed to cdma
// (while subscription source was NV) - we need to trigger NV ready
onNvReady();
} else {
//May new Network allow setupData, so try it here
setupDataOnConnectableApns(Phone.REASON_NW_TYPE_CHANGED,
RetryFailures.ONLY_ON_CHANGE);
}
break;
这几个判断最后都会调用setupDataOnConnectableApns方法。
2,打开关闭飞行模式/数据业务时 也会发起拨号请求。
3,通话之前会断开数据业务,通话完成之后会重新发起拨号请求。
这些都可以在DcTracker中找到对应的方法。