Android 数据连接分析

把网络接入过程简单分为三个阶段 
触发阶段 
—-该阶段是由各种不同事件触发的,比如SIM载入完毕、PS域Attach成功、通话结束、APN改变等,该阶段的最终都是要调用setupDataOnConnectableApns()方法; 
准备连接阶段 
—-该阶段是指,在DcTracker收到建立连接的请求之后,需要进行一系列有效性检测,比如APN是否已经激活、PS是否已经就绪、用户是否打开网络开关等,然后创建/找到核实的DataConnection对象,准备发起连接请求。 
发送连接命令阶段 
—-该阶段是指,在DataConnection收到DcTracker的请求之后,将请求转交给RILJ的过程,RILJ通过socket将请求发送到了RILD,RILD根据请求类型再将AT指令发送到Modem层,由底层完成信令的发送和接收。简单概括就是如下步骤,此处创建的DcAsyncChannel是双向的。 
这里写图片描述

1、触发条件

//漫游打开关闭(EVENT_ROAMING_ON/EVENT_ROAMING_OFF事件)
static final String REASON_ROAMING_ON = "roamingOn";  
static final String REASON_ROAMING_OFF = "roamingOff";  
//PS attach(STATE_IN_SERVICE)
static final String REASON_DATA_ATTACHED = "dataAttached";  
//APN Changed(EVENT_APN_CHANGED)
static final String REASON_APN_CHANGED = "apnChanged";  
//通话结束  
static final String REASON_VOICE_CALL_ENDED = "2GVoiceCallEnded";  
//SIM载入完毕 (EVENT_RECORDS_LOADED 事件)
static final String REASON_SIM_LOADED = "simLoaded";  
//网络模式改变 (EVENT_DATA_RAT_CHANGED 事件)
static final String REASON_NW_TYPE_CHANGED = "nwTypeChanged";  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2、有效性及状态检查

在触发条件满足后都会调用setupDataOnConnectableApns(String reason) 
之后会调用trySetupData,在trySetupData里面会进行有效性检测

    private boolean trySetupData(ApnContext apnContext, ArrayList waitingApns) {
        // Allow SETUP_DATA request for E-APN to be completed during emergency call and MOBILE DATA On/Off cases as well.
        boolean isEmergencyApn = apnContext.getApnType().equals(PhoneConstants.APN_TYPE_EMERGENCY);
        final ServiceStateTracker sst = mPhone.getServiceStateTracker();
        boolean desiredPowerState = sst.getDesiredPowerState();
        boolean checkUserDataEnabled = !(apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IMS));

        if (apnContext.isConnectable() && (isEmergencyApn ||
                (isDataAllowed(apnContext) &&
                getAnyDataEnabled(checkUserDataEnabled) && !isEmergency()))) {

            if (apnContext.getState() == DctConstants.State.FAILED) {
                apnContext.setState(DctConstants.State.IDLE);
            }
            int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
            apnContext.setConcurrentVoiceAndDataAllowed(sst.isConcurrentVoiceAndDataAllowed());
            if (apnContext.getState() == DctConstants.State.IDLE) { 
                if (waitingApns == null) {
                    waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech);
                }
                if (waitingApns.isEmpty()) {
                    notifyNoData(DcFailCause.MISSING_UNKNOWN_APN, apnContext);
                    notifyOffApnsOfAvailability(apnContext.getReason());
                    return false; 
                } else {
                    apnContext.setWaitingApns(waitingApns);
                } 
            }
            boolean retValue = setupData(apnContext, radioTech);
            notifyOffApnsOfAvailability(apnContext.getReason());

            return retValue;
        }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

条件1:apnContext.isConnectable()

    public boolean isConnectable() {
        return isReady() && ((mState == DctConstants.State.IDLE)
                                || (mState == DctConstants.State.SCANNING)
                                || (mState == DctConstants.State.RETRYING)
                                || (mState == DctConstants.State.FAILED));
    }   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

