(N)Telephony分析(七)之DataConnection建立

在前面,我们分析过DcTracker的初始化的时候,我们有看过,在DcTracker的构造方法中,调用有如下这个方法

private void registerForAllEvents() {
    mPhone.mCi.registerForAvailable(this, DctConstants.EVENT_RADIO_AVAILABLE, null);
    mPhone.mCi.registerForOffOrNotAvailable(this,
            DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
    mPhone.mCi.registerForDataNetworkStateChanged(this,
            DctConstants.EVENT_DATA_STATE_CHANGED, null);
    // Note, this is fragile - the Phone is now presenting a merged picture
    // of PS (volte) & CS and by diving into its internals you're just seeing
    // the CS data.  This works well for the purposes this is currently used for
    // but that may not always be the case.  Should probably be redesigned to
    // accurately reflect what we're really interested in (registerForCSVoiceCallEnded).
    mPhone.getCallTracker().registerForVoiceCallEnded(this,
            DctConstants.EVENT_VOICE_CALL_ENDED, null);
    mPhone.getCallTracker().registerForVoiceCallStarted(this,
            DctConstants.EVENT_VOICE_CALL_STARTED, null);
    registerServiceStateTrackerEvents();
 //   SubscriptionManager.registerForDdsSwitch(this,
 //          DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS, null);
    mPhone.mCi.registerForPcoData(this, DctConstants.EVENT_PCO_DATA_RECEIVED, null);
}
其中有调用到registerServiceStateTrackerEvents方法

public void registerServiceStateTrackerEvents() {
	// Leo,注册DataConnectionAttached消息
    mPhone.getServiceStateTracker().registerForDataConnectionAttached(this,
            DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null);
    mPhone.getServiceStateTracker().registerForDataConnectionDetached(this,
            DctConstants.EVENT_DATA_CONNECTION_DETACHED, null);
    mPhone.getServiceStateTracker().registerForDataRoamingOn(this,
            DctConstants.EVENT_ROAMING_ON, null);
    mPhone.getServiceStateTracker().registerForDataRoamingOff(this,
            DctConstants.EVENT_ROAMING_OFF, null);
    mPhone.getServiceStateTracker().registerForPsRestrictedEnabled(this,
            DctConstants.EVENT_PS_RESTRICT_ENABLED, null);
    mPhone.getServiceStateTracker().registerForPsRestrictedDisabled(this,
            DctConstants.EVENT_PS_RESTRICT_DISABLED, null);
    mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged(this,
            DctConstants.EVENT_DATA_RAT_CHANGED, null);
}
这样,就将当前的DcTracker和ServiceStateTracker绑定到一起了,我们看第一个注册消息EVENT_DATA_CONNECTION_ATTACHED,那么他是怎么激活接收的呢?

这个我么就要分析下ServiceStateTracker了,在SIM卡信息加载读取完成后,ServiceStateTracker会发出一个EVENT_SIM_READY消息,然后在其中进行处理

case EVENT_SIM_READY:
    // Reset the mPreviousSubId so we treat a SIM power bounce
    // as a first boot.  See b/19194287
    mOnSubscriptionsChangedListener.mPreviousSubId.set(-1);
    pollState();
    // Signal strength polling stops when radio is off
    queueNextSignalStrengthPoll();
    break;
恩,调用了pollState方法

public void pollState() {
    pollState(false);
}
public void pollState(boolean modemTriggered) {
    mPollingContext = new int[1];
    mPollingContext[0] = 0;

    switch (mCi.getRadioState()) {
        ......

        default:
            // Issue all poll-related commands at once then count down the responses, which
            // are allowed to arrive out-of-order
            mPollingContext[0]++;
            mCi.getOperator(obtainMessage(EVENT_POLL_STATE_OPERATOR, mPollingContext));

            mPollingContext[0]++;
            mCi.getDataRegistrationState(obtainMessage(EVENT_POLL_STATE_GPRS, mPollingContext));

            mPollingContext[0]++;
            mCi.getVoiceRegistrationState(obtainMessage(EVENT_POLL_STATE_REGISTRATION,
                    mPollingContext));

            if (mPhone.isPhoneTypeGsm()) {
                mPollingContext[0]++;
                mCi.getNetworkSelectionMode(obtainMessage(
                        EVENT_POLL_STATE_NETWORK_SELECTION_MODE, mPollingContext));
            }
            break;
    }
}
可以看到,其中分别调用了RIL类的getOperator,getDataRegistrationState和getVoiceRegistrationState方法,针对于这三个方法,我们着重分析一下getDataRegistrationState方法,注意其传入的参数为Message对象,而且Message对象的what为EVENT_POLL_STATE_GPRS,obj为mPollingContext对象

接下来看下RIL类的getDataRegistrationState方法

public void
getDataRegistrationState (Message result) {
    RILRequest rr
            = RILRequest.obtain(RIL_REQUEST_DATA_REGISTRATION_STATE, result);

    if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));

    send(rr);
}
恩,从前面几篇文章中,我们知道,这个方法,其实是和底层RIL进行交互,并且发送RIL_REQUEST_DATA_REGISTRATION_STATE消息,并且在RIL类中处理modem层反馈的结果,然后将发送传入的Message消息对象,至于究竟如何传递,后续再分析,因此

