Android开发

Android P 开启和关闭移动数据流程

本文主要是介绍Android P 开启和关闭移动数据流程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
开启或关闭移动数据有两个地方:1、状态栏里快捷键;2、设置页面。响应开启或关闭,会直接调用 TelephonyManager#setDataEnabled 方法。

Android P 开启移动数据流程

时序图

图片

TelephonyManager#setDataEnabled

@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void setDataEnabled(boolean enable) {
    setDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
}
/**
 * @hide
 * @deprecated use {@link #setDataEnabled(boolean)} instead.
*/
@SystemApi
@Deprecated
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void setDataEnabled(int subId, boolean enable) {
    try {
        Log.d(TAG, "setDataEnabled: enabled=" + enable);
        ITelephony telephony = getITelephony();
        if (telephony != null)
            telephony.setUserDataEnabled(subId, enable);
    } catch (RemoteException e) {
        Log.e(TAG, "Error calling ITelephony#setUserDataEnabled", e);
    }
}

ITelephony 是接口,看其实现类 PhoneInterfaceManager#setUserDataEnabled 方法。

PhoneInterfaceManager#setUserDataEnabled

@Override
public void setUserDataEnabled(int subId, boolean enable) {
    TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
            mApp, subId, "setUserDataEnabled");
    final long identity = Binder.clearCallingIdentity();
    try {
        int phoneId = mSubscriptionController.getPhoneId(subId);
        if (DBG) log("setUserDataEnabled: subId=" + subId + " phoneId=" + phoneId);
        Phone phone = PhoneFactory.getPhone(phoneId);
        if (phone != null) {
            if (DBG) log("setUserDataEnabled: subId=" + subId + " enable=" + enable);
            phone.getDataEnabledSettings().setUserDataEnabled(enable);
        } else {
            loge("setUserDataEnabled: no phone found. Invalid subId=" + subId);
        }
    } finally {
        Binder.restoreCallingIdentity(identity);
    }
}

继续调用 DataEnabledSettings#setUserDataEnabled 方法。

DataEnabledSettings#setUserDataEnabled

public synchronized void setUserDataEnabled(boolean enabled) {
    localLog("UserDataEnabled", enabled);
    //更新数据库设置,注意双卡模式下的异同
    Settings.Global.putInt(mResolver, getMobileDataSettingName(), enabled ? 1 : 0);
    mPhone.notifyUserMobileDataStateChanged(enabled);
    updateDataEnabledAndNotify(REASON_USER_DATA_ENABLED);
}

private String getMobileDataSettingName() {
    // For single SIM phones, this is a per phone property. Or if it's invalid subId, we
    // read default setting.
    int subId = mPhone.getSubId();
    if (TelephonyManager.getDefault().getSimCount() == 1
            || !SubscriptionManager.isValidSubscriptionId(subId)) {
        return Settings.Global.MOBILE_DATA;
    } else {
        return Settings.Global.MOBILE_DATA + mPhone.getSubId();
    }
}

继续看 DataEnabledSettings#updateDataEnabledAndNotify 方法。

DataEnabledSettings#updateDataEnabledAndNotify

private synchronized void updateDataEnabledAndNotify(int reason) {
    boolean prevDataEnabled = mIsDataEnabled;
    updateDataEnabled();
    if (prevDataEnabled != mIsDataEnabled) {
        notifyDataEnabledChanged(!prevDataEnabled, reason);
    }
}

又调用了 DataEnabledSettings#notifyDataEnabledChanged 方法。

DataEnabledSettings#notifyDataEnabledChanged

private void notifyDataEnabledChanged(boolean enabled, int reason) {
    mOverallDataEnabledChangedRegistrants.notifyResult(new Pair<>(enabled, reason));
}
public void registerForDataEnabledChanged(Handler h, int what, Object obj) {
    mOverallDataEnabledChangedRegistrants.addUnique(h, what, obj);
    notifyDataEnabledChanged(isDataEnabled(), REASON_REGISTERED);
}

这里需要看 DataEnabledChanged 哪里被注册,搜索看到是 DcTracker 构造方法里被注册了。

DcTracker 构造方法

public DcTracker(Phone phone, int transportType) {
    //省略部分代码
    mDataEnabledSettings.registerForDataEnabledChanged(this,
            DctConstants.EVENT_DATA_ENABLED_CHANGED, null);
    //省略部分代码
}

然后再看 EVENT_DATA_ENABLED_CHANGED handleMessage 处理的地方。

DcTracker#handleMessage

