Separate data stall detection and recovery from net stats.
Also use the AlarmManager instead of messages so the delays
are consistent whether sleeping or not.
Bug: 5534004
Change-Id: I24118b30214dddf8183c1200a89555d6c528e606
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5754e60..a0652f7 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3620,6 +3620,13 @@
"pdp_watchdog_max_pdp_reset_fail_count";
/**
+ * The number of milliseconds to delay when checking for data stalls
+ * @hide
+ */
+ public static final String DATA_STALL_ALARM_DELAY_IN_MS =
+ "data_stall_alarm_delay_in_ms";
+
+ /**
* The interval in milliseconds at which to check gprs registration
* after the first registration mismatch of gprs and voice service,
* to detect possible data network registration problems.
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index 97e7aa3..5b13603 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -27,12 +27,14 @@
import android.net.LinkCapabilities;
import android.net.LinkProperties;
import android.net.NetworkInfo;
+import android.net.TrafficStats;
import android.net.wifi.WifiManager;
import android.os.AsyncResult;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.preference.PreferenceManager;
import android.provider.Settings;
@@ -56,6 +58,7 @@
*/
public abstract class DataConnectionTracker extends Handler {
protected static final boolean DBG = true;
+ protected static final boolean VDBG = false;
/**
* IDLE: ready to start data connection setup, default state
@@ -114,8 +117,8 @@
protected static final int EVENT_RESTORE_DEFAULT_APN = BASE + 14;
protected static final int EVENT_DISCONNECT_DONE = BASE + 15;
protected static final int EVENT_DATA_CONNECTION_ATTACHED = BASE + 16;
- protected static final int EVENT_START_NETSTAT_POLL = BASE + 17;
- protected static final int EVENT_START_RECOVERY = BASE + 18;
+ protected static final int EVENT_DATA_STALL_ALARM = BASE + 17;
+ protected static final int EVENT_DO_RECOVERY = BASE + 18;
protected static final int EVENT_APN_CHANGED = BASE + 19;
protected static final int EVENT_CDMA_DATA_DETACHED = BASE + 20;
protected static final int EVENT_NV_READY = BASE + 21;
@@ -189,19 +192,16 @@
/**
* After detecting a potential connection problem, this is the max number
- * of subsequent polls before attempting a radio reset. At this point,
- * poll interval is 5 seconds (POLL_NETSTAT_SLOW_MILLIS), so set this to
- * poll for about 2 more minutes.
+ * of subsequent polls before attempting recovery.
*/
protected static final int NO_RECV_POLL_LIMIT = 24;
-
// 1 sec. default polling interval when screen is on.
protected static final int POLL_NETSTAT_MILLIS = 1000;
// 10 min. default polling interval when screen is off.
protected static final int POLL_NETSTAT_SCREEN_OFF_MILLIS = 1000*60*10;
// 2 min for round trip time
protected static final int POLL_LONGEST_RTT = 120 * 1000;
- // 10 for packets without ack
+ // Default sent packets without ack which triggers initial recovery steps
protected static final int NUMBER_SENT_PACKETS_OF_HANG = 10;
// how long to wait before switching back to default APN
protected static final int RESTORE_DEFAULT_APN_DELAY = 1 * 60 * 1000;
@@ -210,6 +210,13 @@
// represents an invalid IP address
protected static final String NULL_IP = "0.0.0.0";
+ // Default for the data stall alarm
+ protected static final int DATA_STALL_ALARM_DELAY_IN_MS_DEFAULT = 1000 * 60 * 3;
+ // If attempt is less than this value we're doing first level recovery
+ protected static final int DATA_STALL_NO_RECV_POLL_LIMIT = 1;
+ // Tag for tracking stale alarms
+ protected static final String DATA_STALL_ALARM_TAG_EXTRA = "data.stall.alram.tag";
+
// TODO: See if we can remove INTENT_RECONNECT_ALARM
// having to have different values for GSM and
// CDMA. If so we can then remove the need for
@@ -240,11 +247,19 @@
protected long mTxPkts;
protected long mRxPkts;
- protected long mSentSinceLastRecv;
protected int mNetStatPollPeriod;
- protected int mNoRecvPollCount = 0;
protected boolean mNetStatPollEnabled = false;
+ protected TxRxSum mDataStallTxRxSum = new TxRxSum(0, 0);
+ // Used to track stale data stall alarms.
+ protected int mDataStallAlarmTag = (int) SystemClock.elapsedRealtime();
+ // The current data stall alarm intent
+ protected PendingIntent mDataStallAlarmIntent = null;
+ // Number of packets sent since the last received packet
+ protected long mSentSinceLastRecv;
+ // Controls when a simple recovery attempt it to be tried
+ protected int mNoRecvPollCount = 0;
+
// wifi connection status will be updated by sticky intent
protected boolean mIsWifiConnected = false;
@@ -313,7 +328,8 @@
} else if (action.startsWith(getActionIntentReconnectAlarm())) {
log("Reconnect alarm. Previous state was " + mState);
onActionIntentReconnectAlarm(intent);
-
+ } else if (action.equals(getActionIntentDataStallAlarm())) {
+ onActionIntentDataStallAlarm(intent);
} else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
final android.net.NetworkInfo networkInfo = (NetworkInfo)
intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
@@ -363,6 +379,71 @@
}
}
+ /**
+ * Maintian the sum of transmit and receive packets.
+ *
+ * The packet counts are initizlied and reset to -1 and
+ * remain -1 until they can be updated.
+ */
+ public class TxRxSum {
+ public long txPkts;
+ public long rxPkts;
+
+ public TxRxSum() {
+ reset();
+ }
+
+ public TxRxSum(long txPkts, long rxPkts) {
+ this.txPkts = txPkts;
+ this.rxPkts = rxPkts;
+ }
+
+ public TxRxSum(TxRxSum sum) {
+ txPkts = sum.txPkts;
+ rxPkts = sum.rxPkts;
+ }
+
+ public void reset() {
+ txPkts = -1;
+ rxPkts = -1;
+ }
+
+ public String toString() {
+ return "{txSum=" + txPkts + " rxSum=" + rxPkts + "}";
+ }
+
+ public void updateTxRxSum() {
+ boolean txUpdated = false, rxUpdated = false;
+ long txSum = 0, rxSum = 0;
+ for (ApnContext apnContext : mApnContexts.values()) {
+ if (apnContext.getState() == State.CONNECTED) {
+ DataConnectionAc dcac = apnContext.getDataConnectionAc();
+ if (dcac == null) continue;
+
+ LinkProperties linkProp = dcac.getLinkPropertiesSync();
+ if (linkProp == null) continue;
+
+ String iface = linkProp.getInterfaceName();
+
+ if (iface != null) {
+ long stats = TrafficStats.getTxPackets(iface);
+ if (stats > 0) {
+ txUpdated = true;
+ txSum += stats;
+ }
+ stats = TrafficStats.getRxPackets(iface);
+ if (stats > 0) {
+ rxUpdated = true;
+ rxSum += stats;
+ }
+ }
+ }
+ }
+ if (txUpdated) this.txPkts = txSum;
+ if (rxUpdated) this.rxPkts = rxSum;
+ }
+ }
+
protected boolean isDataSetupCompleteOk(AsyncResult ar) {
if (ar.exception != null) {
if (DBG) log("isDataSetupCompleteOk return false, ar.result=" + ar.result);
@@ -394,6 +475,13 @@
sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA));
}
+ protected void onActionIntentDataStallAlarm(Intent intent) {
+ if (VDBG) log("onActionIntentDataStallAlarm: action=" + intent.getAction());
+ Message msg = obtainMessage(EVENT_DATA_STALL_ALARM, intent.getAction());
+ msg.arg1 = intent.getIntExtra(DATA_STALL_ALARM_TAG_EXTRA, 0);
+ sendMessage(msg);
+ }
+
/**
* Default constructor
*/
@@ -529,6 +617,7 @@
// abstract methods
protected abstract String getActionIntentReconnectAlarm();
+ protected abstract String getActionIntentDataStallAlarm();
protected abstract void startNetStatPoll();
protected abstract void stopNetStatPoll();
protected abstract void restartRadio();
@@ -553,6 +642,10 @@
protected abstract void onCleanUpAllConnections(String cause);
protected abstract boolean isDataPossible(String apnType);
+ protected void onDataStallAlarm(int tag) {
+ loge("onDataStallAlarm: not impleted tag=" + tag);
+ }
+
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -575,6 +668,10 @@
onTrySetupData(reason);
break;
+ case EVENT_DATA_STALL_ALARM:
+ onDataStallAlarm(msg.arg1);
+ break;
+
case EVENT_ROAMING_OFF:
if (getDataOnRoamingEnabled() == false) {
resetAllRetryCounts();
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index f5d05a1..5889372 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -69,6 +69,10 @@
private static final String INTENT_RECONNECT_ALARM =
"com.android.internal.telephony.cdma-reconnect";
+ private static final String INTENT_DATA_STALL_ALARM =
+ "com.android.internal.telephony.cdma-data-stall";
+
+
/**
* Constants for the data connection activity:
* physical link down/up
@@ -149,6 +153,11 @@
}
@Override
+ protected String getActionIntentDataStallAlarm() {
+ return INTENT_DATA_STALL_ALARM;
+ }
+
+ @Override
protected void setState(State s) {
if (DBG) log ("setState: " + s);
if (mState != s) {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index e166401..b1684f9 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -96,22 +96,37 @@
private boolean mReregisterOnReconnectFailure = false;
private ContentResolver mResolver;
- // Count of PDP reset attempts; reset when we see incoming,
- // call reRegisterNetwork, or pingTest succeeds.
- private int mPdpResetCount = 0;
-
// Recovery action taken in case of data stall
- enum RecoveryAction {REREGISTER, RADIO_RESTART, RADIO_RESET};
- private RecoveryAction mRecoveryAction = RecoveryAction.REREGISTER;
-
+ class RecoveryAction {
+ public static final int GET_DATA_CALL_LIST = 0;
+ public static final int CLEANUP = 1;
+ public static final int REREGISTER = 2;
+ public static final int RADIO_RESTART = 3;
+ public static final int RADIO_RESET = 4;
+ }
+ public int getRecoveryAction() {
+ int action = Settings.System.getInt(mPhone.getContext().getContentResolver(),
+ "radio.data.stall.recovery.action", RecoveryAction.GET_DATA_CALL_LIST);
+ if (VDBG) log("getRecoveryAction: " + action);
+ return action;
+ }
+ public void putRecoveryAction(int action) {
+ Settings.System.putInt(mPhone.getContext().getContentResolver(),
+ "radio.data.stall.recovery.action", action);
+ if (VDBG) log("putRecoveryAction: " + action);
+ }
//***** Constants
private static final int POLL_PDP_MILLIS = 5 * 1000;
- private static final String INTENT_RECONNECT_ALARM = "com.android.internal.telephony.gprs-reconnect";
+ private static final String INTENT_RECONNECT_ALARM =
+ "com.android.internal.telephony.gprs-reconnect";
private static final String INTENT_RECONNECT_ALARM_EXTRA_TYPE = "type";
+ private static final String INTENT_DATA_STALL_ALARM =
+ "com.android.internal.telephony.gprs-data-stall";
+
static final Uri PREFERAPN_URI = Uri.parse("content://telephony/carriers/preferapn");
static final String APN_ID = "apn_id";
private boolean canSetPreferApn = false;
@@ -163,6 +178,11 @@
p.getServiceStateTracker().registerForPsRestrictedDisabled(this,
EVENT_PS_RESTRICT_DISABLED, null);
+ // install reconnect intent filter for this data connection.
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(INTENT_DATA_STALL_ALARM);
+ p.getContext().registerReceiver(mIntentReceiver, filter, null, p);
+
mDataConnectionTracker = this;
mResolver = mPhone.getContext().getContentResolver();
@@ -241,6 +261,11 @@
return INTENT_RECONNECT_ALARM;
}
+ @Override
+ protected String getActionIntentDataStallAlarm() {
+ return INTENT_DATA_STALL_ALARM;
+ }
+
private ApnContext addApnContext(String type) {
ApnContext apnContext = new ApnContext(type, LOG_TAG);
apnContext.setDependencyMet(false);
@@ -552,6 +577,7 @@
*/
if (DBG) log ("onDataConnectionDetached: stop polling and notify detached");
stopNetStatPoll();
+ stopDataStallAlarm();
notifyDataConnection(Phone.REASON_DATA_DETACHED);
}
@@ -560,6 +586,7 @@
if (getOverallState() == State.CONNECTED) {
if (DBG) log("onDataConnectionAttached: start polling notify attached");
startNetStatPoll();
+ startDataStallAlarm();
notifyDataConnection(Phone.REASON_DATA_ATTACHED);
} else {
// update APN availability so that APN can be enabled.
@@ -764,6 +791,8 @@
}
stopNetStatPoll();
+ stopDataStallAlarm();
+
// TODO: Do we need mRequestedApnType?
mRequestedApnType = Phone.APN_TYPE_DEFAULT;
}
@@ -1238,6 +1267,7 @@
// setState(State.CONNECTED);
mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
startNetStatPoll();
+ startDataStallAlarm();
// reset reconnect timer
apnContext.getDataConnection().resetRetryCount();
}
@@ -1252,59 +1282,60 @@
private void resetPollStats() {
mTxPkts = -1;
mRxPkts = -1;
- mSentSinceLastRecv = 0;
mNetStatPollPeriod = POLL_NETSTAT_MILLIS;
- mNoRecvPollCount = 0;
}
private void doRecovery() {
if (getOverallState() == State.CONNECTED) {
- int maxPdpReset = Settings.Secure.getInt(mResolver,
- Settings.Secure.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT,
- DEFAULT_MAX_PDP_RESET_FAIL);
- if (mPdpResetCount < maxPdpReset) {
- mPdpResetCount++;
- EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, mSentSinceLastRecv);
- if (DBG) log("doRecovery() cleanup all connections mPdpResetCount < max");
+ // Go through a series of recovery steps, each action transitions to the next action
+ int recoveryAction = getRecoveryAction();
+ switch (recoveryAction) {
+ case RecoveryAction.GET_DATA_CALL_LIST:
+ EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_GET_DATA_CALL_LIST,
+ mSentSinceLastRecv);
+ if (DBG) log("doRecovery() get data call list");
+ mPhone.mCM.getDataCallList(obtainMessage(EVENT_DATA_STATE_CHANGED));
+ putRecoveryAction(RecoveryAction.CLEANUP);
+ break;
+ case RecoveryAction.CLEANUP:
+ EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_CLEANUP, mSentSinceLastRecv);
+ if (DBG) log("doRecovery() cleanup all connections");
cleanUpAllConnections(true, Phone.REASON_PDP_RESET);
- } else {
- mPdpResetCount = 0;
- switch (mRecoveryAction) {
- case REREGISTER:
- EventLog.writeEvent(EventLogTags.PDP_REREGISTER_NETWORK, mSentSinceLastRecv);
- if (DBG) log("doRecovery() re-register getting preferred network type");
- mPhone.getServiceStateTracker().reRegisterNetwork(null);
- mRecoveryAction = RecoveryAction.RADIO_RESTART;
- break;
- case RADIO_RESTART:
- EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, mSentSinceLastRecv);
- if (DBG) log("restarting radio");
- mRecoveryAction = RecoveryAction.RADIO_RESET;
- restartRadio();
- break;
- case RADIO_RESET:
- // This is in case radio restart has not recovered the data.
- // It will set an additional "gsm.radioreset" property to tell
- // RIL or system to take further action.
- // The implementation of hard reset recovery action is up to OEM product.
- // Once gsm.radioreset property is consumed, it is expected to set back
- // to false by RIL.
- EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, -1);
- if (DBG) log("restarting radio with reset indication");
- SystemProperties.set("gsm.radioreset", "true");
- // give 1 sec so property change can be notified.
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {}
- restartRadio();
- break;
- default:
- throw new RuntimeException("doRecovery: Invalid mRecoveryAction " +
- mRecoveryAction);
- }
+ putRecoveryAction(RecoveryAction.REREGISTER);
+ break;
+ case RecoveryAction.REREGISTER:
+ EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_REREGISTER, mSentSinceLastRecv);
+ if (DBG) log("doRecovery() re-register");
+ mPhone.getServiceStateTracker().reRegisterNetwork(null);
+ putRecoveryAction(RecoveryAction.RADIO_RESTART);
+ break;
+ case RecoveryAction.RADIO_RESTART:
+ EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RESTART, mSentSinceLastRecv);
+ if (DBG) log("restarting radio");
+ putRecoveryAction(RecoveryAction.RADIO_RESET);
+ restartRadio();
+ break;
+ case RecoveryAction.RADIO_RESET:
+ // This is in case radio restart has not recovered the data.
+ // It will set an additional "gsm.radioreset" property to tell
+ // RIL or system to take further action.
+ // The implementation of hard reset recovery action is up to OEM product.
+ // Once gsm.radioreset property is consumed, it is expected to set back
+ // to false by RIL.
+ EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RESTART_WITH_PROP, -1);
+ if (DBG) log("restarting radio with gsm.radioreset to true");
+ SystemProperties.set("gsm.radioreset", "true");
+ // give 1 sec so property change can be notified.
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {}
+ restartRadio();
+ putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
+ break;
+ default:
+ throw new RuntimeException("doRecovery: Invalid recoveryAction=" +
+ recoveryAction);
}
- } else {
- if (DBG) log("doRecovery(): ignore, we're not connected");
}
}
@@ -1342,119 +1373,130 @@
SystemProperties.set("net.ppp.reset-by-timeout", String.valueOf(reset+1));
}
+
+ private void updateDataStallInfo() {
+ long sent, received;
+
+ TxRxSum preTxRxSum = new TxRxSum(mDataStallTxRxSum);
+ mDataStallTxRxSum.updateTxRxSum();
+
+ if (VDBG) {
+ log("updateDataStallInfo: mDataStallTxRxSum=" + mDataStallTxRxSum +
+ " preTxRxSum=" + preTxRxSum);
+ }
+
+ sent = mDataStallTxRxSum.txPkts - preTxRxSum.txPkts;
+ received = mDataStallTxRxSum.rxPkts - preTxRxSum.rxPkts;
+
+ if (VDBG) {
+ 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) log("updateDataStallInfo: IN/OUT");
+ mSentSinceLastRecv = 0;
+ putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
+ } else if (sent > 0 && received == 0) {
+ if (mPhone.getState() == Phone.State.IDLE) {
+ mSentSinceLastRecv += sent;
+ } else {
+ mSentSinceLastRecv = 0;
+ }
+ if (DBG) {
+ log("updateDataStallInfo: OUT sent=" + sent +
+ " mSentSinceLastRecv=" + mSentSinceLastRecv);
+ }
+ } else if (sent == 0 && received > 0) {
+ if (VDBG) log("updateDataStallInfo: IN");
+ mSentSinceLastRecv = 0;
+ putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
+ } else {
+ if (VDBG) log("updateDataStallInfo: NONE");
+ }
+ }
+
+ @Override
+ protected void onDataStallAlarm(int tag) {
+ if (mDataStallAlarmTag != tag) {
+ if (DBG) {
+ log("onDataStallAlarm: ignore, tag=" + tag + " expecting " + mDataStallAlarmTag);
+ }
+ return;
+ }
+ updateDataStallInfo();
+
+ int hangWatchdogTrigger = Settings.Secure.getInt(mResolver,
+ Settings.Secure.PDP_WATCHDOG_TRIGGER_PACKET_COUNT,
+ NUMBER_SENT_PACKETS_OF_HANG);
+
+ if (mSentSinceLastRecv >= hangWatchdogTrigger) {
+ if (DBG) {
+ log("onDataStallAlarm: tag=" + tag + " do recovery action=" + getRecoveryAction());
+ }
+ sendMessage(obtainMessage(EVENT_DO_RECOVERY));
+ } else {
+ if (VDBG) {
+ log("onDataStallAlarm: tag=" + tag + " Sent " + String.valueOf(mSentSinceLastRecv) +
+ " pkts since last received, < watchdogTrigger=" + hangWatchdogTrigger);
+ }
+ }
+ startDataStallAlarm();
+ }
+
+
+ private void updateDataActivity() {
+ long sent, received;
+
+ Activity newActivity;
+
+ TxRxSum preTxRxSum = new TxRxSum(mTxPkts, mRxPkts);
+ TxRxSum curTxRxSum = new TxRxSum();
+ curTxRxSum.updateTxRxSum();
+ mTxPkts = curTxRxSum.txPkts;
+ mRxPkts = curTxRxSum.rxPkts;
+
+ if (VDBG) {
+ log("updateDataActivity: curTxRxSum=" + curTxRxSum + " preTxRxSum=" + preTxRxSum);
+ }
+
+ if (mNetStatPollEnabled && (preTxRxSum.txPkts > 0 || preTxRxSum.rxPkts > 0)) {
+ sent = mTxPkts - preTxRxSum.txPkts;
+ received = mRxPkts - preTxRxSum.rxPkts;
+
+ if (VDBG) log("updateDataActivity: sent=" + sent + " received=" + received);
+ if ( sent > 0 && received > 0 ) {
+ newActivity = Activity.DATAINANDOUT;
+ } else if (sent > 0 && received == 0) {
+ newActivity = Activity.DATAOUT;
+ } else if (sent == 0 && received > 0) {
+ newActivity = Activity.DATAIN;
+ } else {
+ newActivity = Activity.NONE;
+ }
+
+ if (mActivity != newActivity && mIsScreenOn) {
+ if (VDBG) log("updateDataActivity: newActivity=" + newActivity);
+ mActivity = newActivity;
+ mPhone.notifyDataActivity();
+ }
+ }
+ }
+
private Runnable mPollNetStat = new Runnable()
{
-
+ @Override
public void run() {
- long sent, received;
- long preTxPkts = -1, preRxPkts = -1;
+ updateDataActivity();
- Activity newActivity;
-
- preTxPkts = mTxPkts;
- preRxPkts = mRxPkts;
-
- long txSum = 0, rxSum = 0;
- for (ApnContext apnContext : mApnContexts.values()) {
- if (apnContext.getState() == State.CONNECTED) {
- DataConnectionAc dcac = apnContext.getDataConnectionAc();
- if (dcac == null) continue;
-
- LinkProperties linkProp = dcac.getLinkPropertiesSync();
- if (linkProp == null) continue;
-
- String iface = linkProp.getInterfaceName();
-
- if (iface != null) {
- long stats = TrafficStats.getTxPackets(iface);
- if (stats > 0) txSum += stats;
- stats = TrafficStats.getRxPackets(iface);
- if (stats > 0) rxSum += stats;
- }
- }
- }
-
- mTxPkts = txSum;
- mRxPkts = rxSum;
-
- // log("tx " + mTxPkts + " rx " + mRxPkts);
-
- if (mNetStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) {
- sent = mTxPkts - preTxPkts;
- received = mRxPkts - preRxPkts;
-
- if ( sent > 0 && received > 0 ) {
- mSentSinceLastRecv = 0;
- newActivity = Activity.DATAINANDOUT;
- mPdpResetCount = 0;
- mRecoveryAction = RecoveryAction.REREGISTER;
- } else if (sent > 0 && received == 0) {
- if (mPhone.getState() == Phone.State.IDLE) {
- mSentSinceLastRecv += sent;
- } else {
- mSentSinceLastRecv = 0;
- }
- newActivity = Activity.DATAOUT;
- } else if (sent == 0 && received > 0) {
- mSentSinceLastRecv = 0;
- newActivity = Activity.DATAIN;
- mPdpResetCount = 0;
- mRecoveryAction = RecoveryAction.REREGISTER;
- } else if (sent == 0 && received == 0) {
- newActivity = Activity.NONE;
- } else {
- mSentSinceLastRecv = 0;
- newActivity = Activity.NONE;
- }
-
- if (mActivity != newActivity && mIsScreenOn) {
- mActivity = newActivity;
- mPhone.notifyDataActivity();
- }
- }
-
- int watchdogTrigger = Settings.Secure.getInt(mResolver,
- Settings.Secure.PDP_WATCHDOG_TRIGGER_PACKET_COUNT,
- NUMBER_SENT_PACKETS_OF_HANG);
-
- if (mSentSinceLastRecv >= watchdogTrigger) {
- // we already have NUMBER_SENT_PACKETS sent without ack
- if (mNoRecvPollCount == 0) {
- EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET_COUNTDOWN_TRIGGERED,
- mSentSinceLastRecv);
- }
-
- int noRecvPollLimit = Settings.Secure.getInt(mResolver,
- Settings.Secure.PDP_WATCHDOG_ERROR_POLL_COUNT, NO_RECV_POLL_LIMIT);
-
- if (mNoRecvPollCount < noRecvPollLimit) {
- // It's possible the PDP context went down and we weren't notified.
- // Start polling the context list in an attempt to recover.
- if (DBG) log("Polling: no DATAIN in a while; polling PDP");
- mPhone.mCM.getDataCallList(obtainMessage(EVENT_DATA_STATE_CHANGED));
-
- mNoRecvPollCount++;
-
- // Slow down the poll interval to let things happen
- mNetStatPollPeriod = Settings.Secure.getInt(mResolver,
- Settings.Secure.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS,
- POLL_NETSTAT_SLOW_MILLIS);
- } else {
- if (DBG) log("Polling: Sent " + String.valueOf(mSentSinceLastRecv) +
- " pkts since last received start recovery process");
- mNoRecvPollCount = 0;
- sendMessage(obtainMessage(EVENT_START_RECOVERY));
- }
+ if (mIsScreenOn) {
+ mNetStatPollPeriod = Settings.Secure.getInt(mResolver,
+ Settings.Secure.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS);
} else {
- mNoRecvPollCount = 0;
- if (mIsScreenOn) {
- mNetStatPollPeriod = Settings.Secure.getInt(mResolver,
- Settings.Secure.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS);
- } else {
- mNetStatPollPeriod = Settings.Secure.getInt(mResolver,
- Settings.Secure.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS,
- POLL_NETSTAT_SCREEN_OFF_MILLIS);
- }
+ mNetStatPollPeriod = Settings.Secure.getInt(mResolver,
+ Settings.Secure.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS,
+ POLL_NETSTAT_SCREEN_OFF_MILLIS);
}
if (mNetStatPollEnabled) {
@@ -1566,6 +1608,41 @@
}
+ private void startDataStallAlarm() {
+ int delayInMs = Settings.Secure.getInt(mResolver,
+ Settings.Secure.DATA_STALL_ALARM_DELAY_IN_MS,
+ DATA_STALL_ALARM_DELAY_IN_MS_DEFAULT);
+ mDataStallAlarmTag += 1;
+ if (DBG) {
+ log("startDataStallAlarm: tag=" + mDataStallAlarmTag +
+ " delay=" + (delayInMs / 1000) + "s");
+ }
+ AlarmManager am =
+ (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
+
+ Intent intent = new Intent(INTENT_DATA_STALL_ALARM);
+ intent.putExtra(DATA_STALL_ALARM_TAG_EXTRA, mDataStallAlarmTag);
+ mDataStallAlarmIntent = PendingIntent.getBroadcast(mPhone.getContext(), 0, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + delayInMs, mDataStallAlarmIntent);
+ }
+
+ private void stopDataStallAlarm() {
+ AlarmManager am =
+ (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
+
+ if (DBG) {
+ log("stopDataStallAlarm: current tag=" + mDataStallAlarmTag +
+ " mDataStallAlarmIntent=" + mDataStallAlarmIntent);
+ }
+ mDataStallAlarmTag += 1;
+ if (mDataStallAlarmIntent != null) {
+ am.cancel(mDataStallAlarmIntent);
+ mDataStallAlarmIntent = null;
+ }
+ }
+
private void notifyNoData(GsmDataConnection.FailCause lastFailCauseCode,
ApnContext apnContext) {
if (DBG) log( "notifyNoData: type=" + apnContext.getApnType());
@@ -1930,6 +2007,7 @@
if (isConnected() && ! mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
if (DBG) log("onVoiceCallStarted stop polling");
stopNetStatPoll();
+ stopDataStallAlarm();
notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
}
}
@@ -1940,6 +2018,7 @@
if (isConnected()) {
if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
startNetStatPoll();
+ startDataStallAlarm();
notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
} else {
// clean slate after call end.
@@ -2251,11 +2330,7 @@
onPollPdp();
break;
- case EVENT_START_NETSTAT_POLL:
- startNetStatPoll();
- break;
-
- case EVENT_START_RECOVERY:
+ case EVENT_DO_RECOVERY:
doRecovery();
break;
@@ -2272,6 +2347,7 @@
*/
if (DBG) log("EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
stopNetStatPoll();
+ stopDataStallAlarm();
mIsPsRestricted = true;
break;
@@ -2284,6 +2360,7 @@
mIsPsRestricted = false;
if (isConnected()) {
startNetStatPoll();
+ startDataStallAlarm();
} else {
// TODO: Should all PDN states be checked to fail?
if (mState == State.FAILED) {