Android P 开启移动数据流程
@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 方法。
@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 方法。
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 方法。
private synchronized void updateDataEnabledAndNotify(int reason) { boolean prevDataEnabled = mIsDataEnabled; updateDataEnabled(); if (prevDataEnabled != mIsDataEnabled) { notifyDataEnabledChanged(!prevDataEnabled, reason); } }
又调用了 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 构造方法里被注册了。
public DcTracker(Phone phone, int transportType) { //省略部分代码 mDataEnabledSettings.registerForDataEnabledChanged(this, DctConstants.EVENT_DATA_ENABLED_CHANGED, null); //省略部分代码 }
然后再看 EVENT_DATA_ENABLED_CHANGED 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 方法。
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 方法。
// 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 方法。
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 方法。
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 方法。
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 消息,业务逻辑的详情 如下。
@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 方法。
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 方法往下跟踪。
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 实现。
@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 处理。
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。
@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 方法将响应此消息回调。
@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; }
这样开启移动数据业务流程分析完毕。
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 方法。
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上下文,则将其删除。
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); }
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 回调。
@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; }
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 移动数据业务的处理。
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 实现。
@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 处理。
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。
@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 方法将响应此消息回调。
@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; }
这样关闭移动数据业务流程也分析完毕了。