@Override
public void handleMessage (Message msg) {
    if (VDBG) log("handleMessage msg=" + msg);
    switch (msg.what) {
       	//省略部分代码
        case DctConstants.EVENT_DATA_ENABLED_CHANGED:
            AsyncResult 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);
            }
            break;
        default:
            Rlog.e("DcTracker", "Unhandled event=" + msg);
            break;
    }
}

继续调用了 DcTracker#onDataEnabledChanged 方法。

DcTracker#onDataEnabledChanged

private void onDataEnabledChanged(boolean enable,
                                  @DataEnabledChangedReason int enabledChangedReason) {
    if (DBG) {
        log("onDataEnabledChanged: enable=" + enable + ", enabledChangedReason="
                + enabledChangedReason);
    }
    if (enable) {//开启移动数据业务
        reevaluateDataConnections();
        onTrySetupData(Phone.REASON_DATA_ENABLED);
    } 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);
    }
}

这里有两个分支,先看开启移动数据业务,看 DcTracker#onTrySetupData 方法。

DcTracker#onTrySetupData

// TODO: We shouldnt need this.
private boolean onTrySetupData(String reason) {
    if (DBG) log("onTrySetupData: reason=" + reason);
    setupDataOnConnectableApns(reason);
    return true;
}

private void setupDataOnConnectableApns(String reason) {
    setupDataOnConnectableApns(reason, RetryFailures.ALWAYS);
}
private void setupDataOnConnectableApns(String reason, RetryFailures retryFailures) {
    //省略部分代码
    for (ApnContext apnContext : mPrioritySortedApnContexts) {
        if (VDBG) log("setupDataOnConnectableApns: apnContext " + apnContext);
        if (apnContext.getState() == DctConstants.State.FAILED
                || apnContext.getState() == DctConstants.State.SCANNING) {
            if (retryFailures == RetryFailures.ALWAYS) {
                apnContext.releaseDataConnection(reason);
            } else if (apnContext.isConcurrentVoiceAndDataAllowed() == false &&
                    mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
                // RetryFailures.ONLY_ON_CHANGE - check if voice concurrency has changed
                apnContext.releaseDataConnection(reason);
            }
        }
        //可连接的 apnContext
        if (apnContext.isConnectable()) {
            log("isConnectable() call trySetupData");
            //设置开启移动数据业务的原因
            apnContext.setReason(reason);
            trySetupData(apnContext);
        }
    }
}

继续看 DcTracker#trySetupData 方法。

DcTracker#trySetupData

