数据业务建立流程之常规APN参数的创建

        区别于前面的紧急APN,这里的APN参数主要指常规的SIM卡APN参数,手机在上网时必须传递正确的APN参数给运营商才可以接入移动网络,而常规APN参数的创建是由监听器触发的。
        前面在DcTracker初始化过程中注册了大量监听器,其中有两个监听器可以触发APN的创建过程:1、SIM载入完毕;2、APN改变。这两个事件所导致的APN创建流程也都是类似的,分别是:
        【当SIM载入完毕时】,将会触发onRecordsLoaded():
  1. private void onRecordsLoaded() {  
  2.     mAutoAttachOnCreationConfig = mPhone.getContext().getResources().getBoolean(com.android.internal.R.bool.config_auto_attach_data_on_creation);  
  3.     //创建APN参数  
  4.     createAllApnList();  
  5.     setInitialAttachApn();  
  6.     if (mPhone.mCi.getRadioState().isOn()) {  
  7.         notifyOffApnsOfAvailability(Phone.REASON_SIM_LOADED);  
  8.     }  
  9.     //尝试发起数据业务  
  10.     setupDataOnConnectableApns(Phone.REASON_SIM_LOADED);  
  11. }  
        private void onRecordsLoaded() {
            mAutoAttachOnCreationConfig = mPhone.getContext().getResources().getBoolean(com.android.internal.R.bool.config_auto_attach_data_on_creation);
            //创建APN参数
            createAllApnList();
            setInitialAttachApn();
            if (mPhone.mCi.getRadioState().isOn()) {
                notifyOffApnsOfAvailability(Phone.REASON_SIM_LOADED);
            }
            //尝试发起数据业务
            setupDataOnConnectableApns(Phone.REASON_SIM_LOADED);
        }
        【当APN改变时】,将会触发onApnChanged():
  1. private void onApnChanged() {  
  2.     DctConstants.State overallState = getOverallState();  
  3.     boolean isDisconnected = (overallState == DctConstants.State.IDLE || overallState == DctConstants.State.FAILED);  
  4.   
  5.   
  6.     if (mPhone instanceof GSMPhone) {  
  7.         ((GSMPhone)mPhone).updateCurrentCarrierInProvider();  
  8.     }  
  9.     //创建APN参数  
  10.     createAllApnList();  
  11.     setInitialAttachApn();  
  12.     //清除旧的连接  
  13.     cleanUpAllConnections(!isDisconnected, Phone.REASON_APN_CHANGED);  
  14.     //尝试发起数据业务  
  15.     setupDataOnConnectableApns(Phone.REASON_APN_CHANGED);  
  16. }  
        private void onApnChanged() {
            DctConstants.State overallState = getOverallState();
            boolean isDisconnected = (overallState == DctConstants.State.IDLE || overallState == DctConstants.State.FAILED);


            if (mPhone instanceof GSMPhone) {
                ((GSMPhone)mPhone).updateCurrentCarrierInProvider();
            }
            //创建APN参数
            createAllApnList();
            setInitialAttachApn();
            //清除旧的连接
            cleanUpAllConnections(!isDisconnected, Phone.REASON_APN_CHANGED);
            //尝试发起数据业务
            setupDataOnConnectableApns(Phone.REASON_APN_CHANGED);
        }
        从上面两个过程对比我们发现,他们都通过两个步骤进行APN的创建,分别是createAllApnList()和setInitialAttachApn(),他们的作用分别是创建APN和设置默认APN。

        下面分别来介绍这个过程。


