Loading src/com/android/networkstack/netlink/TcpInfo.java +43 −89 Original line number Diff line number Diff line Loading @@ -22,11 +22,9 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import java.nio.BufferOverflowException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; /** Loading Loading @@ -91,27 +89,39 @@ public class TcpInfo { } private static final String TAG = "TcpInfo"; private final Map<Field, Number> mFieldsValues; @VisibleForTesting static final int LOST_OFFSET = getFieldOffset(Field.LOST); @VisibleForTesting static final int RETRANSMITS_OFFSET = getFieldOffset(Field.RETRANSMITS); @VisibleForTesting static final int SEGS_IN_OFFSET = getFieldOffset(Field.SEGS_IN); @VisibleForTesting static final int SEGS_OUT_OFFSET = getFieldOffset(Field.SEGS_OUT); final int mSegsIn; final int mSegsOut; final int mLost; final int mRetransmits; private static int getFieldOffset(@NonNull final Field needle) { int offset = 0; for (final Field field : Field.values()) { if (field == needle) return offset; offset += field.size; } throw new IllegalArgumentException("Unknown field"); } private TcpInfo(@NonNull ByteBuffer bytes, int infolen) { // SEGS_IN is the last required field in the buffer, so if the buffer is long enough for // SEGS_IN it's long enough for everything if (SEGS_IN_OFFSET + Field.SEGS_IN.size > infolen) { throw new IllegalArgumentException("Length " + infolen + " is less than required."); } final int start = bytes.position(); final LinkedHashMap<Field, Number> fields = new LinkedHashMap<>(); for (final Field field : Field.values()) { switch (field.size) { case Byte.BYTES: fields.put(field, getByte(bytes, start, infolen)); break; case Integer.BYTES: fields.put(field, getInt(bytes, start, infolen)); break; case Long.BYTES: fields.put(field, getLong(bytes, start, infolen)); break; default: Log.e(TAG, "Unexpected size:" + field.size); } } mFieldsValues = Collections.unmodifiableMap(fields); mSegsIn = bytes.getInt(start + SEGS_IN_OFFSET); mSegsOut = bytes.getInt(start + SEGS_OUT_OFFSET); mLost = bytes.getInt(start + LOST_OFFSET); mRetransmits = bytes.get(start + RETRANSMITS_OFFSET); // tcp_info structure grows over time as new fields are added. Jump to the end of the // structure, as unknown fields might remain at the end of the structure if the tcp_info // struct was expanded. Loading @@ -119,12 +129,11 @@ public class TcpInfo { } @VisibleForTesting TcpInfo(@NonNull Map<Field, Number> info) { final LinkedHashMap<Field, Number> fields = new LinkedHashMap<>(); for (final Field field : Field.values()) { fields.put(field, info.get(field)); } mFieldsValues = Collections.unmodifiableMap(fields); TcpInfo(int retransmits, int lost, int segsOut, int segsIn) { mRetransmits = retransmits; mLost = lost; mSegsOut = segsOut; mSegsIn = segsIn; } /** Parse a TcpInfo from a giving ByteBuffer with a specific length. */ Loading @@ -132,53 +141,13 @@ public class TcpInfo { public static TcpInfo parse(@NonNull ByteBuffer bytes, int infolen) { try { return new TcpInfo(bytes, infolen); } catch (BufferUnderflowException | IllegalArgumentException e) { } catch (BufferUnderflowException | BufferOverflowException | IllegalArgumentException | IndexOutOfBoundsException e) { Log.e(TAG, "parsing error.", e); return null; } } /** * Helper function for handling different struct tcp_info versions in the kernel. */ private static boolean isValidTargetPosition(int start, int len, int pos, int targetBytes) throws IllegalArgumentException { // Equivalent to new Range(start, start + len).contains(new Range(pos, pos + targetBytes)) if (len < 0 || targetBytes < 0) throw new IllegalArgumentException(); // Check that start < pos < start + len if (pos < start || pos > start + len) return false; // Pos is inside the range and targetBytes is positive. Offset is valid if end of 2nd range // is below end of 1st range. return pos + targetBytes <= start + len; } /** Get value for specific key. */ @Nullable public Number getValue(@NonNull Field key) { return mFieldsValues.get(key); } @Nullable private static Byte getByte(@NonNull ByteBuffer buffer, int start, int len) { if (!isValidTargetPosition(start, len, buffer.position(), Byte.BYTES)) return null; return buffer.get(); } @Nullable private static Integer getInt(@NonNull ByteBuffer buffer, int start, int len) { if (!isValidTargetPosition(start, len, buffer.position(), Integer.BYTES)) return null; return buffer.getInt(); } @Nullable private static Long getLong(@NonNull ByteBuffer buffer, int start, int len) { if (!isValidTargetPosition(start, len, buffer.position(), Long.BYTES)) return null; return buffer.getLong(); } private static String decodeWscale(byte num) { return String.valueOf((num >> 4) & 0x0f) + ":" + String.valueOf(num & 0x0f); } Loading Loading @@ -210,33 +179,18 @@ public class TcpInfo { if (!(obj instanceof TcpInfo)) return false; TcpInfo other = (TcpInfo) obj; for (final Field key : mFieldsValues.keySet()) { if (!Objects.equals(mFieldsValues.get(key), other.mFieldsValues.get(key))) { return false; } } return true; return mSegsIn == other.mSegsIn && mSegsOut == other.mSegsOut && mRetransmits == other.mRetransmits && mLost == other.mLost; } @Override public int hashCode() { return Objects.hash(mFieldsValues.values().toArray()); return Objects.hash(mLost, mRetransmits, mSegsIn, mSegsOut); } @Override public String toString() { String str = "TcpInfo{ "; for (final Field key : mFieldsValues.keySet()) { str += key.name().toLowerCase() + "="; if (key == Field.STATE) { str += getTcpStateName(mFieldsValues.get(key).intValue()) + " "; } else if (key == Field.WSCALE) { str += decodeWscale(mFieldsValues.get(key).byteValue()) + " "; } else { str += mFieldsValues.get(key) + " "; } } str += "}"; return str; return "TcpInfo{lost=" + mLost + ", retransmit=" + mRetransmits + ", received=" + mSegsIn + ", sent=" + mSegsOut + "}"; } } src/com/android/networkstack/netlink/TcpSocketTracker.java +8 −8 Original line number Diff line number Diff line Loading @@ -340,16 +340,16 @@ public class TcpSocketTracker { 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(); stat.sentCount = current.tcpInfo.mSegsOut; stat.receivedCount = current.tcpInfo.mSegsIn; stat.lostCount = current.tcpInfo.mLost; stat.retransmitCount = current.tcpInfo.mRetransmits; 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(); stat.lostCount -= previous.tcpInfo.getValue(TcpInfo.Field.LOST).intValue(); stat.retransmitCount -= previous.tcpInfo.getValue(TcpInfo.Field.RETRANSMITS).intValue(); stat.sentCount -= previous.tcpInfo.mSegsOut; stat.receivedCount -= previous.tcpInfo.mSegsIn; stat.lostCount -= previous.tcpInfo.mLost; stat.retransmitCount -= previous.tcpInfo.mRetransmits; } return stat; Loading tests/unit/src/com/android/networkstack/netlink/TcpInfoTest.java +26 −41 Original line number Diff line number Diff line Loading @@ -89,6 +89,8 @@ public class TcpInfoTest { "0000000000000000"; // sndBufLimited = 0 private static final byte[] TCP_INFO_BYTES = HexEncoding.decode(TCP_INFO_HEX.toCharArray(), false); private static final TcpInfo TEST_TCPINFO = new TcpInfo(0 /* retransmits */, 0 /* lost */, 2 /* segsOut */, 1 /* segsIn */); private static final String EXPANDED_TCP_INFO_HEX = TCP_INFO_HEX + "00000000" // tcpi_delivered Loading @@ -100,39 +102,47 @@ public class TcpInfoTest { @Test public void testParseTcpInfo() { final ByteBuffer buffer = ByteBuffer.wrap(TCP_INFO_BYTES); final Map<TcpInfo.Field, Number> expected = makeTestTcpInfoHash(); // Length is less than required final TcpInfo nullInfo = TcpInfo.parse(buffer, SHORT_TEST_TCP_INFO); assertEquals(nullInfo, null); final TcpInfo parsedInfo = TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1); assertEquals(parsedInfo, TEST_TCPINFO); // Make a data that TcpInfo is not started from the begining of the buffer. final ByteBuffer bufferWithHeader = ByteBuffer.allocate(EXPANDED_TCP_INFO_BYTES.length + TCP_INFO_BYTES.length); bufferWithHeader.put(EXPANDED_TCP_INFO_BYTES); bufferWithHeader.put(TCP_INFO_BYTES); final TcpInfo infoWithHeader = TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1); bufferWithHeader.position(EXPANDED_TCP_INFO_BYTES.length); assertEquals(parsedInfo, TEST_TCPINFO); } assertEquals(parsedInfo, new TcpInfo(expected)); @Test public void testFieldOffset() { assertEquals(TcpInfo.RETRANSMITS_OFFSET, 2); assertEquals(TcpInfo.LOST_OFFSET, 32); assertEquals(TcpInfo.SEGS_OUT_OFFSET, 136); assertEquals(TcpInfo.SEGS_IN_OFFSET, 140); } @Test public void testParseTcpInfoExpanded() { final ByteBuffer buffer = ByteBuffer.wrap(EXPANDED_TCP_INFO_BYTES); final Map<TcpInfo.Field, Number> expected = makeTestTcpInfoHash(); final TcpInfo parsedInfo = TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1 + EXPANDED_TCP_INFO_LENGTH); assertEquals(parsedInfo, new TcpInfo(expected)); assertEquals(parsedInfo, TEST_TCPINFO); assertEquals(buffer.limit(), buffer.position()); // reset the index. buffer.position(0); final TcpInfo parsedInfoShorterLen = TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1); assertEquals(parsedInfoShorterLen, new TcpInfo(expected)); assertEquals(parsedInfoShorterLen, TEST_TCPINFO); assertEquals(TCP_INFO_LENGTH_V1, buffer.position()); } @Test public void testValidOffset() { final ByteBuffer buffer = ByteBuffer.wrap(TCP_INFO_BYTES); final Map<TcpInfo.Field, Number> expected = makeShortTestTcpInfoHash(); final TcpInfo parsedInfo = TcpInfo.parse(buffer, SHORT_TEST_TCP_INFO); assertEquals(parsedInfo, new TcpInfo(expected)); } @Test public void testTcpStateName() { assertEquals(TcpInfo.getTcpStateName(4), TCP_FIN_WAIT1); Loading @@ -156,39 +166,14 @@ public class TcpInfoTest { @Test public void testMalformedTcpInfo() { final ByteBuffer buffer = ByteBuffer.wrap(MALFORMED_TCP_INFO_BYTES); final Map<TcpInfo.Field, Number> expected = makeShortTestTcpInfoHash(); TcpInfo parsedInfo = TcpInfo.parse(buffer, SHORT_TEST_TCP_INFO); assertEquals(parsedInfo, new TcpInfo(expected)); assertEquals(parsedInfo, null); parsedInfo = TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1); assertEquals(parsedInfo, null); } @Test public void testGetValue() { ByteBuffer buffer = ByteBuffer.wrap(TCP_INFO_BYTES); final Map<TcpInfo.Field, Number> expected = makeShortTestTcpInfoHash(); expected.put(TcpInfo.Field.MAX_PACING_RATE, 10_000L); expected.put(TcpInfo.Field.FACKETS, 10); final TcpInfo expectedInfo = new TcpInfo(expected); assertEquals((byte) 0x01, expectedInfo.getValue(TcpInfo.Field.STATE)); assertEquals((byte) 0x00, expectedInfo.getValue(TcpInfo.Field.CASTATE)); assertEquals((byte) 0x00, expectedInfo.getValue(TcpInfo.Field.RETRANSMITS)); assertEquals((byte) 0x00, expectedInfo.getValue(TcpInfo.Field.PROBES)); assertEquals((byte) 0x00, expectedInfo.getValue(TcpInfo.Field.BACKOFF)); assertEquals((byte) 0x07, expectedInfo.getValue(TcpInfo.Field.OPTIONS)); assertEquals((byte) 0x88, expectedInfo.getValue(TcpInfo.Field.WSCALE)); assertEquals((byte) 0x00, expectedInfo.getValue(TcpInfo.Field.DELIVERY_RATE_APP_LIMITED)); assertEquals(10_000L, expectedInfo.getValue(TcpInfo.Field.MAX_PACING_RATE)); assertEquals(10, expectedInfo.getValue(TcpInfo.Field.FACKETS)); assertEquals(null, expectedInfo.getValue(TcpInfo.Field.RTT)); } // Make a TcpInfo contains only first 8 bytes. private Map<TcpInfo.Field, Number> makeShortTestTcpInfoHash() { final Map<TcpInfo.Field, Number> info = new LinkedHashMap<>(); Loading tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java +4 −47 Original line number Diff line number Diff line Loading @@ -61,7 +61,6 @@ import org.mockito.quality.Strictness; import java.io.FileDescriptor; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.HashMap; // TODO: Add more tests for missing coverage. @RunWith(AndroidJUnit4.class) Loading Loading @@ -174,6 +173,8 @@ public class TcpSocketTrackerTest { "0000000000000000"; // deliverRate = 0 private static final byte[] SOCK_DIAG_TCP_INET_BYTES = HexEncoding.decode(SOCK_DIAG_TCP_INET_HEX.toCharArray(), false); private static final TcpInfo TEST_TCPINFO = new TcpInfo(5 /* retransmits */, 0 /* lost */, 10 /* segsOut */, 0 /* segsIn */); private static final String TEST_RESPONSE_HEX = SOCK_DIAG_TCP_INET_HEX // struct nlmsghdr Loading Loading @@ -253,52 +254,8 @@ public class TcpSocketTrackerTest { buffer.position(SOCKDIAG_MSG_HEADER_SIZE); final TcpSocketTracker.SocketInfo parsed = tst.parseSockInfo(buffer, AF_INET, 276, 100L); final HashMap<TcpInfo.Field, Number> expected = new HashMap<>(); expected.put(TcpInfo.Field.STATE, (byte) 0x01); expected.put(TcpInfo.Field.CASTATE, (byte) 0x00); expected.put(TcpInfo.Field.RETRANSMITS, (byte) 0x05); expected.put(TcpInfo.Field.PROBES, (byte) 0x00); expected.put(TcpInfo.Field.BACKOFF, (byte) 0x00); expected.put(TcpInfo.Field.OPTIONS, (byte) 0x07); expected.put(TcpInfo.Field.WSCALE, (byte) 0x88); expected.put(TcpInfo.Field.DELIVERY_RATE_APP_LIMITED, (byte) 0x00); expected.put(TcpInfo.Field.RTO, 1806666); expected.put(TcpInfo.Field.ATO, 0); expected.put(TcpInfo.Field.SND_MSS, 1326); expected.put(TcpInfo.Field.RCV_MSS, 536); expected.put(TcpInfo.Field.UNACKED, 0); expected.put(TcpInfo.Field.SACKED, 0); 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); expected.put(TcpInfo.Field.LAST_ACK_SENT, 0); expected.put(TcpInfo.Field.LAST_DATA_RECV, 187); expected.put(TcpInfo.Field.LAST_ACK_RECV, 187); expected.put(TcpInfo.Field.PMTU, 1500); expected.put(TcpInfo.Field.RCV_SSTHRESH, 87600); expected.put(TcpInfo.Field.RTT, 601150); expected.put(TcpInfo.Field.RTTVAR, 300575); expected.put(TcpInfo.Field.SND_SSTHRESH, 1400); expected.put(TcpInfo.Field.SND_CWND, 10); expected.put(TcpInfo.Field.ADVMSS, 1448); expected.put(TcpInfo.Field.REORDERING, 3); expected.put(TcpInfo.Field.RCV_RTT, 0); expected.put(TcpInfo.Field.RCV_SPACE, 87600); expected.put(TcpInfo.Field.TOTAL_RETRANS, 0); expected.put(TcpInfo.Field.PACING_RATE, 44115L); expected.put(TcpInfo.Field.MAX_PACING_RATE, -1L); expected.put(TcpInfo.Field.BYTES_ACKED, 1L); expected.put(TcpInfo.Field.BYTES_RECEIVED, 0L); expected.put(TcpInfo.Field.SEGS_OUT, 10); expected.put(TcpInfo.Field.SEGS_IN, 0); expected.put(TcpInfo.Field.NOTSENT_BYTES, 0); expected.put(TcpInfo.Field.MIN_RTT, 601150); expected.put(TcpInfo.Field.DATA_SEGS_IN, 0); expected.put(TcpInfo.Field.DATA_SEGS_OUT, 0); expected.put(TcpInfo.Field.DELIVERY_RATE, 0L); assertEquals(parsed.tcpInfo, new TcpInfo(expected)); assertEquals(parsed.tcpInfo, TEST_TCPINFO); assertEquals(parsed.fwmark, 789125); assertEquals(parsed.updateTime, 100); assertEquals(parsed.ipFamily, AF_INET); Loading Loading
src/com/android/networkstack/netlink/TcpInfo.java +43 −89 Original line number Diff line number Diff line Loading @@ -22,11 +22,9 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import java.nio.BufferOverflowException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; /** Loading Loading @@ -91,27 +89,39 @@ public class TcpInfo { } private static final String TAG = "TcpInfo"; private final Map<Field, Number> mFieldsValues; @VisibleForTesting static final int LOST_OFFSET = getFieldOffset(Field.LOST); @VisibleForTesting static final int RETRANSMITS_OFFSET = getFieldOffset(Field.RETRANSMITS); @VisibleForTesting static final int SEGS_IN_OFFSET = getFieldOffset(Field.SEGS_IN); @VisibleForTesting static final int SEGS_OUT_OFFSET = getFieldOffset(Field.SEGS_OUT); final int mSegsIn; final int mSegsOut; final int mLost; final int mRetransmits; private static int getFieldOffset(@NonNull final Field needle) { int offset = 0; for (final Field field : Field.values()) { if (field == needle) return offset; offset += field.size; } throw new IllegalArgumentException("Unknown field"); } private TcpInfo(@NonNull ByteBuffer bytes, int infolen) { // SEGS_IN is the last required field in the buffer, so if the buffer is long enough for // SEGS_IN it's long enough for everything if (SEGS_IN_OFFSET + Field.SEGS_IN.size > infolen) { throw new IllegalArgumentException("Length " + infolen + " is less than required."); } final int start = bytes.position(); final LinkedHashMap<Field, Number> fields = new LinkedHashMap<>(); for (final Field field : Field.values()) { switch (field.size) { case Byte.BYTES: fields.put(field, getByte(bytes, start, infolen)); break; case Integer.BYTES: fields.put(field, getInt(bytes, start, infolen)); break; case Long.BYTES: fields.put(field, getLong(bytes, start, infolen)); break; default: Log.e(TAG, "Unexpected size:" + field.size); } } mFieldsValues = Collections.unmodifiableMap(fields); mSegsIn = bytes.getInt(start + SEGS_IN_OFFSET); mSegsOut = bytes.getInt(start + SEGS_OUT_OFFSET); mLost = bytes.getInt(start + LOST_OFFSET); mRetransmits = bytes.get(start + RETRANSMITS_OFFSET); // tcp_info structure grows over time as new fields are added. Jump to the end of the // structure, as unknown fields might remain at the end of the structure if the tcp_info // struct was expanded. Loading @@ -119,12 +129,11 @@ public class TcpInfo { } @VisibleForTesting TcpInfo(@NonNull Map<Field, Number> info) { final LinkedHashMap<Field, Number> fields = new LinkedHashMap<>(); for (final Field field : Field.values()) { fields.put(field, info.get(field)); } mFieldsValues = Collections.unmodifiableMap(fields); TcpInfo(int retransmits, int lost, int segsOut, int segsIn) { mRetransmits = retransmits; mLost = lost; mSegsOut = segsOut; mSegsIn = segsIn; } /** Parse a TcpInfo from a giving ByteBuffer with a specific length. */ Loading @@ -132,53 +141,13 @@ public class TcpInfo { public static TcpInfo parse(@NonNull ByteBuffer bytes, int infolen) { try { return new TcpInfo(bytes, infolen); } catch (BufferUnderflowException | IllegalArgumentException e) { } catch (BufferUnderflowException | BufferOverflowException | IllegalArgumentException | IndexOutOfBoundsException e) { Log.e(TAG, "parsing error.", e); return null; } } /** * Helper function for handling different struct tcp_info versions in the kernel. */ private static boolean isValidTargetPosition(int start, int len, int pos, int targetBytes) throws IllegalArgumentException { // Equivalent to new Range(start, start + len).contains(new Range(pos, pos + targetBytes)) if (len < 0 || targetBytes < 0) throw new IllegalArgumentException(); // Check that start < pos < start + len if (pos < start || pos > start + len) return false; // Pos is inside the range and targetBytes is positive. Offset is valid if end of 2nd range // is below end of 1st range. return pos + targetBytes <= start + len; } /** Get value for specific key. */ @Nullable public Number getValue(@NonNull Field key) { return mFieldsValues.get(key); } @Nullable private static Byte getByte(@NonNull ByteBuffer buffer, int start, int len) { if (!isValidTargetPosition(start, len, buffer.position(), Byte.BYTES)) return null; return buffer.get(); } @Nullable private static Integer getInt(@NonNull ByteBuffer buffer, int start, int len) { if (!isValidTargetPosition(start, len, buffer.position(), Integer.BYTES)) return null; return buffer.getInt(); } @Nullable private static Long getLong(@NonNull ByteBuffer buffer, int start, int len) { if (!isValidTargetPosition(start, len, buffer.position(), Long.BYTES)) return null; return buffer.getLong(); } private static String decodeWscale(byte num) { return String.valueOf((num >> 4) & 0x0f) + ":" + String.valueOf(num & 0x0f); } Loading Loading @@ -210,33 +179,18 @@ public class TcpInfo { if (!(obj instanceof TcpInfo)) return false; TcpInfo other = (TcpInfo) obj; for (final Field key : mFieldsValues.keySet()) { if (!Objects.equals(mFieldsValues.get(key), other.mFieldsValues.get(key))) { return false; } } return true; return mSegsIn == other.mSegsIn && mSegsOut == other.mSegsOut && mRetransmits == other.mRetransmits && mLost == other.mLost; } @Override public int hashCode() { return Objects.hash(mFieldsValues.values().toArray()); return Objects.hash(mLost, mRetransmits, mSegsIn, mSegsOut); } @Override public String toString() { String str = "TcpInfo{ "; for (final Field key : mFieldsValues.keySet()) { str += key.name().toLowerCase() + "="; if (key == Field.STATE) { str += getTcpStateName(mFieldsValues.get(key).intValue()) + " "; } else if (key == Field.WSCALE) { str += decodeWscale(mFieldsValues.get(key).byteValue()) + " "; } else { str += mFieldsValues.get(key) + " "; } } str += "}"; return str; return "TcpInfo{lost=" + mLost + ", retransmit=" + mRetransmits + ", received=" + mSegsIn + ", sent=" + mSegsOut + "}"; } }
src/com/android/networkstack/netlink/TcpSocketTracker.java +8 −8 Original line number Diff line number Diff line Loading @@ -340,16 +340,16 @@ public class TcpSocketTracker { 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(); stat.sentCount = current.tcpInfo.mSegsOut; stat.receivedCount = current.tcpInfo.mSegsIn; stat.lostCount = current.tcpInfo.mLost; stat.retransmitCount = current.tcpInfo.mRetransmits; 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(); stat.lostCount -= previous.tcpInfo.getValue(TcpInfo.Field.LOST).intValue(); stat.retransmitCount -= previous.tcpInfo.getValue(TcpInfo.Field.RETRANSMITS).intValue(); stat.sentCount -= previous.tcpInfo.mSegsOut; stat.receivedCount -= previous.tcpInfo.mSegsIn; stat.lostCount -= previous.tcpInfo.mLost; stat.retransmitCount -= previous.tcpInfo.mRetransmits; } return stat; Loading
tests/unit/src/com/android/networkstack/netlink/TcpInfoTest.java +26 −41 Original line number Diff line number Diff line Loading @@ -89,6 +89,8 @@ public class TcpInfoTest { "0000000000000000"; // sndBufLimited = 0 private static final byte[] TCP_INFO_BYTES = HexEncoding.decode(TCP_INFO_HEX.toCharArray(), false); private static final TcpInfo TEST_TCPINFO = new TcpInfo(0 /* retransmits */, 0 /* lost */, 2 /* segsOut */, 1 /* segsIn */); private static final String EXPANDED_TCP_INFO_HEX = TCP_INFO_HEX + "00000000" // tcpi_delivered Loading @@ -100,39 +102,47 @@ public class TcpInfoTest { @Test public void testParseTcpInfo() { final ByteBuffer buffer = ByteBuffer.wrap(TCP_INFO_BYTES); final Map<TcpInfo.Field, Number> expected = makeTestTcpInfoHash(); // Length is less than required final TcpInfo nullInfo = TcpInfo.parse(buffer, SHORT_TEST_TCP_INFO); assertEquals(nullInfo, null); final TcpInfo parsedInfo = TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1); assertEquals(parsedInfo, TEST_TCPINFO); // Make a data that TcpInfo is not started from the begining of the buffer. final ByteBuffer bufferWithHeader = ByteBuffer.allocate(EXPANDED_TCP_INFO_BYTES.length + TCP_INFO_BYTES.length); bufferWithHeader.put(EXPANDED_TCP_INFO_BYTES); bufferWithHeader.put(TCP_INFO_BYTES); final TcpInfo infoWithHeader = TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1); bufferWithHeader.position(EXPANDED_TCP_INFO_BYTES.length); assertEquals(parsedInfo, TEST_TCPINFO); } assertEquals(parsedInfo, new TcpInfo(expected)); @Test public void testFieldOffset() { assertEquals(TcpInfo.RETRANSMITS_OFFSET, 2); assertEquals(TcpInfo.LOST_OFFSET, 32); assertEquals(TcpInfo.SEGS_OUT_OFFSET, 136); assertEquals(TcpInfo.SEGS_IN_OFFSET, 140); } @Test public void testParseTcpInfoExpanded() { final ByteBuffer buffer = ByteBuffer.wrap(EXPANDED_TCP_INFO_BYTES); final Map<TcpInfo.Field, Number> expected = makeTestTcpInfoHash(); final TcpInfo parsedInfo = TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1 + EXPANDED_TCP_INFO_LENGTH); assertEquals(parsedInfo, new TcpInfo(expected)); assertEquals(parsedInfo, TEST_TCPINFO); assertEquals(buffer.limit(), buffer.position()); // reset the index. buffer.position(0); final TcpInfo parsedInfoShorterLen = TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1); assertEquals(parsedInfoShorterLen, new TcpInfo(expected)); assertEquals(parsedInfoShorterLen, TEST_TCPINFO); assertEquals(TCP_INFO_LENGTH_V1, buffer.position()); } @Test public void testValidOffset() { final ByteBuffer buffer = ByteBuffer.wrap(TCP_INFO_BYTES); final Map<TcpInfo.Field, Number> expected = makeShortTestTcpInfoHash(); final TcpInfo parsedInfo = TcpInfo.parse(buffer, SHORT_TEST_TCP_INFO); assertEquals(parsedInfo, new TcpInfo(expected)); } @Test public void testTcpStateName() { assertEquals(TcpInfo.getTcpStateName(4), TCP_FIN_WAIT1); Loading @@ -156,39 +166,14 @@ public class TcpInfoTest { @Test public void testMalformedTcpInfo() { final ByteBuffer buffer = ByteBuffer.wrap(MALFORMED_TCP_INFO_BYTES); final Map<TcpInfo.Field, Number> expected = makeShortTestTcpInfoHash(); TcpInfo parsedInfo = TcpInfo.parse(buffer, SHORT_TEST_TCP_INFO); assertEquals(parsedInfo, new TcpInfo(expected)); assertEquals(parsedInfo, null); parsedInfo = TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1); assertEquals(parsedInfo, null); } @Test public void testGetValue() { ByteBuffer buffer = ByteBuffer.wrap(TCP_INFO_BYTES); final Map<TcpInfo.Field, Number> expected = makeShortTestTcpInfoHash(); expected.put(TcpInfo.Field.MAX_PACING_RATE, 10_000L); expected.put(TcpInfo.Field.FACKETS, 10); final TcpInfo expectedInfo = new TcpInfo(expected); assertEquals((byte) 0x01, expectedInfo.getValue(TcpInfo.Field.STATE)); assertEquals((byte) 0x00, expectedInfo.getValue(TcpInfo.Field.CASTATE)); assertEquals((byte) 0x00, expectedInfo.getValue(TcpInfo.Field.RETRANSMITS)); assertEquals((byte) 0x00, expectedInfo.getValue(TcpInfo.Field.PROBES)); assertEquals((byte) 0x00, expectedInfo.getValue(TcpInfo.Field.BACKOFF)); assertEquals((byte) 0x07, expectedInfo.getValue(TcpInfo.Field.OPTIONS)); assertEquals((byte) 0x88, expectedInfo.getValue(TcpInfo.Field.WSCALE)); assertEquals((byte) 0x00, expectedInfo.getValue(TcpInfo.Field.DELIVERY_RATE_APP_LIMITED)); assertEquals(10_000L, expectedInfo.getValue(TcpInfo.Field.MAX_PACING_RATE)); assertEquals(10, expectedInfo.getValue(TcpInfo.Field.FACKETS)); assertEquals(null, expectedInfo.getValue(TcpInfo.Field.RTT)); } // Make a TcpInfo contains only first 8 bytes. private Map<TcpInfo.Field, Number> makeShortTestTcpInfoHash() { final Map<TcpInfo.Field, Number> info = new LinkedHashMap<>(); Loading
tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java +4 −47 Original line number Diff line number Diff line Loading @@ -61,7 +61,6 @@ import org.mockito.quality.Strictness; import java.io.FileDescriptor; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.HashMap; // TODO: Add more tests for missing coverage. @RunWith(AndroidJUnit4.class) Loading Loading @@ -174,6 +173,8 @@ public class TcpSocketTrackerTest { "0000000000000000"; // deliverRate = 0 private static final byte[] SOCK_DIAG_TCP_INET_BYTES = HexEncoding.decode(SOCK_DIAG_TCP_INET_HEX.toCharArray(), false); private static final TcpInfo TEST_TCPINFO = new TcpInfo(5 /* retransmits */, 0 /* lost */, 10 /* segsOut */, 0 /* segsIn */); private static final String TEST_RESPONSE_HEX = SOCK_DIAG_TCP_INET_HEX // struct nlmsghdr Loading Loading @@ -253,52 +254,8 @@ public class TcpSocketTrackerTest { buffer.position(SOCKDIAG_MSG_HEADER_SIZE); final TcpSocketTracker.SocketInfo parsed = tst.parseSockInfo(buffer, AF_INET, 276, 100L); final HashMap<TcpInfo.Field, Number> expected = new HashMap<>(); expected.put(TcpInfo.Field.STATE, (byte) 0x01); expected.put(TcpInfo.Field.CASTATE, (byte) 0x00); expected.put(TcpInfo.Field.RETRANSMITS, (byte) 0x05); expected.put(TcpInfo.Field.PROBES, (byte) 0x00); expected.put(TcpInfo.Field.BACKOFF, (byte) 0x00); expected.put(TcpInfo.Field.OPTIONS, (byte) 0x07); expected.put(TcpInfo.Field.WSCALE, (byte) 0x88); expected.put(TcpInfo.Field.DELIVERY_RATE_APP_LIMITED, (byte) 0x00); expected.put(TcpInfo.Field.RTO, 1806666); expected.put(TcpInfo.Field.ATO, 0); expected.put(TcpInfo.Field.SND_MSS, 1326); expected.put(TcpInfo.Field.RCV_MSS, 536); expected.put(TcpInfo.Field.UNACKED, 0); expected.put(TcpInfo.Field.SACKED, 0); 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); expected.put(TcpInfo.Field.LAST_ACK_SENT, 0); expected.put(TcpInfo.Field.LAST_DATA_RECV, 187); expected.put(TcpInfo.Field.LAST_ACK_RECV, 187); expected.put(TcpInfo.Field.PMTU, 1500); expected.put(TcpInfo.Field.RCV_SSTHRESH, 87600); expected.put(TcpInfo.Field.RTT, 601150); expected.put(TcpInfo.Field.RTTVAR, 300575); expected.put(TcpInfo.Field.SND_SSTHRESH, 1400); expected.put(TcpInfo.Field.SND_CWND, 10); expected.put(TcpInfo.Field.ADVMSS, 1448); expected.put(TcpInfo.Field.REORDERING, 3); expected.put(TcpInfo.Field.RCV_RTT, 0); expected.put(TcpInfo.Field.RCV_SPACE, 87600); expected.put(TcpInfo.Field.TOTAL_RETRANS, 0); expected.put(TcpInfo.Field.PACING_RATE, 44115L); expected.put(TcpInfo.Field.MAX_PACING_RATE, -1L); expected.put(TcpInfo.Field.BYTES_ACKED, 1L); expected.put(TcpInfo.Field.BYTES_RECEIVED, 0L); expected.put(TcpInfo.Field.SEGS_OUT, 10); expected.put(TcpInfo.Field.SEGS_IN, 0); expected.put(TcpInfo.Field.NOTSENT_BYTES, 0); expected.put(TcpInfo.Field.MIN_RTT, 601150); expected.put(TcpInfo.Field.DATA_SEGS_IN, 0); expected.put(TcpInfo.Field.DATA_SEGS_OUT, 0); expected.put(TcpInfo.Field.DELIVERY_RATE, 0L); assertEquals(parsed.tcpInfo, new TcpInfo(expected)); assertEquals(parsed.tcpInfo, TEST_TCPINFO); assertEquals(parsed.fwmark, 789125); assertEquals(parsed.updateTime, 100); assertEquals(parsed.ipFamily, AF_INET); Loading