Android11 移动数据开关流程——DcTracker解析

Android11 移动数据开关流程——DcTracker解析


该章只分析DcTracker部分逻辑


前言

Android移动数据上网是通过打开apn,三大运营商都有不同apn配置,如果需要上专网也要自行配置相关的apn配置。
该文主要是解析打开apn时DcTracker部分的流程,也是apn上网在framework中比较核心的一个地方。


一、DataEnabledSettings

frameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java

先看一下在此之前是怎么调到DcTracker里面的接口的
这个基本就是可以暴露给用户使用的打开数据流量开关的接口
最终往下进行走的是 updateDataEnabledAndNotify(REASON_USER_DATA_ENABLED);

    /**
     * @param notifyMultiSimSettingController if setUserDataEnabled is already from propagating
     *        from MultiSimSettingController, don't notify MultiSimSettingController again.
     *        For example, if sub1 and sub2 are in the same group and user enables data for sub
     *        1, sub 2 will also be enabled but with propagateToGroup = false.
     */
    public synchronized void setUserDataEnabled(boolean enabled,
            boolean notifyMultiSimSettingController) {
        // Can't disable data for stand alone opportunistic subscription.
        //判断是否是受限的subid,也就是订阅的卡槽
        if (isStandAloneOpportunistic(mPhone.getSubId(), mPhone.getContext()) && !enabled) return;

        boolean changed = GlobalSettingsHelper.setInt(mPhone.getContext(), //设置上setting值
                Settings.Global.MOBILE_DATA, mPhone.getSubId(), (enabled ? 1 : 0));
        if (changed) {
            localLog("UserDataEnabled", enabled);
            mPhone.notifyUserMobileDataStateChanged(enabled);//通知给上层开关状态改变
            updateDataEnabledAndNotify(REASON_USER_DATA_ENABLED); //1
            if (notifyMultiSimSettingController) {
                MultiSimSettingController.getInstance().notifyUserDataEnabled(
                        mPhone.getSubId(), enabled);
            }
        }
    }

updateDataEnabledAndNotify
这个方法很简单就是执行了两个方法

    private synchronized void updateDataEnabledAndNotify(int reason) {
        boolean prevDataEnabled = mIsDataEnabled;

        updateDataEnabled();//1

        if (prevDataEnabled != mIsDataEnabled) {
            notifyDataEnabledChanged(!prevDataEnabled, reason);//2
        }
    }

updateDataEnabled
先看第一个

    private synchronized void updateDataEnabled() {
        if (isProvisioning()) {//一般开机引导完事之后都是false
            mIsDataEnabled = isProvisioningDataEnabled();
        } else { //更新上层状态
            mIsDataEnabled = mInternalDataEnabled && (isUserDataEnabled() || mDataEnabledOverride
                    .shouldOverrideDataEnabledSettings(mPhone, ApnSetting.TYPE_ALL))
                    && mPolicyDataEnabled && mCarrierDataEnabled;
        }
    }

notifyDataEnabledChanged

    private void notifyDataEnabledChanged(boolean enabled, int reason) {
        mOverallDataEnabledChangedRegistrants.notifyResult(new Pair<>(enabled, reason));
    }

mOverallDataEnabledChangedRegistrants这个变量是DcTracker构造方法内执行的注册近来的回调
因为DcTracker就是一个Handler
所以开始分析其内部接收到回调之后的操作

二、DcTracker

1.引入库

frameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
这个就是其注册的代码
连同接收的地方一起写下

        mDataEnabledSettings.registerForDataEnabledChanged(this,
                DctConstants.EVENT_DATA_ENABLED_CHANGED, null);

            case DctConstants.EVENT_DATA_ENABLED_CHANGED:
                ar = (AsyncResult) msg.obj;
                if (ar.result instanceof Pair) {
                    Pair<Boolean, Integer> p = (Pair<Boolean, Integer>) ar.result;
                    boolean enabled = p.first;
                    int reason = p.second;
                    onDataEnabledChanged(enabled, reason);//1
                }
                break;

