Snap for 9324941 from 9512ee642f50cdd3200ea703f7bee9c5a00175fb to mainline-permission-release

Change-Id: I4ef55b8dba36ed14d39d3f98663a54a3bcf2eb80
diff --git a/Android.bp b/Android.bp
index 0aba37c..116d6a6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -67,6 +67,7 @@
     min_sdk_version: "29",
     sdk_version: "module_current",
     libs: [
+        "framework-configinfrastructure",
         "framework-connectivity",
         "framework-connectivity-t",
         "framework-statsd",
diff --git a/TEST_MAPPING b/TEST_MAPPING
index c1bc9cf..3869fc9 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -20,8 +20,16 @@
     {
       "name": "NetworkStackTests[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]"
     },
+    // This is used to verify NetworkStackRootTests with all latest modules listed below on
+    // preivous OS platforms.
     {
-      "name": "NetworkStackIntegrationTests[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]"
+      "name": "NetworkStackRootTests[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]"
+    },
+    // This is used to verify NetworkStackRootTests with latest CaptivePortalLoginGoogle.apk
+    // but with other modules on different version(e.g. an older tethering module to verify
+    // backwards compatibility of APIs).
+    {
+      "name": "NetworkStackRootTests[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk]"
     }
   ],
   "imports": [
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index d9f3843..71db3ab 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -19,8 +19,8 @@
     <string name="notification_channel_name_connected" msgid="1795068343200033922">"Captive portal authentication"</string>
     <string name="notification_channel_description_connected" msgid="7239184168268014518">"Notifications shown when the device has successfully authenticated to a captive portal network"</string>
     <string name="notification_channel_name_network_venue_info" msgid="6526543187249265733">"Network venue information"</string>
-    <string name="notification_channel_description_network_venue_info" msgid="5131499595382733605">"Notifications shown to indicate that the network has a venue information page"</string>
+    <string name="notification_channel_description_network_venue_info" msgid="5131499595382733605">"Notifications shown to indicate the network has a venue information page"</string>
     <string name="connected" msgid="4563643884927480998">"Connected"</string>
-    <string name="tap_for_info" msgid="6849746325626883711">"Connected/Tap to view website"</string>
+    <string name="tap_for_info" msgid="6849746325626883711">"Connected / Tap to view website"</string>
     <string name="application_label" msgid="1322847171305285454">"Network manager"</string>
 </resources>
diff --git a/src/com/android/networkstack/netlink/TcpSocketTracker.java b/src/com/android/networkstack/netlink/TcpSocketTracker.java
index ec130e6..33d3e61 100644
--- a/src/com/android/networkstack/netlink/TcpSocketTracker.java
+++ b/src/com/android/networkstack/netlink/TcpSocketTracker.java
@@ -89,14 +89,15 @@
     private static final String TAG = TcpSocketTracker.class.getSimpleName();
     private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
     private static final int[] ADDRESS_FAMILIES = new int[] {AF_INET6, AF_INET};
-    // Enough for parsing v1 tcp_info for more than 200 sockets per time.
-    private static final int DEFAULT_RECV_BUFSIZE = 60_000;
+    // This is for individual message. The individual messages should not be excessively large.
+    private static final int DEFAULT_RECV_BUFSIZE = 8192;
     // Default I/O timeout time in ms of the socket request.
     private static final long IO_TIMEOUT = 3_000L;
     /** Cookie offset of an InetMagMessage header. */
     private static final int IDIAG_COOKIE_OFFSET = 44;
     private static final int UNKNOWN_MARK = 0xffffffff;
     private static final int NULL_MASK = 0;
+    private static final int END_OF_PARSING = -1;
     /**
      *  Gather the socket info.
      *
@@ -195,6 +196,7 @@
     public boolean pollSocketsInfo() {
         if (!mDependencies.isTcpInfoParsingSupported()) return false;
         FileDescriptor fd = null;
+
         try {
             final long time = SystemClock.elapsedRealtime();
             fd = mDependencies.connectToKernel();
@@ -202,61 +204,12 @@
             final TcpStat stat = new TcpStat();
             for (final int family : ADDRESS_FAMILIES) {
                 mDependencies.sendPollingRequest(fd, mSockDiagMsg.get(family));
-                // Messages are composed with the following format. Stop parsing when receiving
-                // message with nlmsg_type NLMSG_DONE.
-                // +------------------+---------------+--------------+--------+
-                // | Netlink Header   | Family Header | Attributes   | rtattr |
-                // | struct nlmsghdr  | struct rtmsg  | struct rtattr|  data  |
-                // +------------------+---------------+--------------+--------+
-                //               :           :               :
-                // +------------------+---------------+--------------+--------+
-                // | Netlink Header   | Family Header | Attributes   | rtattr |
-                // | struct nlmsghdr  | struct rtmsg  | struct rtattr|  data  |
-                // +------------------+---------------+--------------+--------+
-                final ByteBuffer bytes = mDependencies.recvMessage(fd);
-                try {
-                    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 + ", limit=" + bytes.limit());
-                        // End of the message. Stop parsing.
-                        if (nlmsghdr.nlmsg_type == NLMSG_DONE) break;
 
-                        if (nlmsghdr.nlmsg_type != SOCK_DIAG_BY_FAMILY) {
-                            Log.e(TAG, "Expect to get family " + family
-                                    + " SOCK_DIAG_BY_FAMILY message but get "
-                                    + nlmsghdr.nlmsg_type);
-                            break;
-                        }
-
-                        if (isValidInetDiagMsgSize(nlmsgLen)) {
-                            // Get the socket cookie value. Composed by two Integers value.
-                            // Corresponds to inet_diag_sockid in
-                            // &lt;linux_src&gt;/include/uapi/linux/inet_diag.h
-                            bytes.position(bytes.position() + IDIAG_COOKIE_OFFSET);
-                            // 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()
-                                    + 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);
-                        }
-                    }
-                } catch (IllegalArgumentException | BufferUnderflowException e) {
-                    Log.wtf(TAG, "Unexpected socket info parsing, family " + family
-                            + " buffer:" + bytes + " "
-                            + Base64.getEncoder().encodeToString(bytes.array()), e);
+                while (parseMessage(mDependencies.recvMessage(fd), family, stat, time)) {
+                    log("Pending info exist. Attempt to read more");
                 }
             }
+
             // Calculate mLatestReceiveCount, mSentSinceLastRecv and mLatestPacketFailPercentage.
             mSentSinceLastRecv = (stat.receivedCount == 0)
                     ? (mSentSinceLastRecv + stat.sentCount) : 0;
@@ -276,6 +229,85 @@
         return false;
     }
 
+    // Return true if there are more pending messages to read
+    private boolean parseMessage(ByteBuffer bytes, int family, TcpStat stat, long time) {
+        if (!enoughBytesRemainForValidNlMsg(bytes)) {
+            // This is unlikely to happen in real cases. Check this first for testing.
+            Log.e(TAG, "Size is less than header size. Ignored.");
+            return false;
+        }
+
+        // Messages are composed with the following format. Stop parsing when receiving
+        // message with nlmsg_type NLMSG_DONE.
+        // +------------------+---------------+--------------+--------+
+        // | Netlink Header   | Family Header | Attributes   | rtattr |
+        // | struct nlmsghdr  | struct rtmsg  | struct rtattr|  data  |
+        // +------------------+---------------+--------------+--------+
+        //               :           :               :
+        // +------------------+---------------+--------------+--------+
+        // | Netlink Header   | Family Header | Attributes   | rtattr |
+        // | struct nlmsghdr  | struct rtmsg  | struct rtattr|  data  |
+        // +------------------+---------------+--------------+--------+
+        try {
+            do {
+                final int nlmsgLen = getLengthAndVerifyMsgHeader(bytes, family);
+                if (nlmsgLen == END_OF_PARSING) return false;
+
+                if (isValidInetDiagMsgSize(nlmsgLen)) {
+                    // Get the socket cookie value. Composed by two Integers value.
+                    // Corresponds to inet_diag_sockid in
+                    // &lt;linux_src&gt;/include/uapi/linux/inet_diag.h
+                    bytes.position(bytes.position() + IDIAG_COOKIE_OFFSET);
+
+                    // It's stored in native with 2 int. Parse it as long for
+                    // convenience.
+                    final long cookie = bytes.getLong();
+                    log("cookie=" + cookie);
+                    // Skip the rest part of StructInetDiagMsg.
+                    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);
+                }
+            } while (enoughBytesRemainForValidNlMsg(bytes));
+        } catch (IllegalArgumentException | BufferUnderflowException e) {
+            Log.wtf(TAG, "Unexpected socket info parsing, family " + family
+                    + " buffer:" + bytes + " "
+                    + Base64.getEncoder().encodeToString(bytes.array()), e);
+            return false;
+        }
+
+        return true;
+    }
+
+    private int getLengthAndVerifyMsgHeader(@NonNull ByteBuffer bytes, int family) {
+        final StructNlMsgHdr nlmsghdr = StructNlMsgHdr.parse(bytes);
+        if (nlmsghdr == null) {
+            Log.e(TAG, "Badly formatted data.");
+            return END_OF_PARSING;
+        }
+
+        log("pollSocketsInfo: nlmsghdr=" + nlmsghdr + ", limit=" + bytes.limit());
+        // End of the message. Stop parsing.
+        if (nlmsghdr.nlmsg_type == NLMSG_DONE) {
+            return END_OF_PARSING;
+        }
+
+        if (nlmsghdr.nlmsg_type != SOCK_DIAG_BY_FAMILY) {
+            Log.e(TAG, "Expect to get family " + family
+                    + " SOCK_DIAG_BY_FAMILY message but get "
+                    + nlmsghdr.nlmsg_type);
+            return END_OF_PARSING;
+        }
+
+        return nlmsghdr.nlmsg_len;
+    }
+
     private void cleanupSocketInfo(final long time) {
         final int size = mSocketInfos.size();
         final List<Long> toRemove = new ArrayList<Long>();
diff --git a/tests/integration/Android.bp b/tests/integration/Android.bp
index fca588d..5926fec 100644
--- a/tests/integration/Android.bp
+++ b/tests/integration/Android.bp
@@ -82,6 +82,7 @@
     platform_apis: true,
     test_suites: ["device-tests"],
     jarjar_rules: ":NetworkStackJarJarRules",
+    host_required: ["net-tests-utils-host-common"],
     test_config_template: "AndroidTestTemplate_Integration.xml",
 }
 
@@ -104,6 +105,7 @@
     platform_apis: true,
     test_suites: ["device-tests"],
     jarjar_rules: ":NetworkStackJarJarRules",
+    host_required: ["net-tests-utils-host-common"],
     test_config_template: "AndroidTestTemplate_Integration.xml",
 }
 
@@ -123,8 +125,11 @@
     ],
     platform_apis: true,
     test_suites: ["general-tests", "mts-networking"],
+    compile_multilib: "both",
     manifest: "AndroidManifest_root.xml",
     jarjar_rules: ":NetworkStackJarJarRules",
+    host_required: ["net-tests-utils-host-common"],
+    test_config_template: "AndroidTestTemplate_Integration.xml",
 }
 
 // Special version of the network stack tests that includes all tests necessary for code coverage
diff --git a/tests/integration/AndroidTestTemplate_Integration.xml b/tests/integration/AndroidTestTemplate_Integration.xml
index 7ea8ad6..6107ccc 100644
--- a/tests/integration/AndroidTestTemplate_Integration.xml
+++ b/tests/integration/AndroidTestTemplate_Integration.xml
@@ -23,8 +23,12 @@
          that the shell can write to and that the networkstack can read from. -->
     <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
 
+    <target_preparer class="com.android.testutils.DisableConfigSyncTargetPreparer" />
+
+    <option name="config-descriptor:metadata" key="mainline-param" value="CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex" />
+    <option name="config-descriptor:metadata" key="mainline-param" value="CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
-        <option name="package" value="com.android.server.networkstack.integrationtests" />
+        <option name="package" value="{PACKAGE}" />
         <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
         <!-- By default, include and exclude filters go into /data/local/tmp/ajur/, which these
              tests cannot access because they run in the network_stack selinux context. Move
diff --git a/tests/integration/signature/android/net/util/NetworkStackUtilsIntegrationTest.kt b/tests/integration/signature/android/net/util/NetworkStackUtilsIntegrationTest.kt
index 17153de..3436cb6 100644
--- a/tests/integration/signature/android/net/util/NetworkStackUtilsIntegrationTest.kt
+++ b/tests/integration/signature/android/net/util/NetworkStackUtilsIntegrationTest.kt
@@ -59,8 +59,10 @@
 import java.net.Inet4Address
 import java.net.Inet6Address
 import java.nio.ByteBuffer
+import java.util.Arrays
 import kotlin.reflect.KClass
 import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
 import kotlin.test.assertTrue
 import kotlin.test.fail
 
@@ -75,6 +77,10 @@
     private val TEST_TARGET_MAC = MacAddress.fromString("01:23:45:67:89:0A")
     private val TEST_INET6ADDR_1 = parseNumericAddress("2001:db8::1") as Inet6Address
     private val TEST_INET6ADDR_2 = parseNumericAddress("2001:db8::2") as Inet6Address
+    private val TEST_INET6ADDR_3 = parseNumericAddress("fd01:db8::3") as Inet6Address
+
+    // RFC4291 section 2.7.1
+    private val SOLICITED_NODE_MULTICAST_PREFIX = "FF02:0:0:0:0:1:FF00::/104"
 
     private val readerHandler = HandlerThread(
             NetworkStackUtilsIntegrationTest::class.java.simpleName)
@@ -185,6 +191,31 @@
         assertArrayEquals("Received packet != expected $descr",
                 expected, buffer.copyOfRange(0, readPacket))
     }
+
+    private fun assertSolicitedNodeMulticastAddress(
+        expected: Inet6Address?,
+        unicast: Inet6Address
+    ) {
+        assertNotNull(expected)
+        val prefix = IpPrefix(SOLICITED_NODE_MULTICAST_PREFIX)
+        assertTrue(prefix.contains(expected))
+        assertTrue(expected.isMulticastAddress())
+        // check the last 3 bytes of address
+        assertArrayEquals(Arrays.copyOfRange(expected.getAddress(), 13, 15),
+                Arrays.copyOfRange(unicast.getAddress(), 13, 15))
+    }
+
+    @Test
+    fun testConvertIpv6AddressToSolicitedNodeMulticast() {
+        val addr1 = NetworkStackUtils.ipv6AddressToSolicitedNodeMulticast(TEST_INET6ADDR_1)
+        assertSolicitedNodeMulticastAddress(addr1, TEST_INET6ADDR_1)
+
+        val addr2 = NetworkStackUtils.ipv6AddressToSolicitedNodeMulticast(TEST_INET6ADDR_2)
+        assertSolicitedNodeMulticastAddress(addr2, TEST_INET6ADDR_2)
+
+        val addr3 = NetworkStackUtils.ipv6AddressToSolicitedNodeMulticast(TEST_INET6ADDR_3)
+        assertSolicitedNodeMulticastAddress(addr3, TEST_INET6ADDR_3)
+    }
 }
 
 private fun ByteBuffer.readAsArray(): ByteArray {
diff --git a/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java b/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java
index 457f0c3..1177f96 100644
--- a/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java
+++ b/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java
@@ -23,7 +23,6 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.net.module.util.netlink.NetlinkConstants.SOCKDIAG_MSG_HEADER_SIZE;
 
 import static junit.framework.Assert.assertEquals;
@@ -35,6 +34,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
@@ -76,28 +76,11 @@
     private static final int TEST_BUFFER_SIZE = 1024;
     private static final String DIAG_MSG_HEX =
             // struct nlmsghdr.
-            "58000000" +      // length = 88
+            "10000000" +     // length = 16
             "1400" +         // type = SOCK_DIAG_BY_FAMILY
             "0301" +         // flags = NLM_F_REQUEST | NLM_F_DUMP
             "00000000" +     // seqno
-            "00000000" +     // pid (0 == kernel)
-            // struct inet_diag_req_v2
-            "02" +           // family = AF_INET
-            "06" +           // state
-            "00" +           // timer
-            "00" +           // retrans
-            // inet_diag_sockid
-            "DEA5" +         // idiag_sport = 42462
-            "71B9" +         // idiag_dport = 47473
-            "0a006402000000000000000000000000" + // idiag_src = 10.0.100.2
-            "08080808000000000000000000000000" + // idiag_dst = 8.8.8.8
-            "00000000" +    // idiag_if
-            "34ED000076270000" + // idiag_cookie = 43387759684916
-            "00000000" +    // idiag_expires
-            "00000000" +    // idiag_rqueue
-            "00000000" +    // idiag_wqueue
-            "00000000" +    // idiag_uid
-            "00000000";    // idiag_inode
+            "00000000";      // pid (0 == kernel)
     private static final byte[] SOCK_DIAG_MSG_BYTES =
             HexEncoding.decode(DIAG_MSG_HEX.toCharArray(), false);
     // Hexadecimal representation of a SOCK_DIAG response with tcp info.
@@ -300,8 +283,9 @@
         assertEquals(-1, tst.getLatestPacketFailPercentage());
         assertEquals(0, tst.getSentSinceLastRecv());
 
-        final ByteBuffer tcpBuffer = getByteBuffer(TEST_RESPONSE_BYTES);
-        when(mDependencies.recvMessage(any())).thenReturn(tcpBuffer);
+        final ByteBuffer tcpBufferV6 = getByteBuffer(TEST_RESPONSE_BYTES);
+        final ByteBuffer tcpBufferV4 = getByteBuffer(TEST_RESPONSE_BYTES);
+        doReturn(tcpBufferV6, tcpBufferV4).when(mDependencies).recvMessage(any());
         assertTrue(tst.pollSocketsInfo());
 
         assertEquals(10, tst.getSentSinceLastRecv());
@@ -377,15 +361,16 @@
         // This test requires shims that provide API 30 access
         assumeTrue(ConstantsShim.VERSION >= 30);
         final TcpSocketTracker tst = new TcpSocketTracker(mDependencies, mNetwork);
-        ByteBuffer tcpBuffer = getByteBuffer(TEST_RESPONSE_BYTES);
-
-        when(mDependencies.recvMessage(any())).thenReturn(tcpBuffer);
+        final ByteBuffer tcpBufferV6 = getByteBuffer(TEST_RESPONSE_BYTES);
+        final ByteBuffer tcpBufferV4 = getByteBuffer(TEST_RESPONSE_BYTES);
+        doReturn(tcpBufferV6, tcpBufferV4).when(mDependencies).recvMessage(any());
         assertTrue(tst.pollSocketsInfo());
         assertEquals(10, tst.getSentSinceLastRecv());
         assertEquals(50, tst.getLatestPacketFailPercentage());
 
-        tcpBuffer = getByteBuffer(BAD_SOCK_DIAG_MSG_BYTES);
-        when(mDependencies.recvMessage(any())).thenReturn(tcpBuffer);
+        final ByteBuffer badTcpBufferV6 = getByteBuffer(BAD_SOCK_DIAG_MSG_BYTES);
+        final ByteBuffer badTcpBufferV4 = getByteBuffer(BAD_SOCK_DIAG_MSG_BYTES);
+        doReturn(badTcpBufferV6, badTcpBufferV4).when(mDependencies).recvMessage(any());
         assertTrue(tst.pollSocketsInfo());
         // Expect no additional packets, so still 10.
         assertEquals(10, tst.getSentSinceLastRecv());
@@ -398,8 +383,9 @@
         when(mNetd.getFwmarkForNetwork(eq(TEST_NETID2)))
                 .thenReturn(makeMarkMaskParcel(NETID_MASK, TEST_NETID2_FWMARK));
         final TcpSocketTracker tst = new TcpSocketTracker(mDependencies, mOtherNetwork);
-        final ByteBuffer tcpBuffer = getByteBuffer(TEST_RESPONSE_BYTES);
-        when(mDependencies.recvMessage(any())).thenReturn(tcpBuffer);
+        final ByteBuffer tcpBufferV6 = getByteBuffer(TEST_RESPONSE_BYTES);
+        final ByteBuffer tcpBufferV4 = getByteBuffer(TEST_RESPONSE_BYTES);
+        doReturn(tcpBufferV6, tcpBufferV4).when(mDependencies).recvMessage(any());
         assertTrue(tst.pollSocketsInfo());
 
         assertEquals(0, tst.getSentSinceLastRecv());