Merge "[NS01.cn] Adjust calls for NetworkScore"
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/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index 8df3045..0f777d5 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);
@@ -828,7 +834,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 +881,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/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/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index e3208e7..43e9970 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -1603,15 +1603,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.