case EVENT_POLL_STATE_REGISTRATION:
case EVENT_POLL_STATE_GPRS:
case EVENT_POLL_STATE_OPERATOR:
    ar = (AsyncResult) msg.obj;
    handlePollStateResult(msg.what, ar);
    break;
调用了handlePollStateResult方法,其中参数一为消息what,参数二为modem层反馈上来的数据

protected void handlePollStateResult(int what, AsyncResult ar) {
    ......

    mPollingContext[0]--;

    if (mPollingContext[0] == 0) {
        if (mPhone.isPhoneTypeGsm()) {
            updateRoamingState();
            mNewSS.setEmergencyOnly(mEmergencyOnly);
        } else {
            ......
        }
        pollStateDone();
    }

}
设置SIM卡网络的状态信息,并调用pollStateDone方法

private void pollStateDone() {
    if (mPhone.isPhoneTypeGsm()) {
        pollStateDoneGsm();
    } else if (mPhone.isPhoneTypeCdma()) {
        pollStateDoneCdma();
    } else {
        pollStateDoneCdmaLte();
    }
}
由于SIM卡为GSM格式

private void pollStateDoneGsm() {
    ......
    boolean hasGprsAttached =
            mSS.getDataRegState() != ServiceState.STATE_IN_SERVICE
                    && mNewSS.getDataRegState() == ServiceState.STATE_IN_SERVICE;

    ......

    if (hasGprsAttached) {
        mAttachedRegistrants.notifyRegistrants();
    }

    if (hasGprsDetached) {
        mDetachedRegistrants.notifyRegistrants();
    }
    ......
}
因此,在此处发送了Data Attached的消息,而由于在DcTracker中,将EVENT_DATA_CONNECTION_ATTACHED消息,加入到mAttachedRegistrants中了,因此,DcTracker会接收到EVENT_DATA_CONNECTION_ATTACHED消息,并处理

case DctConstants.EVENT_DATA_CONNECTION_ATTACHED:
    onDataConnectionAttached();
    break;
调用onDataConnectionAttached方法

private void onDataConnectionAttached() {
    if (DBG) log("onDataConnectionAttached");
    // Leo,设置为true
    mAttached.set(true);
    // Leo,条件不成立
    if (getOverallState() == DctConstants.State.CONNECTED) {
        if (DBG) log("onDataConnectionAttached: start polling notify attached");
        startNetStatPoll();
        startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
        notifyDataConnection(Phone.REASON_DATA_ATTACHED);
    } else {
    	// Leo,会直接走else分之,设置其申请DataConnection的建立原因为REASON_DATA_ATTACHED
        // update APN availability so that APN can be enabled.
        notifyOffApnsOfAvailability(Phone.REASON_DATA_ATTACHED);
    }
    if (mAutoAttachOnCreationConfig) {
    	// Leo, 设置为true
        mAutoAttachOnCreation.set(true);
    }
    // Leo,最后调用此方法
    setupDataOnConnectableApns(Phone.REASON_DATA_ATTACHED);
}
可以看到,在上面,有几个设置为true的地方,结合前面的DataConnection的初始化,我们知道,这两个参数正式影响到DataConnection建立的条件

最后,调用了setupDataOnConnectableApns方法

private void setupDataOnConnectableApns(String reason) {
    setupDataOnConnectableApns(reason, RetryFailures.ALWAYS);
}
private void setupDataOnConnectableApns(String reason, RetryFailures retryFailures) {
    ......
    if (apnContext.isConnectable()) {
        log("isConnectable() call trySetupData");
        apnContext.setReason(reason);
        trySetupData(apnContext, waitingApns);
    }
}
apnContext.isConnectable()返回值为true,这个请自行分析