其中isReady()就是检查APN type有没有使能,所以在数据链接之前一定要调用ApnContext的setEnabled()将要打开的数据类型对应的APN使能;然后就是检查APN的状态,这个状态会在DataConnection状态机随着联网状态的变迁而改变。 
条件2:isEmergencyApn 是否是紧急APN 
条件3:isDataAllowed(apnContext)

  private boolean isDataAllowed(ApnContext apnContext) {
        return apnContext.isReady() && isDataAllowed(); 
    }

    protected boolean isDataAllowed() {
        final boolean internalDataEnabled;
        synchronized (mDataEnabledLock) {
            internalDataEnabled = mInternalDataEnabled;
        }

        boolean attachedState = mAttached.get();
        boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
        IccRecords r = mIccRecords.get();
        boolean recordsLoaded = false;
        if (r != null) {
            recordsLoaded = r.getRecordsLoaded();
        }

        //FIXME always attach
        boolean psRestricted = mIsPsRestricted;
        int phoneNum = TelephonyManager.getDefault().getPhoneCount();
        if (phoneNum > 1) {
            attachedState = true;
            psRestricted = false;
        }
        int dataSub = SubscriptionManager.getDefaultDataSubId();
        boolean defaultDataSelected = SubscriptionManager.isValidSubscriptionId(dataSub);
        PhoneConstants.State state = PhoneConstants.State.IDLE;
        if (mPhone.getCallTracker() != null) {
            state = mPhone.getCallTracker().getState(); 
        }            
        boolean allowed =
                    (attachedState || mAutoAttachOnCreation) &&
                    recordsLoaded &&
                    (state == PhoneConstants.State.IDLE ||
                     mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) &&
                    internalDataEnabled &&
                    defaultDataSelected &&
                    (!mPhone.getServiceState().getDataRoaming() || getDataOnRoamingEnabled()) &&
                    //!mIsPsRestricted &&
                    !psRestricted &&
                    desiredPowerState;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

—-判断是否已经ATTACH成功,SIM是否初始化完毕,当前手机服务是否支持,漫游下是否允许上网等 
条件4:getAnyDataEnabled(checkUserDataEnabled) 
—-该条件主要判断用户是否打开了数据开关 
这部分代码很混乱,如果出现问题得一一排查

3、找到可用的APN

其中waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech)便是找到可用的APN 
buildWaitingApns中主要的逻辑有两个: 
1、如果用户是否设置了Preferred,该值通过以下代码读取的:

        usePreferred = ! mPhone.getContext().getResources().getBoolean(com.android.
                internal.R.bool.config_dontPreferApn);
  • 1
  • 2
  • 3

如果用户设置了Preferred APN,并且mPreferredApn不为空(mPreferredApn来自于数据业务APN参数的创建createAllApnList中保存的,例如用户手动选择了APN TYPE那么这个APN就会被保留下来)因此此时waitingApns就会等于mPreferredApn 
2、如果用户没有设置Preferred APN,那么就会在mAllApnSettings(来自于createAllApnList)中去寻找可用的APN,并将所有的可用的APN放在waitingApns中 
最终联网采用的是waitingApns0

4、找到/创建DataConnection状态机

    private boolean setupData(ApnContext apnContext, int radioTech) {
        if (dcac == null) {
            dcac = findFreeDataConnection();
            if (dcac == null) {
                dcac = createDataConnection();
            }
        }
        apnContext.setDataConnectionAc(dcac);
        apnContext.setApnSetting(apnSetting);
        apnContext.setState(DctConstants.State.CONNECTING);
        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());

        Message msg = obtainMessage();
        msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
        msg.obj = apnContext;
        dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, radioTech, mAutoAttachOnCreation,msg);

        return true;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

