【Android 数据业务解析】APN参数创建
来源:互联网 发布:淘宝二手iphone能买吗 编辑:程序博客网 时间:2024/06/04 20:11
手机可以上网,首先要建立数据连接,建立数据连接之前需要有apn才可以,所以本节先研究APN参数的创建过程。
DcTracker的createAllApnList方法:
/** * Based on the sim operator numeric, create a list for all possible * Data Connections and setup the preferredApn. */// 创建APN列表并创建preferredapnprivate void createAllApnList() { mAllApnSettings = new ArrayList<ApnSetting>(); // 获取到保存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,没搞懂是什么意思}
createAllApnList方法主要有3个步骤:
(1)从数据库中读取出符合要求的apn列表;
(2)添加emergency的apn到列表中并合并apn;
(3)获取到preferredApn。
(1)从数据库中读取出符合要求的apn列表
从数据库中按照mccmnc来获取所有的apn,并选择出符合要求的apn。在createApnList方法中,得到符合要求的apn集合。
createApnList方法
// 从数据库中读取出所有符合要求的apnprivate ArrayList<ApnSetting> createApnList(Cursor cursor) { ArrayList<ApnSetting> mnoApns = new ArrayList<ApnSetting>(); // 实体运营商 ArrayList<ApnSetting> mvnoApns = new ArrayList<ApnSetting>(); // 虚拟运营商 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<ApnSetting> result = mvnoApns.isEmpty() ? mnoApns : mvnoApns; if (DBG) log("createApnList: X result=" + result); return result;}
createApnList方法中,一个一个构建apn对象,并按照虚拟运营商和实体运营商来划分,当有虚拟运营商的apn时,就用虚拟运营商的apn,没有虚拟运营商的apn时,就用实体运营商的apn。
构建apn对象的方法:makeApnSetting
// 利用ApnSetting的构造方法创建出一个apnsettingprivate 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;}
获取一个构建的apn对象后,判断该apn是否是用于配置虚拟运营商的apn。
ApnSetting的hasMvnoParams方法:
/** * Returns true if there are MVNO params specified. */// 判断apn是否为虚拟运营商的apnpublic boolean hasMvnoParams() { return !TextUtils.isEmpty(mvnoType) && !TextUtils.isEmpty(mvnoMatchData);}
如果apn的参数mvnoType和mvnoMatchData都不为空,则说明属于虚拟运营商的apn,接下来就要看看这个apn是否属于这张SIM卡的虚拟apn,即要将apn的虚拟运营商参数与SIM卡中的数据对比。根据mvnoType的类型,来对比mvnoMatchData是否相同。
ApnSetting的mvnoMatches方法
// 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那么严格}
有三个比较的类型,分别是spn、imsi和gid,说白了就是比较内容是否一致。在配置apn的虚拟运营商参数时,可以不用考虑大小写的影响。
其中imsiMatches方法的比较如下:
// 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<len; idx++) { char c = imsiDB.charAt(idx); if ((c == 'x') || (c == 'X') || (c == imsiSIM.charAt(idx))) { continue; } else { return false; } } return true;}
如果说apn的虚拟运营商参数与SIM卡中的数据吻合,则将该apn加入到虚拟运营商的apn集合中。如果该apn没有虚拟运营商的参数,则加入到实体运营商的apn集合中。最后判断是该用虚拟运营商的apn集合还是用实体运营商的apn集合。
备注:当apn的虚拟运营商参数跟SIM卡中的数据不匹配时,则这个apn就是废弃的。当SIM卡为虚拟运营商,但是apn数据库中没有该mccmnc对应的虚拟运营商apn时,最终的apn就是实体运营商的apn集合了。
(2)添加emergency的apn到列表中并合并apn
DcTracker的addEmergencyApnSetting方法
/** * Add the Emergency APN settings to APN settings list */// 添加emergency的apn到apn集合中private void addEmergencyApnSetting() { if(mEmergencyApn != null) { if(mAllApnSettings == null) { mAllApnSettings = new ArrayList<ApnSetting>(); } 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方法
// 合并相似的apnprivate void dedupeApnSettings() { ArrayList<ApnSetting> resultApns = new ArrayList<ApnSetting>(); // 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++; }}
上述方法基本上对比了任意两个apn,当满足apnsSimilar方法,就认为这两个apn需要合并了。
apnsSimilar方法:
// 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一样,或者有一方为空}
apnTypeSameAny方法:
//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}
在满足apnsSimilar方法后,就要进行合并apn了。
mergeApns方法:
// 合并apn的处理方法private ApnSetting mergeApns(ApnSetting dest, ApnSetting src) { int id = dest.id; // id先暂定为小的那个,因为mAllApnSettings是按照顺序排列的 ArrayList<String> resultTypes = new ArrayList<String>(); 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);}
(3)获取到preferredApn
在获取到apn集合后,就需要看看用哪个apn来进行上网了。当用户使用一张卡时,可能手动选择过使用的apn,因此,当用户再次插拔卡或者重启手机后,getPreferredApn用于找出用户之前选择的APN。
getPreferredApn方法:
// 得到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;}
getPreferredApn方法就是从数据库中读取出preferred的apn,如果存在,且preferredapn的id与之前创建的apn集合中一个apn的id相同,且满足该apn的type参数中有一个default,则成功获取preferredapn。
接着,在createAllApnList方法中,对该preferredapn进行判断,如果该apn的mccmnc不是这张卡的mccmnc,则将preferredapn置为空,且清除数据库中的数据。
setPreferredApn(-1)方法:
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); }}
到此为止,APN参数创建完毕。
阅读全文
0 0
- 【Android 数据业务解析】APN参数创建
- 四、 Android 数据业务APN参数的创建
- 数据业务建立流程之常规APN参数的创建
- 数据业务建立流程之常规APN参数的创建
- 数据业务建立流程之常规APN参数的创建
- 谈数据业务的漫游时解析 APN
- 数据业务建立流程之常规APN参数的创建(原)
- 数据业务建立流程之APN参数的激活(原)
- 数据业务建立流程之APN参数的激活
- 五、 数据业务APN参数的开机默认使能
- 数据业务建立流程之APN参数的激活
- 数据业务建立流程之APN参数的激活
- android APN解析
- 【Android 数据业务解析】nwTypeChanged引发的原因
- 【Android 数据业务解析】PreferredApn修改的源码分析
- android apn
- Android APN
- Android APN
- python_爬取新浪新闻
- leetcode之Construct Binary Tree from Inorder and Postorder Traversal 问题
- vs的一些坑
- js定时器
- 两个类之间转换JSON.parseObject(
- 【Android 数据业务解析】APN参数创建
- 前后端分离之JWT用户认证
- android注意设置httpclient连接数
- java List集合
- 表格操作总结
- [论文学习]An Effective Approach for Mining Mobile User Habits:一种高效挖掘移动用户习惯的方法
- 购物车的几种实现方式
- 通过反射动态加载DLL
- 架构师之路(十五)