private boolean trySetupData(ApnContext apnContext) {
    if (mPhone.getSimulatedRadioControl() != null) {
        // Assume data is connected on the simulator
        // FIXME  this can be improved
        apnContext.setState(DctConstants.State.CONNECTED);
        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
        log("trySetupData: X We're on the simulator; assuming connected retValue=true");
        return true;
    }
    DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
    boolean isDataAllowed = isDataAllowed(apnContext, dataConnectionReasons);
    String logStr = "trySetupData for APN type " + apnContext.getApnType() + ", reason: "
            + apnContext.getReason() + ". " + dataConnectionReasons.toString();
    if (DBG) log(logStr);
    apnContext.requestLog(logStr);
    if (isDataAllowed) {
        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(mPhone.getServiceStateTracker()
                .isConcurrentVoiceAndDataAllowed());
        if (apnContext.getState() == DctConstants.State.IDLE) {
            ArrayList<ApnSetting> waitingApns =
                    buildWaitingApns(apnContext.getApnType(), radioTech);
            if (waitingApns.isEmpty()) {
                notifyNoData(DataFailCause.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 {
        //省略部分代码
        return false;
    }
}

继续看 DcTracker#setupData 方法。

DcTracker#setupData

private boolean setupData(ApnContext apnContext, int radioTech) {
    if (DBG) log("setupData: apnContext=" + apnContext);
    apnContext.requestLog("setupData");
    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.
    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)
            || ServiceState.isGsm(mPhone.getServiceState().getRilDataRadioTechnology())) {
        dataConnection = checkForCompatibleConnectedApnContext(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;
            }
            if (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IMS)) {
                // 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();
        if (dataConnection == null) {
           	//创建 DataConnection 对象
            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);
    }
    apnContext.setDataConnection(dataConnection);
    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);
    //使用 bringUp 激活移动数据业务
    dataConnection.bringUp(apnContext, profileId, radioTech, msg, generation);
    if (DBG) log("setupData: initing!");
    return true;
}

继续看 DataConnection#bringUp 方法。

DataConnection#bringUp

public void bringUp(ApnContext apnContext, int profileId, int rilRadioTechnology,
                    Message onCompletedMsg, int connectionGeneration) {
    if (DBG) {
        log("bringUp: apnContext=" + apnContext + " onCompletedMsg=" + onCompletedMsg);
    }
    sendMessage(DataConnection.EVENT_CONNECT,
            new ConnectionParams(apnContext, profileId, rilRadioTechnology, onCompletedMsg,
                    connectionGeneration));
}

通过 sendMessage 发送 DataConnection.EVENT_CONNECT 信息,问题来了,哪里接受处理该 Message 消息呢?因为这里搜索有很多地方接受了。

那就是由 StateMachine 对象 SmHandler  handleMessage 方法响应该消息。由 StateMachine 运行机制和业务流程,可以确定 DataConnection.mInactiveState(非活动状态) 对象的 processMessage 方法将响应并处理此 Message 消息,业务逻辑的详情 如下。

DcInactiveState#processMessage

@Override
public boolean processMessage(Message msg) {
    boolean retVal;
    switch (msg.what) {
        //省略部分代码
        case EVENT_CONNECT:
            if (DBG) log("DcInactiveState: mag.what=EVENT_CONNECT");
            ConnectionParams cp = (ConnectionParams) msg.obj;
            if (initConnection(cp)) {
                onConnect(mConnectionParams);
                //完成 DataConnection 状态切换
                transitionTo(mActivatingState);
            } else {
                if (DBG) {
                    log("DcInactiveState: msg.what=EVENT_CONNECT initConnection failed");
                }
                notifyConnectCompleted(cp, DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER,
                        false);
            }
            retVal = HANDLED;
            break;
         	//省略部分代码
    }
    return retVal;
}

继续看 DataConnection#onConnect 方法。

DataConnection#onConnect

private void onConnect(ConnectionParams cp) {
    if (DBG) {
        log("onConnect: carrier='" + mApnSetting.getEntryName()
                + "' APN='" + mApnSetting.getApnName()
                + "' proxy='" + mApnSetting.getProxyAddressAsString()
                + "' port='" + mApnSetting.getProxyPort() + "'");
    }
    if (cp.mApnContext != null) cp.mApnContext.requestLog("DataConnection.onConnect");
    // Check if we should fake an error.
    if (mDcTesterFailBringUpAll.getDcFailBringUp().mCounter  > 0) {
        DataCallResponse response = new DataCallResponse(
                mDcTesterFailBringUpAll.getDcFailBringUp().mFailCause,
                mDcTesterFailBringUpAll.getDcFailBringUp().mSuggestedRetryTime, 0, 0, "", "",
                null, null, null, null, PhoneConstants.UNSET_MTU);
        Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
        AsyncResult.forMessage(msg, response, null);
        sendMessage(msg);
        if (DBG) {
            log("onConnect: FailBringUpAll=" + mDcTesterFailBringUpAll.getDcFailBringUp()
                    + " send error response=" + response);
        }
        mDcTesterFailBringUpAll.getDcFailBringUp().mCounter -= 1;
        return;
    }
    mCreateTime = -1;
    mLastFailTime = -1;
    mLastFailCause = DataFailCause.NONE;
    Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
    msg.obj = cp;
    DataProfile dp = DcTracker.createDataProfile(mApnSetting, cp.mProfileId,
            mApnSetting.equals(mDct.getPreferredApn()));
    // We need to use the actual modem roaming state instead of the framework roaming state
    // here. This flag is only passed down to ril_service for picking the correct protocol (for
    // old modem backward compatibility).
    boolean isModemRoaming = mPhone.getServiceState().getDataRoamingFromRegistration();
    // Set this flag to true if the user turns on data roaming. Or if we override the roaming
    // state in framework, we should set this flag to true as well so the modem will not reject
    // the data call setup (because the modem actually thinks the device is roaming).
    boolean allowRoaming = mPhone.getDataRoamingEnabled()
            || (isModemRoaming && !mPhone.getServiceState().getDataRoaming());
    //注意这里的参数msg,移动数据完成激活会通过 EVENT_SETUP_DATA_CONNECTION_DONE 回调。
    mDataServiceManager.setupDataCall(
            ServiceState.rilRadioTechnologyToAccessNetworkType(cp.mRilRat), dp, isModemRoaming,
            allowRoaming, DataService.REQUEST_REASON_NORMAL, null, msg);
    TelephonyMetrics.getInstance().writeSetupDataCall(mPhone.getPhoneId(), cp.mRilRat,
            dp.getProfileId(), dp.getApn(), dp.getProtocol());
}

继续看 DataServiceManager.setupDataCall 方法往下跟踪。

DataServiceManager.setupDataCall

public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
                          boolean allowRoaming, int reason, LinkProperties linkProperties,
                          Message onCompleteMessage) {
    if (DBG) log("setupDataCall");
    if (!mBound) {
        loge("Data service not bound.");
        sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
        return;
    }
    CellularDataServiceCallback callback = null;
    if (onCompleteMessage != null) {
        callback = new CellularDataServiceCallback();
        mMessageMap.put(callback.asBinder(), onCompleteMessage);
    }
    try {
        mIDataService.setupDataCall(mPhone.getPhoneId(), accessNetworkType, dataProfile,
                isRoaming, allowRoaming, reason, linkProperties, callback);
    } catch (RemoteException e) {
        loge("Cannot invoke setupDataCall on data service.");
        if (callback != null) {
            mMessageMap.remove(callback.asBinder());
        }
        sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
    }
}

