Android 12 版本 Data Call 移动数据业务流程分析

编程入门 行业动态 更新时间:2024-10-09 03:23:11

Android 12 版本 Data Call 移动数据<a href=https://www.elefans.com/category/jswz/34/1762571.html style=业务流程分析"/>

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 移动数据业务流程分析

本文发布于:2024-02-13 08:04:38,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1757775.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:业务流程   版本   数据   Android   Call

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!