Merge "Revert^2 "Replace the usage of UidRange""
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index e9deeff..164bda4 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -35,7 +35,10 @@
         "offload.o",
         "test.o",
     ],
-    apps: ["Tethering"],
+    apps: [
+        "ServiceConnectivityResources",
+        "Tethering",
+    ],
     manifest: "manifest.json",
     key: "com.android.tethering.key",
 
@@ -58,6 +61,7 @@
     base: "com.android.tethering",
     package_name: "com.android.tethering.inprocess",
     apps: [
+        "ServiceConnectivityResources",
         "InProcessTethering",
     ],
 }
diff --git a/Tethering/bpf_progs/Android.bp b/Tethering/bpf_progs/Android.bp
index 2b10f89..289d75d 100644
--- a/Tethering/bpf_progs/Android.bp
+++ b/Tethering/bpf_progs/Android.bp
@@ -51,8 +51,6 @@
     include_dirs: [
         // TODO: get rid of system/netd.
         "system/netd/bpf_progs",             // for bpf_net_helpers.h
-        "system/netd/libnetdbpf/include",    // for bpf_shared.h
-        "system/netd/libnetdutils/include",  // for UidConstants.h
     ],
 }
 
@@ -66,7 +64,5 @@
     include_dirs: [
         // TODO: get rid of system/netd.
         "system/netd/bpf_progs",             // for bpf_net_helpers.h
-        "system/netd/libnetdbpf/include",    // for bpf_shared.h
-        "system/netd/libnetdutils/include",  // for UidConstants.h
     ],
 }
diff --git a/Tethering/bpf_progs/offload.c b/Tethering/bpf_progs/offload.c
index 7f9754d..5f29d4f 100644
--- a/Tethering/bpf_progs/offload.c
+++ b/Tethering/bpf_progs/offload.c
@@ -72,11 +72,11 @@
 DEFINE_BPF_MAP_GRW(tether_error_map, ARRAY, uint32_t, uint32_t, BPF_TETHER_ERR__MAX,
                    AID_NETWORK_STACK)
 
-#define COUNT_AND_RETURN(counter, ret) do {                  \
+#define COUNT_AND_RETURN(counter, ret) do {                     \
     uint32_t code = BPF_TETHER_ERR_ ## counter;                 \
     uint32_t *count = bpf_tether_error_map_lookup_elem(&code);  \
