赞
踩
目录
5.2 setupDataOnAllConnectableApns调用分析
拨号分析也就是数据业务如何建立起来,本文档用于分析及总结拨号的流程,用于后面快速定位RIL拨号这块出现的问题。
从流程图来看,调用很清楚,但重点在DataService里并没有去真正实现setupDataCall功能,而是通过其他provider类继承他实现setupDataCall功能。通过继承特性,我们可以看出,这个设计意思是后面计划有可能多个provider,但实际代码搜了下,目前只有CellularDataServiceProvider,可能框架先做好,后面想添加时,直接实现就行。从这里看出,大老设计的代码就是优秀,普通人不会想那么长远。这也体现了代码的艺术。
从类图层次来看,拨号可分为4层,自上到下分别为Data应用、Data接口、Data服务、RILJ。
Data应用:重点由DcTracker、ServiceStateTracker和DataConnecion 3个类组成。ServiceStateTracker用于处理当前的网络状态,如data voice 信号等。DcTracker用于拨号的逻辑处理流程,如是否打开数据业务,APN组装等。DataConnection是拨号的一个状态机。DataServiceManager用于提供DataService的接口。
Data接口:封装服务对外提供的接口,按设计模式的思路是外面不需要关心具体的接口如何实现,只管调用。
Data服务:此处可以再细分为提供者和服务。提供者由CellularDataServiceProvider和CellularDataService组成,从类图可以看出,都是继承了服务里的相关类;服务重点由DataService组成。DataService用于提供拨号的相关服务,DataServiceProvider和IDataServiceWrapper通过向DataServiceHandler发Message消息进行相关逻辑处理,IDataServiceWrapper是实现AIDL接口,也就是对外提供的服务接口。
RILJ用于向RILD处下RIL消息,本文档不重点介绍此处功能。
完整拨号的时序图如下。
在RILJ 的4个网络状态operator、voice、data、selection mode查询时,会触发对应的handle消息然后调用handlePollStateResult,见如下代码:
- case EVENT_POLL_STATE_CS_CELLULAR_REGISTRATION:
- case EVENT_POLL_STATE_PS_CELLULAR_REGISTRATION:
- case EVENT_POLL_STATE_PS_IWLAN_REGISTRATION:
- case EVENT_POLL_STATE_OPERATOR:
- ar = (AsyncResult) msg.obj;
- handlePollStateResult(msg.what, ar);
- break;
-
- case EVENT_POLL_STATE_NETWORK_SELECTION_MODE:
- if (DBG) log("EVENT_POLL_STATE_NETWORK_SELECTION_MODE");
- ar = (AsyncResult) msg.obj;
- if (mPhone.isPhoneTypeGsm()) {
- handlePollStateResult(msg.what, ar);
- } else {
- if (ar.exception == null && ar.result != null) {
- ints = (int[])ar.result;
- if (ints[0] == 1) { // Manual selection.
- mPhone.setNetworkSelectionModeAutomatic(null);
- }
- } else {
- log("Unable to getNetworkSelectionMode");
- }
- }
- break;
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
而handlePollStateResult的作用是更新mNewSS的状态,mNewSS是ServiceState的对象,用于获取本次网络的最新状态。在4个网络状态完成查询后,会调用pollStateDone函数,同时mNewSS也完成了更新。
pollStateDone函数作用是根据oldSS和mNewSS的变化来修改一些状态,然后根据这些状态发出消息做出相应的动作。比如针对拨号流程需要确认hasDataAttached是否附着上,附着的判断条件代码如下:
- boolean changed = (oldNrs == null || !oldNrs.isInService() || hasAirplaneModeOnChanged)&& (newNrs != null && newNrs.isInService());
- hasDataAttached.put(transport, changed);
changed条件为ture的情况是(oldNrs为空或者oldNrs未注册上或者飞行模式变化)并且(newNrs不为空并且newNrs已注册)。oldNrs和newNrs分别是通过如下代码获取。
- NetworkRegistrationInfo oldNrs = mSS.getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS, transport);
- NetworkRegistrationInfo newNrs = mNewSS.getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS, transport);
在状态完成修改后,mSS和mNewSS的变化如下图,mNewSS的最新状态给mSS,而mNewSS自行clean掉。后面应用或系统获取的网络状态都是从mSS获取。
- ServiceState tss = mSS;
- mSS = mNewSS;
- mNewSS = tss;
- // clean slate for next time
- mNewSS.setStateOutOfService();
hasDataDetached.put(transport, changed);这语句里的transport通过跟踪代码可以有两个值,一个是wwan另一个是wlan,此处是wwan无线广域网。如果changed为true的话,那么下面这段判断代码会被执行。
- if (hasDataAttached.get(transport)) {
- shouldLogAttachedChange = true;
- if (mAttachedRegistrants.get(transport) != null) {
- mAttachedRegistrants.get(transport).notifyRegistrants();
- }
- }
mAttachedRegistrants发出通知,通过mAttachedRegistrants搜关键字,最终是在DcTracker的registerServiceStateTrackerEvents函数进行的注册,这里其实用到了观察者设计模式。
- public void registerServiceStateTrackerEvents() {
- mPhone.getServiceStateTracker().registerForDataConnectionAttached(mTransportType, this,
- DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null);
- mPhone.getServiceStateTracker().registerForDataConnectionDetached(mTransportType, this,
- DctConstants.EVENT_DATA_CONNECTION_DETACHED, null);
- mPhone.getServiceStateTracker().registerForDataRoamingOn(this,
- DctConstants.EVENT_ROAMING_ON, null);
- mPhone.getServiceStateTracker().registerForDataRoamingOff(this,
- DctConstants.EVENT_ROAMING_OFF, null, true);
- mPhone.getServiceStateTracker().registerForPsRestrictedEnabled(this,
- DctConstants.EVENT_PS_RESTRICT_ENABLED, null);
- mPhone.getServiceStateTracker().registerForPsRestrictedDisabled(this,
- DctConstants.EVENT_PS_RESTRICT_DISABLED, null);
- mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged(mTransportType, this,
- DctConstants.EVENT_DATA_RAT_CHANGED, null);
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
至此,我们开始进入到了DcTracker类分析。通过代码跟踪消息EVENT_DATA_CONNECTION_ATTACHED发出后,依次调用onDataConnectionAttached-->setupDataOnAllConnectableApns-->setupDataOnConnectableApn-->trySetupData。
通过分析trySetupData代码,做了两个动作,一个是确认是否允许拨号;另一个是准备APN。
1)允许拨号检测:函数为isDataAllowed,里面有步骤介绍。因代码量有点多,就不帖出来。
- DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
- boolean isDataAllowed = isDataAllowed(apnContext, requestType, dataConnectionReasons);
- String logStr = "trySetupData for APN type " + apnContext.getApnType() + ", reason: "
- + apnContext.getReason() + ", requestType=" + requestTypeToString(requestType)
- + ". " + dataConnectionReasons.toString();
步骤1:获取所有环境条件,比如是否打开数据业务,Data是否Attach,radio是否关了等;
步骤2:如果是紧急的APN,允许拨号;
步骤3:根据状态,把不允许拨号的原因添加到reasons来;
步骤4:确定在某些特殊条件下是否允许数据。此步骤如果成立,会把步骤3的数据清空。
步骤5:返回reaseons.allowed(),此函数的作用是步骤3添加的条数是否为0,如果为0,则允许。
此处的细节也很多,也没有去细看,只看了个大概框架,后面如果有问题没有拨号的话,可以看看这里是否不允许拨号引起。
2)APN准备:buildWaitingApns使用用户设置的preferred APN构建一个可用于数据连接的备选APN列表,即waitingApns列表(当有preferred APN,该列表就只有一个)。若用户没有设置preferred APN,则将所有类型匹配的APN添加到waitingApns列表(如default类型)。当trySetupData函数检查有waitingApns列表中有可用的APN时,就会去尝试建立连接。
完成如上步骤后调用setupData,再进入到DataConnection调用bringUp。
- boolean retValue = setupData(apnContext, radioTech, requestType);
-
- if (DBG) log("trySetupData: X retValue=" + retValue);
- return retValue;
DataConnection是继承StateMachine,是个状态机类。
如果对状态机的处理机制不理解的话,完全是不知道消息怎么处理或在哪处理。这里根据个人从网上学习总结的状态机原理如下。
接口说明:
enter:进入节点执行此函数
exit:退出节点执行此函数
processMessage:消息处理函数
消息处理机制:
1)先执行子节点再执行父节点;
2)子节点的消息如果已找到,就不去调用父节点;
3)子节点的消息未找到,则到父节点上找,直到找到。
切换(transitionTo)处理机制:
状态A到状态B切换时的调用栈就是从状态A回到两者的公共父节点,再走到状态B的最短路线。这里的回就是执行exit函数,走就是enter函数。
示例:
如下面的状态树
假设S4为初始状态,如果要从S4切到S7,状态树是如何处理。其切换机制是这样:S4.exit->S1.exit->S2.enter->S7.enter。
注意:父节点不需要退出再进入。
而关于本类DataConnection的状态树代码如下:
- addState(mDefaultState);
- addState(mInactiveState, mDefaultState);
- addState(mActivatingState, mDefaultState);
- addState(mActiveState, mDefaultState);
- addState(mDisconnectingState, mDefaultState);
- addState(mDisconnectingErrorCreatingConnection, mDefaultState);
- setInitialState(mInactiveState);
对应的状态树如下:
开始状态为mInactiveState。
此函数就简单的发出EVENT_CONNECT消息,而如果在此类搜EVENT_CONNECT关键字,有很多地方处理,所以如果不理解状态树时,完全不知道走的是哪个地方。通过上面对状态树的了解,当前的开始状态是mInactiveState,所以我们到mInactiveState这个类看看看有没有对EVENT_CONNECT的处理,通过代码发现,果然有,代码如下:
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case EVENT_RESET:
- case EVENT_REEVALUATE_RESTRICTED_STATE:
- if (DBG) {
- log("DcInactiveState: msg.what=" + getWhatToString(msg.what)
- + ", ignore we're already done");
- }
- return HANDLED;
- case EVENT_CONNECT:
- if (DBG) log("DcInactiveState: mag.what=EVENT_CONNECT");
- ConnectionParams cp = (ConnectionParams) msg.obj;
-
- if (!initConnection(cp)) {
- log("DcInactiveState: msg.what=EVENT_CONNECT initConnection failed");
- notifyConnectCompleted(cp, DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER,
- false);
- transitionTo(mInactiveState);
- return HANDLED;
- }
-
- int cause = connect(cp);
- if (cause != DataFailCause.NONE) {
- log("DcInactiveState: msg.what=EVENT_CONNECT connect failed");
- notifyConnectCompleted(cp, cause, false);
- transitionTo(mInactiveState);
- return HANDLED;
- }
-
- if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- mSubId = cp.mSubId;
- }
-
- transitionTo(mActivatingState);
- return HANDLED;
- case EVENT_DISCONNECT:
- if (DBG) log("DcInactiveState: msg.what=EVENT_DISCONNECT");
- notifyDisconnectCompleted((DisconnectParams)msg.obj, false);
- return HANDLED;
- case EVENT_DISCONNECT_ALL:
- if (DBG) log("DcInactiveState: msg.what=EVENT_DISCONNECT_ALL");
- notifyDisconnectCompleted((DisconnectParams)msg.obj, false);
- return HANDLED;
- default:
- if (VDBG) {
- log("DcInactiveState not handled msg.what=" + getWhatToString(msg.what));
- }
- return NOT_HANDLED;
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
从代码可以看出,如果connect失败了,状态会继续切到mInactiveState,而如果connect成功,状态会切到mActivatingState。
connect函数最终调用如下代码处理拨号流程。
- mDataServiceManager.setupDataCall(
- ServiceState.rilRadioTechnologyToAccessNetworkType(cp.mRilRat),
- dp,
- isModemRoaming,
- allowRoaming,
- reason,
- linkProperties,
- msg);
- TelephonyMetrics.getInstance().writeSetupDataCall(mPhone.getPhoneId(), cp.mRilRat,dp.getProfileId(), dp.getApn(), dp.getProtocolType());
- return DataFailCause.NONE;
上面介绍的类都是关于拨号的逻辑处理,到了这一步跟拨号逻辑没关,而是跟设计模式有关了。Android出现Manager类,几乎都是接口的提供,相当代理。拨号调用此类的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 = new CellularDataServiceCallback("setupDataCall");
- if (onCompleteMessage != null) {
- mMessageMap.put(callback.asBinder(), onCompleteMessage);
- }
- try {
- sendMessageDelayed(obtainMessage(EVENT_WATCHDOG_TIMEOUT, callback),
- REQUEST_UNRESPONDED_TIMEOUT);
- mIDataService.setupDataCall(mPhone.getPhoneId(), accessNetworkType, dataProfile,
- isRoaming, allowRoaming, reason, linkProperties, callback);
- } catch (RemoteException e) {
- loge("Cannot invoke setupDataCall on data service.");
- mMessageMap.remove(callback.asBinder());
- sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
然后通过对象mIDataService调用setupDataCall。而mIDataService是怎么来有什么用,这里简单分析下。首先要了解下类的关系。
CellularDataService是继承DataService,DataService继承Service,可知是个服务。DataService又有一个IDataServiceWrapper,IDataServiceWrapper这个类是继承IDataService.Stub,可以看出是个AIDL接口,DataService后面对外接口都是由此类提供。
CellularDataService是在packages\services\Telephony\Android.mk注册,代码如下:
CellularDataService的启动是通过在DataServiceManager类的函数bindDataService里调用bindService启动。
- try {
- mServiceConnection = new CellularDataServiceConnection();
- if (!mPhone.getContext().bindService(
- intent, mServiceConnection, Context.BIND_AUTO_CREATE)) {
- loge("Cannot bind to the data service.");
- return;
- }
- mTargetBindingPackageName = packageName;
- } catch (Exception e) {
- loge("Cannot bind to the data service. Exception: " + e);
- }
CellularDataService成功启动后,通过回调mServiceConnection. onServiceConnected函数,这里面就可以获取到IDataServiceWrapper的对象,上面已说,此对象是DataService对外提供的接口。代码如下:
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- if (DBG) log("onServiceConnected");
- mIDataService = IDataService.Stub.asInterface(service);
- mDeathRecipient = new DataServiceManagerDeathRecipient();
- mBound = true;
-
- try {
- service.linkToDeath(mDeathRecipient, 0);
- mIDataService.createDataServiceProvider(mPhone.getPhoneId());
- mIDataService.registerForDataCallListChanged(mPhone.getPhoneId(),
- new CellularDataServiceCallback("dataCallListChanged"));
- } catch (RemoteException e) {
- mDeathRecipient.binderDied();
- loge("Remote exception. " + e);
- return;
- }
- removeMessages(EVENT_WATCHDOG_TIMEOUT);
- mServiceBindingChangedRegistrants.notifyResult(true);
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
继续回头分析mIDataService.setupDataCall,上面已提到,mIDataService对象就由IDataServiceWrapper提供。setupDataCall发出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;
接下来是通过serviceProvider调用setupDataCall,而serviceProvider从哪来?
从DataServiceManager介绍,当mServiceConnection.onServiceConnected回调后,会通过调用mIDataService.createDataServiceProvider函数。
- private final class CellularDataServiceConnection implements ServiceConnection {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- if (DBG) log("onServiceConnected");
- mIDataService = IDataService.Stub.asInterface(service);
- mDeathRecipient = new DataServiceManagerDeathRecipient();
- mBound = true;
-
- try {
- service.linkToDeath(mDeathRecipient, 0);
- mIDataService.createDataServiceProvider(mPhone.getPhoneId());
- mIDataService.registerForDataCallListChanged(mPhone.getPhoneId(),
- new CellularDataServiceCallback("dataCallListChanged"));
- } catch (RemoteException e) {
- mDeathRecipient.binderDied();
- loge("Remote exception. " + e);
- return;
- }
- removeMessages(EVENT_WATCHDOG_TIMEOUT);
- mServiceBindingChangedRegistrants.notifyResult(true);
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
createDataServiceProvider函数发出消息DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER,处理代码如下:
- case DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER:
- serviceProvider = onCreateDataServiceProvider(message.arg1);
- if (serviceProvider != null) {
- mServiceMap.put(slotIndex, serviceProvider);
- }
- break;
最后调用onCreateDataServiceProvider完成serviceProvider创建,但onCreateDataServiceProvider是DataService的抽像函数,真正实现的类是在CellularDataService,代码如下:
- public DataServiceProvider onCreateDataServiceProvider(int slotIndex) {
- log("Cellular data service created for slot " + slotIndex);
- if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
- loge("Tried to cellular data service with invalid slotId " + slotIndex);
- return null;
- }
- return new CellularDataServiceProvider(slotIndex);
- }
也就是serviceProvider是通过CellularDataServiceProvider创建,CellularDataServiceProvider的setupDataCall函数内容如下:
- @Override
- public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
- boolean allowRoaming, int reason, LinkProperties linkProperties,
- DataServiceCallback callback) {
- if (DBG) log("setupDataCall " + getSlotIndex());
-
- 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(accessNetworkType, dataProfile, isRoaming, allowRoaming,
- reason, linkProperties, message);
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
通过代码可以看出,最后调用了RILJ的setupDataCall。
相关时序如图下:
本章节主要再细讲下pollStateDone和setupDataOnAllConnectableApns两函数,对这两函数了解了,相当于对整个过程起到中流砥柱作用。
通过代码搜索看到,pollStateDone除了handlePollStateResult函数调用外,还有pollStateInternal函数调用,pollStateInternal具体流程如下。
- private void pollStateInternal(boolean modemTriggered) {
- mPollingContext = new int[1];
- mPollingContext[0] = 0;
-
- log("pollState: modemTriggered=" + modemTriggered);
-
- switch (mCi.getRadioState()) {
- case TelephonyManager.RADIO_POWER_UNAVAILABLE:
- mNewSS.setStateOutOfService();
- setSignalStrengthDefaultValues();
- mLastNitzData = null;
- mNitzState.handleNetworkUnavailable();
- pollStateDone();
- break;
-
- case TelephonyManager.RADIO_POWER_OFF:
- mNewSS.setStateOff();
- setSignalStrengthDefaultValues();
- mLastNitzData = null;
- mNitzState.handleNetworkUnavailable();
- // don't poll when device is shutting down or the poll was not modemTrigged
- // (they sent us new radio data) and current network is not IWLAN
- if (mDeviceShuttingDown ||
- (!modemTriggered && ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
- != mSS.getRilDataRadioTechnology())) {
- pollStateDone();
- break;
- }
-
- default:
- // Issue all poll-related commands at once then count down the responses, which
- // are allowed to arrive out-of-order
- mPollingContext[0]++;
- mCi.getOperator(obtainMessage(EVENT_POLL_STATE_OPERATOR, mPollingContext));
-
- mPollingContext[0]++;
- mRegStateManagers.get(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .requestNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
- obtainMessage(EVENT_POLL_STATE_PS_CELLULAR_REGISTRATION,
- mPollingContext));
-
- mPollingContext[0]++;
- mRegStateManagers.get(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .requestNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_CS,
- obtainMessage(EVENT_POLL_STATE_CS_CELLULAR_REGISTRATION, mPollingContext));
-
- if (mRegStateManagers.get(AccessNetworkConstants.TRANSPORT_TYPE_WLAN) != null) {
- mPollingContext[0]++;
- mRegStateManagers.get(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
- .requestNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
- obtainMessage(EVENT_POLL_STATE_PS_IWLAN_REGISTRATION,
- mPollingContext));
- }
-
- if (mPhone.isPhoneTypeGsm()) {
- mPollingContext[0]++;
- mCi.getNetworkSelectionMode(obtainMessage(
- EVENT_POLL_STATE_NETWORK_SELECTION_MODE, mPollingContext));
- }
- break;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
在第三章有略带过讲到了拨号流程过程中会调用setupDataOnAllConnectableApns此函数。通过在代码搜索,调用的地方有:
onDataConnectionAttached:Data附着上
onApnChanged:APN有修改
onCarrierConfigChanged:SIM完成加载
onSimStateUpdated:SIM卡状态更新,如插入,拔出,ready,完成加载这类状态
onDataRoamingOnOrSettingsChanged:打开漫游
onVoiceCallEnded:语音通话结束
DctConstants.EVENT_DATA_RAT_CHANGED:radio technology 有变化
onDataEnabledChanged:数据业务开关
针对出现不拨号的问题都可以根据这些函数入手。从这些函数调用可以看出,拨号接口函数应该是setupDataOnAllConnectableApns,而不是trySetupDataCall。
函数代码:
- protected void setupDataOnAllConnectableApns(String reason, RetryFailures retryFailures) {
- if (VDBG) log("setupDataOnAllConnectableApns: " + reason);
-
- if (DBG && !VDBG) {
- StringBuilder sb = new StringBuilder(120);
- for (ApnContext apnContext : mPrioritySortedApnContexts) {
- sb.append(apnContext.getApnType());
- sb.append(":[state=");
- sb.append(apnContext.getState());
- sb.append(",enabled=");
- sb.append(apnContext.isEnabled());
- sb.append("] ");
- }
- log("setupDataOnAllConnectableApns: " + reason + " " + sb);
- }
-
- for (ApnContext apnContext : mPrioritySortedApnContexts) {
- setupDataOnConnectableApn(apnContext, reason, retryFailures);
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
此函数通过过滤mPrioritySortedApnContexts的ApnContext,确认哪个ApnContext可连接然后进行拨号,而确认ApnContext可连接的函数是在setupDataOnConnectableApn
- protected void setupDataOnConnectableApn(ApnContext apnContext, String reason,
- RetryFailures retryFailures) {
- if (VDBG) log("setupDataOnAllConnectableApns: apnContext " + apnContext);
-
- if (apnContext.getState() == DctConstants.State.FAILED
- || apnContext.getState() == DctConstants.State.RETRYING) {
- if (retryFailures == RetryFailures.ALWAYS) {
- apnContext.releaseDataConnection(reason);
- } else if (!apnContext.isConcurrentVoiceAndDataAllowed()
- && mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
- // RetryFailures.ONLY_ON_CHANGE - check if voice concurrency has changed
- apnContext.releaseDataConnection(reason);
- }
- }
- if (apnContext.isConnectable()) {
- log("isConnectable() call trySetupData");
- apnContext.setReason(reason);
- trySetupData(apnContext, REQUEST_TYPE_NORMAL);
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
这里不再详细讲ApnContext如何被置为可连接,因为这涉及到另外一个功能:网络评分机制,后面再总结。而mPrioritySortedApnContexts的值从哪来有什么用,这里简单说一下。
initApnContexts-->initApnContexts (PersistableBundle carrierConfig)
从上图可以看到,最终是由initApnContexts函数的参数carrierConfig决定。carrierConfig的值是由下图获取。
通过代码跟踪(这里很不好跟,结合网上资料才知道),最终是解析packages\apps\CarrierConfig\assets里面的xml获得。解析函数是:packages\apps\CarrierConfig\src\com\android\carrierconfig\DefaultCarrierConfigService.java类里的loadConfig。但通过log发现,里面并没有相关国内运营商的配置。也就是carrierConfig是没有任何值的。但log里面又显示有值:
一开始还怀疑是不是跟错了,流程又看了几次还是没找到原因。最终通过看到一个不起眼的函数找到了答案。重新回头看看initApnContexts (PersistableBundle carrierConfig)函数
这里通过ApnConfigTypeRepository 创建了types对象,进入到ApnConfigTypeRepository类里面的创建过程调用了下面函数
private void setup(PersistableBundle carrierConfig) {
addApns(getCarrierApnTypeMap(CarrierConfigManager.getDefaultConfig()));
addApns(getCarrierApnTypeMap(carrierConfig));
}
CarrierConfigManager.getDefaultConfig()代码如下,关键参数是sDefaults
public static PersistableBundle getDefaultConfig() {
return new PersistableBundle(sDefaults);
}
sDefaults的初始化是在CarrierConfigManager类里面,关键代码是
sDefaults.putStringArray(KEY_APN_PRIORITY_STRING_ARRAY, new String[] {
"default:0", "mms:2", "supl:2", "dun:2", "hipri:3", "fota:2",
"ims:2", "cbs:2", "ia:2", "emergency:2", "mcx:3", "xcap:3"
});
至此,终于找到了初始化的地方,跟LOG打印一致。而mPrioritySortedApnContexts的作用目前来看是通过Apn类型创建ApnContexts,用来明确此运营商支持哪些APN类型。
本文档其实细节的内容不少,有些只是带过讲而已。但由于篇幅问题,细节的问题可以留到后面具体问题具体展开分析。因为有了整体流程,细节就不是问题了。比如根据之前调试RIL库时遇到不拨号的问题有ApnContext未enable,APN未添加,Data未附着,数据业务未打开等都是可以从本文章展开分析。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。