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.