继续调用 mIDataService.setupDataCall,由 IDataServiceWrapper.setupDataCall 实现。

IDataServiceWrapper#setupDataCall

@Override
public void setupDataCall(int slotId, int accessNetworkType, DataProfile dataProfile,
                          boolean isRoaming, boolean allowRoaming, int reason,
                          LinkProperties linkProperties, IDataServiceCallback callback) {
    mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, slotId, 0,
            new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming,
                    allowRoaming, reason, linkProperties, callback))
            .sendToTarget();
}

直接看 DATA_SERVICE_REQUEST_SETUP_DATA_CALL 处理。

DataServiceHandler#handleMessage

case DATA_SERVICE_REQUEST_SETUP_DATA_CALL:
    if (serviceProvider == null) break;
    SetupDataCallRequest setupDataCallRequest = (SetupDataCallRequest) message.obj;
    serviceProvider.setupDataCall(setupDataCallRequest.accessNetworkType,
            setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming,
            setupDataCallRequest.allowRoaming, setupDataCallRequest.reason,
            setupDataCallRequest.linkProperties,
            (setupDataCallRequest.callback != null)
                    ? new DataServiceCallback(setupDataCallRequest.callback)
                    : null);
    break;

点击这里 SetupDataCallRequest#setupDataCall 会跳到 DataServiceProvider#setupDataCall,DataServiceProvider 是抽象类,看它继承类 CellularDataServiceProvider#setupDataCall。

CellularDataServiceProvider#setupDataCall

@Override
public void setupDataCall(int radioTechnology, DataProfile dataProfile, boolean isRoaming,
                          boolean allowRoaming, int reason, LinkProperties linkProperties,
                          DataServiceCallback callback) {
    if (DBG) log("setupDataCall " + getSlotId());
    Message message = null;
    // Only obtain the message when the caller wants a callback. If the caller doesn't care
    // the request completed or results, then no need to pass the message down.
    if (callback != null) {
        message = Message.obtain(mHandler, SETUP_DATA_CALL_COMPLETE);
        mCallbackMap.put(message, callback);
    }
    mPhone.mCi.setupDataCall(radioTechnology, dataProfile, isRoaming, allowRoaming, reason,
            linkProperties, message);
}

上面代码中最关键的是对 mPhone . mCi .setupDataCall 方法的调用, mCi 即 RILJ 对象,最终由 RIL 完成 Data Call 移动数据业务的处理。

RIL 完成激活 Data Call 移动数据业务处理之后,EVENT_SETUP_DATA_CONNECTION_DONE 作为 setupDataCall 方法调用的参数之一,会使用此 Message 对象发起  Callback 调用 。

DataConnection . SmHandler 对象的 handleMessage 方法进行晌应, StateMachine 的状态已经 切换到 DataConnection.mActivatingState ,因此 mActivatingState 对象的 processMessage 方法将响应此消息回调。

DcActivatingState#processMessage