onDataEnabledChanged

    private void onDataEnabledChanged(boolean enable,
                                      @DataEnabledChangedReason int enabledChangedReason) {
        if (DBG) {
            log("onDataEnabledChanged: enable=" + enable + ", enabledChangedReason="
                    + enabledChangedReason);
        }

        if (enable) { //打开和关闭数据流量还不一样的流程
            reevaluateDataConnections();//1
            setupDataOnAllConnectableApns(Phone.REASON_DATA_ENABLED, RetryFailures.ALWAYS);//2
        } else {
            String cleanupReason;
            switch (enabledChangedReason) {//根据不同原因,在执行清除连接方法的时候作为入参传进去
                case DataEnabledSettings.REASON_INTERNAL_DATA_ENABLED:
                    cleanupReason = Phone.REASON_DATA_DISABLED_INTERNAL;
                    break;
                case DataEnabledSettings.REASON_DATA_ENABLED_BY_CARRIER:
                    cleanupReason = Phone.REASON_CARRIER_ACTION_DISABLE_METERED_APN;
                    break;
                case DataEnabledSettings.REASON_USER_DATA_ENABLED:
                case DataEnabledSettings.REASON_POLICY_DATA_ENABLED:
                case DataEnabledSettings.REASON_PROVISIONED_CHANGED:
                case DataEnabledSettings.REASON_PROVISIONING_DATA_ENABLED_CHANGED:
                default:
                    cleanupReason = Phone.REASON_DATA_SPECIFIC_DISABLED;
                    break;

            }
            cleanUpAllConnectionsInternal(true, cleanupReason);//3
        }
    }

第一个reevaluateDataConnections
reevaluateDataConnections
简单来说就是重新评估当前所有网络连接的可行性,更新状态,该打开的打开,该关闭的关闭,为打开的新的连接做准备

    /**
     * Reevaluate existing data connections when conditions change.
     *
     * For example, handle reverting restricted networks back to unrestricted. If we're changing
     * user data to enabled and this makes data truly enabled (not disabled by other factors) we
     * need to reevaluate and possibly add NET_CAPABILITY_NOT_RESTRICTED capability to the data
     * connection. This allows non-privilege apps to use the network.
     *
     * Or when we brought up a unmetered data connection while data is off, we only limit this
     * data connection for unmetered use only. When data is turned back on, we need to tear that
     * down so a full capable data connection can be re-established.
     */
    private void reevaluateDataConnections() {
        for (DataConnection dataConnection : mDataConnections.values()) {
            dataConnection.reevaluateRestrictedState();
        }
    }

第二个(重要)
setupDataOnAllConnectableApns
遍历所有的apn类型,然后一次调用setupDataOnConnectableApn方法

    protected void setupDataOnAllConnectableApns(String reason, RetryFailures retryFailures) {
        if (VDBG) log("setupDataOnAllConnectableApns: " + reason);

        if (DBG) {
            StringBuilder sb = new StringBuilder(120);
            for (ApnContext apnContext : mPrioritySortedApnContexts) {
                sb.append(apnContext.getApnType());
                sb.append(":[state=");
                sb.append(apnContext.getState());
                sb.append(",enabled=");
                sb.append(apnContext.isEnabled());
                sb.append("] ");
            }
            log("setupDataOnAllConnectableApns: " + reason + " " + sb);
        }

        for (ApnContext apnContext : mPrioritySortedApnContexts) {//遍历
            setupDataOnConnectableApn(apnContext, reason, retryFailures);//1
        }
    }

