赞
踩
目录
本文档将基于Android 12 AOSP源码进行流程分析,围绕DcTracker的核心处理机制和关键业务流程,疏通Android手机的移动数据业务基本原理和关键流程,我把流程图放在了最后。
之前我疏忽了,Android12仍然采用的是DcTracker,但从13开始已经默认使用DataNetworkController作为数据栈了,14开始将会彻底删除老版本的数据栈DcTracker,下次会给大家带来最新的DataNetworkController的解析。
TeleService系统应用在创建GsmCdmaPhone对象时,在该Phone对象的构造方法内会同时创建DcTracker对象。DcTracker自身继承于Handler,在构造方法中主要实现了以下三个业务:
- /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传过去方便之后传递消息,当数据业务开关状态改变时会通过这个通知DCTracker
- mDataEnabledSettings.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);
- //初始化各类ApnContext
- initApnContexts();
-
- addDefaultApnSettingsAsNeeded();
-
- mSettingsObserver = new SettingsObserver(mPhone.getContext(), this);
- registerSettingsObserver();
-
- mThrottleStatusCallback = new ThrottleStatusChangedCallback();
- mDataThrottler.registerForThrottleStatusChanges(mThrottleStatusCallback);
- }
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配置呢?有三个方法:
这里厂商常用的是前两个方法,我们不可能要求用户自己手动添加配置,因此厂商在定制开发过程中,会将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"
- />
在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是用来在Telephony业务模型中管理移动数据业务的类,一个DataConnection对象代表手机移动数据业务的一个数据连接。
DataConnection继承与StateMachine类,是一个典型的状态机,内部定义了6个状态类,全部继承与State类,每个状态类内部封装了对应状态下的较复杂的逻辑处理。
State子类 | 说明 |
DcDefaultState | 默认状态 |
DcInactiveState | 不活动状态 |
DcActivatingState | 正在激活状态 |
DcActiveState | 激活状态 |
DcDisconnectingState | 正在断开中状态 |
DcDisconnectionErrorCreatingConnection | 在创建连接后正处于断开中状态 |
源自状态机模式带来的优势,DataConnection切换不同连接状态时,不必操心切换状态的复杂逻辑处理,只需要通过使用内部的handler发送消息切到对应状态,之后交给对应状态类内部实现的processMessage方法处理即可。
在DataConnection的构造方法里面通过addState方法添加这6个状态对象,并且给除了DcDefaultState以外的每个状态指定父状态为DcDefaultState,这样就形成了一个树形结构,当一个State对象不能处理一个消息时就会向上交给父节点处理。
手机可以通过以下两个交互界面开启或关闭移动数据业务:
流程分析:
打开或关闭移动数据业务的入口是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);
- }
主要有四种理由:
TelephonyManager的私有方法通过IBinder远程调用 ITelephony(PhoneInterfaceManager).setDataEnabledForReason 方法,接下来我们重点关注用户操作控制这一条链路:
- /packages/services/Telephony/src/com/android/phone/PhoneInterfaceManager.java
- @Override
- public 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);
- }
- //获取驻网的移动数据业务RadioTechnology
- int 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;
- ......
- //调用主类的私有方法connect
- int 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 {
- @Override
- public 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 {
- @Override
- public 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.java
- private class CellularDataServiceProvider extends DataService.DataServiceProvider {
- @Override
- public 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-
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。