@Override
public boolean processMessage(Message msg) {
    boolean retVal;
    AsyncResult ar;
    ConnectionParams cp;
    if (DBG) log("DcActivatingState: msg=" + msgToString(msg));
    switch (msg.what) {
        case EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED:
        case EVENT_CONNECT:
            // Activating can't process until we're done.
            deferMessage(msg);
            retVal = HANDLED;
            break;
        case EVENT_SETUP_DATA_CONNECTION_DONE:
            cp = (ConnectionParams) msg.obj;
            DataCallResponse dataCallResponse =
                    msg.getData().getParcelable(DataServiceManager.DATA_CALL_RESPONSE);
            SetupResult result = onSetupConnectionCompleted(msg.arg1, dataCallResponse, cp);
            if (result != SetupResult.ERROR_STALE) {
                if (mConnectionParams != cp) {
                    loge("DcActivatingState: WEIRD mConnectionsParams:"+ mConnectionParams
                            + " != cp:" + cp);
                }
            }
            if (DBG) {
                log("DcActivatingState onSetupConnectionCompleted result=" + result
                        + " dc=" + DataConnection.this);
            }
            if (cp.mApnContext != null) {
                cp.mApnContext.requestLog("onSetupConnectionCompleted result=" + result);
            }
            switch (result) {
                case SUCCESS://成功激活 Data Call 移动数据业务
                    // All is well
                    mDcFailCause = DataFailCause.NONE;
                    transitionTo(mActiveState);
                    break;
                case ERROR_RADIO_NOT_AVAILABLE:
                    // Vendor ril rejected the command and didn't connect.
                    // Transition to inactive but send notifications after
                    // we've entered the mInactive state.
                    mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
                    transitionTo(mInactiveState);
                    break;
                case ERROR_INVALID_ARG:
                    // The addresses given from the RIL are bad
                    tearDownData(cp);
                    transitionTo(mDisconnectingErrorCreatingConnection);
                    break;
                case ERROR_DATA_SERVICE_SPECIFIC_ERROR:
                    // Retrieve the suggested retry delay from the modem and save it.
                    // If the modem want us to retry the current APN again, it will
                    // suggest a positive delay value (in milliseconds). Otherwise we'll get
                    // NO_SUGGESTED_RETRY_DELAY here.
                    long delay = getSuggestedRetryDelay(dataCallResponse);
                    cp.mApnContext.setModemSuggestedDelay(delay);
                    String str = "DcActivatingState: ERROR_DATA_SERVICE_SPECIFIC_ERROR "
                            + " delay=" + delay
                            + " result=" + result
                            + " result.isRadioRestartFailure="
                            + DataFailCause.isRadioRestartFailure(mPhone.getContext(),
                            result.mFailCause, mPhone.getSubId())
                            + " isPermanentFailure=" +
                            mDct.isPermanentFailure(result.mFailCause);
                    if (DBG) log(str);
                    if (cp.mApnContext != null) cp.mApnContext.requestLog(str);
                    // Save the cause. DcTracker.onDataSetupComplete will check this
                    // failure cause and determine if we need to retry this APN later
                    // or not.
                    mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
                    transitionTo(mInactiveState);
                    break;
                case ERROR_STALE:
                    loge("DcActivatingState: stale EVENT_SETUP_DATA_CONNECTION_DONE"
                            + " tag:" + cp.mTag + " != mTag:" + mTag);
                    break;
                default:
                    throw new RuntimeException("Unknown SetupResult, should not happen");
            }
            retVal = HANDLED;
            break;
        default:
            if (VDBG) {
                log("DcActivatingState not handled msg.what=" +
                        getWhatToString(msg.what) + " RefCount=" + mApnContexts.size());
            }
            retVal = NOT_HANDLED;
            break;
    }
    return retVal;
}

这样开启移动数据业务流程分析完毕。

Android P 关闭移动数据流程

时序图

图片

DcTracker#onDataEnabledChanged

DcTracker#onDataEnabledChanged 有两个分支,开启和关闭移动数据业务。

private void onDataEnabledChanged(boolean enable,
                                  @DataEnabledChangedReason int enabledChangedReason) {
    if (DBG) {
        log("onDataEnabledChanged: enable=" + enable + ", enabledChangedReason="
                + enabledChangedReason);
    }
    if (enable) {//开启移动数据业务
        reevaluateDataConnections();
        onTrySetupData(Phone.REASON_DATA_ENABLED);
    } 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);
    }
}

现在看关闭移动数据流程,来看 DcTracker#cleanUpAllConnectionsInternal 方法。

DcTracker#cleanUpAllConnectionsInternal