其实际log大概是
D DCT-C-0 : setupDataOnAllConnectableApns: nwTypeChanged mcx:[state=IDLE,enabled=false] xcap:[state=IDLE,enabled=false] hipri:[state=IDLE,enabled=false] fota:[state=IDLE,enabled=false] ims:[state=IDLE,enabled=false] cbs:[state=IDLE,enabled=false] ia:[state=IDLE,enabled=false] emergency:[state=IDLE,enabled=false] unisoc1:[state=IDLE,enabled=false] mms:[state=IDLE,enabled=false] unisoc2:[state=IDLE,enabled=false] supl:[state=IDLE,enabled=false] unisoc3:[state=IDLE,enabled=false] dun:[state=IDLE,enabled=false] unisoc4:[state=IDLE,enabled=false] unisoc5:[state=IDLE,enabled=false] default:[state=CONNECTED,enabled=true]
可以看到我这只有默认是打开的 也就是普通消费者用三大运营商上网
第三个cleanUpAllConnectionsInternal
通过将APN上下文从数据连接中分离来清理所有数据连接

    /**
     * Clean up all data connections by detaching the APN contexts from the data connections, which
     * eventually tearing down all data connections after all APN contexts are detached from the
     * data connections.
     *
     * @param detach {@code true} if detaching APN context from the underlying data connection (when
     * no other APN context is attached to the data connection, the data connection will be torn
     * down.) {@code false} to only reset the data connection's state machine.
     *
     * @param reason reason for the clean up.
     * @return boolean - true if we did cleanup any connections, false if they
     *                   were already all disconnected.
     */
    private boolean cleanUpAllConnectionsInternal(boolean detach, String reason) {
        if (DBG) log("cleanUpAllConnectionsInternal: detach=" + detach + " reason=" + reason);
        boolean didDisconnect = false;
        boolean disableMeteredOnly = false;

        // reasons that only metered apn will be torn down
        if (!TextUtils.isEmpty(reason)) {
            disableMeteredOnly = reason.equals(Phone.REASON_DATA_SPECIFIC_DISABLED) ||
                    reason.equals(Phone.REASON_ROAMING_ON) ||
                    reason.equals(Phone.REASON_CARRIER_ACTION_DISABLE_METERED_APN);
        }

        for (ApnContext apnContext : mApnContexts.values()) {
            // Exclude the IMS APN from single data connection case.
            if (reason.equals(Phone.REASON_SINGLE_PDN_ARBITRATION)
                    && apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IMS)) {
                continue;
            }

            if (shouldCleanUpConnection(apnContext, disableMeteredOnly,
                    reason.equals(Phone.REASON_SINGLE_PDN_ARBITRATION))) {
                // TODO - only do cleanup if not disconnected
                if (apnContext.isDisconnected() == false) didDisconnect = true;  //如果已经;断联,直接清除
                apnContext.setReason(reason);
                cleanUpConnectionInternal(detach, RELEASE_TYPE_DETACH, apnContext);//执行清除方法
            } else if (DBG) {
                log("cleanUpAllConnectionsInternal: APN type " + apnContext.getApnType()
                        + " shouldn't be cleaned up.");
            }
        }

        stopNetStatPoll();// 停止网络统计轮询
        stopDataStallAlarm();//停止数据暂停报警

        // TODO: Do we need mRequestedApnType?
        mRequestedApnType = ApnSetting.TYPE_DEFAULT;

        log("cleanUpAllConnectionsInternal: mDisconnectPendingCount = "
                + mDisconnectPendingCount);
        if (detach && mDisconnectPendingCount == 0) {
            notifyAllDataDisconnected();//通知断连监听,phone进程有监听,SST也有监听
        }

        return didDisconnect;
    }

接下来继续讲述开启流量
走到setupDataOnConnectableApn方法

    protected void setupDataOnConnectableApn(ApnContext apnContext, String reason,
            RetryFailures retryFailures) {
        if (VDBG) log("setupDataOnAllConnectableApns: apnContext " + apnContext);

        if (apnContext.getState() == DctConstants.State.FAILED
                || apnContext.getState() == DctConstants.State.RETRYING) {
            if (retryFailures == RetryFailures.ALWAYS) {//如果总是打开失败,则放弃掉
                apnContext.releaseDataConnection(reason);
            } else if (!apnContext.isConcurrentVoiceAndDataAllowed() //该apn连接不支持语音和数据并发,也释放掉
                    && mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
                // RetryFailures.ONLY_ON_CHANGE - check if voice concurrency has changed
                apnContext.releaseDataConnection(reason);
            }
        }
        if (apnContext.isConnectable()) {//如果apn可供连接
            log("isConnectable() call trySetupData");
            apnContext.setReason(reason);
            trySetupData(apnContext, REQUEST_TYPE_NORMAL);//1
        }
    }