OK,又回到了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();

    // set to false if apn type is non-metered or if we have a restricted (priveleged)
    // request for the network.
    // TODO - may want restricted requests to only apply to carrier-limited data access
    //        rather than applying to user limited as well.
    // Exclude DUN for the purposes of the override until we get finer grained
    // intention in NetworkRequests
    boolean checkUserDataEnabled =
            ApnSetting.isMeteredApnType(apnContext.getApnType(), mPhone.getContext(),
                    mPhone.getSubId(), mPhone.getServiceState().getDataRoaming()) &&
            apnContext.hasNoRestrictedRequests(true /*exclude DUN */);

    DataAllowFailReason failureReason = new DataAllowFailReason();

    // allow data if currently in roaming service, roaming setting disabled
    // and requested apn type is non-metered for roaming.
    boolean isDataAllowed = isDataAllowed(failureReason) ||
            (failureReason.isFailForSingleReason(DataAllowFailReasonType.ROAMING_DISABLED) &&
            !(ApnSetting.isMeteredApnType(apnContext.getApnType(), mPhone.getContext(),
            mPhone.getSubId(), mPhone.getServiceState().getDataRoaming())));

    if (apnContext.isConnectable() && (isEmergencyApn ||
            (isDataAllowed && isDataAllowedForApn(apnContext) &&
                    mDataEnabledSettings.isDataEnabled(checkUserDataEnabled) && !isEmergency()))) {
        if (apnContext.getState() == DctConstants.State.FAILED) {
            String str = "trySetupData: make a FAILED ApnContext IDLE so its reusable";
            if (DBG) log(str);
            apnContext.requestLog(str);
            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());
                String str = "trySetupData: X No APN found retValue=false";
                if (DBG) log(str);
                apnContext.requestLog(str);
                return false;
            } else {
                apnContext.setWaitingApns(waitingApns);
                if (DBG) {
                    log ("trySetupData: Create from mAllApnSettings : "
                                + apnListToString(mAllApnSettings));
                }
            }
        }
        
        boolean retValue = setupData(apnContext, radioTech);
        notifyOffApnsOfAvailability(apnContext.getReason());

        if (DBG) log("trySetupData: X retValue=" + retValue);
        return retValue;
    } else {
        ......
    }
}
由于前面在onDataConnectionAttached方法中,设置了两个值为true,因此此处的isDataAllowed返回值为true,因此,会走如上的代码

最终会调用setupData方法,来建立DataConnection

private boolean setupData(ApnContext apnContext, int radioTech) {
    if (DBG) log("setupData: apnContext=" + apnContext);
    apnContext.requestLog("setupData");
    ApnSetting apnSetting;
    DcAsyncChannel dcac = null;

    apnSetting = apnContext.getNextApnSetting();

    if (apnSetting == null) {
        if (DBG) log("setupData: return for no apn found!");
        return false;
    }

    int profileId = apnSetting.profileId;
    if (profileId == 0) {
        profileId = getApnProfileID(apnContext.getApnType());
    }

    // On CDMA, if we're explicitly asking for DUN, we need have
    // a dun-profiled connection so we can't share an existing one
    // On GSM/LTE we can share existing apn connections provided they support
    // this type.
    if (apnContext.getApnType() != PhoneConstants.APN_TYPE_DUN ||
            teardownForDun() == false) {
        dcac = checkForCompatibleConnectedApnContext(apnContext);
        if (dcac != null) {
            // Get the dcacApnSetting for the connection we want to share.
            ApnSetting dcacApnSetting = dcac.getApnSettingSync();
            if (dcacApnSetting != null) {
                // Setting is good, so use it.
                apnSetting = dcacApnSetting;
            }
        }
    }
    if (dcac == null) {
        if (isOnlySingleDcAllowed(radioTech)) {
            if (isHigherPriorityApnContextActive(apnContext)) {
                if (DBG) {
                    log("setupData: Higher priority ApnContext active.  Ignoring call");
                }
                return false;
            }

            // Only lower priority calls left.  Disconnect them all in this single PDP case
            // so that we can bring up the requested higher priority call (once we receive
            // response for deactivate request for the calls we are about to disconnect
            if (cleanUpAllConnections(true, Phone.REASON_SINGLE_PDN_ARBITRATION)) {
                // If any call actually requested to be disconnected, means we can't
                // bring up this connection yet as we need to wait for those data calls
                // to be disconnected.
                if (DBG) log("setupData: Some calls are disconnecting first.  Wait and retry");
                return false;
            }

            // No other calls are active, so proceed
            if (DBG) log("setupData: Single pdp. Continue setting up data call.");
        }

        dcac = findFreeDataConnection();

        if (dcac == null) {
            dcac = createDataConnection();
        }

        if (dcac == null) {
            if (DBG) log("setupData: No free DataConnection and couldn't create one, WEIRD");
            return false;
        }
    }
    final int generation = apnContext.incAndGetConnectionGeneration();
    if (DBG) {
        log("setupData: dcac=" + dcac + " apnSetting=" + apnSetting + " gen#=" + generation);
    }

    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 = new Pair(apnContext, generation);
    dcac.bringUp(apnContext, profileId, radioTech, msg, generation);

    if (DBG) log("setupData: initing!");
    return true;
}
分析得出结论,最终通过调用createDataConnection方法,新建DcAsyncChannel对象

private DcAsyncChannel createDataConnection() {
    if (DBG) log("createDataConnection E");

    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);
    } else {
        loge("createDataConnection: Could not connect to dcac=" + dcac + " status=" + status);
    }

    if (DBG) log("createDataConnection() X id=" + id + " dc=" + conn);
    return dcac;
}
通过DataConnection的makeDataConnection方法新建一个DataConnection。

至此,DataConnection已经建立

你可能感兴趣的:(Telephony分析)