diff options
6 files changed, 151 insertions, 87 deletions
diff --git a/common/netlinkclient/src/android/net/netlink/NetlinkConstants.java b/common/netlinkclient/src/android/net/netlink/NetlinkConstants.java index 181a361..b8fd4b1 100644 --- a/common/netlinkclient/src/android/net/netlink/NetlinkConstants.java +++ b/common/netlinkclient/src/android/net/netlink/NetlinkConstants.java @@ -35,6 +35,14 @@ public class NetlinkConstants { private NetlinkConstants() {} public static final int NLA_ALIGNTO = 4; + /** + * Flag for dumping struct tcp_info. + * Corresponding to enum definition in external/strace/linux/inet_diag.h. + */ + public static final int INET_DIAG_MEMINFO = 1; + + public static final int SOCKDIAG_MSG_HEADER_SIZE = + StructNlMsgHdr.STRUCT_SIZE + StructInetDiagMsg.STRUCT_SIZE; public static final int alignedLengthOf(short length) { final int intLength = (int) length & 0xffff; diff --git a/src/android/net/util/DataStallUtils.java b/src/android/net/util/DataStallUtils.java index 454faf6..5787879 100644 --- a/src/android/net/util/DataStallUtils.java +++ b/src/android/net/util/DataStallUtils.java @@ -98,7 +98,7 @@ public class DataStallUtils { * Type: int * Valid values: 0 to 100. */ - public static final String CONFIG_TCP_PACKETS_FAIL_RATE = "tcp_packets_fail_rate"; + public static final String CONFIG_TCP_PACKETS_FAIL_PERCENTAGE = "tcp_packets_fail_percentage"; /** Corresponds to enum from bionic/libc/include/netinet/tcp.h. */ public static final int TCP_ESTABLISHED = 1; diff --git a/src/com/android/networkstack/netlink/TcpSocketTracker.java b/src/com/android/networkstack/netlink/TcpSocketTracker.java index 8eb81b2..fc762cb 100644 --- a/src/com/android/networkstack/netlink/TcpSocketTracker.java +++ b/src/com/android/networkstack/netlink/TcpSocketTracker.java @@ -16,11 +16,14 @@ package com.android.networkstack.netlink; import static android.net.netlink.InetDiagMessage.InetDiagReqV2; +import static android.net.netlink.NetlinkConstants.INET_DIAG_MEMINFO; +import static android.net.netlink.NetlinkConstants.NLA_ALIGNTO; import static android.net.netlink.NetlinkConstants.NLMSG_DONE; +import static android.net.netlink.NetlinkConstants.SOCKDIAG_MSG_HEADER_SIZE; import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP; import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST; import static android.net.util.DataStallUtils.CONFIG_MIN_PACKETS_THRESHOLD; -import static android.net.util.DataStallUtils.CONFIG_TCP_PACKETS_FAIL_RATE; +import static android.net.util.DataStallUtils.CONFIG_TCP_PACKETS_FAIL_PERCENTAGE; import static android.net.util.DataStallUtils.DEFAULT_DATA_STALL_MIN_PACKETS_THRESHOLD; import static android.net.util.DataStallUtils.DEFAULT_TCP_PACKETS_FAIL_PERCENTAGE; import static android.net.util.DataStallUtils.TCP_MONITOR_STATE_FILTER; @@ -35,13 +38,15 @@ import static android.system.OsConstants.SOCK_DGRAM; import static android.system.OsConstants.SOL_SOCKET; import static android.system.OsConstants.SO_SNDTIMEO; +import android.content.Context; import android.net.netlink.NetlinkSocket; import android.net.netlink.StructInetDiagMsg; import android.net.netlink.StructNlMsgHdr; import android.net.util.NetworkStackUtils; import android.net.util.SocketUtils; -import android.os.Build; +import android.os.AsyncTask; import android.os.SystemClock; +import android.provider.DeviceConfig; import android.system.ErrnoException; import android.system.Os; import android.system.StructTimeval; @@ -53,7 +58,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; -import com.android.networkstack.apishim.ShimUtils; import java.io.FileDescriptor; import java.io.InterruptedIOException; @@ -65,7 +69,7 @@ import java.util.List; /** * Class for NetworkStack to send a SockDiag request and parse the returned tcp info. * - * This should be only access from the NetworkMonitor statemahcine thread. + * This is not thread-safe. This should be only accessed from one thread. */ public class TcpSocketTracker { private static final String TAG = "TcpSocketTracker"; @@ -75,16 +79,6 @@ public class TcpSocketTracker { private static final int DEFAULT_RECV_BUFSIZE = 60_000; // Default I/O timeout time in ms of the socket request. private static final long IO_TIMEOUT = 3_000L; - // Map to definition in bionic/libc/kernel/uapi/linux/netlink.h. - private static final int NLMSG_ALIGNTO = 4; - /** - * Flag for dumping struct tcp_info. - * Corresponding to enum definition in external/strace/linux/inet_diag.h. - */ - private static final int INET_DIAG_MEMINFO = 1; - @VisibleForTesting - public static final int SOCKDIAG_MSG_HEADER_SIZE = - StructNlMsgHdr.STRUCT_SIZE + StructInetDiagMsg.STRUCT_SIZE; /** Cookie offset of an InetMagMessage header. */ private static final int IDIAG_COOKIE_OFFSET = 44; /** @@ -98,7 +92,9 @@ public class TcpSocketTracker { // Number of packets sent since the last received packet private int mSentSinceLastRecv; // The latest fail rate calculated by the latest tcp info. - private int mLatestPacketFailRate; + private int mLatestPacketFailPercentage; + // Number of packets received in the latest polling cycle. + private int mLatestReceivedCount; /** * Request to send to kernel to request tcp info. * @@ -106,15 +102,30 @@ public class TcpSocketTracker { * Value: Bytes array represent the {@Code InetDiagReqV2}. */ private final SparseArray<byte[]> mSockDiagMsg = new SparseArray<>(); + private final Dependencies mDependencies; + private int mMinPacketsThreshold = DEFAULT_DATA_STALL_MIN_PACKETS_THRESHOLD; + private int mTcpPacketsFailRateThreshold = DEFAULT_TCP_PACKETS_FAIL_PERCENTAGE; @VisibleForTesting - public final Dependencies mDependencies; + protected final DeviceConfig.OnPropertiesChangedListener mConfigListener = + new DeviceConfig.OnPropertiesChangedListener() { + @Override + public void onPropertiesChanged(DeviceConfig.Properties properties) { + mMinPacketsThreshold = mDependencies.getDeviceConfigPropertyInt( + NAMESPACE_CONNECTIVITY, + CONFIG_MIN_PACKETS_THRESHOLD, + DEFAULT_DATA_STALL_MIN_PACKETS_THRESHOLD); + mTcpPacketsFailRateThreshold = mDependencies.getDeviceConfigPropertyInt( + NAMESPACE_CONNECTIVITY, + CONFIG_TCP_PACKETS_FAIL_PERCENTAGE, + DEFAULT_TCP_PACKETS_FAIL_PERCENTAGE); + } + }; - public TcpSocketTracker(Dependencies dps) { + public TcpSocketTracker(@NonNull final Dependencies dps) { + mDependencies = dps; // Request tcp info from NetworkStack directly needs extra SELinux permission added after Q // release. - mDependencies = dps; if (!mDependencies.isTcpInfoParsingSupported()) return; - // Build SocketDiag messages. for (final int family : ADDRESS_FAMILIES) { mSockDiagMsg.put( @@ -128,14 +139,14 @@ public class TcpSocketTracker { 1 << INET_DIAG_MEMINFO /* idiagExt */, TCP_MONITOR_STATE_FILTER)); } + mDependencies.addDeviceConfigChangedListener(mConfigListener); } /** * Request to send a SockDiag Netlink request. Receive and parse the returned message. This - * function should only be called from statemachine thread of NetworkMonitor. + * function is not thread-safe and should only be called from only one thread. * * @Return if this polling request executes successfully or not. - * * TODO: Need to filter socket info based on the target network. */ public boolean pollSocketsInfo() { @@ -143,7 +154,7 @@ public class TcpSocketTracker { FileDescriptor fd = null; try { final long time = SystemClock.elapsedRealtime(); - fd = mDependencies.connectToKernel(); + fd = mDependencies.connectToKernel(); final TcpStat stat = new TcpStat(); for (final int family : ADDRESS_FAMILIES) { @@ -163,6 +174,10 @@ public class TcpSocketTracker { while (enoughBytesRemainForValidNlMsg(bytes)) { final StructNlMsgHdr nlmsghdr = StructNlMsgHdr.parse(bytes); + if (nlmsghdr == null) { + Log.e(TAG, "Badly formatted data."); + break; + } final int nlmsgLen = nlmsghdr.nlmsg_len; log("pollSocketsInfo: nlmsghdr=" + nlmsghdr); if (nlmsghdr.nlmsg_type == NLMSG_DONE) break; @@ -175,19 +190,20 @@ public class TcpSocketTracker { // It's stored in native with 2 int. Parse it as long for convenience. final long cookie = bytes.getLong(); // Skip the rest part of StructInetDiagMsg. - bytes.position(bytes.position() + 5 * Integer.BYTES); - final SocketInfo info = - parseSockInfo(bytes, family, nlmsgLen, time); + bytes.position(bytes.position() + + StructInetDiagMsg.STRUCT_SIZE - IDIAG_COOKIE_OFFSET - Long.BYTES); + final SocketInfo info = parseSockInfo(bytes, family, nlmsgLen, time); // Update TcpStats based on previous and current socket info. stat.accumulate(calculateLatestPacketsStat(info, mSocketInfos.get(cookie))); mSocketInfos.put(cookie, info); } } } - // Calculate mSentSinceLastRecv and mLatestPacketFailRate. + // Calculate mLatestReceiveCount, mSentSinceLastRecv and mLatestPacketFailPercentage. mSentSinceLastRecv = (stat.receivedCount == 0) ? (mSentSinceLastRecv + stat.sentCount) : 0; - mLatestPacketFailRate = ((stat.sentCount != 0) + mLatestReceivedCount = stat.receivedCount; + mLatestPacketFailPercentage = ((stat.sentCount != 0) ? ((stat.retransmitCount + stat.lostCount) * 100 / stat.sentCount) : 0); // Remove out-of-date socket info. @@ -252,20 +268,25 @@ public class TcpSocketTracker { */ public boolean isDataStallSuspected() { if (!mDependencies.isTcpInfoParsingSupported()) return false; - return (getLatestPacketFailRate() >= getTcpPacketsFailRateThreshold()); + return (getLatestPacketFailPercentage() >= getTcpPacketsFailRateThreshold()); } /** Calculate the change between the {@param current} and {@param previous}. */ + @Nullable private TcpStat calculateLatestPacketsStat(@NonNull final SocketInfo current, @Nullable final SocketInfo previous) { final TcpStat stat = new TcpStat(); - if (current.tcpInfo != null) { - stat.sentCount = current.tcpInfo.getValue(TcpInfo.Field.SEGS_OUT).intValue(); - stat.receivedCount = current.tcpInfo.getValue(TcpInfo.Field.SEGS_IN).intValue(); - stat.lostCount = current.tcpInfo.getValue(TcpInfo.Field.LOST).intValue(); - stat.retransmitCount = current.tcpInfo.getValue(TcpInfo.Field.RETRANSMITS).intValue(); + if (current.tcpInfo == null) { + log("Current tcpInfo is null."); + return null; } + + stat.sentCount = current.tcpInfo.getValue(TcpInfo.Field.SEGS_OUT).intValue(); + stat.receivedCount = current.tcpInfo.getValue(TcpInfo.Field.SEGS_IN).intValue(); + stat.lostCount = current.tcpInfo.getValue(TcpInfo.Field.LOST).intValue(); + stat.retransmitCount = current.tcpInfo.getValue(TcpInfo.Field.RETRANSMITS).intValue(); + if (previous != null && previous.tcpInfo != null) { stat.sentCount -= previous.tcpInfo.getValue(TcpInfo.Field.SEGS_OUT).intValue(); stat.receivedCount -= previous.tcpInfo.getValue(TcpInfo.Field.SEGS_IN).intValue(); @@ -278,12 +299,14 @@ public class TcpSocketTracker { /** * Get tcp connection fail rate based on packet lost and retransmission count. + * + * @return the latest packet fail percentage. -1 denotes that there is no available data. */ - public int getLatestPacketFailRate() { - if (!mDependencies.isTcpInfoParsingSupported()) return 0; + public int getLatestPacketFailPercentage() { + if (!mDependencies.isTcpInfoParsingSupported()) return -1; // Only return fail rate if device sent enough packets. - if (getSentSinceLastRecv() < getMinPacketsThreshold()) return 0; - return mLatestPacketFailRate; + if (getSentSinceLastRecv() < getMinPacketsThreshold()) return -1; + return mLatestPacketFailPercentage; } /** @@ -291,18 +314,14 @@ public class TcpSocketTracker { * between each polling period, not an accurate number. */ public int getSentSinceLastRecv() { - if (!mDependencies.isTcpInfoParsingSupported()) return 0; + if (!mDependencies.isTcpInfoParsingSupported()) return -1; return mSentSinceLastRecv; } - private int getMinPacketsThreshold() { - return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY, - CONFIG_MIN_PACKETS_THRESHOLD, DEFAULT_DATA_STALL_MIN_PACKETS_THRESHOLD); - } - - private int getTcpPacketsFailRateThreshold() { - return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY, - CONFIG_TCP_PACKETS_FAIL_RATE, DEFAULT_TCP_PACKETS_FAIL_PERCENTAGE); + /** Return the number of the packets received in the latest polling cycle. */ + public int getLatestReceivedCount() { + if (!mDependencies.isTcpInfoParsingSupported()) return -1; + return mLatestReceivedCount; } /** Check if the length and position of the given ByteBuffer is valid for a nlmsghdr message. */ @@ -315,6 +334,14 @@ public class TcpSocketTracker { return nlMsgLen >= SOCKDIAG_MSG_HEADER_SIZE; } + private int getMinPacketsThreshold() { + return mMinPacketsThreshold; + } + + private int getTcpPacketsFailRateThreshold() { + return mTcpPacketsFailRateThreshold; + } + /** * Method to skip the remaining attributes bytes. * Corresponds to NLMSG_NEXT in bionic/libc/kernel/uapi/linux/netlink.h. @@ -324,12 +351,12 @@ public class TcpSocketTracker { */ private void skipRemainingAttributesBytesAligned(@NonNull final ByteBuffer buffer, final int len) { - // Data in {@Code RoutingAttribute} is followed after header with size {@Code NLMSG_ALIGNTO} + // Data in {@Code RoutingAttribute} is followed after header with size {@Code NLA_ALIGNTO} // bytes long for each block. Next attribute will start after the padding bytes if any. // If all remaining bytes after header are valid in a data block, next attr will just start // after valid bytes. // - // E.g. With NLMSG_ALIGNTO(4), an attr struct with length 5 means 1 byte valid data remains + // E.g. With NLA_ALIGNTO(4), an attr struct with length 5 means 1 byte valid data remains // after header and 3(4-1) padding bytes. Next attr with length 8 will start after the // padding bytes and contain 4(8-4) valid bytes of data. The next attr start after the // valid bytes, like: @@ -337,7 +364,7 @@ public class TcpSocketTracker { // [HEADER(L=5)][ 4-Bytes DATA ][ HEADER(L=8) ][4 bytes DATA][Next attr] // [ 5 valid bytes ][3 padding bytes ][ 8 valid bytes ] ... final int cur = buffer.position(); - buffer.position(cur + ((len + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1))); + buffer.position(cur + ((len + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))); } private void log(final String str) { @@ -423,7 +450,9 @@ public class TcpSocketTracker { public int retransmitCount; public int receivedCount; - void accumulate(final TcpStat stat) { + void accumulate(@Nullable final TcpStat stat) { + if (stat == null) return; + sentCount += stat.sentCount; lostCount += stat.lostCount; receivedCount += stat.receivedCount; @@ -431,12 +460,19 @@ public class TcpSocketTracker { } } - /** * Dependencies class for testing. */ @VisibleForTesting public static class Dependencies { + private final Context mContext; + private final boolean mIsTcpInfoParsingSupported; + + public Dependencies(final Context context, final boolean tcpSupport) { + mContext = context; + mIsTcpInfoParsingSupported = tcpSupport; + } + /** * Connect to kernel via netlink socket. * @@ -451,6 +487,7 @@ public class TcpSocketTracker { return fd; } + /** * Send composed message request to kernel. * @param fd see {@Code FileDescriptor} @@ -484,7 +521,7 @@ public class TcpSocketTracker { public boolean isTcpInfoParsingSupported() { // Request tcp info from NetworkStack directly needs extra SELinux permission added // after Q release. - return ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q); + return mIsTcpInfoParsingSupported; } /** @@ -494,5 +531,16 @@ public class TcpSocketTracker { throws ErrnoException, InterruptedIOException { return NetlinkSocket.recvMessage(fd, DEFAULT_RECV_BUFSIZE, IO_TIMEOUT); } + + public Context getContext() { + return mContext; + } + + /** Add device config change listener */ + public void addDeviceConfigChangedListener( + @NonNull final DeviceConfig.OnPropertiesChangedListener listener) { + DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_CONNECTIVITY, + AsyncTask.THREAD_POOL_EXECUTOR, listener); + } } } diff --git a/src/com/android/server/connectivity/NetworkMonitor.java b/src/com/android/server/connectivity/NetworkMonitor.java index 63c294c..3dfe004 100644 --- a/src/com/android/server/connectivity/NetworkMonitor.java +++ b/src/com/android/server/connectivity/NetworkMonitor.java @@ -126,6 +126,7 @@ import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.internal.util.TrafficStatsConstants; import com.android.networkstack.R; +import com.android.networkstack.apishim.ShimUtils; import com.android.networkstack.metrics.DataStallDetectionStats; import com.android.networkstack.metrics.DataStallStatsUtils; import com.android.networkstack.netlink.TcpSocketTracker; @@ -389,13 +390,15 @@ public class NetworkMonitor extends StateMachine { public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network, SharedLog validationLog) { this(context, cb, network, new IpConnectivityLog(), validationLog, - Dependencies.DEFAULT, new DataStallStatsUtils()); + Dependencies.DEFAULT, new DataStallStatsUtils(), new TcpSocketTracker( + new TcpSocketTracker.Dependencies(context, + ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q)))); } @VisibleForTesting public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network, IpConnectivityLog logger, SharedLog validationLogs, - Dependencies deps, DataStallStatsUtils detectionStatsUtils) { + Dependencies deps, DataStallStatsUtils detectionStatsUtils, TcpSocketTracker tst) { // Add suffix indicating which NetworkMonitor we're talking about. super(TAG + "/" + network.toString()); @@ -442,7 +445,7 @@ public class NetworkMonitor extends StateMachine { mDataStallMinEvaluateTime = getDataStallMinEvaluateTime(); mDataStallValidDnsTimeThreshold = getDataStallValidDnsTimeThreshold(); mDataStallEvaluationType = getDataStallEvaluationType(); - mTcpTracker = new TcpSocketTracker(new TcpSocketTracker.Dependencies()); + mTcpTracker = tst; // Provide empty LinkProperties and NetworkCapabilities to make sure they are never null, // even before notifyNetworkConnected. @@ -2139,7 +2142,7 @@ public class NetworkMonitor extends StateMachine { // 2. Accumulate enough packets count. // TODO: Need to filter per target network. if (dataStallEvaluateTypeEnabled(DATA_STALL_EVALUATION_TYPE_TCP)) { - if (getTcpSocketTracker().getSentSinceLastRecv() > 0) { + if (getTcpSocketTracker().getLatestReceivedCount() > 0) { result = false; } else if (getTcpSocketTracker().isDataStallSuspected()) { result = true; @@ -2160,8 +2163,8 @@ public class NetworkMonitor extends StateMachine { if (VDBG_STALL) { log("isDataStall: result=" + result + ", consecutive dns timeout count=" + mDnsStallDetector.getConsecutiveTimeoutCount() - + ", tcp packets received=" + getTcpSocketTracker().getSentSinceLastRecv() - + ", tcp fail rate=" + getTcpSocketTracker().getLatestPacketFailRate()); + + ", tcp packets received=" + getTcpSocketTracker().getLatestReceivedCount() + + ", tcp fail rate=" + getTcpSocketTracker().getLatestPacketFailPercentage()); } return (result == null) ? false : result; diff --git a/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java b/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java index aa94b76..6a92d8a 100644 --- a/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java +++ b/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java @@ -16,13 +16,12 @@ package com.android.networkstack.netlink; -import static android.net.util.DataStallUtils.CONFIG_TCP_PACKETS_FAIL_RATE; +import static android.net.netlink.NetlinkConstants.SOCKDIAG_MSG_HEADER_SIZE; +import static android.net.util.DataStallUtils.CONFIG_TCP_PACKETS_FAIL_PERCENTAGE; import static android.net.util.DataStallUtils.DEFAULT_TCP_PACKETS_FAIL_PERCENTAGE; import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; import static android.system.OsConstants.AF_INET; -import static com.android.networkstack.netlink.TcpSocketTracker.SOCKDIAG_MSG_HEADER_SIZE; - import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; @@ -111,7 +110,7 @@ public class TcpSocketTrackerTest { "00000000" + // data "0008" + // len = 8 "000F" + // type = 15(INET_DIAG_MARK) - "000C0064" + // data, socket mark=786532 + "000C1A85" + // data, socket mark=793221 "00AC" + // len = 172 "0002" + // type = 2(INET_DIAG_INFO) // tcp_info @@ -129,7 +128,7 @@ public class TcpSocketTrackerTest { "00000218" + // rcvMss = 536 "00000000" + // unsacked = 0 "00000000" + // acked = 0 - "00000005" + // lost = 5 + "00000000" + // lost = 0 "00000000" + // retrans = 0 "00000000" + // fackets = 0 "000000BB" + // lastDataSent = 187 @@ -185,7 +184,7 @@ public class TcpSocketTrackerTest { when(mDependencies.connectToKernel()).thenReturn(mMockFd); when(mDependencies.getDeviceConfigPropertyInt( eq(NAMESPACE_CONNECTIVITY), - eq(CONFIG_TCP_PACKETS_FAIL_RATE), + eq(CONFIG_TCP_PACKETS_FAIL_PERCENTAGE), anyInt())).thenReturn(DEFAULT_TCP_PACKETS_FAIL_PERCENTAGE); } @@ -211,7 +210,7 @@ public class TcpSocketTrackerTest { expected.put(TcpInfo.Field.RCV_MSS, 536); expected.put(TcpInfo.Field.UNACKED, 0); expected.put(TcpInfo.Field.SACKED, 0); - expected.put(TcpInfo.Field.LOST, 5); + expected.put(TcpInfo.Field.LOST, 0); expected.put(TcpInfo.Field.RETRANS, 0); expected.put(TcpInfo.Field.FACKETS, 0); expected.put(TcpInfo.Field.LAST_DATA_SENT, 187); @@ -242,7 +241,7 @@ public class TcpSocketTrackerTest { expected.put(TcpInfo.Field.DELIVERY_RATE, 0L); assertEquals(parsed.tcpInfo, new TcpInfo(expected)); - assertEquals(parsed.fwmark, 786532); + assertEquals(parsed.fwmark, 793221); assertEquals(parsed.updateTime, 100); assertEquals(parsed.ipFamily, AF_INET); } @@ -262,18 +261,6 @@ public class TcpSocketTrackerTest { } @Test - public void testIsDataStallSuspected() { - when(mDependencies.isTcpInfoParsingSupported()).thenReturn(false); - final TcpSocketTracker tst = new TcpSocketTracker(mDependencies); - assertFalse(tst.isDataStallSuspected()); - when(mDependencies.isTcpInfoParsingSupported()).thenReturn(true); - assertFalse(tst.isDataStallSuspected()); - when(mDependencies.getDeviceConfigPropertyInt(any(), eq(CONFIG_TCP_PACKETS_FAIL_RATE), - anyInt())).thenReturn(0); - assertTrue(tst.isDataStallSuspected()); - } - - @Test public void testPollSocketsInfo() throws Exception { when(mDependencies.isTcpInfoParsingSupported()).thenReturn(false); final TcpSocketTracker tst = new TcpSocketTracker(mDependencies); @@ -284,20 +271,30 @@ public class TcpSocketTrackerTest { final ByteBuffer invalidBuffer = ByteBuffer.allocate(1); when(mDependencies.recvMesssage(any())).thenReturn(invalidBuffer); assertTrue(tst.pollSocketsInfo()); - assertEquals(0, tst.getLatestPacketFailRate()); + assertEquals(-1, tst.getLatestPacketFailPercentage()); assertEquals(0, tst.getSentSinceLastRecv()); // Header only. final ByteBuffer headerBuffer = ByteBuffer.wrap(SOCK_DIAG_MSG_BYTES); when(mDependencies.recvMesssage(any())).thenReturn(headerBuffer); assertTrue(tst.pollSocketsInfo()); + assertEquals(-1, tst.getLatestPacketFailPercentage()); assertEquals(0, tst.getSentSinceLastRecv()); - assertEquals(0, tst.getLatestPacketFailRate()); final ByteBuffer tcpBuffer = ByteBuffer.wrap(TEST_RESPONSE_BYTES); when(mDependencies.recvMesssage(any())).thenReturn(tcpBuffer); assertTrue(tst.pollSocketsInfo()); + assertEquals(10, tst.getSentSinceLastRecv()); - assertEquals(100, tst.getLatestPacketFailRate()); + assertEquals(50, tst.getLatestPacketFailPercentage()); + assertFalse(tst.isDataStallSuspected()); + // Lower the threshold. + when(mDependencies.getDeviceConfigPropertyInt(any(), eq(CONFIG_TCP_PACKETS_FAIL_PERCENTAGE), + anyInt())).thenReturn(40); + // No device config change. Using cache value. + assertFalse(tst.isDataStallSuspected()); + // Trigger a config update + tst.mConfigListener.onPropertiesChanged(null /* properties */); + assertTrue(tst.isDataStallSuspected()); } } diff --git a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java index 21dfb2f..75491ec 100644 --- a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java +++ b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java @@ -154,7 +154,7 @@ public class NetworkMonitorTest { private @Mock WifiInfo mWifiInfo; private @Captor ArgumentCaptor<String> mNetworkTestedRedirectUrlCaptor; private @Mock TcpSocketTracker.Dependencies mTstDependencies; - + private @Mock TcpSocketTracker mTst; private HashSet<WrappedNetworkMonitor> mCreatedNetworkMonitors; private HashSet<BroadcastReceiver> mRegisteredReceivers; @@ -449,11 +449,10 @@ public class NetworkMonitorTest { private class WrappedNetworkMonitor extends NetworkMonitor { private long mProbeTime = 0; private final ConditionVariable mQuitCv = new ConditionVariable(false); - private final TcpSocketTracker mTst = new TcpSocketTracker(mTstDependencies); WrappedNetworkMonitor() { super(mContext, mCallbacks, mNetwork, mLogger, mValidationLogger, - mDependencies, mDataStallStatsUtils); + mDependencies, mDataStallStatsUtils, mTst); } @Override @@ -494,6 +493,7 @@ public class NetworkMonitorTest { HandlerUtilsKt.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS); mCreatedNetworkMonitors.add(nm); when(mTstDependencies.isTcpInfoParsingSupported()).thenReturn(false); + return nm; } @@ -753,8 +753,16 @@ public class NetworkMonitorTest { wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); assertFalse(wrappedMonitor.isDataStall()); - - when(wrappedMonitor.getTcpSocketTracker().isDataStallSuspected()).thenReturn(true); + // Packet received. + when(mTstDependencies.isTcpInfoParsingSupported()).thenReturn(true); + when(mTst.getLatestReceivedCount()).thenReturn(5); + // Trigger a tcp event immediately. + setTcpPollingInterval(0); + wrappedMonitor.sendTcpPollingEvent(); + HandlerUtilsKt.waitForIdle(wrappedMonitor.getHandler(), HANDLER_TIMEOUT_MS); + assertFalse(wrappedMonitor.isDataStall()); + when(mTst.getLatestReceivedCount()).thenReturn(0); + when(mTst.isDataStallSuspected()).thenReturn(true); // Trigger a tcp event immediately. setTcpPollingInterval(0); wrappedMonitor.sendTcpPollingEvent(); |