Loading src/android/net/util/DataStallUtils.java +6 −5 Original line number Diff line number Diff line Loading @@ -31,11 +31,12 @@ public class DataStallUtils { /** Detect data stall using tcp connection fail rate. */ public static final int DATA_STALL_EVALUATION_TYPE_TCP = 1 << 1; @IntDef(prefix = { "DATA_STALL_EVALUATION_TYPE_" }, value = { @IntDef(prefix = { "DATA_STALL_EVALUATION_TYPE_" }, flag = true, value = { DATA_STALL_EVALUATION_TYPE_NONE, DATA_STALL_EVALUATION_TYPE_DNS, DATA_STALL_EVALUATION_TYPE_TCP, }) DATA_STALL_EVALUATION_TYPE_TCP, }) @Retention(RetentionPolicy.SOURCE) public @interface EvaluationType { } Loading src/com/android/server/connectivity/NetworkMonitor.java +36 −27 Original line number Diff line number Diff line Loading @@ -493,7 +493,8 @@ public class NetworkMonitor extends StateMachine { @Nullable private final DnsStallDetector mDnsStallDetector; private long mLastProbeTime; // The signal causing a data stall to be suspected. Reset to 0 after metrics are sent to statsd. // A bitmask of signals causing a data stall to be suspected. Reset to // {@link DataStallUtils#DATA_STALL_EVALUATION_TYPE_NONE} after metrics are sent to statsd. private @EvaluationType int mDataStallTypeToCollect; private boolean mAcceptPartialConnectivity = false; private final EvaluationState mEvaluationState = new EvaluationState(); Loading Loading @@ -3206,7 +3207,8 @@ public class NetworkMonitor extends StateMachine { return false; } Boolean result = null; int typeToCollect = 0; final int notStall = -1; final StringJoiner msg = (DBG || VDBG_STALL) ? new StringJoiner(", ") : null; // Reevaluation will generate traffic. Thus, set a minimal reevaluation timer to limit the // possible traffic cost in metered network. Loading @@ -3221,18 +3223,9 @@ public class NetworkMonitor extends StateMachine { final TcpSocketTracker tst = getTcpSocketTracker(); if (dataStallEvaluateTypeEnabled(DATA_STALL_EVALUATION_TYPE_TCP) && tst != null) { if (tst.getLatestReceivedCount() > 0) { result = false; typeToCollect = notStall; } else if (tst.isDataStallSuspected()) { result = true; mDataStallTypeToCollect = DATA_STALL_EVALUATION_TYPE_TCP; final DataStallReportParcelable p = new DataStallReportParcelable(); p.detectionMethod = DETECTION_METHOD_TCP_METRICS; p.timestampMillis = SystemClock.elapsedRealtime(); p.tcpPacketFailRate = tst.getLatestPacketFailPercentage(); p.tcpMetricsCollectionPeriodMillis = getTcpPollingInterval(); notifyDataStallSuspected(p); typeToCollect |= DATA_STALL_EVALUATION_TYPE_TCP; } if (DBG || VDBG_STALL) { msg.add("tcp packets received=" + tst.getLatestReceivedCount()) Loading @@ -3244,32 +3237,48 @@ public class NetworkMonitor extends StateMachine { // 1. The number of consecutive DNS query timeouts >= mConsecutiveDnsTimeoutThreshold. // 2. Those consecutive DNS queries happened in the last mValidDataStallDnsTimeThreshold ms. final DnsStallDetector dsd = getDnsStallDetector(); if ((result == null) && (dsd != null) if ((typeToCollect != notStall) && (dsd != null) && dataStallEvaluateTypeEnabled(DATA_STALL_EVALUATION_TYPE_DNS)) { if (dsd.isDataStallSuspected(mConsecutiveDnsTimeoutThreshold, mDataStallValidDnsTimeThreshold)) { result = true; mDataStallTypeToCollect = DATA_STALL_EVALUATION_TYPE_DNS; if (dsd.isDataStallSuspected( mConsecutiveDnsTimeoutThreshold, mDataStallValidDnsTimeThreshold)) { typeToCollect |= DATA_STALL_EVALUATION_TYPE_DNS; logNetworkEvent(NetworkEvent.NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND); } if (DBG || VDBG_STALL) { msg.add("consecutive dns timeout count=" + dsd.getConsecutiveTimeoutCount()); } } if (typeToCollect > 0) { mDataStallTypeToCollect = typeToCollect; final DataStallReportParcelable p = new DataStallReportParcelable(); p.detectionMethod = DETECTION_METHOD_DNS_EVENTS; int detectionMethod = 0; p.timestampMillis = SystemClock.elapsedRealtime(); if (isDataStallTypeDetected(typeToCollect, DATA_STALL_EVALUATION_TYPE_DNS)) { detectionMethod |= DETECTION_METHOD_DNS_EVENTS; p.dnsConsecutiveTimeouts = mDnsStallDetector.getConsecutiveTimeoutCount(); notifyDataStallSuspected(p); } if (DBG || VDBG_STALL) { msg.add("consecutive dns timeout count=" + dsd.getConsecutiveTimeoutCount()); if (isDataStallTypeDetected(typeToCollect, DATA_STALL_EVALUATION_TYPE_TCP)) { detectionMethod |= DETECTION_METHOD_TCP_METRICS; p.tcpPacketFailRate = tst.getLatestPacketFailPercentage(); p.tcpMetricsCollectionPeriodMillis = getTcpPollingInterval(); } p.detectionMethod = detectionMethod; notifyDataStallSuspected(p); } // log only data stall suspected. if ((DBG && Boolean.TRUE.equals(result)) || VDBG_STALL) { log("isDataStall: result=" + result + ", " + msg); if ((DBG && (typeToCollect > 0)) || VDBG_STALL) { log("isDataStall: result=" + typeToCollect + ", " + msg); } return (result == null) ? false : result; return typeToCollect > 0; } private static boolean isDataStallTypeDetected(int typeToCollect, int evaluationType) { return (typeToCollect & evaluationType) != 0; } // Class to keep state of evaluation results and probe results. // // The main purpose was to ensure NetworkMonitor can notify ConnectivityService of probe results Loading tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java +28 −2 Original line number Diff line number Diff line Loading @@ -1618,6 +1618,25 @@ public class NetworkMonitorTest { verify(mCallbacks).notifyDataStallSuspected(matchTcpDataStallParcelable()); } @Test public void testIsDataStall_EvaluationDnsAndTcp() throws Exception { setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS | DATA_STALL_EVALUATION_TYPE_TCP); setupTcpDataStall(); final WrappedNetworkMonitor nm = makeMonitor(CELL_METERED_CAPABILITIES); nm.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); makeDnsTimeoutEvent(nm, DEFAULT_DNS_TIMEOUT_THRESHOLD); assertTrue(nm.isDataStall()); verify(mCallbacks).notifyDataStallSuspected( matchDnsAndTcpDataStallParcelable(DEFAULT_DNS_TIMEOUT_THRESHOLD)); when(mTst.getLatestReceivedCount()).thenReturn(5); // Trigger a tcp event immediately. setTcpPollingInterval(0); nm.sendTcpPollingEvent(); HandlerUtilsKt.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS); assertFalse(nm.isDataStall()); } @Test public void testIsDataStall_DisableTcp() { // Disable tcp detection with only DNS detect. keep the tcp signal but set to no DNS signal. Loading Loading @@ -2657,13 +2676,20 @@ public class NetworkMonitorTest { && Objects.equals(p.redirectUrl, redirectUrl)); } private DataStallReportParcelable matchDnsAndTcpDataStallParcelable(final int timeoutCount) { return argThat(p -> (p.detectionMethod & ConstantsShim.DETECTION_METHOD_DNS_EVENTS) != 0 && (p.detectionMethod & ConstantsShim.DETECTION_METHOD_TCP_METRICS) != 0 && p.dnsConsecutiveTimeouts == timeoutCount); } private DataStallReportParcelable matchDnsDataStallParcelable(final int timeoutCount) { return argThat(p -> p.detectionMethod == ConstantsShim.DETECTION_METHOD_DNS_EVENTS return argThat(p -> (p.detectionMethod & ConstantsShim.DETECTION_METHOD_DNS_EVENTS) != 0 && p.dnsConsecutiveTimeouts == timeoutCount); } private DataStallReportParcelable matchTcpDataStallParcelable() { return argThat(p -> p.detectionMethod == ConstantsShim.DETECTION_METHOD_TCP_METRICS); return argThat(p -> (p.detectionMethod & ConstantsShim.DETECTION_METHOD_TCP_METRICS) != 0); } } Loading
src/android/net/util/DataStallUtils.java +6 −5 Original line number Diff line number Diff line Loading @@ -31,11 +31,12 @@ public class DataStallUtils { /** Detect data stall using tcp connection fail rate. */ public static final int DATA_STALL_EVALUATION_TYPE_TCP = 1 << 1; @IntDef(prefix = { "DATA_STALL_EVALUATION_TYPE_" }, value = { @IntDef(prefix = { "DATA_STALL_EVALUATION_TYPE_" }, flag = true, value = { DATA_STALL_EVALUATION_TYPE_NONE, DATA_STALL_EVALUATION_TYPE_DNS, DATA_STALL_EVALUATION_TYPE_TCP, }) DATA_STALL_EVALUATION_TYPE_TCP, }) @Retention(RetentionPolicy.SOURCE) public @interface EvaluationType { } Loading
src/com/android/server/connectivity/NetworkMonitor.java +36 −27 Original line number Diff line number Diff line Loading @@ -493,7 +493,8 @@ public class NetworkMonitor extends StateMachine { @Nullable private final DnsStallDetector mDnsStallDetector; private long mLastProbeTime; // The signal causing a data stall to be suspected. Reset to 0 after metrics are sent to statsd. // A bitmask of signals causing a data stall to be suspected. Reset to // {@link DataStallUtils#DATA_STALL_EVALUATION_TYPE_NONE} after metrics are sent to statsd. private @EvaluationType int mDataStallTypeToCollect; private boolean mAcceptPartialConnectivity = false; private final EvaluationState mEvaluationState = new EvaluationState(); Loading Loading @@ -3206,7 +3207,8 @@ public class NetworkMonitor extends StateMachine { return false; } Boolean result = null; int typeToCollect = 0; final int notStall = -1; final StringJoiner msg = (DBG || VDBG_STALL) ? new StringJoiner(", ") : null; // Reevaluation will generate traffic. Thus, set a minimal reevaluation timer to limit the // possible traffic cost in metered network. Loading @@ -3221,18 +3223,9 @@ public class NetworkMonitor extends StateMachine { final TcpSocketTracker tst = getTcpSocketTracker(); if (dataStallEvaluateTypeEnabled(DATA_STALL_EVALUATION_TYPE_TCP) && tst != null) { if (tst.getLatestReceivedCount() > 0) { result = false; typeToCollect = notStall; } else if (tst.isDataStallSuspected()) { result = true; mDataStallTypeToCollect = DATA_STALL_EVALUATION_TYPE_TCP; final DataStallReportParcelable p = new DataStallReportParcelable(); p.detectionMethod = DETECTION_METHOD_TCP_METRICS; p.timestampMillis = SystemClock.elapsedRealtime(); p.tcpPacketFailRate = tst.getLatestPacketFailPercentage(); p.tcpMetricsCollectionPeriodMillis = getTcpPollingInterval(); notifyDataStallSuspected(p); typeToCollect |= DATA_STALL_EVALUATION_TYPE_TCP; } if (DBG || VDBG_STALL) { msg.add("tcp packets received=" + tst.getLatestReceivedCount()) Loading @@ -3244,32 +3237,48 @@ public class NetworkMonitor extends StateMachine { // 1. The number of consecutive DNS query timeouts >= mConsecutiveDnsTimeoutThreshold. // 2. Those consecutive DNS queries happened in the last mValidDataStallDnsTimeThreshold ms. final DnsStallDetector dsd = getDnsStallDetector(); if ((result == null) && (dsd != null) if ((typeToCollect != notStall) && (dsd != null) && dataStallEvaluateTypeEnabled(DATA_STALL_EVALUATION_TYPE_DNS)) { if (dsd.isDataStallSuspected(mConsecutiveDnsTimeoutThreshold, mDataStallValidDnsTimeThreshold)) { result = true; mDataStallTypeToCollect = DATA_STALL_EVALUATION_TYPE_DNS; if (dsd.isDataStallSuspected( mConsecutiveDnsTimeoutThreshold, mDataStallValidDnsTimeThreshold)) { typeToCollect |= DATA_STALL_EVALUATION_TYPE_DNS; logNetworkEvent(NetworkEvent.NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND); } if (DBG || VDBG_STALL) { msg.add("consecutive dns timeout count=" + dsd.getConsecutiveTimeoutCount()); } } if (typeToCollect > 0) { mDataStallTypeToCollect = typeToCollect; final DataStallReportParcelable p = new DataStallReportParcelable(); p.detectionMethod = DETECTION_METHOD_DNS_EVENTS; int detectionMethod = 0; p.timestampMillis = SystemClock.elapsedRealtime(); if (isDataStallTypeDetected(typeToCollect, DATA_STALL_EVALUATION_TYPE_DNS)) { detectionMethod |= DETECTION_METHOD_DNS_EVENTS; p.dnsConsecutiveTimeouts = mDnsStallDetector.getConsecutiveTimeoutCount(); notifyDataStallSuspected(p); } if (DBG || VDBG_STALL) { msg.add("consecutive dns timeout count=" + dsd.getConsecutiveTimeoutCount()); if (isDataStallTypeDetected(typeToCollect, DATA_STALL_EVALUATION_TYPE_TCP)) { detectionMethod |= DETECTION_METHOD_TCP_METRICS; p.tcpPacketFailRate = tst.getLatestPacketFailPercentage(); p.tcpMetricsCollectionPeriodMillis = getTcpPollingInterval(); } p.detectionMethod = detectionMethod; notifyDataStallSuspected(p); } // log only data stall suspected. if ((DBG && Boolean.TRUE.equals(result)) || VDBG_STALL) { log("isDataStall: result=" + result + ", " + msg); if ((DBG && (typeToCollect > 0)) || VDBG_STALL) { log("isDataStall: result=" + typeToCollect + ", " + msg); } return (result == null) ? false : result; return typeToCollect > 0; } private static boolean isDataStallTypeDetected(int typeToCollect, int evaluationType) { return (typeToCollect & evaluationType) != 0; } // Class to keep state of evaluation results and probe results. // // The main purpose was to ensure NetworkMonitor can notify ConnectivityService of probe results Loading
tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java +28 −2 Original line number Diff line number Diff line Loading @@ -1618,6 +1618,25 @@ public class NetworkMonitorTest { verify(mCallbacks).notifyDataStallSuspected(matchTcpDataStallParcelable()); } @Test public void testIsDataStall_EvaluationDnsAndTcp() throws Exception { setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS | DATA_STALL_EVALUATION_TYPE_TCP); setupTcpDataStall(); final WrappedNetworkMonitor nm = makeMonitor(CELL_METERED_CAPABILITIES); nm.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); makeDnsTimeoutEvent(nm, DEFAULT_DNS_TIMEOUT_THRESHOLD); assertTrue(nm.isDataStall()); verify(mCallbacks).notifyDataStallSuspected( matchDnsAndTcpDataStallParcelable(DEFAULT_DNS_TIMEOUT_THRESHOLD)); when(mTst.getLatestReceivedCount()).thenReturn(5); // Trigger a tcp event immediately. setTcpPollingInterval(0); nm.sendTcpPollingEvent(); HandlerUtilsKt.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS); assertFalse(nm.isDataStall()); } @Test public void testIsDataStall_DisableTcp() { // Disable tcp detection with only DNS detect. keep the tcp signal but set to no DNS signal. Loading Loading @@ -2657,13 +2676,20 @@ public class NetworkMonitorTest { && Objects.equals(p.redirectUrl, redirectUrl)); } private DataStallReportParcelable matchDnsAndTcpDataStallParcelable(final int timeoutCount) { return argThat(p -> (p.detectionMethod & ConstantsShim.DETECTION_METHOD_DNS_EVENTS) != 0 && (p.detectionMethod & ConstantsShim.DETECTION_METHOD_TCP_METRICS) != 0 && p.dnsConsecutiveTimeouts == timeoutCount); } private DataStallReportParcelable matchDnsDataStallParcelable(final int timeoutCount) { return argThat(p -> p.detectionMethod == ConstantsShim.DETECTION_METHOD_DNS_EVENTS return argThat(p -> (p.detectionMethod & ConstantsShim.DETECTION_METHOD_DNS_EVENTS) != 0 && p.dnsConsecutiveTimeouts == timeoutCount); } private DataStallReportParcelable matchTcpDataStallParcelable() { return argThat(p -> p.detectionMethod == ConstantsShim.DETECTION_METHOD_TCP_METRICS); return argThat(p -> (p.detectionMethod & ConstantsShim.DETECTION_METHOD_TCP_METRICS) != 0); } }