Merge "Separate data stall detection and recovery from net stats." into ics-mr0

This commit is contained in:
Wink Saville
2011-11-03 21:26:35 -07:00
committed by Android (Google) Code Review
4 changed files with 367 additions and 177 deletions

View File

@ -3619,6 +3619,13 @@ public final class Settings {
public static final String PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT = public static final String PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT =
"pdp_watchdog_max_pdp_reset_fail_count"; "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 * The interval in milliseconds at which to check gprs registration
* after the first registration mismatch of gprs and voice service, * after the first registration mismatch of gprs and voice service,

View File

@ -27,12 +27,14 @@ import android.database.ContentObserver;
import android.net.LinkCapabilities; import android.net.LinkCapabilities;
import android.net.LinkProperties; import android.net.LinkProperties;
import android.net.NetworkInfo; import android.net.NetworkInfo;
import android.net.TrafficStats;
import android.net.wifi.WifiManager; import android.net.wifi.WifiManager;
import android.os.AsyncResult; import android.os.AsyncResult;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.os.Messenger; import android.os.Messenger;
import android.os.SystemClock;
import android.os.SystemProperties; import android.os.SystemProperties;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.provider.Settings; import android.provider.Settings;
@ -56,6 +58,7 @@ import java.util.concurrent.atomic.AtomicInteger;
*/ */
public abstract class DataConnectionTracker extends Handler { public abstract class DataConnectionTracker extends Handler {
protected static final boolean DBG = true; protected static final boolean DBG = true;
protected static final boolean VDBG = false;
/** /**
* IDLE: ready to start data connection setup, default state * IDLE: ready to start data connection setup, default state
@ -114,8 +117,8 @@ public abstract class DataConnectionTracker extends Handler {
protected static final int EVENT_RESTORE_DEFAULT_APN = BASE + 14; protected static final int EVENT_RESTORE_DEFAULT_APN = BASE + 14;
protected static final int EVENT_DISCONNECT_DONE = BASE + 15; protected static final int EVENT_DISCONNECT_DONE = BASE + 15;
protected static final int EVENT_DATA_CONNECTION_ATTACHED = BASE + 16; 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_DATA_STALL_ALARM = BASE + 17;
protected static final int EVENT_START_RECOVERY = BASE + 18; protected static final int EVENT_DO_RECOVERY = BASE + 18;
protected static final int EVENT_APN_CHANGED = BASE + 19; protected static final int EVENT_APN_CHANGED = BASE + 19;
protected static final int EVENT_CDMA_DATA_DETACHED = BASE + 20; protected static final int EVENT_CDMA_DATA_DETACHED = BASE + 20;
protected static final int EVENT_NV_READY = BASE + 21; protected static final int EVENT_NV_READY = BASE + 21;
@ -189,19 +192,16 @@ public abstract class DataConnectionTracker extends Handler {
/** /**
* After detecting a potential connection problem, this is the max number * After detecting a potential connection problem, this is the max number
* of subsequent polls before attempting a radio reset. At this point, * of subsequent polls before attempting recovery.
* poll interval is 5 seconds (POLL_NETSTAT_SLOW_MILLIS), so set this to
* poll for about 2 more minutes.
*/ */
protected static final int NO_RECV_POLL_LIMIT = 24; protected static final int NO_RECV_POLL_LIMIT = 24;
// 1 sec. default polling interval when screen is on. // 1 sec. default polling interval when screen is on.
protected static final int POLL_NETSTAT_MILLIS = 1000; protected static final int POLL_NETSTAT_MILLIS = 1000;
// 10 min. default polling interval when screen is off. // 10 min. default polling interval when screen is off.
protected static final int POLL_NETSTAT_SCREEN_OFF_MILLIS = 1000*60*10; protected static final int POLL_NETSTAT_SCREEN_OFF_MILLIS = 1000*60*10;
// 2 min for round trip time // 2 min for round trip time
protected static final int POLL_LONGEST_RTT = 120 * 1000; 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; protected static final int NUMBER_SENT_PACKETS_OF_HANG = 10;
// how long to wait before switching back to default APN // how long to wait before switching back to default APN
protected static final int RESTORE_DEFAULT_APN_DELAY = 1 * 60 * 1000; protected static final int RESTORE_DEFAULT_APN_DELAY = 1 * 60 * 1000;
@ -210,6 +210,13 @@ public abstract class DataConnectionTracker extends Handler {
// represents an invalid IP address // represents an invalid IP address
protected static final String NULL_IP = "0.0.0.0"; 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 // TODO: See if we can remove INTENT_RECONNECT_ALARM
// having to have different values for GSM and // having to have different values for GSM and
// CDMA. If so we can then remove the need for // CDMA. If so we can then remove the need for
@ -240,11 +247,19 @@ public abstract class DataConnectionTracker extends Handler {
protected long mTxPkts; protected long mTxPkts;
protected long mRxPkts; protected long mRxPkts;
protected long mSentSinceLastRecv;
protected int mNetStatPollPeriod; protected int mNetStatPollPeriod;
protected int mNoRecvPollCount = 0;
protected boolean mNetStatPollEnabled = false; 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 // wifi connection status will be updated by sticky intent
protected boolean mIsWifiConnected = false; protected boolean mIsWifiConnected = false;
@ -313,7 +328,8 @@ public abstract class DataConnectionTracker extends Handler {
} else if (action.startsWith(getActionIntentReconnectAlarm())) { } else if (action.startsWith(getActionIntentReconnectAlarm())) {
log("Reconnect alarm. Previous state was " + mState); log("Reconnect alarm. Previous state was " + mState);
onActionIntentReconnectAlarm(intent); onActionIntentReconnectAlarm(intent);
} else if (action.equals(getActionIntentDataStallAlarm())) {
onActionIntentDataStallAlarm(intent);
} else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
final android.net.NetworkInfo networkInfo = (NetworkInfo) final android.net.NetworkInfo networkInfo = (NetworkInfo)
intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
@ -363,6 +379,71 @@ public abstract class DataConnectionTracker extends Handler {
} }
} }
/**
* 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) { protected boolean isDataSetupCompleteOk(AsyncResult ar) {
if (ar.exception != null) { if (ar.exception != null) {
if (DBG) log("isDataSetupCompleteOk return false, ar.result=" + ar.result); if (DBG) log("isDataSetupCompleteOk return false, ar.result=" + ar.result);
@ -394,6 +475,13 @@ public abstract class DataConnectionTracker extends Handler {
sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA)); 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 * Default constructor
*/ */
@ -529,6 +617,7 @@ public abstract class DataConnectionTracker extends Handler {
// abstract methods // abstract methods
protected abstract String getActionIntentReconnectAlarm(); protected abstract String getActionIntentReconnectAlarm();
protected abstract String getActionIntentDataStallAlarm();
protected abstract void startNetStatPoll(); protected abstract void startNetStatPoll();
protected abstract void stopNetStatPoll(); protected abstract void stopNetStatPoll();
protected abstract void restartRadio(); protected abstract void restartRadio();
@ -553,6 +642,10 @@ public abstract class DataConnectionTracker extends Handler {
protected abstract void onCleanUpAllConnections(String cause); protected abstract void onCleanUpAllConnections(String cause);
protected abstract boolean isDataPossible(String apnType); protected abstract boolean isDataPossible(String apnType);
protected void onDataStallAlarm(int tag) {
loge("onDataStallAlarm: not impleted tag=" + tag);
}
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
switch (msg.what) { switch (msg.what) {
@ -575,6 +668,10 @@ public abstract class DataConnectionTracker extends Handler {
onTrySetupData(reason); onTrySetupData(reason);
break; break;
case EVENT_DATA_STALL_ALARM:
onDataStallAlarm(msg.arg1);
break;
case EVENT_ROAMING_OFF: case EVENT_ROAMING_OFF:
if (getDataOnRoamingEnabled() == false) { if (getDataOnRoamingEnabled() == false) {
resetAllRetryCounts(); resetAllRetryCounts();

View File

@ -69,6 +69,10 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
private static final String INTENT_RECONNECT_ALARM = private static final String INTENT_RECONNECT_ALARM =
"com.android.internal.telephony.cdma-reconnect"; "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: * Constants for the data connection activity:
* physical link down/up * physical link down/up
@ -148,6 +152,11 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
return INTENT_RECONNECT_ALARM; return INTENT_RECONNECT_ALARM;
} }
@Override
protected String getActionIntentDataStallAlarm() {
return INTENT_DATA_STALL_ALARM;
}
@Override @Override
protected void setState(State s) { protected void setState(State s) {
if (DBG) log ("setState: " + s); if (DBG) log ("setState: " + s);

View File

@ -96,22 +96,37 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
private boolean mReregisterOnReconnectFailure = false; private boolean mReregisterOnReconnectFailure = false;
private ContentResolver mResolver; 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 // Recovery action taken in case of data stall
enum RecoveryAction {REREGISTER, RADIO_RESTART, RADIO_RESET}; class RecoveryAction {
private RecoveryAction mRecoveryAction = RecoveryAction.REREGISTER; 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 //***** Constants
private static final int POLL_PDP_MILLIS = 5 * 1000; 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_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 Uri PREFERAPN_URI = Uri.parse("content://telephony/carriers/preferapn");
static final String APN_ID = "apn_id"; static final String APN_ID = "apn_id";
private boolean canSetPreferApn = false; private boolean canSetPreferApn = false;
@ -163,6 +178,11 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
p.getServiceStateTracker().registerForPsRestrictedDisabled(this, p.getServiceStateTracker().registerForPsRestrictedDisabled(this,
EVENT_PS_RESTRICT_DISABLED, null); 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; mDataConnectionTracker = this;
mResolver = mPhone.getContext().getContentResolver(); mResolver = mPhone.getContext().getContentResolver();
@ -241,6 +261,11 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
return INTENT_RECONNECT_ALARM; return INTENT_RECONNECT_ALARM;
} }
@Override
protected String getActionIntentDataStallAlarm() {
return INTENT_DATA_STALL_ALARM;
}
private ApnContext addApnContext(String type) { private ApnContext addApnContext(String type) {
ApnContext apnContext = new ApnContext(type, LOG_TAG); ApnContext apnContext = new ApnContext(type, LOG_TAG);
apnContext.setDependencyMet(false); apnContext.setDependencyMet(false);
@ -552,6 +577,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
*/ */
if (DBG) log ("onDataConnectionDetached: stop polling and notify detached"); if (DBG) log ("onDataConnectionDetached: stop polling and notify detached");
stopNetStatPoll(); stopNetStatPoll();
stopDataStallAlarm();
notifyDataConnection(Phone.REASON_DATA_DETACHED); notifyDataConnection(Phone.REASON_DATA_DETACHED);
} }
@ -560,6 +586,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
if (getOverallState() == State.CONNECTED) { if (getOverallState() == State.CONNECTED) {
if (DBG) log("onDataConnectionAttached: start polling notify attached"); if (DBG) log("onDataConnectionAttached: start polling notify attached");
startNetStatPoll(); startNetStatPoll();
startDataStallAlarm();
notifyDataConnection(Phone.REASON_DATA_ATTACHED); notifyDataConnection(Phone.REASON_DATA_ATTACHED);
} else { } else {
// update APN availability so that APN can be enabled. // update APN availability so that APN can be enabled.
@ -764,6 +791,8 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
} }
stopNetStatPoll(); stopNetStatPoll();
stopDataStallAlarm();
// TODO: Do we need mRequestedApnType? // TODO: Do we need mRequestedApnType?
mRequestedApnType = Phone.APN_TYPE_DEFAULT; mRequestedApnType = Phone.APN_TYPE_DEFAULT;
} }
@ -1238,6 +1267,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
// setState(State.CONNECTED); // setState(State.CONNECTED);
mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType()); mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
startNetStatPoll(); startNetStatPoll();
startDataStallAlarm();
// reset reconnect timer // reset reconnect timer
apnContext.getDataConnection().resetRetryCount(); apnContext.getDataConnection().resetRetryCount();
} }
@ -1252,60 +1282,61 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
private void resetPollStats() { private void resetPollStats() {
mTxPkts = -1; mTxPkts = -1;
mRxPkts = -1; mRxPkts = -1;
mSentSinceLastRecv = 0;
mNetStatPollPeriod = POLL_NETSTAT_MILLIS; mNetStatPollPeriod = POLL_NETSTAT_MILLIS;
mNoRecvPollCount = 0;
} }
private void doRecovery() { private void doRecovery() {
if (getOverallState() == State.CONNECTED) { if (getOverallState() == State.CONNECTED) {
int maxPdpReset = Settings.Secure.getInt(mResolver, // Go through a series of recovery steps, each action transitions to the next action
Settings.Secure.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT, int recoveryAction = getRecoveryAction();
DEFAULT_MAX_PDP_RESET_FAIL); switch (recoveryAction) {
if (mPdpResetCount < maxPdpReset) { case RecoveryAction.GET_DATA_CALL_LIST:
mPdpResetCount++; EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_GET_DATA_CALL_LIST,
EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, mSentSinceLastRecv); mSentSinceLastRecv);
if (DBG) log("doRecovery() cleanup all connections mPdpResetCount < max"); if (DBG) log("doRecovery() get data call list");
cleanUpAllConnections(true, Phone.REASON_PDP_RESET); mPhone.mCM.getDataCallList(obtainMessage(EVENT_DATA_STATE_CHANGED));
} else { putRecoveryAction(RecoveryAction.CLEANUP);
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; break;
case RADIO_RESTART: case RecoveryAction.CLEANUP:
EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, mSentSinceLastRecv); EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_CLEANUP, mSentSinceLastRecv);
if (DBG) log("doRecovery() cleanup all connections");
cleanUpAllConnections(true, Phone.REASON_PDP_RESET);
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"); if (DBG) log("restarting radio");
mRecoveryAction = RecoveryAction.RADIO_RESET; putRecoveryAction(RecoveryAction.RADIO_RESET);
restartRadio(); restartRadio();
break; break;
case RADIO_RESET: case RecoveryAction.RADIO_RESET:
// This is in case radio restart has not recovered the data. // This is in case radio restart has not recovered the data.
// It will set an additional "gsm.radioreset" property to tell // It will set an additional "gsm.radioreset" property to tell
// RIL or system to take further action. // RIL or system to take further action.
// The implementation of hard reset recovery action is up to OEM product. // The implementation of hard reset recovery action is up to OEM product.
// Once gsm.radioreset property is consumed, it is expected to set back // Once gsm.radioreset property is consumed, it is expected to set back
// to false by RIL. // to false by RIL.
EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, -1); EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RESTART_WITH_PROP, -1);
if (DBG) log("restarting radio with reset indication"); if (DBG) log("restarting radio with gsm.radioreset to true");
SystemProperties.set("gsm.radioreset", "true"); SystemProperties.set("gsm.radioreset", "true");
// give 1 sec so property change can be notified. // give 1 sec so property change can be notified.
try { try {
Thread.sleep(1000); Thread.sleep(1000);
} catch (InterruptedException e) {} } catch (InterruptedException e) {}
restartRadio(); restartRadio();
putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
break; break;
default: default:
throw new RuntimeException("doRecovery: Invalid mRecoveryAction " + throw new RuntimeException("doRecovery: Invalid recoveryAction=" +
mRecoveryAction); recoveryAction);
} }
} }
} else {
if (DBG) log("doRecovery(): ignore, we're not connected");
}
} }
@Override @Override
@ -1342,111 +1373,123 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
SystemProperties.set("net.ppp.reset-by-timeout", String.valueOf(reset+1)); SystemProperties.set("net.ppp.reset-by-timeout", String.valueOf(reset+1));
} }
private Runnable mPollNetStat = new Runnable()
{
public void run() { private void updateDataStallInfo() {
long sent, received; long sent, received;
long preTxPkts = -1, preRxPkts = -1;
Activity newActivity; TxRxSum preTxRxSum = new TxRxSum(mDataStallTxRxSum);
mDataStallTxRxSum.updateTxRxSum();
preTxPkts = mTxPkts; if (VDBG) {
preRxPkts = mRxPkts; log("updateDataStallInfo: mDataStallTxRxSum=" + mDataStallTxRxSum +
" preTxRxSum=" + preTxRxSum);
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; sent = mDataStallTxRxSum.txPkts - preTxRxSum.txPkts;
mRxPkts = rxSum; received = mDataStallTxRxSum.rxPkts - preTxRxSum.rxPkts;
// log("tx " + mTxPkts + " rx " + mRxPkts);
if (mNetStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) {
sent = mTxPkts - preTxPkts;
received = mRxPkts - preRxPkts;
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 ( sent > 0 && received > 0 ) {
if (VDBG) log("updateDataStallInfo: IN/OUT");
mSentSinceLastRecv = 0; mSentSinceLastRecv = 0;
newActivity = Activity.DATAINANDOUT; putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
mPdpResetCount = 0;
mRecoveryAction = RecoveryAction.REREGISTER;
} else if (sent > 0 && received == 0) { } else if (sent > 0 && received == 0) {
if (mPhone.getState() == Phone.State.IDLE) { if (mPhone.getState() == Phone.State.IDLE) {
mSentSinceLastRecv += sent; mSentSinceLastRecv += sent;
} else { } else {
mSentSinceLastRecv = 0; 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; newActivity = Activity.DATAOUT;
} else if (sent == 0 && received > 0) { } else if (sent == 0 && received > 0) {
mSentSinceLastRecv = 0;
newActivity = Activity.DATAIN; newActivity = Activity.DATAIN;
mPdpResetCount = 0;
mRecoveryAction = RecoveryAction.REREGISTER;
} else if (sent == 0 && received == 0) {
newActivity = Activity.NONE;
} else { } else {
mSentSinceLastRecv = 0;
newActivity = Activity.NONE; newActivity = Activity.NONE;
} }
if (mActivity != newActivity && mIsScreenOn) { if (mActivity != newActivity && mIsScreenOn) {
if (VDBG) log("updateDataActivity: newActivity=" + newActivity);
mActivity = newActivity; mActivity = newActivity;
mPhone.notifyDataActivity(); 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, private Runnable mPollNetStat = new Runnable()
Settings.Secure.PDP_WATCHDOG_ERROR_POLL_COUNT, NO_RECV_POLL_LIMIT); {
@Override
public void run() {
updateDataActivity();
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));
}
} else {
mNoRecvPollCount = 0;
if (mIsScreenOn) { if (mIsScreenOn) {
mNetStatPollPeriod = Settings.Secure.getInt(mResolver, mNetStatPollPeriod = Settings.Secure.getInt(mResolver,
Settings.Secure.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS); Settings.Secure.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS);
@ -1455,7 +1498,6 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
Settings.Secure.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS, Settings.Secure.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS,
POLL_NETSTAT_SCREEN_OFF_MILLIS); POLL_NETSTAT_SCREEN_OFF_MILLIS);
} }
}
if (mNetStatPollEnabled) { if (mNetStatPollEnabled) {
mDataConnectionTracker.postDelayed(this, mNetStatPollPeriod); mDataConnectionTracker.postDelayed(this, mNetStatPollPeriod);
@ -1566,6 +1608,41 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
} }
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, private void notifyNoData(GsmDataConnection.FailCause lastFailCauseCode,
ApnContext apnContext) { ApnContext apnContext) {
if (DBG) log( "notifyNoData: type=" + apnContext.getApnType()); if (DBG) log( "notifyNoData: type=" + apnContext.getApnType());
@ -1930,6 +2007,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
if (isConnected() && ! mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) { if (isConnected() && ! mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
if (DBG) log("onVoiceCallStarted stop polling"); if (DBG) log("onVoiceCallStarted stop polling");
stopNetStatPoll(); stopNetStatPoll();
stopDataStallAlarm();
notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED); notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
} }
} }
@ -1940,6 +2018,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
if (isConnected()) { if (isConnected()) {
if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) { if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
startNetStatPoll(); startNetStatPoll();
startDataStallAlarm();
notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED); notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
} else { } else {
// clean slate after call end. // clean slate after call end.
@ -2251,11 +2330,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
onPollPdp(); onPollPdp();
break; break;
case EVENT_START_NETSTAT_POLL: case EVENT_DO_RECOVERY:
startNetStatPoll();
break;
case EVENT_START_RECOVERY:
doRecovery(); doRecovery();
break; break;
@ -2272,6 +2347,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
*/ */
if (DBG) log("EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted); if (DBG) log("EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
stopNetStatPoll(); stopNetStatPoll();
stopDataStallAlarm();
mIsPsRestricted = true; mIsPsRestricted = true;
break; break;
@ -2284,6 +2360,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
mIsPsRestricted = false; mIsPsRestricted = false;
if (isConnected()) { if (isConnected()) {
startNetStatPoll(); startNetStatPoll();
startDataStallAlarm();
} else { } else {
// TODO: Should all PDN states be checked to fail? // TODO: Should all PDN states be checked to fail?
if (mState == State.FAILED) { if (mState == State.FAILED) {