private boolean cleanUpAllConnectionsInternal(boolean tearDown, String reason) {
    if (DBG) log("cleanUpAllConnections: tearDown=" + tearDown + " 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) ||
                reason.equals(Phone.REASON_PDP_RESET);
    }
    for (ApnContext apnContext : mApnContexts.values()) {
        if (disableMeteredOnly) {
            // Use ApnSetting to decide metered or non-metered.
            // Tear down all metered data connections.
            ApnSetting apnSetting = apnContext.getApnSetting();
            if (apnSetting != null && ApnSettingUtils.isMetered(apnSetting, mPhone)) {
                if (apnContext.isDisconnected() == false) didDisconnect = true;
                if (DBG) log("clean up metered ApnContext Type: " + apnContext.getApnType());
                apnContext.setReason(reason);
                //将APN上下文与关联的数据连接分离,如果没有附加其他APN上下文,则将其删除。
                cleanUpConnectionInternal(tearDown, apnContext);
            }
        } else {
            // Exclude the IMS APN from single DataConenction case.
            if (reason.equals(Phone.REASON_SINGLE_PDN_ARBITRATION)
                    && apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IMS)) {
                continue;
            }
            // TODO - only do cleanup if not disconnected
            if (apnContext.isDisconnected() == false) didDisconnect = true;
            apnContext.setReason(reason);
            cleanUpConnectionInternal(tearDown, apnContext);
        }
    }
    stopNetStatPoll();
    stopDataStallAlarm();
    // TODO: Do we need mRequestedApnType?
    mRequestedApnType = ApnSetting.TYPE_DEFAULT;
    log("cleanUpConnectionInternal: mDisconnectPendingCount = " + mDisconnectPendingCount);
    if (tearDown && mDisconnectPendingCount == 0) {
        notifyAllDataDisconnected();
    }
    return didDisconnect;
}

DcTracker#cleanUpConnectionInternal 方法将APN上下文与关联的数据连接分离,如果没有附加其他APN上下文,则将其删除。

DcTracker#cleanUpConnectionInternal

private void cleanUpConnectionInternal(boolean tearDown, ApnContext apnContext) {
    if (apnContext == null) {
        if (DBG) log("cleanUpConnectionInternal: apn context is null");
        return;
    }
    DataConnection dataConnection = apnContext.getDataConnection();
    String str = "cleanUpConnectionInternal: tearDown=" + tearDown + " reason="
            + apnContext.getReason();
    if (VDBG) log(str + " apnContext=" + apnContext);
    apnContext.requestLog(str);
    if (tearDown) {
        if (apnContext.isDisconnected()) {
            // The request is tearDown and but ApnContext is not connected.
            // If apnContext is not enabled anymore, break the linkage to the data connection.
            apnContext.setState(DctConstants.State.IDLE);
            if (!apnContext.isReady()) {
                if (dataConnection != null) {
                    str = "cleanUpConnectionInternal: teardown, disconnected, !ready";
                    if (DBG) log(str + " apnContext=" + apnContext);
                    apnContext.requestLog(str);
                    dataConnection.tearDown(apnContext, "", null);
                }
                apnContext.setDataConnection(null);
            }
        } else {
            // Connection is still there. Try to clean up.
            if (dataConnection != null) {
                if (apnContext.getState() != DctConstants.State.DISCONNECTING) {
                    boolean disconnectAll = false;
                    if (PhoneConstants.APN_TYPE_DUN.equals(apnContext.getApnType())) {
                        // CAF_MSIM is this below condition required.
                        // if (PhoneConstants.APN_TYPE_DUN.equals(PhoneConstants.APN_TYPE_DEFAULT)) {
                        if (teardownForDun()) {
                            if (DBG) {
                                log("cleanUpConnectionInternal: disconnectAll DUN connection");
                            }
                            // we need to tear it down - we brought it up just for dun and
                            // other people are camped on it and now dun is done.  We need
                            // to stop using it and let the normal apn list get used to find
                            // connections for the remaining desired connections
                            disconnectAll = true;
                        }
                    }
                    final int generation = apnContext.getConnectionGeneration();
                    str = "cleanUpConnectionInternal: tearing down"
                            + (disconnectAll ? " all" : "") + " using gen#" + generation;
                    if (DBG) log(str + "apnContext=" + apnContext);
                    apnContext.requestLog(str);
                    Pair<ApnContext, Integer> pair = new Pair<>(apnContext, generation);
                    Message msg = obtainMessage(DctConstants.EVENT_DISCONNECT_DONE, pair);
                    if (disconnectAll) {
                        dataConnection.tearDownAll(apnContext.getReason(), msg);
                    } else {
                        dataConnection.tearDown(apnContext, apnContext.getReason(), msg);
                    }
                    apnContext.setState(DctConstants.State.DISCONNECTING);
                    mDisconnectPendingCount++;
                }
            } else {
                // apn is connected but no reference to the data connection.
                // Should not be happen, but reset the state in case.
                apnContext.setState(DctConstants.State.IDLE);
                apnContext.requestLog("cleanUpConnectionInternal: connected, bug no dc");
                mPhone.notifyDataConnection(apnContext.getReason(),
                                            apnContext.getApnType());
            }
        }
    } else {
        // force clean up the data connection.
        if (dataConnection != null) dataConnection.reset();
        apnContext.setState(DctConstants.State.IDLE);
        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
        apnContext.setDataConnection(null);
    }
    // Make sure reconnection alarm is cleaned up if there is no ApnContext
    // associated to the connection.
    if (dataConnection != null) {
        cancelReconnectAlarm(apnContext);
    }
    str = "cleanUpConnectionInternal: X tearDown=" + tearDown + " reason="
            + apnContext.getReason();
    if (DBG) log(str + " apnContext=" + apnContext + " dc=" + apnContext.getDataConnection());
    apnContext.requestLog(str);
}

