1. 程式人生 > >(N)Telephony分析(七)之DataConnection建立

(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<ApnSetting> 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, Integer>(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已經建立