这个方法内部主要完成了两个任务: 
1、更新当前APN参数的状态并把状态发送到系统中(还是通过notifyDataConnection()来完成) 
2、通过DcAsyncChannel的bringUp()方法发起连接请求 
这里显示通过findFreeDataConnection()方法搜索可用的DcAsyncChannel,找不到的话就通过createDataConnection()创建,如果没有找到,就需要创建新的DcAsyncChannel 
/* Return the DC AsyncChannel for the new data connection 
private DcAsyncChannel createDataConnection() {

    int id = mUniqueIdGenerator.getAndIncrement();
    DataConnection conn = DataConnection.makeDataConnection(mPhone, id,this, mDcTesterFailBringUpAll, mDcc);
    mDataConnections.put(id, conn);
    DcAsyncChannel dcac = new DcAsyncChannel(conn, LOG_TAG);
    int status = dcac.fullyConnectSync(mPhone.getContext(), this, conn.getHandler());
    if (status == AsyncChannel.STATUS_SUCCESSFUL) {
        mDataConnectionAcHashMap.put(dcac.getDataConnectionIdSync(), dcac); //将创建好的DcAsyncChannel保存起来,以便下一次链接的时候可以找到可用的DcAsyncChannel
    }
    return dcac;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

创建DcAsyncChannel通道,是链接DcTracker和DataConnection的通道,但是他们不在同一个线程中,在DcTrackerBase的构造中new了一个新的HandlerThread,并将looper给了DcController和DataConnection。 
为什么要闯将DcAsyncChannel通道,而不直接将DcTracker消息传到DataConnection中去呢? 
因为在DcTracker中有需要同步获取DataConnection中的情况,比如在findFreeDataConnection中调用了

dcac.isInactiveSync()
    private DcAsyncChannel findFreeDataConnection() {
        for (DcAsyncChannel dcac : mDataConnectionAcHashMap.values()) {
            if (dcac.isInactiveSync() && dataConnectionNotInUse(dcac)) {
                return dcac;
            }   
        }
        return null;
    }

    public boolean isInactiveSync() {
        boolean value;
        if (isCallerOnDifferentThread()) {
            Message response = sendMessageSynchronously(REQ_IS_INACTIVE);
            if ((response != null) && (response.what == RSP_IS_INACTIVE)) {
                value = rspIsInactive(response);
            } else {
                value = false;
            }
        } else {
            value = mDc.getIsInactive();
        }   
        return value;
    }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

其中的sendMessageSynchronously(REQ_IS_INACTIVE);即需要获取DataConnection中的是否处于INACTIVE状态,这不能是异步的。

5、通过DcAsyncChannel将消息传入状态机

将apnContext通过dcac传入DataConnection状态机进行数据连接

dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, radioTech, mAutoAttachOnCreation,msg);
  • 1

6、状态机状态轮转

DataConnection默认状态是DcInactiveState,首先会在DcInactiveState调用onConnect去调用setupDataCall

private void onConnect(ConnectionParams cp) {
    // msg.obj will be returned in AsyncResult.userObj;
    Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
    msg.obj = cp; 

    int authType = mApnSetting.authType;
    if (authType == -1) {
        authType = TextUtils.isEmpty(mApnSetting.user) ? RILConstants.SETUP_DATA_AUTH_NONE
                : RILConstants.SETUP_DATA_AUTH_PAP_CHAP;
    }

    String protocol;
    if (mPhone.getServiceState().getDataRoaming()) {
        protocol = mApnSetting.roamingProtocol;
    } else {    
        protocol = mApnSetting.protocol;
    }

    mPhone.mCi.setupDataCall(
            Integer.toString(cp.mRilRat + 2),
            Integer.toString(cp.mProfileId),
            mApnSetting.apn, mApnSetting.user, mApnSetting.password,
            Integer.toString(authType),
            protocol, msg);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

在onConnect会包装一个EVENT_SETUP_DATA_CONNECTION_DONE消息在msg中,这个消息会一并发往RILJ->RILD,当RILD返回响应时会携带这消息并发出来,DataConnection状态机的DcActivatingState会扑捉到此消息进行状态机轮状,然后这里面全是状态机轮状来维护网络的链接状态: 
这里写图片描述这里写图片描述 
至此数据连接的发起过程完毕,但是理上网还有很远,后续工作还要更新路由表等等

你可能感兴趣的:(android,data部分)