(N)Telephony分析(七)之DataConnection建立
阿新 • • 發佈:2019-02-05
在前面,我們分析過DcTracker的初始化的時候,我們有看過,在DcTracker的構造方法中,呼叫有如下這個方法
其中有呼叫到registerServiceStateTrackerEvents方法private void registerForAllEvents() { mPhone.mCi.registerForAvailable(this, DctConstants.EVENT_RADIO_AVAILABLE, null); mPhone.mCi.registerForOffOrNotAvailable(this, DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); mPhone.mCi.registerForDataNetworkStateChanged(this, DctConstants.EVENT_DATA_STATE_CHANGED, null); // Note, this is fragile - the Phone is now presenting a merged picture // of PS (volte) & CS and by diving into its internals you're just seeing // the CS data. This works well for the purposes this is currently used for // but that may not always be the case. Should probably be redesigned to // accurately reflect what we're really interested in (registerForCSVoiceCallEnded). mPhone.getCallTracker().registerForVoiceCallEnded(this, DctConstants.EVENT_VOICE_CALL_ENDED, null); mPhone.getCallTracker().registerForVoiceCallStarted(this, DctConstants.EVENT_VOICE_CALL_STARTED, null); registerServiceStateTrackerEvents(); // SubscriptionManager.registerForDdsSwitch(this, // DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS, null); mPhone.mCi.registerForPcoData(this, DctConstants.EVENT_PCO_DATA_RECEIVED, null); }
這樣,就將當前的DcTracker和ServiceStateTracker繫結到一起了,我們看第一個註冊訊息EVENT_DATA_CONNECTION_ATTACHED,那麼他是怎麼啟用接收的呢?public void registerServiceStateTrackerEvents() { // Leo,註冊DataConnectionAttached訊息 mPhone.getServiceStateTracker().registerForDataConnectionAttached(this, DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null); mPhone.getServiceStateTracker().registerForDataConnectionDetached(this, DctConstants.EVENT_DATA_CONNECTION_DETACHED, null); mPhone.getServiceStateTracker().registerForDataRoamingOn(this, DctConstants.EVENT_ROAMING_ON, null); mPhone.getServiceStateTracker().registerForDataRoamingOff(this, DctConstants.EVENT_ROAMING_OFF, null); mPhone.getServiceStateTracker().registerForPsRestrictedEnabled(this, DctConstants.EVENT_PS_RESTRICT_ENABLED, null); mPhone.getServiceStateTracker().registerForPsRestrictedDisabled(this, DctConstants.EVENT_PS_RESTRICT_DISABLED, null); mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged(this, DctConstants.EVENT_DATA_RAT_CHANGED, null); }
這個我麼就要分析下ServiceStateTracker了,在SIM卡資訊載入讀取完成後,ServiceStateTracker會發出一個EVENT_SIM_READY訊息,然後在其中進行處理
恩,呼叫了pollState方法case EVENT_SIM_READY: // Reset the mPreviousSubId so we treat a SIM power bounce // as a first boot. See b/19194287 mOnSubscriptionsChangedListener.mPreviousSubId.set(-1); pollState(); // Signal strength polling stops when radio is off queueNextSignalStrengthPoll(); break;
public void pollState() {
pollState(false);
}
public void pollState(boolean modemTriggered) {
mPollingContext = new int[1];
mPollingContext[0] = 0;
switch (mCi.getRadioState()) {
......
default:
// Issue all poll-related commands at once then count down the responses, which
// are allowed to arrive out-of-order
mPollingContext[0]++;
mCi.getOperator(obtainMessage(EVENT_POLL_STATE_OPERATOR, mPollingContext));
mPollingContext[0]++;
mCi.getDataRegistrationState(obtainMessage(EVENT_POLL_STATE_GPRS, mPollingContext));
mPollingContext[0]++;
mCi.getVoiceRegistrationState(obtainMessage(EVENT_POLL_STATE_REGISTRATION,
mPollingContext));
if (mPhone.isPhoneTypeGsm()) {
mPollingContext[0]++;
mCi.getNetworkSelectionMode(obtainMessage(
EVENT_POLL_STATE_NETWORK_SELECTION_MODE, mPollingContext));
}
break;
}
}
可以看到,其中分別呼叫了RIL類的getOperator,getDataRegistrationState和getVoiceRegistrationState方法,針對於這三個方法,我們著重分析一下getDataRegistrationState方法,注意其傳入的引數為Message物件,而且Message物件的what為EVENT_POLL_STATE_GPRS,obj為mPollingContext物件
接下來看下RIL類的getDataRegistrationState方法
public void
getDataRegistrationState (Message result) {
RILRequest rr
= RILRequest.obtain(RIL_REQUEST_DATA_REGISTRATION_STATE, result);
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
send(rr);
}
恩,從前面幾篇文章中,我們知道,這個方法,其實是和底層RIL進行互動,並且傳送RIL_REQUEST_DATA_REGISTRATION_STATE訊息,並且在RIL類中處理modem層反饋的結果,然後將傳送傳入的Message訊息物件,至於究竟如何傳遞,後續再分析,因此
case EVENT_POLL_STATE_REGISTRATION:
case EVENT_POLL_STATE_GPRS:
case EVENT_POLL_STATE_OPERATOR:
ar = (AsyncResult) msg.obj;
handlePollStateResult(msg.what, ar);
break;
呼叫了handlePollStateResult方法,其中引數一為訊息what,引數二為modem層反饋上來的資料
protected void handlePollStateResult(int what, AsyncResult ar) {
......
mPollingContext[0]--;
if (mPollingContext[0] == 0) {
if (mPhone.isPhoneTypeGsm()) {
updateRoamingState();
mNewSS.setEmergencyOnly(mEmergencyOnly);
} else {
......
}
pollStateDone();
}
}
設定SIM卡網路的狀態資訊,並呼叫pollStateDone方法
private void pollStateDone() {
if (mPhone.isPhoneTypeGsm()) {
pollStateDoneGsm();
} else if (mPhone.isPhoneTypeCdma()) {
pollStateDoneCdma();
} else {
pollStateDoneCdmaLte();
}
}
由於SIM卡為GSM格式
private void pollStateDoneGsm() {
......
boolean hasGprsAttached =
mSS.getDataRegState() != ServiceState.STATE_IN_SERVICE
&& mNewSS.getDataRegState() == ServiceState.STATE_IN_SERVICE;
......
if (hasGprsAttached) {
mAttachedRegistrants.notifyRegistrants();
}
if (hasGprsDetached) {
mDetachedRegistrants.notifyRegistrants();
}
......
}
因此,在此處傳送了Data Attached的訊息,而由於在DcTracker中,將EVENT_DATA_CONNECTION_ATTACHED訊息,加入到mAttachedRegistrants中了,因此,DcTracker會接收到EVENT_DATA_CONNECTION_ATTACHED訊息,並處理
case DctConstants.EVENT_DATA_CONNECTION_ATTACHED:
onDataConnectionAttached();
break;
呼叫onDataConnectionAttached方法
private void onDataConnectionAttached() {
if (DBG) log("onDataConnectionAttached");
// Leo,設定為true
mAttached.set(true);
// Leo,條件不成立
if (getOverallState() == DctConstants.State.CONNECTED) {
if (DBG) log("onDataConnectionAttached: start polling notify attached");
startNetStatPoll();
startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
notifyDataConnection(Phone.REASON_DATA_ATTACHED);
} else {
// Leo,會直接走else分之,設定其申請DataConnection的建立原因為REASON_DATA_ATTACHED
// update APN availability so that APN can be enabled.
notifyOffApnsOfAvailability(Phone.REASON_DATA_ATTACHED);
}
if (mAutoAttachOnCreationConfig) {
// Leo, 設定為true
mAutoAttachOnCreation.set(true);
}
// Leo,最後呼叫此方法
setupDataOnConnectableApns(Phone.REASON_DATA_ATTACHED);
}
可以看到,在上面,有幾個設定為true的地方,結合前面的DataConnection的初始化,我們知道,這兩個引數正式影響到DataConnection建立的條件
最後,呼叫了setupDataOnConnectableApns方法
private void setupDataOnConnectableApns(String reason) {
setupDataOnConnectableApns(reason, RetryFailures.ALWAYS);
}
private void setupDataOnConnectableApns(String reason, RetryFailures retryFailures) {
......
if (apnContext.isConnectable()) {
log("isConnectable() call trySetupData");
apnContext.setReason(reason);
trySetupData(apnContext, waitingApns);
}
}
apnContext.isConnectable()返回值為true,這個請自行分析
OK,又回到了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();
// set to false if apn type is non-metered or if we have a restricted (priveleged)
// request for the network.
// TODO - may want restricted requests to only apply to carrier-limited data access
// rather than applying to user limited as well.
// Exclude DUN for the purposes of the override until we get finer grained
// intention in NetworkRequests
boolean checkUserDataEnabled =
ApnSetting.isMeteredApnType(apnContext.getApnType(), mPhone.getContext(),
mPhone.getSubId(), mPhone.getServiceState().getDataRoaming()) &&
apnContext.hasNoRestrictedRequests(true /*exclude DUN */);
DataAllowFailReason failureReason = new DataAllowFailReason();
// allow data if currently in roaming service, roaming setting disabled
// and requested apn type is non-metered for roaming.
boolean isDataAllowed = isDataAllowed(failureReason) ||
(failureReason.isFailForSingleReason(DataAllowFailReasonType.ROAMING_DISABLED) &&
!(ApnSetting.isMeteredApnType(apnContext.getApnType(), mPhone.getContext(),
mPhone.getSubId(), mPhone.getServiceState().getDataRoaming())));
if (apnContext.isConnectable() && (isEmergencyApn ||
(isDataAllowed && isDataAllowedForApn(apnContext) &&
mDataEnabledSettings.isDataEnabled(checkUserDataEnabled) && !isEmergency()))) {
if (apnContext.getState() == DctConstants.State.FAILED) {
String str = "trySetupData: make a FAILED ApnContext IDLE so its reusable";
if (DBG) log(str);
apnContext.requestLog(str);
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());
String str = "trySetupData: X No APN found retValue=false";
if (DBG) log(str);
apnContext.requestLog(str);
return false;
} else {
apnContext.setWaitingApns(waitingApns);
if (DBG) {
log ("trySetupData: Create from mAllApnSettings : "
+ apnListToString(mAllApnSettings));
}
}
}
boolean retValue = setupData(apnContext, radioTech);
notifyOffApnsOfAvailability(apnContext.getReason());
if (DBG) log("trySetupData: X retValue=" + retValue);
return retValue;
} else {
......
}
}
由於前面在onDataConnectionAttached方法中,設定了兩個值為true,因此此處的isDataAllowed返回值為true,因此,會走如上的程式碼
最終會呼叫setupData方法,來建立DataConnection
private boolean setupData(ApnContext apnContext, int radioTech) {
if (DBG) log("setupData: apnContext=" + apnContext);
apnContext.requestLog("setupData");
ApnSetting apnSetting;
DcAsyncChannel dcac = null;
apnSetting = apnContext.getNextApnSetting();
if (apnSetting == null) {
if (DBG) log("setupData: return for no apn found!");
return false;
}
int profileId = apnSetting.profileId;
if (profileId == 0) {
profileId = getApnProfileID(apnContext.getApnType());
}
// On CDMA, if we're explicitly asking for DUN, we need have
// a dun-profiled connection so we can't share an existing one
// On GSM/LTE we can share existing apn connections provided they support
// this type.
if (apnContext.getApnType() != PhoneConstants.APN_TYPE_DUN ||
teardownForDun() == false) {
dcac = checkForCompatibleConnectedApnContext(apnContext);
if (dcac != null) {
// Get the dcacApnSetting for the connection we want to share.
ApnSetting dcacApnSetting = dcac.getApnSettingSync();
if (dcacApnSetting != null) {
// Setting is good, so use it.
apnSetting = dcacApnSetting;
}
}
}
if (dcac == null) {
if (isOnlySingleDcAllowed(radioTech)) {
if (isHigherPriorityApnContextActive(apnContext)) {
if (DBG) {
log("setupData: Higher priority ApnContext active. Ignoring call");
}
return false;
}
// Only lower priority calls left. Disconnect them all in this single PDP case
// so that we can bring up the requested higher priority call (once we receive
// response for deactivate request for the calls we are about to disconnect
if (cleanUpAllConnections(true, Phone.REASON_SINGLE_PDN_ARBITRATION)) {
// If any call actually requested to be disconnected, means we can't
// bring up this connection yet as we need to wait for those data calls
// to be disconnected.
if (DBG) log("setupData: Some calls are disconnecting first. Wait and retry");
return false;
}
// No other calls are active, so proceed
if (DBG) log("setupData: Single pdp. Continue setting up data call.");
}
dcac = findFreeDataConnection();
if (dcac == null) {
dcac = createDataConnection();
}
if (dcac == null) {
if (DBG) log("setupData: No free DataConnection and couldn't create one, WEIRD");
return false;
}
}
final int generation = apnContext.incAndGetConnectionGeneration();
if (DBG) {
log("setupData: dcac=" + dcac + " apnSetting=" + apnSetting + " gen#=" + generation);
}
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 = new Pair<ApnContext, Integer>(apnContext, generation);
dcac.bringUp(apnContext, profileId, radioTech, msg, generation);
if (DBG) log("setupData: initing!");
return true;
}
分析得出結論,最終通過呼叫createDataConnection方法,新建DcAsyncChannel物件
private DcAsyncChannel createDataConnection() {
if (DBG) log("createDataConnection E");
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);
} else {
loge("createDataConnection: Could not connect to dcac=" + dcac + " status=" + status);
}
if (DBG) log("createDataConnection() X id=" + id + " dc=" + conn);
return dcac;
}
通過DataConnection的makeDataConnection方法新建一個DataConnection。
至此,DataConnection已經建立