DataConnection#tearDown&DataConnection#tearDownAll

public void tearDown(ApnContext apnContext, String reason, Message onCompletedMsg) {
    if (DBG) {
        log("tearDown: apnContext=" + apnContext
                + " reason=" + reason + " onCompletedMsg=" + onCompletedMsg);
    }
    sendMessage(DataConnection.EVENT_DISCONNECT,
            new DisconnectParams(apnContext, reason, onCompletedMsg));
}
public void tearDownAll(String reason, Message onCompletedMsg) {
    if (DBG) log("tearDownAll: reason=" + reason + " onCompletedMsg=" + onCompletedMsg);
    sendMessage(DataConnection.EVENT_DISCONNECT_ALL,
            new DisconnectParams(null, reason, onCompletedMsg));
}

StateMachine 的状态已经切换到 DataConnection.mActiveState ,因此 mActiveState 对象的 processMessage 方法将响应 EVENT_DISCONNECT 和 EVENT_DISCONNECT_ALL 回调。

DcActiveState#processMessage

@Override
public boolean processMessage(Message msg) {
    boolean retVal;
    switch (msg.what) {
        //省略部分代码
        case EVENT_DISCONNECT: {
            DisconnectParams dp = (DisconnectParams) msg.obj;
            if (DBG) {
                log("DcActiveState: EVENT_DISCONNECT dp=" + dp
                        + " dc=" + DataConnection.this);
            }
            if (mApnContexts.containsKey(dp.mApnContext)) {
                if (DBG) {
                    log("DcActiveState msg.what=EVENT_DISCONNECT RefCount="
                            + mApnContexts.size());
                }
                if (mApnContexts.size() == 1) {
                    mApnContexts.clear();
                    mDisconnectParams = dp;
                    mConnectionParams = null;
                    dp.mTag = mTag;
                    tearDownData(dp);
                    transitionTo(mDisconnectingState);
                } else {
                    mApnContexts.remove(dp.mApnContext);
                    // TODO (b/118347948): evaluate if it's still needed after assigning
                    // different scores to different Cellular network.
                    mDisabledApnTypeBitMask |= dp.mApnContext.getApnTypeBitmask();
                    mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities());
                    notifyDisconnectCompleted(dp, false);
                }
            } else {
                log("DcActiveState ERROR no such apnContext=" + dp.mApnContext
                        + " in this dc=" + DataConnection.this);
                notifyDisconnectCompleted(dp, false);
            }
            retVal = HANDLED;
            break;
        }
        case EVENT_DISCONNECT_ALL: {
            if (DBG) {
                log("DcActiveState EVENT_DISCONNECT clearing apn contexts,"
                        + " dc=" + DataConnection.this);
            }
            DisconnectParams dp = (DisconnectParams) msg.obj;
            mDisconnectParams = dp;
            mConnectionParams = null;
            dp.mTag = mTag;
            tearDownData(dp);
            transitionTo(mDisconnectingState);
            retVal = HANDLED;
            break;
        }
        //省略部分代码
        default:
            if (VDBG) {
                log("DcActiveState not handled msg.what=" + getWhatToString(msg.what));
            }
            retVal = NOT_HANDLED;
            break;
    }
    return retVal;
}

DataConnection#tearDownData

private void tearDownData(Object o) {
    int discReason = DataService.REQUEST_REASON_NORMAL;
    ApnContext apnContext = null;
    if ((o != null) && (o instanceof DisconnectParams)) {
        DisconnectParams dp = (DisconnectParams) o;
        apnContext = dp.mApnContext;
        if (TextUtils.equals(dp.mReason, Phone.REASON_RADIO_TURNED_OFF)
                || TextUtils.equals(dp.mReason, Phone.REASON_PDP_RESET)) {
            discReason = DataService.REQUEST_REASON_SHUTDOWN;
        }
    }
    String str = "tearDownData. mCid=" + mCid + ", reason=" + discReason;
    if (DBG) log(str);
    if (apnContext != null) apnContext.requestLog(str);
    mDataServiceManager.deactivateDataCall(mCid, discReason,
            obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, o));
}

