Loading src/com/android/server/connectivity/NetworkMonitor.java +35 −12 Original line number Original line Diff line number Diff line Loading @@ -144,6 +144,7 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.List; import java.util.Random; import java.util.Random; import java.util.StringJoiner; import java.util.UUID; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit; Loading Loading @@ -390,15 +391,15 @@ public class NetworkMonitor extends StateMachine { public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network, public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network, SharedLog validationLog) { SharedLog validationLog) { this(context, cb, network, new IpConnectivityLog(), validationLog, this(context, cb, network, new IpConnectivityLog(), validationLog, Dependencies.DEFAULT, new DataStallStatsUtils(), new TcpSocketTracker( Dependencies.DEFAULT, new DataStallStatsUtils(), new TcpSocketTracker.Dependencies(context, getTcpSocketTrackerOrNull(context)); ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q)))); } } @VisibleForTesting @VisibleForTesting public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network, public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network, IpConnectivityLog logger, SharedLog validationLogs, IpConnectivityLog logger, SharedLog validationLogs, Dependencies deps, DataStallStatsUtils detectionStatsUtils, TcpSocketTracker tst) { Dependencies deps, DataStallStatsUtils detectionStatsUtils, @Nullable TcpSocketTracker tst) { // Add suffix indicating which NetworkMonitor we're talking about. // Add suffix indicating which NetworkMonitor we're talking about. super(TAG + "/" + network.toString()); super(TAG + "/" + network.toString()); Loading Loading @@ -758,8 +759,10 @@ public class NetworkMonitor extends StateMachine { } } break; break; case EVENT_POLL_TCPINFO: case EVENT_POLL_TCPINFO: final TcpSocketTracker tst = getTcpSocketTracker(); if (tst == null) break; // Transit if retrieve socket info is succeeded and suspected as a stall. // Transit if retrieve socket info is succeeded and suspected as a stall. if (getTcpSocketTracker().pollSocketsInfo() && evaluateDataStall()) { if (tst.pollSocketsInfo() && evaluateDataStall()) { transitionTo(mEvaluatingState); transitionTo(mEvaluatingState); } else { } else { sendTcpPollingEvent(); sendTcpPollingEvent(); Loading Loading @@ -2114,6 +2117,7 @@ public class NetworkMonitor extends StateMachine { } } @VisibleForTesting @VisibleForTesting @Nullable protected TcpSocketTracker getTcpSocketTracker() { protected TcpSocketTracker getTcpSocketTracker() { return mTcpTracker; return mTcpTracker; } } Loading @@ -2130,6 +2134,7 @@ public class NetworkMonitor extends StateMachine { @VisibleForTesting @VisibleForTesting protected boolean isDataStall() { protected boolean isDataStall() { Boolean result = null; Boolean result = null; final StringJoiner msg = VDBG_STALL ? new StringJoiner(", ") : null; // Reevaluation will generate traffic. Thus, set a minimal reevaluation timer to limit the // Reevaluation will generate traffic. Thus, set a minimal reevaluation timer to limit the // possible traffic cost in metered network. // possible traffic cost in metered network. if (!mNetworkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED) if (!mNetworkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED) Loading @@ -2141,12 +2146,17 @@ public class NetworkMonitor extends StateMachine { // 1. TCP connection fail rate(lost+retrans) is higher than threshold. // 1. TCP connection fail rate(lost+retrans) is higher than threshold. // 2. Accumulate enough packets count. // 2. Accumulate enough packets count. // TODO: Need to filter per target network. // TODO: Need to filter per target network. if (dataStallEvaluateTypeEnabled(DATA_STALL_EVALUATION_TYPE_TCP)) { final TcpSocketTracker tst = getTcpSocketTracker(); if (getTcpSocketTracker().getLatestReceivedCount() > 0) { if (dataStallEvaluateTypeEnabled(DATA_STALL_EVALUATION_TYPE_TCP) && tst != null) { if (tst.getLatestReceivedCount() > 0) { result = false; result = false; } else if (getTcpSocketTracker().isDataStallSuspected()) { } else if (tst.isDataStallSuspected()) { result = true; result = true; } } if (VDBG_STALL) { msg.add("tcp packets received=" + tst.getLatestReceivedCount()) .add("tcp fail rate=" + tst.getLatestPacketFailPercentage()); } } } // Check dns signal. Suspect it may be a data stall if both : // Check dns signal. Suspect it may be a data stall if both : Loading @@ -2158,13 +2168,14 @@ public class NetworkMonitor extends StateMachine { result = true; result = true; logNetworkEvent(NetworkEvent.NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND); logNetworkEvent(NetworkEvent.NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND); } } if (VDBG_STALL) { msg.add("consecutive dns timeout count=" + mDnsStallDetector.getConsecutiveTimeoutCount()); } } } if (VDBG_STALL) { if (VDBG_STALL) { log("isDataStall: result=" + result + ", consecutive dns timeout count=" log("isDataStall: result=" + result + ", " + msg); + mDnsStallDetector.getConsecutiveTimeoutCount() + ", tcp packets received=" + getTcpSocketTracker().getLatestReceivedCount() + ", tcp fail rate=" + getTcpSocketTracker().getLatestPacketFailPercentage()); } } return (result == null) ? false : result; return (result == null) ? false : result; Loading Loading @@ -2298,4 +2309,16 @@ public class NetworkMonitor extends StateMachine { */ */ void log(String s); void log(String s); } } @Nullable private static TcpSocketTracker getTcpSocketTrackerOrNull(Context context) { return ((Dependencies.DEFAULT.getDeviceConfigPropertyInt( NAMESPACE_CONNECTIVITY, CONFIG_DATA_STALL_EVALUATION_TYPE, DEFAULT_DATA_STALL_EVALUATION_TYPES) & DATA_STALL_EVALUATION_TYPE_TCP) != 0) ? new TcpSocketTracker(new TcpSocketTracker.Dependencies(context, ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q))) : null; } } } tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java +14 −0 Original line number Original line Diff line number Diff line Loading @@ -761,6 +761,7 @@ public class NetworkMonitorTest { wrappedMonitor.sendTcpPollingEvent(); wrappedMonitor.sendTcpPollingEvent(); HandlerUtilsKt.waitForIdle(wrappedMonitor.getHandler(), HANDLER_TIMEOUT_MS); HandlerUtilsKt.waitForIdle(wrappedMonitor.getHandler(), HANDLER_TIMEOUT_MS); assertFalse(wrappedMonitor.isDataStall()); assertFalse(wrappedMonitor.isDataStall()); when(mTst.getLatestReceivedCount()).thenReturn(0); when(mTst.getLatestReceivedCount()).thenReturn(0); when(mTst.isDataStallSuspected()).thenReturn(true); when(mTst.isDataStallSuspected()).thenReturn(true); // Trigger a tcp event immediately. // Trigger a tcp event immediately. Loading @@ -770,6 +771,19 @@ public class NetworkMonitorTest { assertTrue(wrappedMonitor.isDataStall()); assertTrue(wrappedMonitor.isDataStall()); } } @Test public void testIsDataStall_DisableTcp() { // Disable tcp detection with only DNS detect. keep the tcp signal but set to no DNS signal. setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS); WrappedNetworkMonitor wrappedMonitor = makeMonitor(METERED_CAPABILITIES); makeDnsSuccessEvent(wrappedMonitor, 1); wrappedMonitor.sendTcpPollingEvent(); HandlerUtilsKt.waitForIdle(wrappedMonitor.getHandler(), HANDLER_TIMEOUT_MS); assertFalse(wrappedMonitor.isDataStall()); verify(mTst, never()).isDataStallSuspected(); verify(mTst, never()).pollSocketsInfo(); } @Test @Test public void testBrokenNetworkNotValidated() throws Exception { public void testBrokenNetworkNotValidated() throws Exception { setSslException(mHttpsConnection); setSslException(mHttpsConnection); Loading Loading
src/com/android/server/connectivity/NetworkMonitor.java +35 −12 Original line number Original line Diff line number Diff line Loading @@ -144,6 +144,7 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.List; import java.util.Random; import java.util.Random; import java.util.StringJoiner; import java.util.UUID; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit; Loading Loading @@ -390,15 +391,15 @@ public class NetworkMonitor extends StateMachine { public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network, public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network, SharedLog validationLog) { SharedLog validationLog) { this(context, cb, network, new IpConnectivityLog(), validationLog, this(context, cb, network, new IpConnectivityLog(), validationLog, Dependencies.DEFAULT, new DataStallStatsUtils(), new TcpSocketTracker( Dependencies.DEFAULT, new DataStallStatsUtils(), new TcpSocketTracker.Dependencies(context, getTcpSocketTrackerOrNull(context)); ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q)))); } } @VisibleForTesting @VisibleForTesting public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network, public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network, IpConnectivityLog logger, SharedLog validationLogs, IpConnectivityLog logger, SharedLog validationLogs, Dependencies deps, DataStallStatsUtils detectionStatsUtils, TcpSocketTracker tst) { Dependencies deps, DataStallStatsUtils detectionStatsUtils, @Nullable TcpSocketTracker tst) { // Add suffix indicating which NetworkMonitor we're talking about. // Add suffix indicating which NetworkMonitor we're talking about. super(TAG + "/" + network.toString()); super(TAG + "/" + network.toString()); Loading Loading @@ -758,8 +759,10 @@ public class NetworkMonitor extends StateMachine { } } break; break; case EVENT_POLL_TCPINFO: case EVENT_POLL_TCPINFO: final TcpSocketTracker tst = getTcpSocketTracker(); if (tst == null) break; // Transit if retrieve socket info is succeeded and suspected as a stall. // Transit if retrieve socket info is succeeded and suspected as a stall. if (getTcpSocketTracker().pollSocketsInfo() && evaluateDataStall()) { if (tst.pollSocketsInfo() && evaluateDataStall()) { transitionTo(mEvaluatingState); transitionTo(mEvaluatingState); } else { } else { sendTcpPollingEvent(); sendTcpPollingEvent(); Loading Loading @@ -2114,6 +2117,7 @@ public class NetworkMonitor extends StateMachine { } } @VisibleForTesting @VisibleForTesting @Nullable protected TcpSocketTracker getTcpSocketTracker() { protected TcpSocketTracker getTcpSocketTracker() { return mTcpTracker; return mTcpTracker; } } Loading @@ -2130,6 +2134,7 @@ public class NetworkMonitor extends StateMachine { @VisibleForTesting @VisibleForTesting protected boolean isDataStall() { protected boolean isDataStall() { Boolean result = null; Boolean result = null; final StringJoiner msg = VDBG_STALL ? new StringJoiner(", ") : null; // Reevaluation will generate traffic. Thus, set a minimal reevaluation timer to limit the // Reevaluation will generate traffic. Thus, set a minimal reevaluation timer to limit the // possible traffic cost in metered network. // possible traffic cost in metered network. if (!mNetworkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED) if (!mNetworkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED) Loading @@ -2141,12 +2146,17 @@ public class NetworkMonitor extends StateMachine { // 1. TCP connection fail rate(lost+retrans) is higher than threshold. // 1. TCP connection fail rate(lost+retrans) is higher than threshold. // 2. Accumulate enough packets count. // 2. Accumulate enough packets count. // TODO: Need to filter per target network. // TODO: Need to filter per target network. if (dataStallEvaluateTypeEnabled(DATA_STALL_EVALUATION_TYPE_TCP)) { final TcpSocketTracker tst = getTcpSocketTracker(); if (getTcpSocketTracker().getLatestReceivedCount() > 0) { if (dataStallEvaluateTypeEnabled(DATA_STALL_EVALUATION_TYPE_TCP) && tst != null) { if (tst.getLatestReceivedCount() > 0) { result = false; result = false; } else if (getTcpSocketTracker().isDataStallSuspected()) { } else if (tst.isDataStallSuspected()) { result = true; result = true; } } if (VDBG_STALL) { msg.add("tcp packets received=" + tst.getLatestReceivedCount()) .add("tcp fail rate=" + tst.getLatestPacketFailPercentage()); } } } // Check dns signal. Suspect it may be a data stall if both : // Check dns signal. Suspect it may be a data stall if both : Loading @@ -2158,13 +2168,14 @@ public class NetworkMonitor extends StateMachine { result = true; result = true; logNetworkEvent(NetworkEvent.NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND); logNetworkEvent(NetworkEvent.NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND); } } if (VDBG_STALL) { msg.add("consecutive dns timeout count=" + mDnsStallDetector.getConsecutiveTimeoutCount()); } } } if (VDBG_STALL) { if (VDBG_STALL) { log("isDataStall: result=" + result + ", consecutive dns timeout count=" log("isDataStall: result=" + result + ", " + msg); + mDnsStallDetector.getConsecutiveTimeoutCount() + ", tcp packets received=" + getTcpSocketTracker().getLatestReceivedCount() + ", tcp fail rate=" + getTcpSocketTracker().getLatestPacketFailPercentage()); } } return (result == null) ? false : result; return (result == null) ? false : result; Loading Loading @@ -2298,4 +2309,16 @@ public class NetworkMonitor extends StateMachine { */ */ void log(String s); void log(String s); } } @Nullable private static TcpSocketTracker getTcpSocketTrackerOrNull(Context context) { return ((Dependencies.DEFAULT.getDeviceConfigPropertyInt( NAMESPACE_CONNECTIVITY, CONFIG_DATA_STALL_EVALUATION_TYPE, DEFAULT_DATA_STALL_EVALUATION_TYPES) & DATA_STALL_EVALUATION_TYPE_TCP) != 0) ? new TcpSocketTracker(new TcpSocketTracker.Dependencies(context, ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q))) : null; } } }
tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java +14 −0 Original line number Original line Diff line number Diff line Loading @@ -761,6 +761,7 @@ public class NetworkMonitorTest { wrappedMonitor.sendTcpPollingEvent(); wrappedMonitor.sendTcpPollingEvent(); HandlerUtilsKt.waitForIdle(wrappedMonitor.getHandler(), HANDLER_TIMEOUT_MS); HandlerUtilsKt.waitForIdle(wrappedMonitor.getHandler(), HANDLER_TIMEOUT_MS); assertFalse(wrappedMonitor.isDataStall()); assertFalse(wrappedMonitor.isDataStall()); when(mTst.getLatestReceivedCount()).thenReturn(0); when(mTst.getLatestReceivedCount()).thenReturn(0); when(mTst.isDataStallSuspected()).thenReturn(true); when(mTst.isDataStallSuspected()).thenReturn(true); // Trigger a tcp event immediately. // Trigger a tcp event immediately. Loading @@ -770,6 +771,19 @@ public class NetworkMonitorTest { assertTrue(wrappedMonitor.isDataStall()); assertTrue(wrappedMonitor.isDataStall()); } } @Test public void testIsDataStall_DisableTcp() { // Disable tcp detection with only DNS detect. keep the tcp signal but set to no DNS signal. setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS); WrappedNetworkMonitor wrappedMonitor = makeMonitor(METERED_CAPABILITIES); makeDnsSuccessEvent(wrappedMonitor, 1); wrappedMonitor.sendTcpPollingEvent(); HandlerUtilsKt.waitForIdle(wrappedMonitor.getHandler(), HANDLER_TIMEOUT_MS); assertFalse(wrappedMonitor.isDataStall()); verify(mTst, never()).isDataStallSuspected(); verify(mTst, never()).pollSocketsInfo(); } @Test @Test public void testBrokenNetworkNotValidated() throws Exception { public void testBrokenNetworkNotValidated() throws Exception { setSslException(mHttpsConnection); setSslException(mHttpsConnection); Loading