Merge "Make sure the test module uninstall the apks at the end"
diff --git a/src/com/android/networkstack/netlink/TcpInfo.java b/src/com/android/networkstack/netlink/TcpInfo.java
new file mode 100644
index 0000000..7a2adc7
--- /dev/null
+++ b/src/com/android/networkstack/netlink/TcpInfo.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.networkstack.netlink;
+
+import android.util.Log;
+import android.util.Range;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Objects;
+
+/**
+ * Class for tcp_info.
+ *
+ * Corresponds to {@code struct tcp_info} from bionic/libc/kernel/uapi/linux/tcp.h
+ */
+public class TcpInfo {
+ public enum Field {
+ STATE(Byte.BYTES),
+ CASTATE(Byte.BYTES),
+ RETRANSMITS(Byte.BYTES),
+ PROBES(Byte.BYTES),
+ BACKOFF(Byte.BYTES),
+ OPTIONS(Byte.BYTES),
+ WSCALE(Byte.BYTES),
+ DELIVERY_RATE_APP_LIMITED(Byte.BYTES),
+ RTO(Integer.BYTES),
+ ATO(Integer.BYTES),
+ SND_MSS(Integer.BYTES),
+ RCV_MSS(Integer.BYTES),
+ UNACKED(Integer.BYTES),
+ SACKED(Integer.BYTES),
+ LOST(Integer.BYTES),
+ RETRANS(Integer.BYTES),
+ FACKETS(Integer.BYTES),
+ LAST_DATA_SENT(Integer.BYTES),
+ LAST_ACK_SENT(Integer.BYTES),
+ LAST_DATA_RECV(Integer.BYTES),
+ LAST_ACK_RECV(Integer.BYTES),
+ PMTU(Integer.BYTES),
+ RCV_SSTHRESH(Integer.BYTES),
+ RTT(Integer.BYTES),
+ RTTVAR(Integer.BYTES),
+ SND_SSTHRESH(Integer.BYTES),
+ SND_CWND(Integer.BYTES),
+ ADVMSS(Integer.BYTES),
+ REORDERING(Integer.BYTES),
+ RCV_RTT(Integer.BYTES),
+ RCV_SPACE(Integer.BYTES),
+ TOTAL_RETRANS(Integer.BYTES),
+ PACING_RATE(Long.BYTES),
+ MAX_PACING_RATE(Long.BYTES),
+ BYTES_ACKED(Long.BYTES),
+ BYTES_RECEIVED(Long.BYTES),
+ SEGS_OUT(Integer.BYTES),
+ SEGS_IN(Integer.BYTES),
+ NOTSENT_BYTES(Integer.BYTES),
+ MIN_RTT(Integer.BYTES),
+ DATA_SEGS_IN(Integer.BYTES),
+ DATA_SEGS_OUT(Integer.BYTES),
+ DELIVERY_RATE(Long.BYTES),
+ BUSY_TIME(Long.BYTES),
+ RWND_LIMITED(Long.BYTES),
+ SNDBUF_LIMITED(Long.BYTES);
+
+ public final int size;
+
+ Field(int s) {
+ size = s;
+ }
+ }
+
+ private static final String TAG = "TcpInfo";
+ private final LinkedHashMap<Field, Number> mFieldsValues = new LinkedHashMap<Field, Number>();
+
+ private TcpInfo(@NonNull ByteBuffer bytes, int infolen) {
+ final int start = bytes.position();
+ for (final Field field : Field.values()) {
+ switch (field.size) {
+ case Byte.BYTES:
+ mFieldsValues.put(field, getByte(bytes, start, infolen));
+ break;
+ case Integer.BYTES:
+ mFieldsValues.put(field, getInt(bytes, start, infolen));
+ break;
+ case Long.BYTES:
+ mFieldsValues.put(field, getLong(bytes, start, infolen));
+ break;
+ default:
+ Log.e(TAG, "Unexpected size:" + field.size);
+ }
+ }
+
+ }
+
+ @VisibleForTesting
+ TcpInfo(@NonNull HashMap<Field, Number> info) {
+ for (final Field field : Field.values()) {
+ mFieldsValues.put(field, info.get(field));
+ }
+ }
+
+ /** Parse a TcpInfo from a giving ByteBuffer with a specific length. */
+ @Nullable
+ public static TcpInfo parse(@NonNull ByteBuffer bytes, int infolen) {
+ try {
+ TcpInfo info = new TcpInfo(bytes, infolen);
+ return info;
+ } catch (BufferUnderflowException e) {
+ Log.e(TAG, "parsing error.", e);
+ return null;
+ }
+ }
+
+ /**
+ * Helper function for handling different struct tcp_info versions in the kernel.
+ */
+ private static boolean isValidOffset(int start, int len, int pos, int targetBytes) {
+ final Range a = new Range(start, start + len);
+ final Range b = new Range(pos, pos + targetBytes);
+ return a.contains(b);
+ }
+
+ /** 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 (!isValidOffset(start, len, buffer.position(), Byte.BYTES)) return null;
+
+ return buffer.get();
+ }
+
+ @Nullable
+ private static Integer getInt(@NonNull ByteBuffer buffer, int start, int len) {
+ if (!isValidOffset(start, len, buffer.position(), Integer.BYTES)) return null;
+
+ return buffer.getInt();
+ }
+
+ @Nullable
+ private static Long getLong(@NonNull ByteBuffer buffer, int start, int len) {
+ if (!isValidOffset(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);
+ }
+
+ /**
+ * Returns a string representing a given tcp state.
+ * Map to enum in bionic/libc/include/netinet/tcp.h
+ */
+ @VisibleForTesting
+ static String getTcpStateName(int state) {
+ switch (state) {
+ case 1: return "TCP_ESTABLISHED";
+ case 2: return "TCP_SYN_SENT";
+ case 3: return "TCP_SYN_RECV";
+ case 4: return "TCP_FIN_WAIT1";
+ case 5: return "TCP_FIN_WAIT2";
+ case 6: return "TCP_TIME_WAIT";
+ case 7: return "TCP_CLOSE";
+ case 8: return "TCP_CLOSE_WAIT";
+ case 9: return "TCP_LAST_ACK";
+ case 10: return "TCP_LISTEN";
+ case 11: return "TCP_CLOSING";
+ default: return "UNKNOWN:" + Integer.toString(state);
+ }
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ 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;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mFieldsValues.values().toArray());
+ }
+
+ @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;
+ }
+}
diff --git a/tests/lib/src/android/networkstack/util/FakeDns.kt b/tests/lib/src/com/android/testutils/FakeDns.kt
similarity index 98%
rename from tests/lib/src/android/networkstack/util/FakeDns.kt
rename to tests/lib/src/com/android/testutils/FakeDns.kt
index 157b6ce..825d748 100644
--- a/tests/lib/src/android/networkstack/util/FakeDns.kt
+++ b/tests/lib/src/com/android/testutils/FakeDns.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.networkstack.util
+package com.android.testutils
import android.net.DnsResolver
import android.net.InetAddresses
diff --git a/tests/unit/src/android/networkstack/metrics/DataStallStatsUtilsTest.kt b/tests/unit/src/com/android/networkstack/metrics/DataStallStatsUtilsTest.kt
similarity index 94%
rename from tests/unit/src/android/networkstack/metrics/DataStallStatsUtilsTest.kt
rename to tests/unit/src/com/android/networkstack/metrics/DataStallStatsUtilsTest.kt
index 3b53b5f..3820165 100644
--- a/tests/unit/src/android/networkstack/metrics/DataStallStatsUtilsTest.kt
+++ b/tests/unit/src/com/android/networkstack/metrics/DataStallStatsUtilsTest.kt
@@ -14,12 +14,11 @@
* limitations under the License.
*/
-package android.networkstack.metrics
+package com.android.networkstack.metrics
import android.net.captiveportal.CaptivePortalProbeResult
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
-import com.android.networkstack.metrics.DataStallStatsUtils
import com.android.server.connectivity.nano.DataStallEventProto
import org.junit.Assert.assertEquals
import org.junit.Test
diff --git a/tests/unit/src/com/android/networkstack/netlink/TcpInfoTest.java b/tests/unit/src/com/android/networkstack/netlink/TcpInfoTest.java
new file mode 100644
index 0000000..415bbe4
--- /dev/null
+++ b/tests/unit/src/com/android/networkstack/netlink/TcpInfoTest.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.networkstack.netlink;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import libcore.util.HexEncoding;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TcpInfoTest {
+ private static final int TCP_INFO_LENGTH_V1 = 192;
+ private static final int SHORT_TEST_TCP_INFO = 8;
+ private static final String TCP_ESTABLISHED = "TCP_ESTABLISHED";
+ private static final String TCP_FIN_WAIT1 = "TCP_FIN_WAIT1";
+ private static final String TCP_SYN_SENT = "TCP_SYN_SENT";
+ private static final String UNKNOWN_20 = "UNKNOWN:20";
+ // Refer to rfc793 for the value definition.
+ private static final String TCP_INFO_HEX =
+ "01" + // state = TCP_ESTABLISHED
+ "00" + // ca_state = TCP_CA_OPEN
+ "00" + // retransmits = 0
+ "00" + // probes = 0
+ "00" + // backoff = 0
+ "07" + // option = TCPI_OPT_WSCALE|TCPI_OPT_SACK|TCPI_OPT_TIMESTAMPS
+ "88" + // wscale = 8
+ "00" + // delivery_rate_app_limited = 0
+ "001B914A" + // rto = 1806666
+ "00000000" + // ato = 0
+ "0000052E" + // sndMss = 1326
+ "00000218" + // rcvMss = 536
+ "00000000" + // unsacked = 0
+ "00000000" + // acked = 0
+ "00000000" + // lost = 0
+ "00000000" + // retrans = 0
+ "00000000" + // fackets = 0
+ "000000BB" + // lastDataSent = 187
+ "00000000" + // lastAckSent = 0
+ "000000BB" + // lastDataRecv = 187
+ "000000BB" + // lastDataAckRecv = 187
+ "000005DC" + // pmtu = 1500
+ "00015630" + // rcvSsthresh = 87600
+ "00092C3E" + // rttt = 601150
+ "0004961F" + // rttvar = 300575
+ "00000578" + // sndSsthresh = 1400
+ "0000000A" + // sndCwnd = 10
+ "000005A8" + // advmss = 1448
+ "00000003" + // reordering = 3
+ "00000000" + // rcvrtt = 0
+ "00015630" + // rcvspace = 87600
+ "00000000" + // totalRetrans = 0
+ "000000000000AC53" + // pacingRate = 44115
+ "FFFFFFFFFFFFFFFF" + // maxPacingRate = 18446744073709551615
+ "0000000000000001" + // bytesAcked = 1
+ "0000000000000000" + // bytesReceived = 0
+ "00000002" + // SegsOut = 2
+ "00000001" + // SegsIn = 1
+ "00000000" + // NotSentBytes = 0
+ "00092C3E" + // minRtt = 601150
+ "00000000" + // DataSegsIn = 0
+ "00000000" + // DataSegsOut = 0
+ "0000000000000000" + // deliverRate = 0
+ "0000000000000000" + // busyTime = 0
+ "0000000000000000" + // RwndLimited = 0
+ "0000000000000000"; // sndBufLimited = 0
+ private static final byte[] TCP_INFO_BYTES =
+ HexEncoding.decode(TCP_INFO_HEX.toCharArray(), false);
+
+ @Test
+ public void testParseTcpInfo() {
+ final ByteBuffer buffer = ByteBuffer.wrap(TCP_INFO_BYTES);
+ final HashMap<TcpInfo.Field, Number> expected = makeTestTcpInfoHash();
+ final TcpInfo parsedInfo = TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1);
+
+ assertEquals(parsedInfo, new TcpInfo(expected));
+ }
+
+ @Test
+ public void testValidOffset() {
+ final ByteBuffer buffer = ByteBuffer.wrap(TCP_INFO_BYTES);
+
+ final HashMap<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);
+ assertEquals(TcpInfo.getTcpStateName(1), TCP_ESTABLISHED);
+ assertEquals(TcpInfo.getTcpStateName(2), TCP_SYN_SENT);
+ assertEquals(TcpInfo.getTcpStateName(20), UNKNOWN_20);
+ }
+
+ private static final String MALFORMED_TCP_INFO_HEX =
+ "01" + // state = TCP_ESTABLISHED
+ "00" + // ca_state = TCP_CA_OPEN
+ "00" + // retransmits = 0
+ "00" + // probes = 0
+ "00" + // backoff = 0
+ "07" + // option = TCPI_OPT_WSCALE|TCPI_OPT_SACK|TCPI_OPT_TIMESTAMPS
+ "88" + // wscale = 8
+ "00" + // delivery_rate_app_limited = 0
+ "001B"; // Incomplete bytes, expect to be an int.
+ private static final byte[] MALFORMED_TCP_INFO_BYTES =
+ HexEncoding.decode(MALFORMED_TCP_INFO_HEX.toCharArray(), false);
+ @Test
+ public void testMalformedTcpInfo() {
+ final ByteBuffer buffer = ByteBuffer.wrap(MALFORMED_TCP_INFO_BYTES);
+ final HashMap<TcpInfo.Field, Number> expected = makeShortTestTcpInfoHash();
+
+ TcpInfo parsedInfo = TcpInfo.parse(buffer, SHORT_TEST_TCP_INFO);
+ assertEquals(parsedInfo, new TcpInfo(expected));
+
+ parsedInfo = TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1);
+ assertEquals(parsedInfo, null);
+ }
+
+ @Test
+ public void testGetValue() {
+ ByteBuffer buffer = ByteBuffer.wrap(TCP_INFO_BYTES);
+
+ final HashMap<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 HashMap<TcpInfo.Field, Number> makeShortTestTcpInfoHash() {
+ final HashMap<TcpInfo.Field, Number> info = new HashMap<TcpInfo.Field, Number>();
+ info.put(TcpInfo.Field.STATE, (byte) 0x01);
+ info.put(TcpInfo.Field.CASTATE, (byte) 0x00);
+ info.put(TcpInfo.Field.RETRANSMITS, (byte) 0x00);
+ info.put(TcpInfo.Field.PROBES, (byte) 0x00);
+ info.put(TcpInfo.Field.BACKOFF, (byte) 0x00);
+ info.put(TcpInfo.Field.OPTIONS, (byte) 0x07);
+ info.put(TcpInfo.Field.WSCALE, (byte) 0x88);
+ info.put(TcpInfo.Field.DELIVERY_RATE_APP_LIMITED, (byte) 0x00);
+
+ return info;
+ }
+
+ private HashMap<TcpInfo.Field, Number> makeTestTcpInfoHash() {
+ final HashMap<TcpInfo.Field, Number> info = makeShortTestTcpInfoHash();
+ info.put(TcpInfo.Field.RTO, 1806666);
+ info.put(TcpInfo.Field.ATO, 0);
+ info.put(TcpInfo.Field.SND_MSS, 1326);
+ info.put(TcpInfo.Field.RCV_MSS, 536);
+ info.put(TcpInfo.Field.UNACKED, 0);
+ info.put(TcpInfo.Field.SACKED, 0);
+ info.put(TcpInfo.Field.LOST, 0);
+ info.put(TcpInfo.Field.RETRANS, 0);
+ info.put(TcpInfo.Field.FACKETS, 0);
+ info.put(TcpInfo.Field.LAST_DATA_SENT, 187);
+ info.put(TcpInfo.Field.LAST_ACK_SENT, 0);
+ info.put(TcpInfo.Field.LAST_DATA_RECV, 187);
+ info.put(TcpInfo.Field.LAST_ACK_RECV, 187);
+ info.put(TcpInfo.Field.PMTU, 1500);
+ info.put(TcpInfo.Field.RCV_SSTHRESH, 87600);
+ info.put(TcpInfo.Field.RTT, 601150);
+ info.put(TcpInfo.Field.RTTVAR, 300575);
+ info.put(TcpInfo.Field.SND_SSTHRESH, 1400);
+ info.put(TcpInfo.Field.SND_CWND, 10);
+ info.put(TcpInfo.Field.ADVMSS, 1448);
+ info.put(TcpInfo.Field.REORDERING, 3);
+ info.put(TcpInfo.Field.RCV_RTT, 0);
+ info.put(TcpInfo.Field.RCV_SPACE, 87600);
+ info.put(TcpInfo.Field.TOTAL_RETRANS, 0);
+ info.put(TcpInfo.Field.PACING_RATE, 44115L);
+ info.put(TcpInfo.Field.MAX_PACING_RATE, -1L);
+ info.put(TcpInfo.Field.BYTES_ACKED, 1L);
+ info.put(TcpInfo.Field.BYTES_RECEIVED, 0L);
+ info.put(TcpInfo.Field.SEGS_OUT, 2);
+ info.put(TcpInfo.Field.SEGS_IN, 1);
+ info.put(TcpInfo.Field.NOTSENT_BYTES, 0);
+ info.put(TcpInfo.Field.MIN_RTT, 601150);
+ info.put(TcpInfo.Field.DATA_SEGS_IN, 0);
+ info.put(TcpInfo.Field.DATA_SEGS_OUT, 0);
+ info.put(TcpInfo.Field.DELIVERY_RATE, 0L);
+ info.put(TcpInfo.Field.BUSY_TIME, 0L);
+ info.put(TcpInfo.Field.RWND_LIMITED, 0L);
+ info.put(TcpInfo.Field.SNDBUF_LIMITED, 0L);
+
+ return info;
+ }
+}
diff --git a/tests/unit/src/android/networkstack/util/DnsUtilsTest.kt b/tests/unit/src/com/android/networkstack/util/DnsUtilsTest.kt
similarity index 97%
rename from tests/unit/src/android/networkstack/util/DnsUtilsTest.kt
rename to tests/unit/src/com/android/networkstack/util/DnsUtilsTest.kt
index 815fc60..59d96be 100644
--- a/tests/unit/src/android/networkstack/util/DnsUtilsTest.kt
+++ b/tests/unit/src/com/android/networkstack/util/DnsUtilsTest.kt
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-package android.networkstack.util
+package com.android.networkstack.netlink.util
import android.net.DnsResolver
import android.net.DnsResolver.FLAG_EMPTY
import android.net.DnsResolver.TYPE_A
import android.net.DnsResolver.TYPE_AAAA
import android.net.Network
+import com.android.testutils.FakeDns
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.networkstack.util.DnsUtils