下面的流程和开启移动数据业务基本相识了,最终也是由 RIL 停用 Data Call 移动数据业务的处理。

DataServiceManager#deactivateDataCall

public void deactivateDataCall(int cid, int reason, Message onCompleteMessage) {
    if (DBG) log("deactivateDataCall");
    if (!mBound) {
        loge("Data service not bound.");
        sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
        return;
    }
    CellularDataServiceCallback callback = null;
    if (onCompleteMessage != null) {
        callback = new CellularDataServiceCallback();
        mMessageMap.put(callback.asBinder(), onCompleteMessage);
    }
    try {
        mIDataService.deactivateDataCall(mPhone.getPhoneId(), cid, reason, callback);
    } catch (RemoteException e) {
        loge("Cannot invoke deactivateDataCall on data service.");
        if (callback != null) {
            mMessageMap.remove(callback.asBinder());
        }
        sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
    }
}

继续调用 mIDataService.setupDataCall,由 IDataServiceWrapper.setupDataCall 实现。

IDataServiceWrapper#deactivateDataCall

@Override
public void deactivateDataCall(int slotId, int cid, int reason,
                               IDataServiceCallback callback) {
    mHandler.obtainMessage(DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL, slotId, 0,
            new DeactivateDataCallRequest(cid, reason, callback))
            .sendToTarget();
}

直接看 DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL 处理。

DataServiceHandler#handleMessage

case DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL:
    if (serviceProvider == null) break;
    DeactivateDataCallRequest deactivateDataCallRequest =
            (DeactivateDataCallRequest) message.obj;
    serviceProvider.deactivateDataCall(deactivateDataCallRequest.cid,
            deactivateDataCallRequest.reason,
            (deactivateDataCallRequest.callback != null)
                    ? new DataServiceCallback(deactivateDataCallRequest.callback)
                    : null);
    break;

点击这里 DeactivateDataCallRequest#deactivateDataCall 会跳到 DataServiceProvider#deactivateDataCall,DataServiceProvider 是抽象类,看它继承类 CellularDataServiceProvider#deactivateDataCall。

CellularDataServiceProvider#deactivateDataCall

@Override
public void deactivateDataCall(int cid, int reason, DataServiceCallback callback) {
    if (DBG) log("deactivateDataCall " + getSlotId());
    Message message = null;
    // Only obtain the message when the caller wants a callback. If the caller doesn't care
    // the request completed or results, then no need to pass the message down.
    if (callback != null) {
        message = Message.obtain(mHandler, DEACTIVATE_DATA_ALL_COMPLETE);
        mCallbackMap.put(message, callback);
    }
    mPhone.mCi.deactivateDataCall(cid, reason, message);
}

mCi 即 RILJ 对象,最终由 RIL 停用 Data Call 移动数据业务的处理,处理成功会回调到 EVENT_DEACTIVATE_DONE,DataConnection.mDisconnectingState  对象的 processMessage 方法将响应此消息回调。

DisconnectingState#processMessage

@Override
public boolean processMessage(Message msg) {
    boolean retVal;
    switch (msg.what) {
        case EVENT_DEACTIVATE_DONE:
            ConnectionParams cp = (ConnectionParams) msg.obj;
            if (cp.mTag == mTag) {
                String str = "DcDisconnectionErrorCreatingConnection" +
                        " msg.what=EVENT_DEACTIVATE_DONE";
                if (DBG) log(str);
                if (cp.mApnContext != null) cp.mApnContext.requestLog(str);
                // Transition to inactive but send notifications after
                // we've entered the mInactive state.
                mInactiveState.setEnterNotificationParams(cp,
                        DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER);
                transitionTo(mInactiveState);
            } else {
                if (DBG) {
                    log("DcDisconnectionErrorCreatingConnection stale EVENT_DEACTIVATE_DONE"
                            + " dp.tag=" + cp.mTag + ", mTag=" + mTag);
                }
            }
            retVal = HANDLED;
            break;
        default:
            if (VDBG) {
                log("DcDisconnectionErrorCreatingConnection not handled msg.what="
                        + getWhatToString(msg.what));
            }
            retVal = NOT_HANDLED;
            break;
    }
    return retVal;
}

这样关闭移动数据业务流程也分析完毕了。


这篇关于Android P 开启和关闭移动数据流程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!