业务流程分析"/>
Android 12 版本 Data Call 移动数据业务流程分析
目录
前言
基础知识
APN
初始化ApnContext
DataConnection
开启移动数据业务
总流程图
前言
本文档将基于Android 12 AOSP源码进行流程分析,围绕DcTracker的核心处理机制和关键业务流程,疏通Android手机的移动数据业务基本原理和关键流程,我把流程图放在了最后。
之前我疏忽了,Android12仍然采用的是DcTracker,但从13开始已经默认使用DataNetworkController作为数据栈了,14开始将会彻底删除老版本的数据栈DcTracker,下次会给大家带来最新的DataNetworkController的解析。
基础知识
TeleService系统应用在创建GsmCdmaPhone对象时,在该Phone对象的构造方法内会同时创建DcTracker对象。DcTracker自身继承于Handler,在构造方法中主要实现了以下三个业务:
- 初始化ApnContext
- 注册各类监听(广播接收器、Observer, Handler消息注册)
- 创建DcController对象
/frameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
public DcTracker(Phone phone, @TransportType int transportType) {super();mPhone = phone;......mResolver = mPhone.getContext().getContentResolver();......IntentFilter filter = new IntentFilter();filter.addAction(Intent.ACTION_SCREEN_ON);filter.addAction(Intent.ACTION_SCREEN_OFF);filter.addAction(INTENT_DATA_STALL_ALARM);filter.addAction(INTENT_PROVISIONING_APN_ALARM);filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);filter.addAction(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);filter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);mDataEnabledSettings = mPhone.getDataEnabledSettings();//注意这个把自身作为handler传过去方便之后传递消息,当数据业务开关状态改变时会通过这个通知DCTrackermDataEnabledSettings.registerForDataEnabledChanged(this, DctConstants.EVENT_DATA_ENABLED_CHANGED, null);mDataEnabledSettings.registerForDataEnabledOverrideChanged(this, DctConstants.EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED);mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);mHandlerThread = new HandlerThread("DcHandlerThread");mHandlerThread.start();Handler dcHandler = new Handler(mHandlerThread.getLooper());//创建DcController对象mDcc = DcController.makeDcc(mPhone, this, mDataServiceManager, dcHandler.getLooper(),tagSuffix);registerForAllEvents();mApnObserver = new ApnChangeObserver();phone.getContext().getContentResolver().registerContentObserver(Telephony.Carriers.CONTENT_URI, true, mApnObserver);//初始化各类ApnContextinitApnContexts();addDefaultApnSettingsAsNeeded();mSettingsObserver = new SettingsObserver(mPhone.getContext(), this);registerSettingsObserver();mThrottleStatusCallback = new ThrottleStatusChangedCallback();mDataThrottler.registerForThrottleStatusChanges(mThrottleStatusCallback);
}
APN
APN(Access Point Name)是Android手机实现移动数据上网业务必须配置的参数,用来决定手机使用哪一种方式访问网络,其配置信息保存在telephony.db中的名为carriers的表中。关键字段如下:
字段 | 说明 |
name | APN配置名称 |
numeric | 运营商编号 |
apn | APN接入点, 中国移动cmwap和cmnet |
proxy | 代理服务器地址 |
port | 端口号 |
mmsproxy | 彩信代理服务器地址 |
mmsport | 彩信代理服务器端口号 |
mmsc | 彩信接入服务地址 |
type | APN接入类型 |
APN配置信息一旦有问题就会导致无法上网。那如果需要做国内手机的Android定制化开发,或是国外的定制化开发,应该怎么增加APN配置呢?有三个方法:
- 定制化项目本地xml配置文件
- 在线更新
- APN配置管理界面手动增加
这里厂商常用的是前两个方法,我们不可能要求用户自己手动添加配置,因此厂商在定制开发过程中,会将APN配置信息提前配好,在TelephonyProvider的initDatabase方法里将APN信息插入表中.
比如我手头上的某手机厂商项目中,使用了前两个方法:
private File getApnConfFile() {//加载本地预置配置文件File confFile = new File(Environment.getRootDirectory(), PARTNER_APNS_PATH);File oemConfFile = new File(Environment.getOemDirectory(), OEM_APNS_PATH);File updatedConfFile = new File(Environment.getDataDirectory(), OTA_UPDATED_APNS_PATH);File productConfFile = new File(Environment.getProductDirectory(), PARTNER_APNS_PATH);//厂商的在线更新配置文件,这样更新重启手机后会更新表中的数据File onlineConfFile = new File(ONLINE_UPDATED_APNS_PATH);confFile = pickSecondIfExists(confFile, oemConfFile);confFile = pickSecondIfExists(confFile, productConfFile);confFile = pickSecondIfExists(confFile, updatedConfFile);confFile = getNewerFile(confFile, onlineConfFile);return confFile;
}
本地配置中截取部分APN配置信息如下:
<apn carrier="中国移动 (China Mobile) GPRS"carrier_id = "1435"mcc="460"mnc="00"apn="cmnet"type="default,supl"/><apn carrier="中国移动 (China Mobile) WAP"carrier_id = "1435"mcc="460"mnc="00"apn="cmwap"proxy="10.0.0.172"port="80"type="default,supl"/>
初始化ApnContext
在DcTracker构造方法里将调用initApnContexts方法初始化ApnContext,每个ApnContext对象用来保存对应APN网络类型的配置参数、连接状态等等。
private void initApnContexts() { PersistableBundle carrierConfig; //查看是否有运营商配置服务 CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); if (configManager != null) { carrierConfig = configManager.getConfigForSubId(mPhone.getSubId());} else { carrierConfig = null;} initApnContexts(carrierConfig);
}private void initApnContexts(PersistableBundle carrierConfig) {// 从本地资源中加载网络配置final Collection<ApnConfigType> types =new ApnConfigTypeRepository(carrierConfig).getTypes();for (ApnConfigType apnConfigType : types) {ApnContext apnContext = new ApnContext(mPhone, apnConfigType.getType(), mLogTag, this,apnConfigType.getPriority());......mPrioritySortedApnContexts.add(apnContext);......}mPrioritySortedApnContexts.sort((c1, c2) -> c2.getPriority() - c1.getPriority());
}
在初始化ApnContext时,会创建ApnConfigTypeRepository对象,sDefaults对象是里面的一个静态对象,在静态代码块完成初始化,在初始化时就会添加很多默认的配置参数,包括我们关注的网络配置信息:
sDefaults.putStringArray(KEY_APN_PRIORITY_STRING_ARRAY, new String[] {"enterprise:0", "default:1", "mms:2", "supl:2", "dun:2", "hipri:3", "fota:2","ims:2", "cbs:2", "ia:2", "emergency:2", "mcx:3", "xcap:3"
});
从默认配置参数可以得到13个ApnConfigType对象,从而创建出13个ApnContext对象,字符串数组中每个字符串中":'之前代表一种网络配置,":"后面的数字代表着该配置的优先级,所以优先级从高到低依次是
hipri -> mcx -> xcap -> mmx -> supl -> dun -> fota -> ims -> cbs -> ia -> emergency -> default -> enterprise。
手机上网将建立default类型的数据连接,当彩信来时,因为彩信建立的是mms类型,优先级比default高,所以会断开default连接而创建mms连接,因此,在发送和接收彩信的同时不能上网 。
DataConnection
DataConnection是用来在Telephony业务模型中管理移动数据业务的类,一个DataConnection对象代表手机移动数据业务的一个数据连接。
DataConnection继承与StateMachine类,是一个典型的状态机,内部定义了6个状态类,全部继承与State类,每个状态类内部封装了对应状态下的较复杂的逻辑处理。
State子类 | 说明 |
DcDefaultState | 默认状态 |
DcInactiveState | 不活动状态 |
DcActivatingState | 正在激活状态 |
DcActiveState | 激活状态 |
DcDisconnectingState | 正在断开中状态 |
DcDisconnectionErrorCreatingConnection | 在创建连接后正处于断开中状态 |
源自状态机模式带来的优势,DataConnection切换不同连接状态时,不必操心切换状态的复杂逻辑处理,只需要通过使用内部的handler发送消息切到对应状态,之后交给对应状态类内部实现的processMessage方法处理即可。
在DataConnection的构造方法里面通过addState方法添加这6个状态对象,并且给除了DcDefaultState以外的每个状态指定父状态为DcDefaultState,这样就形成了一个树形结构,当一个State对象不能处理一个消息时就会向上交给父节点处理。
开启移动数据业务
手机可以通过以下两个交互界面开启或关闭移动数据业务:
- 通知栏快捷控制
- Settings -> Mobile network设置界面
流程分析:
打开或关闭移动数据业务的入口是TelephonyManager.setDataEnabledForReason方法,传入两个参数——切换原因和切换状态。(以前版本用的setDataEnabled方法已过时)
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)@RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)public void setDataEnabledForReason(@DataEnabledReason int reason, boolean enabled) {setDataEnabledForReason(getSubId(), reason, enabled);}
主要有四种理由:
- DATA_ENABLED_REASON_USER(用户操作控制)
- DATA_ENABLED_REASON_POLICY(由于policy数据业务被控制,通常是被限制)
- DATA_ENABLED_REASON_CARRIER(运营商特殊设置控制)
- DATA_ENABLED_REASON_THERMAL(热服务控制)
TelephonyManager的私有方法通过IBinder远程调用 ITelephony(PhoneInterfaceManager).setDataEnabledForReason 方法,接下来我们重点关注用户操作控制这一条链路:
/packages/services/Telephony/src/com/android/phone/PhoneInterfaceManager.java@Overridepublic void setDataEnabledForReason(int subId, @TelephonyManager.DataEnabledReason int reason, boolean enabled, String callingPackage) {//检查权限......phone.getDataEnabledSettings().setDataEnabled(reason, enabled);......}
/frameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
public synchronized void setDataEnabled(@TelephonyManager.DataEnabledReason int reason, boolean enabled) { switch (reason) {case TelephonyManager.DATA_ENABLED_REASON_USER: setUserDataEnabled(enabled);break;//其他理由......}}
}
private synchronized void setUserDataEnabled(boolean enabled) {// By default the change should propagate to the group. setUserDataEnabled(enabled, true);
}
public synchronized void setUserDataEnabled(boolean enabled, boolean notifyMultiSimSettingController) {......//先更新数据库设置boolean changed = GlobalSettingsHelper.setInt(mPhone.getContext(), Settings.Global.MOBILE_DATA, mPhone.getSubId(), (enabled ? 1 : 0)); if (changed) {...... //还记得DcTacker构造方法里面的添加监听方法吗,DcTacker就是在这里面接收到数据业务状态改变的通知updateDataEnabledAndNotify(REASON_USER_DATA_ENABLED); ......}
}
DataEnabledSettings使用从DcTracker传过来的handler对象(也就是DcTracker本身)发送消息,DcTracker在handleMessage方法里开始处理消息:
/frameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@Override
public void handleMessage (Message msg) {......switch (msg.what) {......case DctConstants.EVENT_DATA_ENABLED_CHANGED:ar = (AsyncResult) msg.obj;if (ar.result instanceof Pair) {Pair<Boolean, Integer> p = (Pair<Boolean, Integer>) ar.result;boolean enabled = p.first; //开关状态int reason = p.second; // 切换理由onDataEnabledChanged(enabled, reason);}break;......}
}private void onDataEnabledChanged(boolean enable, @DataEnabledChangedReason int enabledChangedReason) {......if (enable) { //开启移动数据业务reevaluateDataConnections();setupDataOnAllConnectableApns(Phone.REASON_DATA_ENABLED, RetryFailures.ALWAYS);} else { //关闭移动数据业务......cleanUpAllConnectionsInternal(true, cleanupReason);}
}
onDataEnabledChanged方法内有两个逻辑处理逻辑分支:开启移动数据业务和关闭移动数据业务。我们继续走开启移动数据业务逻辑setupDataOnAllConnectableApns。
protected void setupDataOnAllConnectableApns(String reason, RetryFailures retryFailures) {for (ApnContext apnContext : mPrioritySortedApnContexts) { //按照优先级开始遍历调用setupDataOnConnectableApn(apnContext, reason, retryFailures);}
}protected void setupDataOnConnectableApn(ApnContext apnContext, String reason,RetryFailures retryFailures) {......if (apnContext.isConnectable()) {//该ApnContext可连接,apnContext.setReason(reason);trySetupData(apnContext, REQUEST_TYPE_NORMAL, null);}
}
如上代码,DcTracker会按照优先级开始遍历ApnContext,并判断当前ApnContext是否处于可连接状态,ApnContext定义见上面的基础知识。接着我们继续关注 trySetupData方法的内部逻辑:
private void trySetupData(ApnContext apnContext, @RequestNetworkType int requestType, @Nullable Message onHandoverCompleteMsg) {......DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();//判断是否允许传输数据boolean isDataAllowed = isDataAllowed(apnContext, requestType, dataConnectionReasons);if (!isDataAllowed) {......return;}if (apnContext.getState() == DctConstants.State.FAILED) {String str = "trySetupData: make a FAILED ApnContext IDLE so its reusable";ApnContext.requestLog(apnContext, str);apnContext.setState(DctConstants.State.IDLE);}//获取驻网的移动数据业务RadioTechnologyint radioTech = getDataRat();......apnContext.setConcurrentVoiceAndDataAllowed(mPhone.getServiceStateTracker() .isConcurrentVoiceAndDataAllowed());//判断该apnContext是否处于空闲状态if (apnContext.getState() == DctConstants.State.IDLE) {ArrayList<ApnSetting> waitingApns =buildWaitingApns(apnContext.getApnType(), radioTech);if (waitingApns.isEmpty()) {//未找到对应APN配置信息return;} else {apnContext.setWaitingApns(waitingApns);}}if (!setupData(apnContext, radioTech, requestType)&& requestType == REQUEST_TYPE_HANDOVER) {sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false, false);}
}
继续分析setupData方法:
private boolean setupData(ApnContext apnContext, int radioTech,@RequestNetworkType int requestType) {ApnContext.requestLog(apnContext, "setupData. requestType=" + requestTypeToString(requestType));ApnSetting apnSetting;DataConnection dataConnection = null;//从缓存的ApnSettings集合中获取对象,每个ApnSettings保存从我们上面说的apn配置数据表中的一行数据,包含了nmc,mmsc等等apnSetting = apnContext.getNextApnSetting();if (dataConnection == null) {......//先查询是否缓存起来且不在使用状态的DataConnection对象,没有则再创建一个dataConnection = findFreeDataConnection();if (dataConnection == null) {dataConnection = createDataConnection();}......}final int generation = apnContext.incAndGetConnectionGeneration(); //计数器apnContext.setDataConnection(dataConnection);apnContext.setApnSetting(apnSetting); //将apnSetting与该apnContext对象绑定apnContext.setState(DctConstants.State.CONNECTING); //设置为"连接中"状态Message msg = obtainMessage();msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;msg.obj = new Pair<ApnContext, Integer>(apnContext, generation);ApnSetting preferredApn = getPreferredApn();boolean isPreferredApn = apnSetting.equals(preferredApn);dataConnection.bringUp(apnContext, profileId, radioTech, msg, generation, requestType, mPhone.getSubId(), isPreferredApn); //激活移动数据业务return true;
}
接下来我们重点关注DataConnection,DataConnection的定义见基础知识部分。
createDataConnection方法会调用DataConnection.makeDataConnection静态方法创建DataConnection对象并启动状态机。最后分析bringUp方法:
public void bringUp(ApnContext apnContext, int profileId, int rilRadioTechnology, Message onCompletedMsg, int connectionGeneration,@RequestNetworkType int requestType, int subId, boolean isApnPreferred) {if (mApnSetting == null) {mApnSetting = apnContext.getApnSetting();}sendMessage(DataConnection.EVENT_CONNECT,new ConnectionParams(apnContext, profileId, rilRadioTechnology, onCompletedMsg,connectionGeneration, requestType, subId, isApnPreferred));
}
根据传入的参数创建ConnectionParams对象,然后通过sendMessage方法发送DataConnection.EVENT_CONNECT类型的消息。根据我们在基础知识部分讲的那样,根据树状结构,是DataConnection.mInactiveState进行处理,处理逻辑如下:
case EVENT_CONNECT:ConnectionParams cp = (ConnectionParams) msg.obj;......//调用主类的私有方法connectint cause = connect(cp);......//切换到正在激活状态transitionTo(mActivatingState);return HANDLED;
DataConnection对象的connect方法的主要处理逻辑如下。
private @DataFailureCause int connect(ConnectionParams cp) {......mCreateTime = -1;mLastFailTime = -1;mLastFailCause = DataFailCause.NONE;//创建数据连接完成的消息Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);msg.obj = cp;DataProfile dp = new DataProfile.Builder().setApnSetting(mApnSetting).setPreferred(cp.mIsPreferredApn).build();boolean isModemRoaming = mPhone.getServiceState().getDataRoamingFromRegistration();boolean isUnmeteredApnType = !ApnSettingUtils.isMeteredApnType(cp.mApnContext.getApnTypeBitmask(), mPhone);boolean allowRoaming = mPhone.getDataRoamingEnabled()|| (isModemRoaming && (!mPhone.getServiceState().getDataRoaming()|| isUnmeteredApnType));......allocatePduSessionId(psi -> {this.setPduSessionId(psi);mDataServiceManager.setupDataCall(ServiceState.rilRadioTechnologyToAccessNetworkType(cp.mRilRat), dp, isModemRoaming,allowRoaming, reason, linkProperties, psi, null, td, matchAllRuleAllowed, msg);......});return DataFailCause.NONE;
}
走到了DataServiceManager.setupDataCall方法里,
/frameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,@Nullable NetworkSliceInfo sliceInfo, @Nullable TrafficDescriptor trafficDescriptor,boolean matchAllRuleAllowed, Message onCompleteMessage) {CellularDataServiceCallback callback = new CellularDataServiceCallback("setupDataCall");if (onCompleteMessage != null) {mMessageMap.put(callback.asBinder(), onCompleteMessage);}......mIDataService.setupDataCall(mPhone.getPhoneId(), accessNetworkType, dataProfile,isRoaming, allowRoaming, reason, linkProperties, pduSessionId, sliceInfo,trafficDescriptor, matchAllRuleAllowed, callback);......}
DataServiceManager在这里通过IBinder方式远程调用phone进程的实现方法,处于DataService的内部类IDataServiceWrapper里面,通过handler发送DATA_SERVICE_REQUEST_SETUP_DATA_CALL消息:
frameworks/base/telephony/java/android/telephony/data/DataService.java
public abstract class DataService extends Service {private class IDataServiceWrapper extends IDataService.Stub {@Overridepublic void setupDataCall(int slotIndex, int accessNetworkType, DataProfile dataProfile,boolean isRoaming, boolean allowRoaming, int reason,LinkProperties linkProperties, int pduSessionId, NetworkSliceInfo sliceInfo,TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed,IDataServiceCallback callback) {mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, slotIndex, 0,new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming,allowRoaming, reason, linkProperties, pduSessionId, sliceInfo,trafficDescriptor, matchAllRuleAllowed, callback)).sendToTarget();}}private class DataServiceHandler extends Handler {@Overridepublic void handleMessage(Message message) {......case DATA_SERVICE_REQUEST_SETUP_DATA_CALL:serviceProvider.setupDataCall(......);break;}}
}
这个serviceProvider实际上有两种,分别由DataService的两个继承类CellularDataService和IwlanDataService实现提供,这两个类分别代表了蜂窝网数据业务和无线网数据业务。我们这里只看蜂窝网:
/frameworks/opt/telephony/src/java/com/android/internal/telephony/data/CellularDataService.javaprivate class CellularDataServiceProvider extends DataService.DataServiceProvider {@Overridepublic void setupDataCall(int accessNetworkType, DataProfile dataProfile,boolean isRoaming, boolean allowRoaming, int reason, LinkProperties linkProperties,int pduSessionId, NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,boolean matchAllRuleAllowed, DataServiceCallback callback) {......mPhone.mCi.setupDataCall(accessNetworkType, dataProfile, isRoaming, allowRoaming,reason, linkProperties, pduSessionId, sliceInfo, trafficDescriptor,matchAllRuleAllowed, message);}}
mCi是RILJ对象,最终由RIL完成Data Call移动数据业务的处理。这个RIL(Radio Interface Layer)是指无线通信接口层,主要运行在HAL上,HAL层我们就暂时不做分析了,这个RIL横跨了HAL层和Framework层,是telephony和modem沟通的桥梁。RIL处理成功后,DataConnection的状态mActivatingState将转换为mActiveState。
总流程图
这样framework层的移动数据业务启动流程就分析完毕了。开启业务流程图如下所示。
欢迎大家关注我,我今后会努力给大家带来更多的Android源码的解析和实用组件的。
谢谢大家 -v-
更多推荐
Android 12 版本 Data Call 移动数据业务流程分析
发布评论