-    if (count) __sync_fetch_and_add(count, 1);               \
-    return ret;                                              \
+    if (count) __sync_fetch_and_add(count, 1);                  \
+    return ret;                                                 \
 } while(0)
 
 #define TC_DROP(counter) COUNT_AND_RETURN(counter, TC_ACT_SHOT)
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index 8df3045..74eb87b 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -261,6 +261,7 @@
 
         /** Get downstream4 BPF map. */
         @Nullable public BpfMap<Tether4Key, Tether4Value> getBpfDownstream4Map() {
+            if (!isAtLeastS()) return null;
             try {
                 return new BpfMap<>(TETHER_DOWNSTREAM4_MAP_PATH,
                     BpfMap.BPF_F_RDWR, Tether4Key.class, Tether4Value.class);
@@ -272,6 +273,7 @@
 
         /** Get upstream4 BPF map. */
         @Nullable public BpfMap<Tether4Key, Tether4Value> getBpfUpstream4Map() {
+            if (!isAtLeastS()) return null;
             try {
                 return new BpfMap<>(TETHER_UPSTREAM4_MAP_PATH,
                     BpfMap.BPF_F_RDWR, Tether4Key.class, Tether4Value.class);
@@ -283,6 +285,7 @@
 
         /** Get downstream6 BPF map. */
         @Nullable public BpfMap<TetherDownstream6Key, Tether6Value> getBpfDownstream6Map() {
+            if (!isAtLeastS()) return null;
             try {
                 return new BpfMap<>(TETHER_DOWNSTREAM6_FS_PATH,
                     BpfMap.BPF_F_RDWR, TetherDownstream6Key.class, Tether6Value.class);
@@ -294,6 +297,7 @@
 
         /** Get upstream6 BPF map. */
         @Nullable public BpfMap<TetherUpstream6Key, Tether6Value> getBpfUpstream6Map() {
+            if (!isAtLeastS()) return null;
             try {
                 return new BpfMap<>(TETHER_UPSTREAM6_FS_PATH, BpfMap.BPF_F_RDWR,
                         TetherUpstream6Key.class, Tether6Value.class);
@@ -305,6 +309,7 @@
 
         /** Get stats BPF map. */
         @Nullable public BpfMap<TetherStatsKey, TetherStatsValue> getBpfStatsMap() {
+            if (!isAtLeastS()) return null;
             try {
                 return new BpfMap<>(TETHER_STATS_MAP_PATH,
                     BpfMap.BPF_F_RDWR, TetherStatsKey.class, TetherStatsValue.class);
@@ -316,6 +321,7 @@
 
         /** Get limit BPF map. */
         @Nullable public BpfMap<TetherLimitKey, TetherLimitValue> getBpfLimitMap() {
+            if (!isAtLeastS()) return null;
             try {
                 return new BpfMap<>(TETHER_LIMIT_MAP_PATH,
                     BpfMap.BPF_F_RDWR, TetherLimitKey.class, TetherLimitValue.class);
@@ -420,7 +426,8 @@
      * See NetlinkMonitor#handlePacket, NetlinkMessage#parseNfMessage.
      */
     public void startMonitoring(@NonNull final IpServer ipServer) {
-        if (!isUsingBpf()) return;
+        // TODO: Wrap conntrackMonitor starting function into mBpfCoordinatorShim.
+        if (!isUsingBpf() || !mDeps.isAtLeastS()) return;
 
         if (mMonitoringIpServers.contains(ipServer)) {
             Log.wtf(TAG, "The same downstream " + ipServer.interfaceName()
@@ -441,6 +448,9 @@
      * Note that this can be only called on handler thread.
      */
     public void stopMonitoring(@NonNull final IpServer ipServer) {
+        // TODO: Wrap conntrackMonitor stopping function into mBpfCoordinatorShim.
+        if (!isUsingBpf() || !mDeps.isAtLeastS()) return;
+
         mMonitoringIpServers.remove(ipServer);
 
         if (!mMonitoringIpServers.isEmpty()) return;
@@ -828,7 +838,7 @@
             }
             map.forEach((k, v) -> pw.println(ipv6UpstreamRuletoString(k, v)));
         } catch (ErrnoException e) {
-            pw.println("Error dumping IPv4 map: " + e);
+            pw.println("Error dumping IPv6 upstream map: " + e);
         }
     }
 
@@ -875,6 +885,10 @@
     }
 
     private void dumpCounters(@NonNull IndentingPrintWriter pw) {
+        if (!mDeps.isAtLeastS()) {
+            pw.println("No counter support");
+            return;
+        }
         try (BpfMap<U32Struct, U32Struct> map = new BpfMap<>(TETHER_ERROR_MAP_PATH,
                 BpfMap.BPF_F_RDONLY, U32Struct.class, U32Struct.class)) {
 
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfUtils.java b/Tethering/src/com/android/networkstack/tethering/BpfUtils.java
index 289452c..0b44249 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfUtils.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfUtils.java
@@ -56,7 +56,7 @@
     // Sync from system/netd/server/OffloadUtils.h.
     static final short PRIO_TETHER6 = 1;
     static final short PRIO_TETHER4 = 2;
-    static final short PRIO_CLAT = 3;
+    // note that the above must be lower than PRIO_CLAT from netd's OffloadUtils.cpp
 
     private static String makeProgPath(boolean downstream, int ipVersion, boolean ether) {
         String path = "/sys/fs/bpf/tethering/prog_offload_schedcls_tether_"
diff --git a/Tethering/tests/mts/Android.bp b/Tethering/tests/mts/Android.bp
index 20a5f18..edb6356 100644
--- a/Tethering/tests/mts/Android.bp
+++ b/Tethering/tests/mts/Android.bp
@@ -52,7 +52,7 @@
     // Tag this module as a mts test artifact
     test_suites: [
         "general-tests",
-        "mts",
+        "mts-tethering",
     ],
 
     // Include both the 32 and 64 bit versions
diff --git a/Tethering/tests/privileged/src/android/net/ip/RouterAdvertisementDaemonTest.java b/Tethering/tests/privileged/src/android/net/ip/RouterAdvertisementDaemonTest.java
new file mode 100644
index 0000000..14dae5c
--- /dev/null
+++ b/Tethering/tests/privileged/src/android/net/ip/RouterAdvertisementDaemonTest.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2020 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 android.net.ip;
+
+import static com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN;
+import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV6;
+import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_MTU;
+import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_PIO;
+import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_RDNSS;
+import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_SLLA;
+import static com.android.net.module.util.NetworkStackConstants.ICMPV6_RA_HEADER_LEN;
+import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
+import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_NODES_MULTICAST;
+import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_LEN;
+import static com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.net.INetd;
+import android.net.IpPrefix;
+import android.net.MacAddress;
+import android.net.ip.RouterAdvertisementDaemon.RaParams;
+import android.net.util.InterfaceParams;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.net.module.util.Ipv6Utils;
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.structs.EthernetHeader;
+import com.android.net.module.util.structs.Icmpv6Header;
+import com.android.net.module.util.structs.Ipv6Header;
+import com.android.net.module.util.structs.LlaOption;
+import com.android.net.module.util.structs.MtuOption;
+import com.android.net.module.util.structs.PrefixInformationOption;
+import com.android.net.module.util.structs.RaHeader;
+import com.android.net.module.util.structs.RdnssOption;
+import com.android.testutils.TapPacketReader;
+import com.android.testutils.TapPacketReaderRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.util.HashSet;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class RouterAdvertisementDaemonTest {
+    private static final String TAG = RouterAdvertisementDaemonTest.class.getSimpleName();
+    private static final int DATA_BUFFER_LEN = 4096;
+    private static final int PACKET_TIMEOUT_MS = 5_000;
+
+    @Rule
+    public final TapPacketReaderRule mTetheredReader = new TapPacketReaderRule(
+            DATA_BUFFER_LEN, false /* autoStart */);
+
+    private InterfaceParams mTetheredParams;
+    private HandlerThread mHandlerThread;
+    private Handler mHandler;
+    private TapPacketReader mTetheredPacketReader;
+    private RouterAdvertisementDaemon mRaDaemon;
+
+    private static INetd sNetd;
+
+    @BeforeClass
+    public static void setupOnce() {
+        System.loadLibrary("tetherutilsjni");
+
+        final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
+        final IBinder netdIBinder =
+                (IBinder) inst.getContext().getSystemService(Context.NETD_SERVICE);
+        sNetd = INetd.Stub.asInterface(netdIBinder);
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mHandlerThread = new HandlerThread(getClass().getSimpleName());
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+
+        setupTapInterfaces();
+
+        // Looper must be prepared here since AndroidJUnitRunner runs tests on separate threads.
+        if (Looper.myLooper() == null) Looper.prepare();
+
+        mRaDaemon = new RouterAdvertisementDaemon(mTetheredParams);
+        sNetd.networkAddInterface(INetd.LOCAL_NET_ID, mTetheredParams.name);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mTetheredReader.stop();
+        if (mHandlerThread != null) {
+            mHandlerThread.quitSafely();
+            mHandlerThread.join(PACKET_TIMEOUT_MS);
+        }
+
+        if (mTetheredParams != null) {
+            sNetd.networkRemoveInterface(INetd.LOCAL_NET_ID, mTetheredParams.name);
+        }
+    }
+
+    private void setupTapInterfaces() {
+        // Create tethered test iface.
+        mTetheredReader.start(mHandler);
+        mTetheredParams = InterfaceParams.getByName(mTetheredReader.iface.getInterfaceName());
+        assertNotNull(mTetheredParams);
+        mTetheredPacketReader = mTetheredReader.getReader();
+        mHandler.post(mTetheredPacketReader::start);
+    }
+
+    private class TestRaPacket {
+        final RaParams mNewParams, mOldParams;
+
+        TestRaPacket(final RaParams oldParams, final RaParams newParams) {
+            mOldParams = oldParams;
+            mNewParams = newParams;
+        }
+
+        public boolean isPacketMatched(final byte[] pkt) throws Exception {
+            if (pkt.length < (ETHER_HEADER_LEN + IPV6_HEADER_LEN + ICMPV6_RA_HEADER_LEN)) {
+                return false;
+            }
+            final ByteBuffer buf = ByteBuffer.wrap(pkt);
+
+            // Parse Ethernet header
+            final EthernetHeader ethHdr = Struct.parse(EthernetHeader.class, buf);
+            if (ethHdr.etherType != ETHER_TYPE_IPV6) return false;
+
+            // Parse IPv6 header
+            final Ipv6Header ipv6Hdr = Struct.parse(Ipv6Header.class, buf);
+            assertEquals((ipv6Hdr.vtf >> 28), 6 /* ip version*/);
+
+            final int payLoadLength = pkt.length - ETHER_HEADER_LEN - IPV6_HEADER_LEN;
+            assertEquals(payLoadLength, ipv6Hdr.payloadLength);
+
+            // Parse ICMPv6 header
+            final Icmpv6Header icmpv6Hdr = Struct.parse(Icmpv6Header.class, buf);
+            if (icmpv6Hdr.type != (short) ICMPV6_ROUTER_ADVERTISEMENT) return false;
+
+            // Parse RA header
+            final RaHeader raHdr = Struct.parse(RaHeader.class, buf);
+            assertEquals(mNewParams.hopLimit, raHdr.hopLimit);
+
+            while (buf.position() < pkt.length) {
+                final int currentPos = buf.position();
+                final int type = Byte.toUnsignedInt(buf.get());
+                final int length = Byte.toUnsignedInt(buf.get());
+                switch (type) {
+                    case ICMPV6_ND_OPTION_PIO:
+                        assertEquals(4, length);
+
+                        final ByteBuffer pioBuf = ByteBuffer.wrap(buf.array(), currentPos,
+                                Struct.getSize(PrefixInformationOption.class));
+                        final PrefixInformationOption pio =
+                                Struct.parse(PrefixInformationOption.class, pioBuf);
+                        assertEquals((byte) 0xc0, pio.flags); // L & A set
+
+                        final InetAddress address = InetAddress.getByAddress(pio.prefix);
+                        final IpPrefix prefix = new IpPrefix(address, pio.prefixLen);
+                        if (mNewParams.prefixes.contains(prefix)) {
+                            assertTrue(pio.validLifetime > 0);
+                            assertTrue(pio.preferredLifetime > 0);
+                        } else if (mOldParams != null && mOldParams.prefixes.contains(prefix)) {
+                            assertEquals(0, pio.validLifetime);
+                            assertEquals(0, pio.preferredLifetime);
+                        } else {
+                            fail("Unepxected prefix: " + prefix);
+                        }
+
+                        // Move ByteBuffer position to the next option.
+                        buf.position(currentPos + Struct.getSize(PrefixInformationOption.class));
+                        break;
+                    case ICMPV6_ND_OPTION_MTU:
+                        assertEquals(1, length);
+
+                        final ByteBuffer mtuBuf = ByteBuffer.wrap(buf.array(), currentPos,
+                                Struct.getSize(MtuOption.class));
+                        final MtuOption mtu = Struct.parse(MtuOption.class, mtuBuf);
+                        assertEquals(mNewParams.mtu, mtu.mtu);
+
+                        // Move ByteBuffer position to the next option.
+                        buf.position(currentPos + Struct.getSize(MtuOption.class));
+                        break;
+                    case ICMPV6_ND_OPTION_RDNSS:
+                        final int rdnssHeaderLen = Struct.getSize(RdnssOption.class);
+                        final ByteBuffer RdnssBuf = ByteBuffer.wrap(buf.array(), currentPos,
+                                rdnssHeaderLen);
+                        final RdnssOption rdnss = Struct.parse(RdnssOption.class, RdnssBuf);
+                        final String msg =
+                                rdnss.lifetime > 0 ? "Unknown dns" : "Unknown deprecated dns";
+                        final HashSet<Inet6Address> dnses =
+                                rdnss.lifetime > 0 ? mNewParams.dnses : mOldParams.dnses;
+                        assertNotNull(msg, dnses);
+
+                        // Check DNS servers included in this option.
+                        buf.position(currentPos + rdnssHeaderLen); // skip the rdnss option header
+                        final int numOfDnses = (length - 1) / 2;
+                        for (int i = 0; i < numOfDnses; i++) {
+                            byte[] rawAddress = new byte[IPV6_ADDR_LEN];
+                            buf.get(rawAddress);
+                            final Inet6Address dns =
+                                    (Inet6Address) InetAddress.getByAddress(rawAddress);
+                            if (!dnses.contains(dns)) fail("Unexpected dns: " + dns);
+                        }
+                        // Unnecessary to move ByteBuffer position here, since the position has been
+                        // moved forward correctly after reading DNS servers from ByteBuffer.
+                        break;
+                    case ICMPV6_ND_OPTION_SLLA:
+                        // Do nothing, just move ByteBuffer position to the next option.
+                        buf.position(currentPos + Struct.getSize(LlaOption.class));
+                        break;
+                    default:
+                        fail("Unknown RA option type " + type);
+                }
+            }
+            return true;
+        }
+    }
+
+    private RaParams createRaParams(final String ipv6Address) throws Exception {
+        final RaParams params = new RaParams();
+        final Inet6Address address = (Inet6Address) InetAddress.getByName(ipv6Address);
+        params.dnses.add(address);
+        params.prefixes.add(new IpPrefix(address, 64));
+
+        return params;
+    }
+
+    private boolean assertRaPacket(final TestRaPacket testRa)
+            throws Exception {
+        byte[] packet;
+        while ((packet = mTetheredPacketReader.poll(PACKET_TIMEOUT_MS)) != null) {
+            if (testRa.isPacketMatched(packet)) return true;
+        }
+        return false;
+    }
+
+    private ByteBuffer createRsPacket(final String srcIp) throws Exception {
+        final MacAddress dstMac = MacAddress.fromString("33:33:03:04:05:06");
+        final MacAddress srcMac = mTetheredParams.macAddr;
+        final ByteBuffer slla = LlaOption.build((byte) ICMPV6_ND_OPTION_SLLA, srcMac);
+
+        return Ipv6Utils.buildRsPacket(srcMac, dstMac, (Inet6Address) InetAddress.getByName(srcIp),
+                IPV6_ADDR_ALL_NODES_MULTICAST, slla);
+    }
+
+    @Test
+    public void testUnSolicitRouterAdvertisement() throws Exception {
+        assertTrue(mRaDaemon.start());
+        final RaParams params1 = createRaParams("2001:1122:3344::5566");
+        mRaDaemon.buildNewRa(null, params1);
+        assertRaPacket(new TestRaPacket(null, params1));
+
+        final RaParams params2 = createRaParams("2006:3344:5566::7788");
+        mRaDaemon.buildNewRa(params1, params2);
+        assertRaPacket(new TestRaPacket(params1, params2));
+    }
+
+    @Test
+    public void testSolicitRouterAdvertisement() throws Exception {
+        assertTrue(mRaDaemon.start());
+        final RaParams params1 = createRaParams("2001:1122:3344::5566");
+        mRaDaemon.buildNewRa(null, params1);
+        assertRaPacket(new TestRaPacket(null, params1));
+
+        final ByteBuffer rs = createRsPacket("fe80::1122:3344:5566:7788");
+        mTetheredPacketReader.sendResponse(rs);
+        assertRaPacket(new TestRaPacket(null, params1));
+    }
+}
diff --git a/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java b/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java
index 91c7771..9968b5f 100644
--- a/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java
+++ b/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java
@@ -17,27 +17,49 @@
 
 import static android.net.TetheringManager.TETHERING_USB;
 import static android.net.TetheringManager.TETHERING_WIFI;
+import static android.system.OsConstants.AF_UNIX;
+import static android.system.OsConstants.EAGAIN;
+import static android.system.OsConstants.SOCK_CLOEXEC;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.SOCK_NONBLOCK;
 
+import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
 import android.net.LinkAddress;
+import android.net.MacAddress;
 import android.net.TetheringRequestParcel;
+import android.system.ErrnoException;
+import android.system.Os;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.net.module.util.Ipv6Utils;
+import com.android.net.module.util.NetworkStackConstants;
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.structs.EthernetHeader;
+import com.android.net.module.util.structs.Icmpv6Header;
+import com.android.net.module.util.structs.Ipv6Header;
 import com.android.testutils.MiscAsserts;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.FileDescriptor;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class TetheringUtilsTest {
     private static final LinkAddress TEST_SERVER_ADDR = new LinkAddress("192.168.43.1/24");
     private static final LinkAddress TEST_CLIENT_ADDR = new LinkAddress("192.168.43.5/24");
+    private static final int PACKET_SIZE = 1500;
+
     private TetheringRequestParcel mTetheringRequest;
 
     @Before
@@ -84,4 +106,82 @@
 
         MiscAsserts.assertFieldCountEquals(5, TetheringRequestParcel.class);
     }
+
+    // Writes the specified packet to a filedescriptor, skipping the Ethernet header.
+    // Needed because the Ipv6Utils methods for building packets always include the Ethernet header,
+    // but socket filters applied by TetheringUtils expect the packet to start from the IP header.
+    private int writePacket(FileDescriptor fd, ByteBuffer pkt) throws Exception {
+        pkt.flip();
+        int offset = Struct.getSize(EthernetHeader.class);
+        int len = pkt.capacity() - offset;
+        return Os.write(fd, pkt.array(), offset, len);
+    }
+
+    // Reads a packet from the filedescriptor.
+    private ByteBuffer readIpPacket(FileDescriptor fd) throws Exception {
+        ByteBuffer buf = ByteBuffer.allocate(PACKET_SIZE);
+        Os.read(fd, buf);
+        return buf;
+    }
+
+    private interface SocketFilter {
+        void apply(FileDescriptor fd) throws Exception;
+    }
+
+    private ByteBuffer checkIcmpSocketFilter(ByteBuffer passed, ByteBuffer dropped,
+            SocketFilter filter) throws Exception {
+        FileDescriptor in = new FileDescriptor();
+        FileDescriptor out = new FileDescriptor();
+        Os.socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, in, out);
+
+        // Before the filter is applied, it doesn't drop anything.
+        int len = writePacket(out, dropped);
+        ByteBuffer received = readIpPacket(in);
+        assertEquals(len, received.position());
+
+        // Install the socket filter. Then write two packets, the first expected to be dropped and
+        // the second expected to be passed. Check that only the second makes it through.
+        filter.apply(in);
+        writePacket(out, dropped);
+        len = writePacket(out, passed);
+        received = readIpPacket(in);
+        assertEquals(len, received.position());
+        received.flip();
+
+        // Check there are no more packets to read.
+        try {
+            readIpPacket(in);
+        } catch (ErrnoException expected) {
+            assertEquals(EAGAIN, expected.errno);
+        }
+
+        return received;
+    }
+
+    @Test
+    public void testIcmpSocketFilters() throws Exception {
+        MacAddress mac1 = MacAddress.fromString("11:22:33:44:55:66");
+        MacAddress mac2 = MacAddress.fromString("aa:bb:cc:dd:ee:ff");
+        Inet6Address ll1 = (Inet6Address) InetAddress.getByName("fe80::1");
+        Inet6Address ll2 = (Inet6Address) InetAddress.getByName("fe80::abcd");
+        Inet6Address allRouters = NetworkStackConstants.IPV6_ADDR_ALL_ROUTERS_MULTICAST;
+
+        final ByteBuffer na = Ipv6Utils.buildNaPacket(mac1, mac2, ll1, ll2, 0, ll1);
+        final ByteBuffer ns = Ipv6Utils.buildNsPacket(mac1, mac2, ll1, ll2, ll1);
+        final ByteBuffer rs = Ipv6Utils.buildRsPacket(mac1, mac2, ll1, allRouters);
+
+        ByteBuffer received = checkIcmpSocketFilter(na /* passed */, rs /* dropped */,
+                TetheringUtils::setupNaSocket);
+
+        Struct.parse(Ipv6Header.class, received);  // Skip IPv6 header.
+        Icmpv6Header icmpv6 = Struct.parse(Icmpv6Header.class, received);
+        assertEquals(NetworkStackConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT, icmpv6.type);
+
+        received = checkIcmpSocketFilter(ns /* passed */, rs /* dropped */,
+                TetheringUtils::setupNsSocket);
+
+        Struct.parse(Ipv6Header.class, received);  // Skip IPv6 header.
+        icmpv6 = Struct.parse(Icmpv6Header.class, received);
+        assertEquals(NetworkStackConstants.ICMPV6_NEIGHBOR_SOLICITATION, icmpv6.type);
+    }
 }
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
index ba4ed47..293d0df 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
@@ -77,10 +77,13 @@
 import com.android.net.module.util.NetworkStackConstants;
 import com.android.net.module.util.Struct;
 import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
 import com.android.testutils.TestableNetworkStatsProviderCbBinder;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -102,6 +105,9 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class BpfCoordinatorTest {
+    @Rule
+    public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
+
     private static final int DOWNSTREAM_IFINDEX = 10;
     private static final MacAddress DOWNSTREAM_MAC = MacAddress.ALL_ZEROS_ADDRESS;
     private static final InetAddress NEIGH_A = InetAddresses.parseNumericAddress("2001:db8::1");
@@ -1150,6 +1156,7 @@
     }
 
     @Test
+    @IgnoreUpTo(Build.VERSION_CODES.R)
     public void testStartStopConntrackMonitoring() throws Exception {
         setupFunctioningNetdInterface();
 
@@ -1170,6 +1177,23 @@
     }
 
     @Test
+    @IgnoreUpTo(Build.VERSION_CODES.Q)
+    @IgnoreAfter(Build.VERSION_CODES.R)
+    // Only run this test on Android R.
+    public void testStartStopConntrackMonitoring_R() throws Exception {
+        setupFunctioningNetdInterface();
+
+        final BpfCoordinator coordinator = makeBpfCoordinator();
+
+        coordinator.startMonitoring(mIpServer);
+        verify(mConntrackMonitor, never()).start();
+
+        coordinator.stopMonitoring(mIpServer);
+        verify(mConntrackMonitor, never()).stop();
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.R)
     public void testStartStopConntrackMonitoringWithTwoDownstreamIfaces() throws Exception {
         setupFunctioningNetdInterface();
 
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index 62160df..cd69b13 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -91,7 +91,10 @@
     target_sdk_version: "30",
     test_suites: [
         "general-tests",
-        "mts",
+        "mts-dnsresolver",
+        "mts-networking",
+        "mts-tethering",
+        "mts-wifi",
     ],
     test_config_template: "AndroidTestTemplate.xml",
 }
diff --git a/tests/cts/net/native/dns/Android.bp b/tests/cts/net/native/dns/Android.bp
index 1bc5a86..5e9af8e 100644
--- a/tests/cts/net/native/dns/Android.bp
+++ b/tests/cts/net/native/dns/Android.bp
@@ -40,6 +40,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-dnsresolver",
+        "mts-networking",
     ],
 }
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index e3208e7..bfab497 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -19,12 +19,29 @@
 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
 import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
 import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.content.pm.PackageManager.FEATURE_BLUETOOTH;
 import static android.content.pm.PackageManager.FEATURE_ETHERNET;
 import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
 import static android.content.pm.PackageManager.FEATURE_USB_HOST;
+import static android.content.pm.PackageManager.FEATURE_WATCH;
 import static android.content.pm.PackageManager.FEATURE_WIFI;
+import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
+import static android.net.ConnectivityManager.TYPE_ETHERNET;
+import static android.net.ConnectivityManager.TYPE_MOBILE_CBS;
+import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
+import static android.net.ConnectivityManager.TYPE_MOBILE_EMERGENCY;
+import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
+import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
+import static android.net.ConnectivityManager.TYPE_MOBILE_IA;
+import static android.net.ConnectivityManager.TYPE_MOBILE_IMS;
+import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
+import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
+import static android.net.ConnectivityManager.TYPE_PROXY;
+import static android.net.ConnectivityManager.TYPE_VPN;
+import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
@@ -78,7 +95,6 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
-import android.net.NetworkConfig;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkInfo.State;
@@ -100,7 +116,9 @@
 import android.os.VintfRuntimeInfo;
 import android.platform.test.annotations.AppModeFull;
 import android.provider.Settings;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
 
@@ -115,6 +133,7 @@
 import com.android.testutils.CompatUtil;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+import com.android.testutils.DevSdkIgnoreRuleKt;
 import com.android.testutils.RecorderCallback.CallbackEntry;
 import com.android.testutils.SkipPresubmit;
 import com.android.testutils.TestableNetworkCallback;
@@ -146,7 +165,6 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
@@ -201,8 +219,7 @@
     private ConnectivityManagerShim mCmShim;
     private WifiManager mWifiManager;
     private PackageManager mPackageManager;
-    private final HashMap<Integer, NetworkConfig> mNetworks =
-            new HashMap<Integer, NetworkConfig>();
+    private final ArraySet<Integer> mNetworkTypes = new ArraySet<>();
     private UiAutomation mUiAutomation;
     private CtsNetUtils mCtsNetUtils;
 
@@ -216,25 +233,69 @@
         mPackageManager = mContext.getPackageManager();
         mCtsNetUtils = new CtsNetUtils(mContext);
 
-        // Get com.android.internal.R.array.networkAttributes
-        int resId = mContext.getResources().getIdentifier("networkAttributes", "array", "android");
-        String[] naStrings = mContext.getResources().getStringArray(resId);
-        //TODO: What is the "correct" way to determine if this is a wifi only device?
-        boolean wifiOnly = SystemProperties.getBoolean("ro.radio.noril", false);
-        for (String naString : naStrings) {
-            try {
-                NetworkConfig n = new NetworkConfig(naString);
-                if (wifiOnly && ConnectivityManager.isNetworkTypeMobile(n.type)) {
-                    continue;
-                }
-                mNetworks.put(n.type, n);
-            } catch (Exception e) {}
+        if (DevSdkIgnoreRuleKt.isDevSdkInRange(null /* minExclusive */,
+                Build.VERSION_CODES.R /* maxInclusive */)) {
+            addLegacySupportedNetworkTypes();
+        } else {
+            addSupportedNetworkTypes();
         }
+
         mUiAutomation = mInstrumentation.getUiAutomation();
 
         assertNotNull("CTS requires a working Internet connection", mCm.getActiveNetwork());
     }
 
+    private void addLegacySupportedNetworkTypes() {
+        // Network type support as expected for android R-
+        // Get com.android.internal.R.array.networkAttributes
+        int resId = mContext.getResources().getIdentifier("networkAttributes", "array", "android");
+        String[] naStrings = mContext.getResources().getStringArray(resId);
+        boolean wifiOnly = SystemProperties.getBoolean("ro.radio.noril", false);
+        for (String naString : naStrings) {
+            try {
+                final String[] splitConfig = naString.split(",");
+                // Format was name,type,radio,priority,restoreTime,dependencyMet
+                final int type = Integer.parseInt(splitConfig[1]);
+                if (wifiOnly && ConnectivityManager.isNetworkTypeMobile(type)) {
+                    continue;
+                }
+                mNetworkTypes.add(type);
+            } catch (Exception e) {}
+        }
+    }
+
+    private void addSupportedNetworkTypes() {
+        final PackageManager pm = mContext.getPackageManager();
+        if (pm.hasSystemFeature(FEATURE_WIFI)) {
+            mNetworkTypes.add(TYPE_WIFI);
+        }
+        if (pm.hasSystemFeature(FEATURE_WIFI_DIRECT)) {
+            mNetworkTypes.add(TYPE_WIFI_P2P);
+        }
+        if (mContext.getSystemService(TelephonyManager.class).isDataCapable()) {
+            mNetworkTypes.add(TYPE_MOBILE);
+            mNetworkTypes.add(TYPE_MOBILE_MMS);
+            mNetworkTypes.add(TYPE_MOBILE_SUPL);
+            mNetworkTypes.add(TYPE_MOBILE_DUN);
+            mNetworkTypes.add(TYPE_MOBILE_HIPRI);
+            mNetworkTypes.add(TYPE_MOBILE_FOTA);
+            mNetworkTypes.add(TYPE_MOBILE_IMS);
+            mNetworkTypes.add(TYPE_MOBILE_CBS);
+            mNetworkTypes.add(TYPE_MOBILE_IA);
+            mNetworkTypes.add(TYPE_MOBILE_EMERGENCY);
+        }
+        if (pm.hasSystemFeature(FEATURE_BLUETOOTH)) {
+            mNetworkTypes.add(TYPE_BLUETOOTH);
+        }
+        if (pm.hasSystemFeature(FEATURE_WATCH)) {
+            mNetworkTypes.add(TYPE_PROXY);
+        }
+        if (mContext.getSystemService(Context.ETHERNET_SERVICE) != null) {
+            mNetworkTypes.add(TYPE_ETHERNET);
+        }
+        mNetworkTypes.add(TYPE_VPN);
+    }
+
     @After
     public void tearDown() throws Exception {
         // Release any NetworkRequests filed to connect mobile data.
@@ -461,9 +522,9 @@
     }
 
     private boolean shouldBeSupported(int networkType) {
-        return mNetworks.containsKey(networkType) ||
-               (networkType == ConnectivityManager.TYPE_VPN) ||
-               (networkType == ConnectivityManager.TYPE_ETHERNET && shouldEthernetBeSupported());
+        return mNetworkTypes.contains(networkType)
+            || (networkType == ConnectivityManager.TYPE_VPN)
+            || (networkType == ConnectivityManager.TYPE_ETHERNET && shouldEthernetBeSupported());
     }
 
     @Test
@@ -1603,15 +1664,16 @@
 
         // Verify background network cannot be requested without NETWORK_SETTINGS permission.
         final TestableNetworkCallback callback = new TestableNetworkCallback();
+        final Handler handler = new Handler(Looper.getMainLooper());
         assertThrows(SecurityException.class,
-                () -> mCmShim.requestBackgroundNetwork(testRequest, null, callback));
+                () -> mCmShim.requestBackgroundNetwork(testRequest, handler, callback));
 
         Network testNetwork = null;
         try {
             // Request background test network via Shell identity which has NETWORK_SETTINGS
             // permission granted.
             runWithShellPermissionIdentity(
-                    () -> mCmShim.requestBackgroundNetwork(testRequest, null, callback),
+                    () -> mCmShim.requestBackgroundNetwork(testRequest, handler, callback),
                     new String[] { android.Manifest.permission.NETWORK_SETTINGS });
 
             // Register the test network agent which has no foreground request associated to it.
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 827a05e..ef529f8 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -18,6 +18,8 @@
 import android.app.Instrumentation
 import android.content.Context
 import android.net.ConnectivityManager
+import android.net.INetworkAgent
+import android.net.INetworkAgentRegistry
 import android.net.InetAddresses
 import android.net.IpPrefix
 import android.net.KeepalivePacketData
@@ -44,6 +46,7 @@
 import android.net.NetworkInfo
 import android.net.NetworkProvider
 import android.net.NetworkRequest
+import android.net.NetworkScore
 import android.net.RouteInfo
 import android.net.SocketKeepalive
 import android.net.Uri
@@ -65,8 +68,6 @@
 import android.os.Message
 import android.util.DebugUtils.valueToString
 import androidx.test.InstrumentationRegistry
-import com.android.connectivity.aidl.INetworkAgent
-import com.android.connectivity.aidl.INetworkAgentRegistry
 import com.android.modules.utils.build.SdkLevel
 import com.android.net.module.util.ArrayTrackRecord
 import com.android.testutils.CompatUtil
@@ -81,7 +82,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.argThat
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mockito.doReturn
@@ -632,7 +632,7 @@
                 argThat<NetworkInfo> { it.detailedState == NetworkInfo.DetailedState.CONNECTING },
                 any(LinkProperties::class.java),
                 any(NetworkCapabilities::class.java),
-                anyInt() /* score */,
+                any(NetworkScore::class.java),
                 any(NetworkAgentConfig::class.java),
                 eq(NetworkProvider.ID_NONE))
     }
diff --git a/tests/cts/tethering/Android.bp b/tests/cts/tethering/Android.bp
index 824c874..fa52e9b 100644
--- a/tests/cts/tethering/Android.bp
+++ b/tests/cts/tethering/Android.bp
@@ -52,7 +52,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-tethering",
     ],
 
     // Include both the 32 and 64 bit versions