/**
* Based on the sim operator numeric, create a list for all possible
* Data Connections and setup the preferredApn.
*/
// 创建APN列表并创建preferredapn
private void createAllApnList() {
mAllApnSettings = new ArrayList();
// 获取到保存SIM数据的对象
IccRecords r = mIccRecords.get();
// 通过IccRecords获取SIM卡中的MCCMNC,因为要根据MCCMNC来从数据库中读取这个运营商的apn
String operator = (r != null) ? r.getOperatorNumeric() : "";
if (operator != null) {
// 匹配条件和排列顺序
String selection = "numeric = '" + operator + "'";
String orderBy = "_id";
// query only enabled apn.
// carrier_enabled : 1 means enabled apn, 0 disabled apn.
// selection += " and carrier_enabled = 1";
if (DBG) log("createAllApnList: selection=" + selection);
// 查询数据库
Cursor cursor = mPhone.getContext().getContentResolver().query(
Telephony.Carriers.CONTENT_URI, null, selection, null, orderBy);
if (cursor != null) {
if (cursor.getCount() > 0) {
// 创建出apn列表到集合中
mAllApnSettings = createApnList(cursor);
}
cursor.close();
}
}
// 添加emergency的apn到apn集合中
addEmergencyApnSetting();
// 合并相似的apn
dedupeApnSettings();
if (mAllApnSettings.isEmpty()) {
// APN集合为空,mPreferredApn也没用了,因为mPreferredApn肯定在apn集合里面
if (DBG) log("createAllApnList: No APN found for carrier: " + operator);
mPreferredApn = null;
// TODO: What is the right behavior?
//notifyNoData(DataConnection.FailCause.MISSING_UNKNOWN_APN);
} else {
// 获取preferredapn,该apn为用户在UI界面选择的
mPreferredApn = getPreferredApn();
if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) {
// 如果此时mPreferredApn的mccmnc与SIM卡中的不一致,则mPreferredApn置为空
mPreferredApn = null;
// 删除数据库中的数据
setPreferredApn(-1);
}
if (DBG) log("createAllApnList: mPreferredApn=" + mPreferredApn);
}
if (DBG) log("createAllApnList: X mAllApnSettings=" + mAllApnSettings);
setDataProfilesAsNeeded(); // 将apn信息发给modem,没搞懂是什么意思
}
// 从数据库中读取出所有符合要求的apn
private ArrayList createApnList(Cursor cursor) {
ArrayList mnoApns = new ArrayList(); // 实体运营商
ArrayList mvnoApns = new ArrayList(); // 虚拟运营商
IccRecords r = mIccRecords.get();
if (cursor.moveToFirst()) {
do {
ApnSetting apn = makeApnSetting(cursor);
if (apn == null) {
continue;
}
if (apn.hasMvnoParams()) { // apn的mvnoType和mvnoMatchData都不为空,说明该apn是一个虚拟运营商的apn
if (r != null && ApnSetting.mvnoMatches(r, apn.mvnoType, apn.mvnoMatchData)) { // apn的这两个参数与SIM卡中的参数匹配一致
mvnoApns.add(apn); // 添加到虚拟运营商集合中
}
} else {
mnoApns.add(apn); // 添加到实体运营商集合中
}
} while (cursor.moveToNext()); // 移动到下一个游标处
}
// 虚拟运营商的apn集合优先级高
ArrayList result = mvnoApns.isEmpty() ? mnoApns : mvnoApns;
if (DBG) log("createApnList: X result=" + result);
return result;
}
// 利用ApnSetting的构造方法创建出一个apnsetting
private ApnSetting makeApnSetting(Cursor cursor) {
String[] types = parseTypes(
cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
ApnSetting apn = new ApnSetting(
cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)),
cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)),
cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)),
cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)),
NetworkUtils.trimV4AddrZeros(
cursor.getString(
cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY))),
cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT)),
NetworkUtils.trimV4AddrZeros(
cursor.getString(
cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC))),
NetworkUtils.trimV4AddrZeros(
cursor.getString(
cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY))),
cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT)),
cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)),
cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)),
cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)),
types,
cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)),
cursor.getString(cursor.getColumnIndexOrThrow(
Telephony.Carriers.ROAMING_PROTOCOL)),
cursor.getInt(cursor.getColumnIndexOrThrow(
Telephony.Carriers.CARRIER_ENABLED)) == 1,
cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER)),
cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER_BITMASK)),
cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROFILE_ID)),
cursor.getInt(cursor.getColumnIndexOrThrow(
Telephony.Carriers.MODEM_COGNITIVE)) == 1,
cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNS)),
cursor.getInt(cursor.getColumnIndexOrThrow(
Telephony.Carriers.WAIT_TIME)),
cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNS_TIME)),
cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)),
cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MVNO_TYPE)),
cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MVNO_MATCH_DATA)));
return apn;
}
apn有个type参数的获取方法:
/**
* @param types comma delimited list of APN types
* @return array of APN types
*/
// 返回apn的type,以字符串数组的形式返回
private String[] parseTypes(String types) {
String[] result;
// If unset, set to DEFAULT.
if (types == null || types.equals("")) {
result = new String[1];
result[0] = PhoneConstants.APN_TYPE_ALL; // 当types为空或为null,默认为PhoneConstants.APN_TYPE_ALL类型
} else {
// 以逗号隔开,因为在配置的时候当存在多个apn type,就是用逗号隔开的
result = types.split(",");
}
return result;
}
/**
* Returns true if there are MVNO params specified.
*/
// 判断apn是否为虚拟运营商的apn
public boolean hasMvnoParams() {
return !TextUtils.isEmpty(mvnoType) && !TextUtils.isEmpty(mvnoMatchData);
}
// SIM卡与apn参数的mvnoMatchData匹配规则
public static boolean mvnoMatches(IccRecords r, String mvnoType, String mvnoMatchData) {
if (mvnoType.equalsIgnoreCase("spn")) { //spn
if ((r.getServiceProviderName() != null) &&
r.getServiceProviderName().equalsIgnoreCase(mvnoMatchData)) {
return true;
}
} else if (mvnoType.equalsIgnoreCase("imsi")) { // imsi
String imsiSIM = r.getIMSI();
if ((imsiSIM != null) && imsiMatches(mvnoMatchData, imsiSIM)) {
return true;
}
} else if (mvnoType.equalsIgnoreCase("gid")) { // gid
String gid1 = r.getGid1();
int mvno_match_data_length = mvnoMatchData.length();
if ((gid1 != null) && (gid1.length() >= mvno_match_data_length) &&
gid1.substring(0, mvno_match_data_length).equalsIgnoreCase(mvnoMatchData)) {
return true;
}
}
return false;
// equals: 如果两个字符串具有相同的字符和长度,返回true,否则返回false,区分大小写
// equalsIgnoreCase: 如果两个字符串具有相同的字符和长度,返回true,否则返回false,不区分大小写,要求没有equals那么严格
}
// MVNO_TYPE为imsi的mvnoMatchData匹配规则
private static boolean imsiMatches(String imsiDB, String imsiSIM) {
// Note: imsiDB value has digit number or 'x' character for seperating USIM information
// for MVNO operator. And then digit number is matched at same order and 'x' character
// could replace by any digit number.
// ex) if imsiDB inserted '310260x10xxxxxx' for GG Operator,
// that means first 6 digits, 8th and 9th digit
// should be set in USIM for GG Operator.
int len = imsiDB.length();
int idxCompare = 0;
if (len <= 0) return false;
if (len > imsiSIM.length()) return false;
// imsiDB的长度要大于等于0且长度小于imsiSIM的长度
// 按照顺序对比imsiDB和imsiSIM中的每个字符,如imsiDB出现‘x’或‘X’,可以忽略不计
for (int idx=0; idx
/**
* Add the Emergency APN settings to APN settings list
*/
// 添加emergency的apn到apn集合中
private void addEmergencyApnSetting() {
if(mEmergencyApn != null) {
if(mAllApnSettings == null) {
mAllApnSettings = new ArrayList();
} else {
boolean hasEmergencyApn = false;
for (ApnSetting apn : mAllApnSettings) { // 如果在数据库中有emergency类型的apn,那么就直接退出了
if (ArrayUtils.contains(apn.types, PhoneConstants.APN_TYPE_EMERGENCY)) {
hasEmergencyApn = true;
break;
}
}
if(hasEmergencyApn == false) { // 如果数据库中没有,那么就将不为null的mEmergencyApn添加到mAllApnSettings中
mAllApnSettings.add(mEmergencyApn);
} else {
log("addEmergencyApnSetting - E-APN setting is already present");
}
}
}
}
DcTracker的dedupeApnSettings方法
// 合并相似的apn
private void dedupeApnSettings() {
ArrayList resultApns = new ArrayList();
// coalesce APNs if they are similar enough to prevent
// us from bringing up two data calls with the same interface
int i = 0;
while (i < mAllApnSettings.size() - 1) {
ApnSetting first = mAllApnSettings.get(i);
ApnSetting second = null;
int j = i + 1;
while (j < mAllApnSettings.size()) {
second = mAllApnSettings.get(j);
if (apnsSimilar(first, second)) { // 两个apn类似
ApnSetting newApn = mergeApns(first, second); // 合并这两个apn
mAllApnSettings.set(i, newApn); // 合并后的apn放到apn集合的i位置处
first = newApn;
mAllApnSettings.remove(j); // apn集合中移除掉被合并的apn
} else {
j++;
}
}
i++;
}
}
// Check if neither mention DUN and are substantially similar
// 判断两个apn相似的准则
private boolean apnsSimilar(ApnSetting first, ApnSetting second) {
return (first.canHandleType(PhoneConstants.APN_TYPE_DUN) == false && // first的apn不为dun类型
second.canHandleType(PhoneConstants.APN_TYPE_DUN) == false && // second的apn不为dun类型
Objects.equals(first.apn, second.apn) && // apn名称一样
!apnTypeSameAny(first, second) && // 此条件很重要,即只有当first和second的apn type没有重复的时候,才会返回true
xorEquals(first.proxy, second.proxy) && // proxy一样,或有一方者为空
xorEquals(first.port, second.port) && // port一样,或者有一方为空
first.carrierEnabled == second.carrierEnabled && // carrierEnabled一样
first.bearerBitmask == second.bearerBitmask && // bearerBitmask一样
first.profileId == second.profileId && // profileId一样
Objects.equals(first.mvnoType, second.mvnoType) && // mvnoType一样
Objects.equals(first.mvnoMatchData, second.mvnoMatchData) && // mvnoMatchData一样
xorEquals(first.mmsc, second.mmsc) && // mmsc一样,或者有一方为空
xorEquals(first.mmsProxy, second.mmsProxy) && // mmsProxy一样,或者有一方为空
xorEquals(first.mmsPort, second.mmsPort)); // mmsPort一样,或者有一方为空
}
//check whether the types of two APN same (even only one type of each APN is same)
// 两个apnsetting的type类型比较
private boolean apnTypeSameAny(ApnSetting first, ApnSetting second) {
if(VDBG) {
StringBuilder apnType1 = new StringBuilder(first.apn + ": ");
for(int index1 = 0; index1 < first.types.length; index1++) {
apnType1.append(first.types[index1]);
apnType1.append(",");
}
StringBuilder apnType2 = new StringBuilder(second.apn + ": ");
for(int index1 = 0; index1 < second.types.length; index1++) {
apnType2.append(second.types[index1]);
apnType2.append(",");
}
log("APN1: is " + apnType1);
log("APN2: is " + apnType2);
} // 罗列出apn的所有type
// 满足下面三个条件之一,则认为两个apn的type相似:
// ①first的apn type存在all类型
// ②second的apn type存在all类型
// ③frist和second的apn type中有相同的部分
for(int index1 = 0; index1 < first.types.length; index1++) {
for(int index2 = 0; index2 < second.types.length; index2++) {
if(first.types[index1].equals(PhoneConstants.APN_TYPE_ALL) ||
second.types[index2].equals(PhoneConstants.APN_TYPE_ALL) ||
first.types[index1].equals(second.types[index2])) {
if(VDBG)log("apnTypeSameAny: return true");
return true;
}
}
}
if(VDBG)log("apnTypeSameAny: return false");
return false;
// 也就是说,两个apnsetting中的apn type有重复的,则返回true,没有重复的,返回false
}
// 合并apn的处理方法
private ApnSetting mergeApns(ApnSetting dest, ApnSetting src) {
int id = dest.id; // id先暂定为小的那个,因为mAllApnSettings是按照顺序排列的
ArrayList resultTypes = new ArrayList();
resultTypes.addAll(Arrays.asList(dest.types));
// 对apn type的处理,此处很重要
// srcType基本都要加入到resultTypes中
// 如果srcType中存在default类型,那么resultTypes肯定不存在default类型,则id置为src的id
// 因为如果id不是default类型apn的id,那么在ApnSettings界面中选择的preferredapn,在DcTracker中
// preferredapn的id为src.id,而mergeApns的apn的id为dest.id,程序会认为id不对,获取不到preferredapn,
// 从而去拿另一个default的apn去建立数据连接
for (String srcType : src.types) {
if (resultTypes.contains(srcType) == false) resultTypes.add(srcType);
if (srcType.equals(PhoneConstants.APN_TYPE_DEFAULT)) id = src.id;
}
String mmsc = (TextUtils.isEmpty(dest.mmsc) ? src.mmsc : dest.mmsc);
String mmsProxy = (TextUtils.isEmpty(dest.mmsProxy) ? src.mmsProxy : dest.mmsProxy);
String mmsPort = (TextUtils.isEmpty(dest.mmsPort) ? src.mmsPort : dest.mmsPort);
String proxy = (TextUtils.isEmpty(dest.proxy) ? src.proxy : dest.proxy);
String port = (TextUtils.isEmpty(dest.port) ? src.port : dest.port);
String protocol = src.protocol.equals("IPV4V6") ? src.protocol : dest.protocol;
String roamingProtocol = src.roamingProtocol.equals("IPV4V6") ? src.roamingProtocol :
dest.roamingProtocol;
int bearerBitmask = (dest.bearerBitmask == 0 || src.bearerBitmask == 0) ?
0 : (dest.bearerBitmask | src.bearerBitmask);
return new ApnSetting(id, dest.numeric, dest.carrier, dest.apn,
proxy, port, mmsc, mmsProxy, mmsPort, dest.user, dest.password,
dest.authType, resultTypes.toArray(new String[0]), protocol,
roamingProtocol, dest.carrierEnabled, 0, bearerBitmask, dest.profileId,
(dest.modemCognitive || src.modemCognitive), dest.maxConns, dest.waitTime,
dest.maxConnsTime, dest.mtu, dest.mvnoType, dest.mvnoMatchData);
}
// 得到preferredapn的方法
private ApnSetting getPreferredApn() {
if (mAllApnSettings == null || mAllApnSettings.isEmpty()) { // 为空判断
log("getPreferredApn: mAllApnSettings is " + ((mAllApnSettings == null)?"null":"empty"));
return null;
}
String subId = Long.toString(mPhone.getSubId());
Uri uri = Uri.withAppendedPath(PREFERAPN_NO_UPDATE_URI_USING_SUBID, subId);
// 从数据库中读取preferredapn
Cursor cursor = mPhone.getContext().getContentResolver().query(
uri, new String[] { "_id", "name", "apn" },
null, null, Telephony.Carriers.DEFAULT_SORT_ORDER);
if (cursor != null) {
mCanSetPreferApn = true;
} else {
mCanSetPreferApn = false;
}
// mRequestedApnType就是PhoneConstants.APN_TYPE_DEFAULT
log("getPreferredApn: mRequestedApnType=" + mRequestedApnType + " cursor=" + cursor
+ " cursor.count=" + ((cursor != null) ? cursor.getCount() : 0));
if (mCanSetPreferApn && cursor.getCount() > 0) {
int pos;
cursor.moveToFirst();
// 获取preferredapn在数据库中的id号
pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID));
for(ApnSetting p : mAllApnSettings) {
log("getPreferredApn: apnSetting=" + p);
// 遍历apn集合,存在id相同,并且apn type符合preferredapn的要求时,则返回给preferredapn
if (p.id == pos && p.canHandleType(mRequestedApnType)) {
log("getPreferredApn: X found apnSetting" + p);
cursor.close();
return p;
}
}
}
if (cursor != null) {
cursor.close();
}
log("getPreferredApn: X not found");
return null;
}
private void setPreferredApn(int pos) {
if (!mCanSetPreferApn) {
log("setPreferredApn: X !canSEtPreferApn");
return;
}
String subId = Long.toString(mPhone.getSubId());
Uri uri = Uri.withAppendedPath(PREFERAPN_NO_UPDATE_URI_USING_SUBID, subId);
log("setPreferredApn: delete");
ContentResolver resolver = mPhone.getContext().getContentResolver();
// 删除数据库中的数据
resolver.delete(uri, null, null);
if (pos >= 0) {
// 如果pos为-1,则不执行插入,否则插入新的preferredapn,pos即为id
log("setPreferredApn: insert");
ContentValues values = new ContentValues();
values.put(APN_ID, pos);
resolver.insert(uri, values);
}
}