trySetupData

    private boolean trySetupData(ApnContext apnContext, @RequestNetworkType int requestType) {

        if (mPhone.getSimulatedRadioControl() != null) {//模拟器的话直接返回连接成功
            // Assume data is connected on the simulator
            // FIXME  this can be improved
            apnContext.setState(DctConstants.State.CONNECTED);
            mPhone.notifyDataConnection(apnContext.getApnType());

            log("trySetupData: X We're on the simulator; assuming connected retValue=true");
            return true;
        }

        DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
        boolean isDataAllowed = isDataAllowed(apnContext, requestType, dataConnectionReasons);//检查是否允许为给定的APN类型建立数据连接。
        String logStr = "trySetupData for APN type " + apnContext.getApnType() + ", reason: "
                + apnContext.getReason() + ", requestType=" + requestTypeToString(requestType)
                + ". " + dataConnectionReasons.toString();
        if (DBG) log(logStr);
        apnContext.requestLog(logStr);
        if (isDataAllowed) {
            if (apnContext.getState() == DctConstants.State.FAILED) {//如果是失败,置成idle
                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 = getDataRat();//获取网络类型
            if (radioTech == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN && mPhone.getServiceState()
                    .getState() == ServiceState.STATE_IN_SERVICE) {
                radioTech = getVoiceRat();
            }
            log("service state=" + mPhone.getServiceState());
            apnContext.setConcurrentVoiceAndDataAllowed(mPhone.getServiceStateTracker()
                    .isConcurrentVoiceAndDataAllowed());
            if (apnContext.getState() == DctConstants.State.IDLE) {//如果是空闲状态
                ArrayList<ApnSetting> waitingApns =
                        buildWaitingApns(apnContext.getApnType(), radioTech);//建立用于创建PDP的APN列表
                if (waitingApns.isEmpty()) {//如果没有建立起来直接返回失败
                    ApnSetting apn = apnContext != null ? apnContext.getApnSetting() : null;
                    mPhone.notifyDataConnectionFailed(apnContext.getApnType(),
                            apn != null ? apn.getApnName() : null,
                            DataFailCause.MISSING_UNKNOWN_APN);
                    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, requestType);//1

            if (DBG) log("trySetupData: X retValue=" + retValue);
            return retValue;
        } else {
            StringBuilder str = new StringBuilder();

            str.append("trySetupData failed. apnContext = [type=" + apnContext.getApnType()
                    + ", mState=" + apnContext.getState() + ", apnEnabled="
                    + apnContext.isEnabled() + ", mDependencyMet="
                    + apnContext.isDependencyMet() + "] ");

            if (!mDataEnabledSettings.isDataEnabled()) {
                str.append("isDataEnabled() = false. " + mDataEnabledSettings);
            }

            // If this is a data retry, we should set the APN state to FAILED so it won't stay
            // in RETRYING forever.
            if (apnContext.getState() == DctConstants.State.RETRYING) {//保证其不会永远处于重试状态
                apnContext.setState(DctConstants.State.FAILED);
                str.append(" Stop retrying.");
            }

            if (DBG) log(str.toString());
            apnContext.requestLog(str.toString());
            return false;
        }
    }

这其中有一个重要函数带领走向下一步
setupData

    /**
     * Setup a data connection based on given APN type.
     *
     * @param apnContext APN context
     * @param radioTech RAT of the data connection
     * @param requestType Data request type
     * @return True if successful, otherwise false.
     */
    private boolean setupData(ApnContext apnContext, int radioTech,
                              @RequestNetworkType int requestType) {
        if (DBG) {
            log("setupData: apnContext=" + apnContext + ", requestType="
                    + requestTypeToString(requestType));
        }
        apnContext.requestLog("setupData. requestType=" + requestTypeToString(requestType));
        ApnSetting apnSetting;
        DataConnection dataConnection = null;

        apnSetting = apnContext.getNextApnSetting();

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

        // profile id is only meaningful when the profile is persistent on the modem.
        //只有当配置文件在调制解调器上持久存在时,配置文件id才有意义。
        int profileId = DATA_PROFILE_INVALID;
        if (apnSetting.isPersistent()) {
            profileId = apnSetting.getProfileId();
            if (profileId == DATA_PROFILE_DEFAULT) {
                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().equals(PhoneConstants.APN_TYPE_DUN)//cdma相关,现在基本都是5g了
                || ServiceState.isGsm(getDataRat())) {
            dataConnection = checkForCompatibleDataConnection(apnContext);
            if (dataConnection != null) {
                // Get the apn setting used by the data connection
                ApnSetting dataConnectionApnSetting = dataConnection.getApnSetting();
                if (dataConnectionApnSetting != null) {
                    // Setting is good, so use it.
                    apnSetting = dataConnectionApnSetting;
                }
            }
        }
        if (dataConnection == null) {
            if (isOnlySingleDcAllowed(radioTech)) {
                if (isHigherPriorityApnContextActive(apnContext)) {//检查优先级,如果不够,直接返回
                    if (DBG) {
                        log("setupData: Higher priority ApnContext active.  Ignoring call");
                    }
                    return false;
                }

                // Should not start cleanUp if the setupData is for IMS APN
                // or retry of same APN(State==RETRYING).
                if (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IMS)
                        && (apnContext.getState() != DctConstants.State.RETRYING)) {
                    // 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 (cleanUpAllConnectionsInternal(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.");
            }

            dataConnection = findFreeDataConnection();//1,给dataConnection变量赋值,主要是控制数据连接的各种状态

            if (dataConnection == null) {//如果没有现成的就新建一个
                dataConnection = createDataConnection();
            }

            if (dataConnection == 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: dc=" + dataConnection + " apnSetting=" + apnSetting + " gen#="
                    + generation);
        }
        configQos();
        configFallbackCause();
        apnContext.setDataConnection(dataConnection);//把建好的变量设置给apnContext对象
        apnContext.setApnSetting(apnSetting);
        apnContext.setState(DctConstants.State.CONNECTING);//状态变化
        mPhone.notifyDataConnection(apnContext.getApnType());//通知上层数据连接

        Message msg = obtainMessage();
        msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
        msg.obj = new Pair<ApnContext, Integer>(apnContext, generation);

        ApnSetting preferredApn = getPreferredApn();
        boolean isPreferredApn = apnSetting.equals(preferredApn);
        dataConnection.bringUp(apnContext, profileId, radioTech, msg, generation, requestType,
                mPhone.getSubId(), isPreferredApn);

        if (DBG) {
            if (isPreferredApn) {
                log("setupData: initing! isPreferredApn=" + isPreferredApn
                        + ", apnSetting={" + apnSetting.toString() + "}");
            } else {
                String preferredApnStr = preferredApn == null ? "null" : preferredApn.toString();
                log("setupData: initing! isPreferredApn=" + isPreferredApn
                        + ", apnSetting={" + apnSetting + "}"
                        + ", preferredApn={" + preferredApnStr + "}");
            }
        }
        return true;
    }

讲一下刚才说的findFreeDataConnection函数
每个数据连接的背后都会有一个DataConnection对象与之对应
这个函数主要是看一下当前系统有没有已经创建好的相同类型的,就可以拿来直接用,不用再创建了

    private DataConnection findFreeDataConnection() {
        for (DataConnection dataConnection : mDataConnections.values()) {//遍历mDataConnections
            boolean inUse = false;
            for (ApnContext apnContext : mApnContexts.values()) {
                if (apnContext.getDataConnection() == dataConnection) {//有人用的话就标记好
                    inUse = true;
                    break;
                }
            }
            if (!inUse) {
                if (DBG) {
                    log("findFreeDataConnection: found free DataConnection=" + dataConnection);
                }
                return dataConnection;//直接返回
            }
        }
        log("findFreeDataConnection: NO free DataConnection");
        return null;
    }

总结

上述就是DcTracker主要的逻辑处理和重要函数,在apn开启和关闭中起着重要的作用
接下来的任务就交给了DataConnection这个类来处理,后续会分析
该类职责更加明确清楚,任务更加聚焦,同样是一个非常重要的类,值得我们去研究。

  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值