android|Android 数据连接分析
把网络接入过程简单分为三个阶段
触发阶段
—-该阶段是由各种不同事件触发的,比如SIM载入完毕、PS域Attach成功、通话结束、APN改变等,该阶段的最终都是要调用setupDataOnConnectableApns()方法;
准备连接阶段
—-该阶段是指,在DcTracker收到建立连接的请求之后,需要进行一系列有效性检测,比如APN是否已经激活、PS是否已经就绪、用户是否打开网络开关等,然后创建/找到核实的DataConnection对象,准备发起连接请求。
发送连接命令阶段
—-该阶段是指,在DataConnection收到DcTracker的请求之后,将请求转交给RILJ的过程,RILJ通过socket将请求发送到了RILD,RILD根据请求类型再将AT指令发送到Modem层,由底层完成信令的发送和接收。简单概括就是如下步骤,此处创建的DcAsyncChannel是双向的。
文章图片
1、触发条件
//漫游打开关闭(EVENT_ROAMING_ON/EVENT_ROAMING_OFF事件)
static final String REASON_ROAMING_ON = "roamingOn";
static final String REASON_ROAMING_OFF = "roamingOff";
//PS attach(STATE_IN_SERVICE)
static final String REASON_DATA_ATTACHED = "dataAttached";
//APN Changed(EVENT_APN_CHANGED)
static final String REASON_APN_CHANGED = "apnChanged";
//通话结束
static final String REASON_VOICE_CALL_ENDED = "2GVoiceCallEnded";
//SIM载入完毕 (EVENT_RECORDS_LOADED 事件)
static final String REASON_SIM_LOADED = "simLoaded";
//网络模式改变 (EVENT_DATA_RAT_CHANGED 事件)
static final String REASON_NW_TYPE_CHANGED = "nwTypeChanged";
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
之后会调用trySetupData,在trySetupData里面会进行有效性检测
private boolean trySetupData(ApnContext apnContext, ArrayList waitingApns) {
// Allow SETUP_DATA request for E-APN to be completed during emergency call and MOBILE DATA On/Off cases as well.
boolean isEmergencyApn = apnContext.getApnType().equals(PhoneConstants.APN_TYPE_EMERGENCY);
final ServiceStateTracker sst = mPhone.getServiceStateTracker();
boolean desiredPowerState = sst.getDesiredPowerState();
boolean checkUserDataEnabled = !(apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IMS));
if (apnContext.isConnectable() && (isEmergencyApn ||
(isDataAllowed(apnContext) &&
getAnyDataEnabled(checkUserDataEnabled) && !isEmergency()))) {if (apnContext.getState() == DctConstants.State.FAILED) {
apnContext.setState(DctConstants.State.IDLE);
}
int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
apnContext.setConcurrentVoiceAndDataAllowed(sst.isConcurrentVoiceAndDataAllowed());
if (apnContext.getState() == DctConstants.State.IDLE) {
if (waitingApns == null) {
waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech);
}
if (waitingApns.isEmpty()) {
notifyNoData(DcFailCause.MISSING_UNKNOWN_APN, apnContext);
notifyOffApnsOfAvailability(apnContext.getReason());
return false;
} else {
apnContext.setWaitingApns(waitingApns);
}
}
boolean retValue = https://www.it610.com/article/setupData(apnContext, radioTech);
notifyOffApnsOfAvailability(apnContext.getReason());
return retValue;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
public boolean isConnectable() {
return isReady() && ((mState == DctConstants.State.IDLE)
|| (mState == DctConstants.State.SCANNING)
|| (mState == DctConstants.State.RETRYING)
|| (mState == DctConstants.State.FAILED));
}
- 1
- 2
- 3
- 4
- 5
- 6
条件2:isEmergencyApn 是否是紧急APN
条件3:isDataAllowed(apnContext)
private boolean isDataAllowed(ApnContext apnContext) {
return apnContext.isReady() && isDataAllowed();
}protected boolean isDataAllowed() {
final boolean internalDataEnabled;
synchronized (mDataEnabledLock) {
internalDataEnabled = mInternalDataEnabled;
}boolean attachedState = mAttached.get();
boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
IccRecords r = mIccRecords.get();
boolean recordsLoaded = false;
if (r != null) {
recordsLoaded = r.getRecordsLoaded();
}//FIXME always attach
boolean psRestricted = mIsPsRestricted;
int phoneNum = TelephonyManager.getDefault().getPhoneCount();
if (phoneNum > 1) {
attachedState = true;
psRestricted = false;
}
int dataSub = SubscriptionManager.getDefaultDataSubId();
boolean defaultDataSelected = SubscriptionManager.isValidSubscriptionId(dataSub);
PhoneConstants.State state = PhoneConstants.State.IDLE;
if (mPhone.getCallTracker() != null) {
state = mPhone.getCallTracker().getState();
}
boolean allowed =
(attachedState || mAutoAttachOnCreation) &&
recordsLoaded &&
(state == PhoneConstants.State.IDLE ||
mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) &&
internalDataEnabled &&
defaultDataSelected &&
(!mPhone.getServiceState().getDataRoaming() || getDataOnRoamingEnabled()) &&
//!mIsPsRestricted &&
!psRestricted &&
desiredPowerState;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
条件4:getAnyDataEnabled(checkUserDataEnabled)
—-该条件主要判断用户是否打开了数据开关
这部分代码很混乱,如果出现问题得一一排查
3、找到可用的APN 其中waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech)便是找到可用的APN
buildWaitingApns中主要的逻辑有两个:
1、如果用户是否设置了Preferred,该值通过以下代码读取的:
usePreferred = ! mPhone.getContext().getResources().getBoolean(com.android.
internal.R.bool.config_dontPreferApn);
- 1
- 2
- 3
2、如果用户没有设置Preferred APN,那么就会在mAllApnSettings(来自于createAllApnList)中去寻找可用的APN,并将所有的可用的APN放在waitingApns中
最终联网采用的是waitingApns0
4、找到/创建DataConnection状态机
private boolean setupData(ApnContext apnContext, int radioTech) {
if (dcac == null) {
dcac = findFreeDataConnection();
if (dcac == null) {
dcac = createDataConnection();
}
}
apnContext.setDataConnectionAc(dcac);
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 = apnContext;
dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, radioTech, mAutoAttachOnCreation,msg);
return true;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
1、更新当前APN参数的状态并把状态发送到系统中(还是通过notifyDataConnection()来完成)
2、通过DcAsyncChannel的bringUp()方法发起连接请求
这里显示通过findFreeDataConnection()方法搜索可用的DcAsyncChannel,找不到的话就通过createDataConnection()创建,如果没有找到,就需要创建新的DcAsyncChannel
/* Return the DC AsyncChannel for the new data connection /
private DcAsyncChannel createDataConnection() {
int id = mUniqueIdGenerator.getAndIncrement();
DataConnection conn = DataConnection.makeDataConnection(mPhone, id,this, mDcTesterFailBringUpAll, mDcc);
mDataConnections.put(id, conn);
DcAsyncChannel dcac = new DcAsyncChannel(conn, LOG_TAG);
int status = dcac.fullyConnectSync(mPhone.getContext(), this, conn.getHandler());
if (status == AsyncChannel.STATUS_SUCCESSFUL) {
mDataConnectionAcHashMap.put(dcac.getDataConnectionIdSync(), dcac);
//将创建好的DcAsyncChannel保存起来,以便下一次链接的时候可以找到可用的DcAsyncChannel
}
return dcac;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
为什么要闯将DcAsyncChannel通道,而不直接将DcTracker消息传到DataConnection中去呢?
因为在DcTracker中有需要同步获取DataConnection中的情况,比如在findFreeDataConnection中调用了
dcac.isInactiveSync()
private DcAsyncChannel findFreeDataConnection() {
for (DcAsyncChannel dcac : mDataConnectionAcHashMap.values()) {
if (dcac.isInactiveSync() && dataConnectionNotInUse(dcac)) {
return dcac;
}
}
return null;
}public boolean isInactiveSync() {
boolean value;
if (isCallerOnDifferentThread()) {
Message response = sendMessageSynchronously(REQ_IS_INACTIVE);
if ((response != null) && (response.what == RSP_IS_INACTIVE)) {
value = https://www.it610.com/article/rspIsInactive(response);
} else {
value = false;
}
} else {
value = mDc.getIsInactive();
}
return value;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
5、通过DcAsyncChannel将消息传入状态机 将apnContext通过dcac传入DataConnection状态机进行数据连接
dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, radioTech, mAutoAttachOnCreation,msg);
- 1
private void onConnect(ConnectionParams cp) {
// msg.obj will be returned in AsyncResult.userObj;
Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
msg.obj = cp;
int authType = mApnSetting.authType;
if (authType == -1) {
authType = TextUtils.isEmpty(mApnSetting.user) ? RILConstants.SETUP_DATA_AUTH_NONE
: RILConstants.SETUP_DATA_AUTH_PAP_CHAP;
}String protocol;
if (mPhone.getServiceState().getDataRoaming()) {
protocol = mApnSetting.roamingProtocol;
} else {
protocol = mApnSetting.protocol;
}mPhone.mCi.setupDataCall(
Integer.toString(cp.mRilRat + 2),
Integer.toString(cp.mProfileId),
mApnSetting.apn, mApnSetting.user, mApnSetting.password,
Integer.toString(authType),
protocol, msg);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
文章图片
文章图片
至此数据连接的发起过程完毕,但是理上网还有很远,后续工作还要更新路由表等等
推荐阅读
- Docker应用:容器间通信与Mariadb数据库主从复制
- android第三方框架(五)ButterKnife
- Android中的AES加密-下
- 带有Hilt的Android上的依赖注入
- android|android studio中ndk的使用
- 使用协程爬取网页,计算网页数据大小
- Android事件传递源码分析
- Java|Java基础——数组
- Python数据分析(一)(Matplotlib使用)
- Jsr303做前端数据校验