一、创建APN过程


        创建APN是通过createAllApnList()来完成的
  1. private void createAllApnList() {  
  2.     mAllApnSettings = new ArrayList<ApnSetting>();  
  3.     IccRecords r = mIccRecords.get();  
  4.     //获取该SIM的PLMN  
  5.     String operator = (r != null) ? r.getOperatorNumeric() : "";  
  6.     if (operator != null) {  
  7.         String selection = "numeric = '" + operator + "'";  
  8.         //查询当前SIM的APN数据库  
  9.         Cursor cursor = mPhone.getContext().getContentResolver().query(Telephony.Carriers.CONTENT_URI, null, selection, nullnull);  
  10.         if (cursor != null) {  
  11.             if (cursor.getCount() > 0) {  
  12.                 //根据APN参数创建APN列表  
  13.                 mAllApnSettings = createApnList(cursor);  
  14.             }  
  15.             cursor.close();  
  16.         }  
  17.     }  
  18.     //添加紧急APN  
  19.     addEmergencyApnSetting();  
  20.     //合并类似的APN  
  21.     dedupeApnSettings();  
  22.     if (mAllApnSettings.isEmpty()) {  
  23.         mPreferredApn = null;  
  24.     } else {  
  25.         //寻找prefer APN  
  26.         mPreferredApn = getPreferredApn();  
  27.         if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) {  
  28.             mPreferredApn = null;  
  29.             setPreferredApn(-1);  
  30.         }  
  31.     }  
  32.     setDataProfilesAsNeeded();  
  33. }  
        private void createAllApnList() {
            mAllApnSettings = new ArrayList<ApnSetting>();
            IccRecords r = mIccRecords.get();
            //获取该SIM的PLMN
            String operator = (r != null) ? r.getOperatorNumeric() : "";
            if (operator != null) {
                String selection = "numeric = '" + operator + "'";
                //查询当前SIM的APN数据库
                Cursor cursor = mPhone.getContext().getContentResolver().query(Telephony.Carriers.CONTENT_URI, null, selection, null, null);
                if (cursor != null) {
                    if (cursor.getCount() > 0) {
                        //根据APN参数创建APN列表
                        mAllApnSettings = createApnList(cursor);
                    }
                    cursor.close();
                }
            }
            //添加紧急APN
            addEmergencyApnSetting();
            //合并类似的APN
            dedupeApnSettings();
            if (mAllApnSettings.isEmpty()) {
                mPreferredApn = null;
            } else {
                //寻找prefer APN
                mPreferredApn = getPreferredApn();
                if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) {
                    mPreferredApn = null;
                    setPreferredApn(-1);
                }
            }
            setDataProfilesAsNeeded();
        }
        这个方法主要经历了三个步骤:
        1、创建一个APN的列表,其中包含:当前SIM对应的APN、紧急APN;
        2、合并相同的APN;
        3、寻找一个当前Prefer的APN参数;

        前面介绍过,在TelephonyProvider初始化过程中从"etc/apns-conf.xml"配置文件中载入了预置的APN参数存入数据库,而现在需要根据当前SIM信息把匹配的APN读取出来。
        而读取过程就是先获取当前SIM的PLMN,然后创建数据库查询条件"numeric = 当前SIM PLMN",然后通过createApnList()方法将数据库查到的信息创建为APN参数。
        接下来又经历了一次添加紧急APN的过程,这个过程和前面初始化DcTracker时添加紧急APN过程完全一致。
        然后就需要通过dedupeApnSettings()方法去掉APN列表中重复的APN参数:
  1. private void dedupeApnSettings() {  
  2.     ArrayList<ApnSetting> resultApns = new ArrayList<ApnSetting>();  
  3.     int i = 0;  
  4.     while (i < mAllApnSettings.size() - 1) {  
  5.         ApnSetting first = mAllApnSettings.get(i);  
  6.         ApnSetting second = null;  
  7.         int j = i + 1;  
  8.         while (j < mAllApnSettings.size()) {  
  9.             second = mAllApnSettings.get(j);  
  10.             if (apnsSimilar(first, second)) {  
  11.                 ApnSetting newApn = mergeApns(first, second);  
  12.                 mAllApnSettings.set(i, newApn);  
  13.                 first = newApn;  
  14.                 mAllApnSettings.remove(j);  
  15.             } else {  
  16.                 j++;  
  17.             }  
  18.         }  
  19.         i++;  
  20.     }  
  21. }  
        private void dedupeApnSettings() {
            ArrayList<ApnSetting> resultApns = new ArrayList<ApnSetting>();
            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)) {
                        ApnSetting newApn = mergeApns(first, second);
                        mAllApnSettings.set(i, newApn);
                        first = newApn;
                        mAllApnSettings.remove(j);
                    } else {
                        j++;
                    }
                }
                i++;
            }
        }
        这里就一个去重的算法问题,这个算法的原理就是,经过一个循环,可以找到某个参数所有相同的组合。
        再然后就需要从当前众多的APN参数中寻找一个当前合适的(prefer)APN参数,该APN要求其对应的PLMN属于当前的SIM。他的来源是跟随其他预置的APN一起被添加到数据库中的,其特别之处就在于多了“preferapn_no_update”的属性。他的作用就是作为备用APN来发起数据连接。
  1. private ApnSetting getPreferredApn() {  
  2.     if (mAllApnSettings.isEmpty()) {  
  3.         return null;  
  4.     }  
  5.     Cursor cursor = mPhone.getContext().getContentResolver().query( PREFERAPN_NO_UPDATE_URI, new String[] { "_id""name""apn" },  
  6.             nullnull, Telephony.Carriers.DEFAULT_SORT_ORDER);  
  7.   
  8.   
  9.     if (cursor != null) {  
  10.         mCanSetPreferApn = true;  
  11.     } else {  
  12.         mCanSetPreferApn = false;  
  13.     }  
  14.     if (mCanSetPreferApn && cursor.getCount() > 0) {  
  15.         int pos;  
  16.         cursor.moveToFirst();  
  17.         pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID));  
  18.         for(ApnSetting p : mAllApnSettings) {  
  19.             log("getPreferredApn: apnSetting=" + p);  
  20.             if (p.id == pos && p.canHandleType(mRequestedApnType)) {  
  21.                 log("getPreferredApn: X found apnSetting" + p);  
  22.                 cursor.close();  
  23.                 return p;  
  24.             }  
  25.         }  
  26.     }  
  27.     if (cursor != null) {  
  28.         cursor.close();  
  29.     }  
  30.     return null;  
  31. }  
        private ApnSetting getPreferredApn() {
            if (mAllApnSettings.isEmpty()) {
                return null;
            }
            Cursor cursor = mPhone.getContext().getContentResolver().query( PREFERAPN_NO_UPDATE_URI, new String[] { "_id", "name", "apn" },
                    null, null, Telephony.Carriers.DEFAULT_SORT_ORDER);


            if (cursor != null) {
                mCanSetPreferApn = true;
            } else {
                mCanSetPreferApn = false;
            }
            if (mCanSetPreferApn && cursor.getCount() > 0) {
                int pos;
                cursor.moveToFirst();
                pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID));
                for(ApnSetting p : mAllApnSettings) {
                    log("getPreferredApn: apnSetting=" + p);
                    if (p.id == pos && p.canHandleType(mRequestedApnType)) {
                        log("getPreferredApn: X found apnSetting" + p);
                        cursor.close();
                        return p;
                    }
                }
            }
            if (cursor != null) {
                cursor.close();
            }
            return null;
        }

        从其获取途径可以看到,他的URI("content://telephony/carriers/preferapn_no_update")中多了"preferapn_no_update"的参数,这也是该APN的特殊之处。


