Telephony基础之DataCall业务(常规APN参数的创建)

与紧急APN不同,常规APN参数的创建是由监听器触发的。
其中有两个监听器可以触发APN的创建过程:1、SIM载入完毕;2、APN改变。
【当SIM载入完毕时】,将会触发onRecordsLoaded():
    @Override
    public void handleMessage (Message msg) {
        if (VDBG) log("handleMessage msg=" + msg);

        switch (msg.what) {
            case DctConstants.EVENT_RECORDS_LOADED:
                if ((mIccRecords.get() instanceof RuimRecords) &&
                        (mPhone.getSIMRecords() != null)) {
                    mPhone.getSIMRecords().registerForRecordsLoaded(this,
                            EVENT_SIM_RECORDS_LOADED, null);
                } else {
                    onRecordsLoaded();
                }
                break;

            case EVENT_SIM_RECORDS_LOADED:
                onRecordsLoaded();
                mPhone.getSIMRecords().unregisterForRecordsLoaded(this);
                break;
     }


    void onRecordsLoaded() {
        // If onRecordsLoadedOrSubIdChanged() is not called here, it should be called on
        // onSubscriptionsChanged() when a valid subId is available.
        int subId = mPhone.getSubId();
        if (mSubscriptionManager.isActiveSubId(subId)) {
            onRecordsLoadedOrSubIdChanged();
        } else {
            log("Ignoring EVENT_RECORDS_LOADED as subId is not valid: " + subId);
        }
    }

    protected void onRecordsLoadedOrSubIdChanged() {
        if (DBG) log("onRecordsLoadedOrSubIdChanged: createAllApnList");
        mAutoAttachOnCreationConfig = mPhone.getContext().getResources()
                .getBoolean(com.android.internal.R.bool.config_auto_attach_data_on_creation);
    
    //创建APN参数
        createAllApnList();
        setInitialAttachApn();
        if (mPhone.mCi.getRadioState().isOn()) {
            if (DBG) log("onRecordsLoadedOrSubIdChanged: notifying data availability");
            notifyOffApnsOfAvailability(Phone.REASON_SIM_LOADED);
        }
     //尝试发起数据业务
        setupDataOnConnectableApns(Phone.REASON_SIM_LOADED);
    }


【当APN改变时】,将会触发onApnChanged():
 case DctConstants.EVENT_APN_CHANGED:
                onApnChanged();
                break;

    /**
     * Handles changes to the APN database.
     */
    private void onApnChanged() {
        DctConstants.State overallState = getOverallState();
        boolean isDisconnected = (overallState == DctConstants.State.IDLE ||
                overallState == DctConstants.State.FAILED);

        if (mPhone instanceof GsmCdmaPhone) {
            // The "current" may no longer be valid.  MMS depends on this to send properly. TBD
            ((GsmCdmaPhone)mPhone).updateCurrentCarrierInProvider();
        }

        // TODO: It'd be nice to only do this if the changed entrie(s)
        // match the current operator.
        if (DBG) log("onApnChanged: createAllApnList and cleanUpAllConnections");
        createAllApnList();
        setInitialAttachApn();
        cleanUpConnectionsOnUpdatedApns(!isDisconnected);

        // FIXME: See bug 17426028 maybe no conditional is needed.
        if (mPhone.getSubId() == SubscriptionManager.getDefaultDataSubscriptionId()) {
            setupDataOnConnectableApns(Phone.REASON_APN_CHANGED);
        }
    }

上面两个过程都通过两个步骤进行APN的创建,分别是createAllApnList()和setInitialAttachApn(),他们的作用分别是创建APN List和设置默认APN。
下面分别来介绍这个过程。

一、创建APN过程
创建APN是通过createAllApnList()来完成的。
private void createAllApnList() {  
    mAllApnSettings = new ArrayList();  
    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参数;
创建APN List总体思路是通过IccRecords拿到当前SIM卡的OperatorNumeric,以此作为数据库查询的selection,到TelephonyPrivider创建好的数据库表Telephony.Carriers.CONTENT_URI中查询,
之后通过返回的Cursor创建好ApnSetting后放入mAllApnSettings.
接下来又经历了一次添加紧急APN的过程,这个过程和前面初始化DcTracker时添加紧急APN过程完全一致。
然后就需要通过dedupeApnSettings()方法去掉APN列表中重复的APN参数:
private void dedupeApnSettings() {  
    ArrayList resultApns = new ArrayList();  
    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来发起数据连接。
    protected 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);
        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;
        }
        log("getPreferredApn: mRequestedApnType=" + mRequestedApnType + " cursor=" + cursor
                + " cursor.count=" + ((cursor != null) ? cursor.getCount() : 0));

        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();
        }

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


二、设置默认APN过程
设置默认APN过程是通过setInitialAttachApn()方法向Modem设置Attach默认APN。
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);  
    }  
}
可以看到在向Modem设置Attach默认APN前会经过几个if判断设置最终的initialAttachApnSetting,最后以这个initialAttachApnSetting来调用mPhone.mCi.setInitialAttachApn()
向Modem设置Attach默认APN.

至此,所有APN准备工作就绪,接下来就是等待需要上网时,将当前APN激活,然后发起数据连接过程。


 

你可能感兴趣的:(DataCall,Telephony基础)