1. 触发机制
安卓 Telephony 触发 Data Stall Recovery有以下三种情况:
1. ConnectivityService 报告Validation Failed 错误
2. Data Stall Alarm Trigerred 一段时间内收不到数据
3. Data Stall Alarm Trigerred-90秒内连续DNS检查失败超过3次
2. ConnectivityService 报告Validation Failed
【NetworkMonitor.java】
private class ProbingState extends State {
private Thread mThread;
@Override
public void enter() {
if (mEvaluateAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
//Don't continue to blame UID forever.
TrafficStats.clearThreadStatsUid();
}
final int token = ++mProbeToken;
mThread = new Thread(() -> sendMessage(obtainMessage(CMD_PROBE_COMPLETE, token, 0,
isCaptivePortal())));//发送CMD_PROBE_COMPLETE消息
mThread.start();
}
@Override
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_PROBE_COMPLETE:
...
} else if (probeResult.isPortal()) {//200 <= HTTP响应码 <= 399
//agenew add by wucheng 20200122
mEvaluationState.reportEvaluationResult(NETWORK_VALIDATION_RESULT_VALID,
probeResult.redirectUrl);//报告评估结果NETWORK_VALIDATION_RESULT_VALID
mLastPortalProbeResult = probeResult;
transitionTo(mCaptivePortalState);
}
...
}
}
...
}
protected void reportEvaluationResult(int result, @Nullable String redirectUrl) {
mEvaluationResult = result;
mRedirectUrl = redirectUrl;
notifyNetworkTested(getNetworkTestResult(), mRedirectUrl);
}
private void notifyNetworkTested(int result, @Nullable String redirectUrl) {
try {//跨进程回调搜索"INetworkMonitorCallbacks.stub"
mCallback.notifyNetworkTested(result, redirectUrl);//
} catch (RemoteException e) {
Log.e(TAG, "Error sending network test result", e);
}
}
【ConnectivityService.java】
public void notifyNetworkTested(int testResult, @Nullable String redirectUrl) {
mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(EVENT_NETWORK_TESTED,
testResult, mNetId, redirectUrl));//发送消息EVENT_NETWORK_TESTED
}
//消息处理
private boolean maybeHandleNetworkMonitorMessage(Message msg) {
switch (msg.what) {
default:
return false;
case EVENT_NETWORK_TESTED: {
...
//前面传递的消息为NETWORK_VALIDATION_RESULT_VALID,所以valid值为0
final boolean valid = ((msg.arg1 & NETWORK_VALIDATION_RESULT_VALID) != 0);
final boolean wasValidated = nai.lastValidated;
final boolean wasDefault = isDefaultNetwork(nai);
// Only show a connected notification if the network is pending validation
// after the captive portal app was open, and it has now validated.
if (nai.captivePortalValidationPending && valid) {
// User is now logged in, network validated.
nai.captivePortalValidationPending = false;
showNetworkNotification(nai, NotificationType.LOGGED_IN);
}
final String redirectUrl = (msg.obj instanceof String) ? (String) msg.obj : "";
if (DBG) {
final String logMsg = !TextUtils.isEmpty(redirectUrl)
? " with redirect to " + redirectUrl
: "";
log(nai.name() + " validation " + (valid ? "passed" : "failed") + logMsg);
}
if (valid != nai.lastValidated) {//与上一次的状态不一致
if (wasDefault) {
metricsLogger().defaultNetworkMetrics().logDefaultNetworkValidity(
SystemClock.elapsedRealtime(), valid);
}
final int oldScore = nai.getCurrentScore();
nai.lastValidated = valid;
nai.everValidated |= valid;
updateCapabilities(oldScore, nai, nai.networkCapabilities);
// If score has changed, rebroadcast to NetworkFactories. b/17726566
if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
if (valid) {
handleFreshlyValidatedNetwork(nai);
// Clear NO_INTERNET, PARTIAL_CONNECTIVITY and LOST_INTERNET
// notifications if network becomes valid.
mNotifier.clearNotification(nai.network.netId,
NotificationType.NO_INTERNET);
mNotifier.clearNotification(nai.network.netId,
NotificationType.LOST_INTERNET);
mNotifier.clearNotification(nai.network.netId,
NotificationType.PARTIAL_CONNECTIVITY);
}
} else if (partialConnectivityChanged) {
updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
}
updateInetCondition(nai);
// Let the NetworkAgent know the state of its network
Bundle redirectUrlBundle = new Bundle();
redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, redirectUrl);
// TODO: Evaluate to update partial connectivity to status to NetworkAgent.
nai.asyncChannel.sendMessage(
NetworkAgent.CMD_REPORT_NETWORK_STATUS,
(valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
0, redirectUrlBundle);//发送CMD_REPORT_NETWORK_STATUS消息
...
}
...
}
return true;
}
【NetworkAgent.java】
public void handleMessage(Message msg) {
switch (msg.what) {
...
case CMD_REPORT_NETWORK_STATUS: {
String redirectUrl = ((Bundle)msg.obj).getString(REDIRECT_URL_KEY);
if (VDBG) {
log("CMD_REPORT_NETWORK_STATUS(" +
(msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ") + redirectUrl);
}
networkStatus(msg.arg1, redirectUrl);//抽象类的方法搜索"extends NetworkAgent"
break;
}
...
}
}
【DcNetworkAgent.java】
@Override
protected synchronized void networkStatus(int status, String redirectUrl) {
if (mDataConnection == null) {
loge("networkStatus called on no-owner DcNetworkAgent!");
return;
}
logd("validation status: " + status + " with redirection URL: " + redirectUrl);
DcTracker dct = mPhone.getDcTracker(mTransportType);
if (dct != null) {
Message msg = dct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED,
status, 0, redirectUrl);//发送消息EVENT_NETWORK_STATUS_CHANGED
msg.sendToTarget();//全局搜索"DctConstants.EVENT_NETWORK_STATUS_CHANGED"
}
}
【DcTracker.java】
public void handleMessage (Message msg) {
...
switch (msg.what) {
...
case DctConstants.EVENT_NETWORK_STATUS_CHANGED:
int status = msg.arg1;
String url = (String) msg.obj;
onNetworkStatusChanged(status, url);
break;
...
}
}
private void onNetworkStatusChanged(int status, String redirectUrl) {
if (!TextUtils.isEmpty(redirectUrl)) {
Intent intent = new Intent(TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED);
intent.putExtra(TelephonyIntents.EXTRA_REDIRECTION_URL_KEY, redirectUrl);
mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent);
log("Notify carrier signal receivers with redirectUrl: " + redirectUrl);
} else {
...
mDsRecoveryHandler.processNetworkStatusChanged(isValid);
}
}
public void processNetworkStatusChanged(boolean isValid) {
if (isValid) {
mIsValidNetwork = true;
reset();
} else {
if (mIsValidNetwork || isRecoveryAlreadyStarted()) {
mIsValidNetwork = false;
// Check and trigger a recovery if network switched from good
// to bad or recovery is already started before.
if (checkRecovery()//间隔需要小于3分钟
/// M: check if skip data stall alarm @{
&& !mtkSkipDataStallAlarm()) {
/// @}
if (DBG) log("trigger data stall recovery");
triggerRecovery();
}
}
}
}
private void triggerRecovery() {
//发送消息EVENT_DO_RECOVERY去执行Do Recovery机制
sendMessage(obtainMessage(DctConstants.EVENT_DO_RECOVERY));
}
3. Data Stall Alarm Trigerred (一段时间内收不到数据)
数据连接成功后,DcTracker会定期检测发送和接收的字节数,当一定时间内没有接收到数据,并且发送的数据大于设定的值则开始执行Do Recovery恢复机制。
Data Stall Alarm Trigerred的大致流程如下:
【DcTracker.java】 //***** Constructor构造函数 public DcTracker(Phone phone, @TransportType int transportType) { ... registerForAllEvents();//注册所有的事件 ... } protected void registerForAllEvents() { ... registerServiceStateTrackerEvents();//注册网络状相关态的事件处理 mDataServiceManager.registerForServiceBindingChanged(this, DctConstants.EVENT_DATA_SERVICE_BINDING_CHANGED, null); } public void registerServiceStateTrackerEvents() { mPhone.getServiceStateTracker().registerForDataConnectionAttached(mTransportType, this, DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null); ... } //在handleMessage种处理网络状态相关的事件 public void handleMessage (Message msg) { ... switch (msg.what) { ... case DctConstants.EVENT_DATA_CONNECTION_ATTACHED: onDataConnectionAttached();//网络附着成功 break; ... } } protected void onDataConnectionAttached() { mAttached.set(true); if (getOverallState() == DctConstants.State.CONNECTED) { startNetStatPoll(); startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);//开始启动Data Stall Alarm mPhone.notifyDataConnection(); } if (mAutoAttachOnCreationConfig) { mAutoAttachEnabled.set(true); } setupDataOnAllConnectableApns(Phone.REASON_DATA_ATTACHED, RetryFailures.ALWAYS); } protected void startDataStallAlarm(boolean suspectedStall) { int delayInMs; if (mDsRecoveryHandler.isNoRxDataStallDetectionEnabled()//是否启用 && getOverallState() == DctConstants.State.CONNECTED) { // If screen is on or data stall is currently suspected, set the alarm // with an aggressive timeout. if (mIsScreenOn || suspectedStall || mDsRecoveryHandler.isAggressiveRecovery()) { delayInMs = Settings.Global.getInt(mResolver, Settings.Global.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS, DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS_DEFAULT);//1000 * 60 = 1分钟 } else { delayInMs = Settings.Global.getInt(mResolver, Settings.Global.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS, DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS_DEFAULT);//1000 * 60 * 6 = 6分钟 } mDataStallAlarmTag += 1; if (VDBG_STALL) { log("startDataStallAlarm: tag=" + mDataStallAlarmTag + " delay=" + (delayInMs / 1000) + "s"); } Intent intent = new Intent(INTENT_DATA_STALL_ALARM); intent.putExtra(INTENT_DATA_STALL_ALARM_EXTRA_TAG, mDataStallAlarmTag); intent.putExtra(INTENT_DATA_STALL_ALARM_EXTRA_TRANSPORT_TYPE, mTransportType); SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId()); // AOSP will create an PendingIntent with requestCode = 0 when phoneId is 1. // Then phone 0's PendingIntent will be overrided. // Hence, shift phoneId with 1 to make phone 0's PendingIntent be identical. mDataStallAlarmIntent = PendingIntent.getBroadcast(mPhone.getContext(), mPhone.getPhoneId() + 1, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); mAlarmManager.set(AlarmManager.ELAPSED_REALTIME,//设置一个定时器,delayInMs毫秒后发送INTENT_DATA_STALL_ALARM SystemClock.elapsedRealtime() + delayInMs, mDataStallAlarmIntent); } else { if (VDBG_STALL) { log("startDataStallAlarm: NOT started, no connection tag=" + mDataStallAlarmTag); } } } private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver () { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(Intent.ACTION_SCREEN_ON)) { // TODO: Evaluate hooking this up with DeviceStateMonitor if (DBG) log("screen on"); mIsScreenOn = true; stopNetStatPoll(); startNetStatPoll(); restartDataStallAlarm(); } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { if (DBG) log("screen off"); mIsScreenOn = false; stopNetStatPoll(); startNetStatPoll(); restartDataStallAlarm(); } else if (action.startsWith(INTENT_RECONNECT_ALARM)) { onActionIntentReconnectAlarm(intent); } else if (action.equals(INTENT_DATA_STALL_ALARM)) { onActionIntentDataStallAlarm(intent);//准备 } else if (action.equals(INTENT_PROVISIONING_APN_ALARM)) { if (DBG) log("Provisioning apn alarm"); onActionIntentProvisioningApnAlarm(intent); } else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) { if (DBG) log("received carrier config change"); if (mIccRecords.get() != null && mIccRecords.get().getRecordsLoaded()) { setDefaultDataRoamingEnabled(); } } else { if (DBG) log("onReceive: Unknown action=" + action); } } } private void onActionIntentDataStallAlarm(Intent intent) { ... Message msg = obtainMessage(DctConstants.EVENT_DATA_STALL_ALARM, intent.getAction());//发送EVENT_DATA_STALL_ALARM事件 msg.arg1 = intent.getIntExtra(INTENT_DATA_STALL_ALARM_EXTRA_TAG, 0); sendMessage(msg); } //在handleMessage种处理网络状态相关的事件 public void handleMessage (Message msg) { ... switch (msg.what) { ... case DctConstants.EVENT_DATA_STALL_ALARM: onDataStallAlarm(msg.arg1);//处理Data Stall Alarm break; ... } } private void onDataStallAlarm(int tag) { ... updateDataStallInfo();//更新统计计数 int hangWatchdogTrigger = Settings.Global.getInt(mResolver, Settings.Global.PDP_WATCHDOG_TRIGGER_PACKET_COUNT, NUMBER_SENT_PACKETS_OF_HANG);//10 boolean suspectedStall = DATA_STALL_NOT_SUSPECTED; if (mSentSinceLastRecv >= hangWatchdogTrigger) { if (DBG) { log("onDataStallAlarm: tag=" + tag + " do recovery action=" + mDsRecoveryHandler.getRecoveryAction()); } suspectedStall = DATA_STALL_SUSPECTED; sendMessage(obtainMessage(DctConstants.EVENT_DO_RECOVERY));//启动Do Recovery恢复机制 } else { if (VDBG_STALL) { log("onDataStallAlarm: tag=" + tag + " Sent " + String.valueOf(mSentSinceLastRecv) + " pkts since last received, < watchdogTrigger=" + hangWatchdogTrigger); } } //重置Alarm任务,一段时间后再次执行 startDataStallAlarm(suspectedStall); } private void updateDataStallInfo() { long sent, received; TxRxSum preTxRxSum = new TxRxSum(mDataStallTxRxSum);//获取之前的统计结果 mDataStallTxRxSum.updateTcpTxRxSum();//更新TCP数据的所有的发送和接收的总和 /// M: check the current operator if need to update total Tx/Rx packets @{ mtkUpdateTotalTxRxSum(); /// @} if (VDBG_STALL) { log("updateDataStallInfo: mDataStallTxRxSum=" + mDataStallTxRxSum + " preTxRxSum=" + preTxRxSum); } //计算这一段期间内发送和接收的字节数 = 新统计的字节数 - 上一次的字节数 sent = mDataStallTxRxSum.txPkts - preTxRxSum.txPkts; received = mDataStallTxRxSum.rxPkts - preTxRxSum.rxPkts; if (RADIO_TESTS) { if (SystemProperties.getBoolean("radio.test.data.stall", false)) { log("updateDataStallInfo: radio.test.data.stall true received = 0;"); received = 0; } } if ( sent > 0 && received > 0 ) {//有发送 有接收 if (VDBG_STALL) log("updateDataStallInfo: IN/OUT"); mSentSinceLastRecv = 0; mDsRecoveryHandler.reset(); } else if (sent > 0 && received == 0) {//有发送 无接收 if (isPhoneStateIdle()) { mSentSinceLastRecv += sent;//发送的字节数 } else { mSentSinceLastRecv = 0; } if (DBG) { log("updateDataStallInfo: OUT sent=" + sent + " mSentSinceLastRecv=" + mSentSinceLastRecv); } } else if (sent == 0 && received > 0) {//无发送 有接收 if (VDBG_STALL) log("updateDataStallInfo: IN"); mSentSinceLastRecv = 0; mDsRecoveryHandler.reset(); } else { if (VDBG_STALL) log("updateDataStallInfo: NONE"); } }
以下为Data Stall的日志示例,
4. Data Stall Alarm Trigerred( 90秒内连续DNS检查失败超过3次)
5. Do Recovery 的4大策略
恢复机制分为以下四大策略:
1. 向Modem主动查询 DATA_CALL_LIST
2. 清除现有的数据链接
3. 重新驻网
4. 重启Radio
执行顺序从上往下依次执行。
【DcTracker.java】 public void doRecovery() { if (getOverallState() == DctConstants.State.CONNECTED) { // Go through a series of recovery steps, each action transitions to the next action @RecoveryAction final int recoveryAction = getRecoveryAction(); ... switch (recoveryAction) { case RECOVERY_ACTION_GET_DATA_CALL_LIST: ...//向Modem主动查询 DATA_CALL_LIST mDataServiceManager.requestDataCallList(obtainMessage()); putRecoveryAction(RECOVERY_ACTION_CLEANUP); break; case RECOVERY_ACTION_CLEANUP: ...//清除现有的数据链接 cleanUpConnection(mApnContexts.get(ApnSetting.getApnTypeString( ApnSetting.TYPE_DEFAULT))); putRecoveryAction(RECOVERY_ACTION_REREGISTER); break; case RECOVERY_ACTION_REREGISTER: ...//重新驻网 mPhone.getServiceStateTracker().reRegisterNetwork(null); putRecoveryAction(RECOVERY_ACTION_RADIO_RESTART); break; case RECOVERY_ACTION_RADIO_RESTART: ...//重启Radio restartRadio(); reset(); break; default: throw new RuntimeException("doRecovery: Invalid recoveryAction=" + recoveryAction); } mSentSinceLastRecv = 0; mTimeLastRecoveryStartMs = SystemClock.elapsedRealtime(); } }
本文地址:http://huazai.eleuu.com/?post=58
版权声明:若无注明,本文皆为“皮皮华博客”原创,转载请保留文章出处。
发表吐槽
你肿么看?
既然没有吐槽,那就赶紧抢沙发吧!