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;
}
这样关闭移动数据业务流程也分析完毕了。