經(jīng)過前面這些過程,網(wǎng)絡(luò)連接所需要的條件就全部準(zhǔn)備就緒,接下來就是等待網(wǎng)絡(luò)接入
把網(wǎng)絡(luò)接入過程簡單分為三個階段
觸發(fā)階段
----該階段是由各種不同事件觸發(fā)的,比如SIM載入完畢、PS域Attach成功、通話結(jié)束、APN改變等,該階段的最終都是要調(diào)用setupDataOnConnectableApns()方法;
準(zhǔn)備連接階段
----該階段是指,在DcTracker收到建立連接的請求之后,需要進行一系列有效性檢測,比如APN是否已經(jīng)激活、PS是否已經(jīng)就緒、用戶是否打開網(wǎng)絡(luò)開關(guān)等,然后創(chuàng)建/找到核實的DataConnection對象,準(zhǔn)備發(fā)起連接請求。
發(fā)送連接命令階段
----該階段是指,在DataConnection收到DcTracker的請求之后,將請求轉(zhuǎn)交給RILJ的過程,RILJ通過socket將請求發(fā)送到了RILD,RILD根據(jù)請求類型再將AT指令發(fā)送到Modem層,由底層完成信令的發(fā)送和接收。簡單概括就是如下步驟,此處創(chuàng)建的DcAsyncChannel是雙向的。
1、觸發(fā)條件
//漫游打開關(guān)閉(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";
//通話結(jié)束
static final String REASON_VOICE_CALL_ENDED = "2GVoiceCallEnded";
//SIM載入完畢 (EVENT_RECORDS_LOADED 事件)
static final String REASON_SIM_LOADED = "simLoaded";
//網(wǎng)絡(luò)模式改變 (EVENT_DATA_RAT_CHANGED 事件)
static final String REASON_NW_TYPE_CHANGED = "nwTypeChanged";
2、有效性及狀態(tài)檢查##
在觸發(fā)條件滿足后都會調(diào)用setupDataOnConnectableApns(String reason)
之后會調(diào)用trySetupData,在trySetupData里面會進行有效性檢測
private boolean trySetupData(ApnContext apnContext, ArrayList<ApnSetting> 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 = setupData(apnContext, radioTech);
notifyOffApnsOfAvailability(apnContext.getReason());
return retValue;
}
}
條件1:apnContext.isConnectable()
public boolean isConnectable() {
return isReady() && ((mState == DctConstants.State.IDLE)
|| (mState == DctConstants.State.SCANNING)
|| (mState == DctConstants.State.RETRYING)
|| (mState == DctConstants.State.FAILED));
}
其中isReady()就是檢查APN type有沒有使能,所以在數(shù)據(jù)鏈接之前一定要調(diào)用ApnContext的setEnabled()將要打開的數(shù)據(jù)類型對應(yīng)的APN使能;然后就是檢查APN的狀態(tài),這個狀態(tài)會在DataConnection狀態(tài)機隨著聯(lián)網(wǎng)狀態(tài)的變遷而改變。
條件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;
----判斷是否已經(jīng)ATTACH成功,SIM是否初始化完畢,當(dāng)前手機服務(wù)是否支持,漫游下是否允許上網(wǎng)等
條件4:getAnyDataEnabled(checkUserDataEnabled)
----該條件主要判斷用戶是否打開了數(shù)據(jù)開關(guān)
這部分代碼很混亂,如果出現(xiàn)問題得一一排查
3、找到可用的APN
其中waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech)便是找到可用的APN
buildWaitingApns中主要的邏輯有兩個:
1、如果用戶是否設(shè)置了Preferred,該值通過以下代碼讀取的:
usePreferred = ! mPhone.getContext().getResources().getBoolean(com.android.
internal.R.bool.config_dontPreferApn);
如果用戶設(shè)置了Preferred APN,并且mPreferredApn不為空(mPreferredApn來自于數(shù)據(jù)業(yè)務(wù)APN參數(shù)的創(chuàng)建createAllApnList中保存的,例如用戶手動選擇了APN TYPE那么這個APN就會被保留下來)因此此時waitingApns就會等于mPreferredApn
2、如果用戶沒有設(shè)置Preferred APN,那么就會在mAllApnSettings(來自于createAllApnList)中去尋找可用的APN,并將所有的可用的APN放在waitingApns中
最終聯(lián)網(wǎng)采用的是waitingApns0
4、找到/創(chuàng)建DataConnection狀態(tài)機
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;
這個方法內(nèi)部主要完成了兩個任務(wù):
1、更新當(dāng)前APN參數(shù)的狀態(tài)并把狀態(tài)發(fā)送到系統(tǒng)中(還是通過notifyDataConnection()來完成)
2、通過DcAsyncChannel的bringUp()方法發(fā)起連接請求
這里顯示通過findFreeDataConnection()方法搜索可用的DcAsyncChannel,找不到的話就通過createDataConnection()創(chuàng)建,如果沒有找到,就需要創(chuàng)建新的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); //將創(chuàng)建好的DcAsyncChannel保存起來,以便下一次鏈接的時候可以找到可用的DcAsyncChannel
}
return dcac;
}
創(chuàng)建DcAsyncChannel通道,是鏈接DcTracker和DataConnection的通道,但是他們不在同一個線程中,在DcTrackerBase的構(gòu)造中new了一個新的HandlerThread,并將looper給了DcController和DataConnection。
為什么要闖將DcAsyncChannel通道,而不直接將DcTracker消息傳到DataConnection中去呢?
因為在DcTracker中有需要同步獲取DataConnection中的情況,比如在findFreeDataConnection中調(diào)用了
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 = rspIsInactive(response);
} else {
value = false;
}
} else {
value = mDc.getIsInactive();
}
return value;
}
其中的sendMessageSynchronously(REQ_IS_INACTIVE);即需要獲取DataConnection中的是否處于INACTIVE狀態(tài),這不能是異步的。
5、通過DcAsyncChannel將消息傳入狀態(tài)機
將apnContext通過dcac傳入DataConnection狀態(tài)機進行數(shù)據(jù)連接
dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, radioTech, mAutoAttachOnCreation,msg);
6、狀態(tài)機狀態(tài)輪轉(zhuǎn)
DataConnection默認(rèn)狀態(tài)是DcInactiveState,首先會在DcInactiveState調(diào)用onConnect去調(diào)用setupDataCall
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);
}
在onConnect會包裝一個EVENT_SETUP_DATA_CONNECTION_DONE消息在msg中,這個消息會一并發(fā)往RILJ->RILD,當(dāng)RILD返回響應(yīng)時會攜帶這消息并發(fā)出來,DataConnection狀態(tài)機的DcActivatingState會撲捉到此消息進行狀態(tài)機輪狀,然后這里面全是狀態(tài)機輪狀來維護網(wǎng)絡(luò)的鏈接狀態(tài):
至此數(shù)據(jù)連接的發(fā)起過程完畢,但是理上網(wǎng)還有很遠(yuǎn),后續(xù)工作還要更新路由表等等