二、设置默认APN过程


        经过前面的过程,APN的各项参数都已经准备就绪,接下来就是通过setInitialAttachApn()方法向Modem设置Attach默认APN过程。
  1. protected void setInitialAttachApn() {  
  2.     ApnSetting iaApnSetting = null;  
  3.     ApnSetting defaultApnSetting = null;  
  4.     ApnSetting firstApnSetting = null;  
  5.     if (mAllApnSettings != null && !mAllApnSettings.isEmpty()) {  
  6.         firstApnSetting = mAllApnSettings.get(0);  
  7.   
  8.   
  9.         for (ApnSetting apn : mAllApnSettings) {  
  10.             if (ArrayUtils.contains(apn.types, PhoneConstants.APN_TYPE_IA) && apn.carrierEnabled) {  
  11.                 iaApnSetting = apn;  
  12.                 break;  
  13.             } else if ((defaultApnSetting == null) && (apn.canHandleType(PhoneConstants.APN_TYPE_DEFAULT))) {  
  14.                 //找到类型是APN_TYPE_DEFAULT的APN参数作为默认attach用  
  15.                 log("setInitialApn: defaultApnSetting=" + apn);  
  16.                 defaultApnSetting = apn;  
  17.             }  
  18.         }  
  19.     }  
  20.   
  21.   
  22.     ApnSetting initialAttachApnSetting = null;  
  23.     if (iaApnSetting != null) {  
  24.         initialAttachApnSetting = iaApnSetting;  
  25.     } else if (mPreferredApn != null) {  
  26.         initialAttachApnSetting = mPreferredApn;  
  27.     } else if (defaultApnSetting != null) {  
  28.         initialAttachApnSetting = defaultApnSetting;  
  29.     } else if (firstApnSetting != null) {  
  30.         initialAttachApnSetting = firstApnSetting;  
  31.     }  
  32.   
  33.   
  34.     if (initialAttachApnSetting == null) {  
  35.     } else {  
  36.         //设置Attach用的APN参数  
  37.         mPhone.mCi.setInitialAttachApn(initialAttachApnSetting.apn,  
  38.                 initialAttachApnSetting.protocol, initialAttachApnSetting.authType,  
  39.                 initialAttachApnSetting.user, initialAttachApnSetting.password, null);  
  40.     }  
  41. }  
        protected void setInitialAttachApn() {
            ApnSetting iaApnSetting = null;
            ApnSetting defaultApnSetting = null;
            ApnSetting firstApnSetting = null;
            if (mAllApnSettings != null && !mAllApnSettings.isEmpty()) {
                firstApnSetting = mAllApnSettings.get(0);


                for (ApnSetting apn : mAllApnSettings) {
                    if (ArrayUtils.contains(apn.types, PhoneConstants.APN_TYPE_IA) && apn.carrierEnabled) {
                        iaApnSetting = apn;
                        break;
                    } else if ((defaultApnSetting == null) && (apn.canHandleType(PhoneConstants.APN_TYPE_DEFAULT))) {
                        //找到类型是APN_TYPE_DEFAULT的APN参数作为默认attach用
                        log("setInitialApn: defaultApnSetting=" + apn);
                        defaultApnSetting = apn;
                    }
                }
            }


            ApnSetting initialAttachApnSetting = null;
            if (iaApnSetting != null) {
                initialAttachApnSetting = iaApnSetting;
            } else if (mPreferredApn != null) {
                initialAttachApnSetting = mPreferredApn;
            } else if (defaultApnSetting != null) {
                initialAttachApnSetting = defaultApnSetting;
            } else if (firstApnSetting != null) {
                initialAttachApnSetting = firstApnSetting;
            }


            if (initialAttachApnSetting == null) {
            } else {
                //设置Attach用的APN参数
                mPhone.mCi.setInitialAttachApn(initialAttachApnSetting.apn,
                        initialAttachApnSetting.protocol, initialAttachApnSetting.authType,
                        initialAttachApnSetting.user, initialAttachApnSetting.password, null);
            }
        }
        在上面这个过程中,遍历当前所有的APN列表,寻找类型是APN_TYPE_DEFAULT的APN,然后将该APN参数传递给Modem用于初始的Attach。
        至此,所有APN准备工作就绪,接下来就是等待需要上网时,将当前APN激活,然后发起数据连接过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值