Merge "Simplify per-target-SDK LinkProperties tests."
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index 770c092..a7028b7 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -85,7 +85,7 @@
     bpfs: [
         "block.o",
         "clatd.o",
-        "dscp_policy.o",
+        "dscpPolicy.o",
         "netd.o",
         "offload.o",
         "offload@btf.o",
@@ -209,7 +209,7 @@
     bpfs: [
         "block.o",
         "clatd.o",
-        "dscp_policy.o",
+        "dscpPolicy.o",
         "netd.o",
         "offload@inprocess.o",
         "test@inprocess.o",
diff --git a/Tethering/src/android/net/ip/NeighborPacketForwarder.java b/Tethering/src/android/net/ip/NeighborPacketForwarder.java
index 723bd63..8384562 100644
--- a/Tethering/src/android/net/ip/NeighborPacketForwarder.java
+++ b/Tethering/src/android/net/ip/NeighborPacketForwarder.java
@@ -23,6 +23,7 @@
 import static android.system.OsConstants.SOCK_DGRAM;
 import static android.system.OsConstants.SOCK_NONBLOCK;
 import static android.system.OsConstants.SOCK_RAW;
+import static android.system.OsConstants.ENODEV;
 
 import android.net.util.SocketUtils;
 import android.os.Handler;
@@ -131,7 +132,13 @@
                                                         ETH_P_IPV6, mListenIfaceParams.index);
             Os.bind(mFd, bindAddress);
         } catch (ErrnoException | SocketException e) {
-            Log.wtf(mTag, "Failed to create  socket", e);
+            // An ENODEV(No such device) will rise if tethering stopped before this function, this
+            // may happen when enable/disable tethering quickly.
+            if (e instanceof ErrnoException && ((ErrnoException) e).errno == ENODEV) {
+                Log.w(mTag, "Failed to create socket because tethered interface is gone", e);
+            } else {
+                Log.wtf(mTag, "Failed to create socket", e);
+            }
             closeSocketQuietly(mFd);
             return null;
         }
diff --git a/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java b/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java
index 4525568..85ee466 100644
--- a/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java
+++ b/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java
@@ -15,27 +15,23 @@
  */
 package android.tethering.mts;
 
-import static android.Manifest.permission.ACCESS_WIFI_STATE;
 import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
-import static android.Manifest.permission.NETWORK_SETTINGS;
 import static android.Manifest.permission.READ_DEVICE_CONFIG;
-import static android.Manifest.permission.TETHER_PRIVILEGED;
 import static android.Manifest.permission.WRITE_SETTINGS;
 import static android.net.TetheringManager.TETHERING_WIFI;
 import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
 
 import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
+import static com.android.testutils.TestPermissionUtil.runAsShell;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 
-import android.app.UiAutomation;
 import android.content.Context;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.TetheringInterface;
-import android.net.TetheringManager;
 import android.net.cts.util.CtsTetheringUtils;
 import android.net.cts.util.CtsTetheringUtils.TestTetheringEventCallback;
 import android.provider.DeviceConfig;
@@ -46,7 +42,6 @@
 
 import com.android.testutils.TestNetworkTracker;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -60,26 +55,14 @@
 @RunWith(AndroidJUnit4.class)
 public class TetheringModuleTest {
     private Context mContext;
-    private TetheringManager mTm;
     private CtsTetheringUtils mCtsTetheringUtils;
 
-    private UiAutomation mUiAutomation =
-            InstrumentationRegistry.getInstrumentation().getUiAutomation();
-
     @Before
     public void setUp() throws Exception {
-        mUiAutomation.adoptShellPermissionIdentity(MANAGE_TEST_NETWORKS, NETWORK_SETTINGS,
-                WRITE_SETTINGS, READ_DEVICE_CONFIG, TETHER_PRIVILEGED, ACCESS_WIFI_STATE);
         mContext = InstrumentationRegistry.getContext();
-        mTm = mContext.getSystemService(TetheringManager.class);
         mCtsTetheringUtils = new CtsTetheringUtils(mContext);
     }
 
-    @After
-    public void tearDown() throws Exception {
-        mUiAutomation.dropShellPermissionIdentity();
-    }
-
     @Test
     public void testSwitchBasePrefixRangeWhenConflict() throws Exception {
         addressConflictTest(true);
@@ -130,10 +113,8 @@
 
             mCtsTetheringUtils.stopWifiTethering(tetherEventCallback);
         } finally {
-            if (tnt != null) {
-                tnt.teardown();
-            }
-            mTm.stopAllTethering();
+            teardown(tnt);
+            mCtsTetheringUtils.stopAllTethering();
             mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
         }
     }
@@ -169,11 +150,19 @@
     }
 
     private TestNetworkTracker setUpTestNetwork(final LinkAddress address) throws Exception {
-        return initTestNetwork(mContext, address, 10_000L /* test timeout ms*/);
+        return runAsShell(MANAGE_TEST_NETWORKS, WRITE_SETTINGS,
+                () -> initTestNetwork(mContext, address, 10_000L /* test timeout ms*/));
 
     }
 
+    private void teardown(TestNetworkTracker tracker) throws Exception {
+        if (tracker == null) return;
+
+        runAsShell(MANAGE_TEST_NETWORKS, () -> tracker.teardown());
+    }
+
     public static boolean isFeatureEnabled(final String name, final boolean defaultValue) {
-        return DeviceConfig.getBoolean(NAMESPACE_CONNECTIVITY, name, defaultValue);
+        return runAsShell(READ_DEVICE_CONFIG,
+                () -> DeviceConfig.getBoolean(NAMESPACE_CONNECTIVITY, name, defaultValue));
     }
 }
diff --git a/bpf_progs/Android.bp b/bpf_progs/Android.bp
index 6ab5281..c2e28f4 100644
--- a/bpf_progs/Android.bp
+++ b/bpf_progs/Android.bp
@@ -72,8 +72,8 @@
 }
 
 bpf {
-    name: "dscp_policy.o",
-    srcs: ["dscp_policy.c"],
+    name: "dscpPolicy.o",
+    srcs: ["dscpPolicy.c"],
     btf: true,
     cflags: [
         "-Wall",
diff --git a/bpf_progs/dscp_policy.c b/bpf_progs/dscpPolicy.c
similarity index 99%
rename from bpf_progs/dscp_policy.c
rename to bpf_progs/dscpPolicy.c
index e45c1d4..25abd2b 100644
--- a/bpf_progs/dscp_policy.c
+++ b/bpf_progs/dscpPolicy.c
@@ -31,7 +31,7 @@
 #define BPFLOADER_MIN_VER BPFLOADER_T_BETA3_VERSION
 
 #include "bpf_helpers.h"
-#include "dscp_policy.h"
+#include "dscpPolicy.h"
 
 #define ECN_MASK 3
 #define IP4_OFFSET(field, header) (header + offsetof(struct iphdr, field))
diff --git a/bpf_progs/dscp_policy.h b/bpf_progs/dscpPolicy.h
similarity index 100%
rename from bpf_progs/dscp_policy.h
rename to bpf_progs/dscpPolicy.h
diff --git a/framework-t/Android.bp b/framework-t/Android.bp
index 8c32ded..80477f1 100644
--- a/framework-t/Android.bp
+++ b/framework-t/Android.bp
@@ -116,6 +116,7 @@
         "//packages/modules/Connectivity/Tethering/apex",
         // In preparation for future move
         "//packages/modules/Connectivity/apex",
+        "//packages/modules/Connectivity/service", // For R8 only
         "//packages/modules/Connectivity/service-t",
         "//packages/modules/Nearby/service",
         "//frameworks/base",
diff --git a/framework-t/Sources.bp b/framework-t/Sources.bp
index b30ee80..391a562 100644
--- a/framework-t/Sources.bp
+++ b/framework-t/Sources.bp
@@ -14,125 +14,13 @@
 // limitations under the License.
 //
 
-// NetworkStats related libraries.
-
-filegroup {
-    name: "framework-connectivity-netstats-internal-sources",
-    srcs: [
-        "src/android/app/usage/*.java",
-        "src/android/net/DataUsageRequest.*",
-        "src/android/net/INetworkStatsService.aidl",
-        "src/android/net/INetworkStatsSession.aidl",
-        "src/android/net/NetworkIdentity.java",
-        "src/android/net/NetworkIdentitySet.java",
-        "src/android/net/NetworkStateSnapshot.*",
-        "src/android/net/NetworkStats.*",
-        "src/android/net/NetworkStatsAccess.*",
-        "src/android/net/NetworkStatsCollection.*",
-        "src/android/net/NetworkStatsHistory.*",
-        "src/android/net/NetworkTemplate.*",
-        "src/android/net/TrafficStats.java",
-        "src/android/net/UnderlyingNetworkInfo.*",
-        "src/android/net/netstats/**/*.*",
-    ],
-    path: "src",
-    visibility: [
-        "//visibility:private",
-    ],
-}
-
-filegroup {
-    name: "framework-connectivity-netstats-sources",
-    srcs: [
-        ":framework-connectivity-netstats-internal-sources",
-    ],
-    visibility: [
-        "//visibility:private",
-    ],
-}
-
-// Nsd related libraries.
-
-filegroup {
-    name: "framework-connectivity-nsd-internal-sources",
-    srcs: [
-        "src/android/net/nsd/*.aidl",
-        "src/android/net/nsd/*.java",
-    ],
-    path: "src",
-    visibility: [
-        "//visibility:private",
-    ],
-}
-
-filegroup {
-    name: "framework-connectivity-nsd-sources",
-    srcs: [
-        ":framework-connectivity-nsd-internal-sources",
-    ],
-    visibility: [
-        "//visibility:private",
-    ],
-}
-
-// IpSec related libraries.
-
-filegroup {
-    name: "framework-connectivity-ipsec-sources",
-    srcs: [
-        "src/android/net/IIpSecService.aidl",
-        "src/android/net/IpSec*.*",
-    ],
-    path: "src",
-    visibility: [
-        "//visibility:private",
-    ],
-}
-
-// Ethernet related libraries.
-
-filegroup {
-    name: "framework-connectivity-ethernet-sources",
-    srcs: [
-        "src/android/net/EthernetManager.java",
-        "src/android/net/EthernetNetworkManagementException.java",
-        "src/android/net/EthernetNetworkManagementException.aidl",
-        "src/android/net/EthernetNetworkSpecifier.java",
-        "src/android/net/EthernetNetworkUpdateRequest.java",
-        "src/android/net/EthernetNetworkUpdateRequest.aidl",
-        "src/android/net/IEthernetManager.aidl",
-        "src/android/net/IEthernetServiceListener.aidl",
-        "src/android/net/INetworkInterfaceOutcomeReceiver.aidl",
-        "src/android/net/ITetheredInterfaceCallback.aidl",
-    ],
-    path: "src",
-    visibility: [
-        "//visibility:private",
-    ],
-}
-
-// Connectivity-T common libraries.
-
-filegroup {
-    name: "framework-connectivity-tiramisu-internal-sources",
-    srcs: [
-        "src/android/net/ConnectivityFrameworkInitializerTiramisu.java",
-    ],
-    path: "src",
-    visibility: [
-        "//visibility:private",
-    ],
-}
-
 filegroup {
     name: "framework-connectivity-tiramisu-updatable-sources",
     srcs: [
-        ":framework-connectivity-ethernet-sources",
-        ":framework-connectivity-ipsec-sources",
-        ":framework-connectivity-netstats-sources",
-        ":framework-connectivity-nsd-sources",
-        ":framework-connectivity-tiramisu-internal-sources",
+        "src/**/*.java",
+        "src/**/*.aidl",
     ],
+    path: "src",
     visibility: [
         "//frameworks/base",
         "//packages/modules/Connectivity:__subpackages__",
diff --git a/framework/src/android/net/ITestNetworkManager.aidl b/framework/src/android/net/ITestNetworkManager.aidl
index d18b931..9432acb 100644
--- a/framework/src/android/net/ITestNetworkManager.aidl
+++ b/framework/src/android/net/ITestNetworkManager.aidl
@@ -30,7 +30,8 @@
 interface ITestNetworkManager
 {
     TestNetworkInterface createInterface(boolean isTun, boolean hasCarrier, boolean bringUp,
-            in LinkAddress[] addrs, in @nullable String iface);
+            boolean disableIpv6ProvisioningDelay, in LinkAddress[] addrs,
+            in @nullable String iface);
 
     void setCarrierEnabled(in TestNetworkInterface iface, boolean enabled);
 
diff --git a/framework/src/android/net/TestNetworkManager.java b/framework/src/android/net/TestNetworkManager.java
index 788834a..b64299f 100644
--- a/framework/src/android/net/TestNetworkManager.java
+++ b/framework/src/android/net/TestNetworkManager.java
@@ -59,6 +59,8 @@
     private static final boolean TUN = true;
     private static final boolean BRING_UP = true;
     private static final boolean CARRIER_UP = true;
+    // sets disableIpv6ProvisioningDelay to false.
+    private static final boolean USE_IPV6_PROV_DELAY = false;
     private static final LinkAddress[] NO_ADDRS = new LinkAddress[0];
 
     /** @hide */
@@ -167,8 +169,8 @@
     public TestNetworkInterface createTunInterface(@NonNull Collection<LinkAddress> linkAddrs) {
         try {
             final LinkAddress[] arr = new LinkAddress[linkAddrs.size()];
-            return mService.createInterface(TUN, CARRIER_UP, BRING_UP, linkAddrs.toArray(arr),
-                    null /* iface */);
+            return mService.createInterface(TUN, CARRIER_UP, BRING_UP, USE_IPV6_PROV_DELAY,
+                    linkAddrs.toArray(arr), null /* iface */);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -186,7 +188,8 @@
     @NonNull
     public TestNetworkInterface createTapInterface() {
         try {
-            return mService.createInterface(TAP, CARRIER_UP, BRING_UP, NO_ADDRS, null /* iface */);
+            return mService.createInterface(TAP, CARRIER_UP, BRING_UP, USE_IPV6_PROV_DELAY,
+                    NO_ADDRS, null /* iface */);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -204,7 +207,8 @@
     @NonNull
     public TestNetworkInterface createTapInterface(@NonNull LinkAddress[] linkAddrs) {
         try {
-            return mService.createInterface(TAP, CARRIER_UP, BRING_UP, linkAddrs, null /* iface */);
+            return mService.createInterface(TAP, CARRIER_UP, BRING_UP, USE_IPV6_PROV_DELAY,
+                    linkAddrs, null /* iface */);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -223,7 +227,8 @@
     @NonNull
     public TestNetworkInterface createTapInterface(boolean bringUp) {
         try {
-            return mService.createInterface(TAP, CARRIER_UP, bringUp, NO_ADDRS, null /* iface */);
+            return mService.createInterface(TAP, CARRIER_UP, bringUp, USE_IPV6_PROV_DELAY,
+                    NO_ADDRS, null /* iface */);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -245,7 +250,8 @@
     @NonNull
     public TestNetworkInterface createTapInterface(boolean bringUp, @NonNull String iface) {
         try {
-            return mService.createInterface(TAP, CARRIER_UP, bringUp, NO_ADDRS, iface);
+            return mService.createInterface(TAP, CARRIER_UP, bringUp, USE_IPV6_PROV_DELAY,
+                    NO_ADDRS, iface);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -264,7 +270,49 @@
     @NonNull
     public TestNetworkInterface createTapInterface(boolean carrierUp, boolean bringUp) {
         try {
-            return mService.createInterface(TAP, carrierUp, bringUp, NO_ADDRS, null /* iface */);
+            return mService.createInterface(TAP, carrierUp, bringUp, USE_IPV6_PROV_DELAY, NO_ADDRS,
+                    null /* iface */);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Create a tap interface for testing purposes.
+     *
+     * @param carrierUp whether the created interface has a carrier or not.
+     * @param bringUp whether to bring up the interface before returning it.
+     * @param disableIpv6ProvisioningDelay whether to disable DAD and RS delay.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_TEST_NETWORKS)
+    @NonNull
+    public TestNetworkInterface createTapInterface(boolean carrierUp, boolean bringUp,
+            boolean disableIpv6ProvisioningDelay) {
+        try {
+            return mService.createInterface(TAP, carrierUp, bringUp, disableIpv6ProvisioningDelay,
+                    NO_ADDRS, null /* iface */);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Create a tap interface for testing purposes.
+     *
+     * @param disableIpv6ProvisioningDelay whether to disable DAD and RS delay.
+     * @param linkAddrs an array of LinkAddresses to assign to the TAP interface
+     * @return A TestNetworkInterface representing the underlying TAP interface. Close the contained
+     *     ParcelFileDescriptor to tear down the TAP interface.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_TEST_NETWORKS)
+    @NonNull
+    public TestNetworkInterface createTapInterface(boolean disableIpv6ProvisioningDelay,
+            @NonNull LinkAddress[] linkAddrs) {
+        try {
+            return mService.createInterface(TAP, CARRIER_UP, BRING_UP, disableIpv6ProvisioningDelay,
+                    linkAddrs, null /* iface */);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
index 604afc3..e5bddf6 100644
--- a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
+++ b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
@@ -649,13 +649,12 @@
             if (!up) { // was up, goes down
                 // retract network offer and stop IpClient.
                 unregisterNetworkOfferAndStop();
-                // If only setting the interface down, send a callback to signal completion.
-                EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, name, null);
             } else { // was down, goes up
                 // register network offer
                 registerNetworkOffer();
             }
 
+            EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, name, null);
             return true;
         }
 
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index fef6afb..08d2a3c 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -2809,7 +2809,7 @@
                     throw new IllegalStateException("invalid tethering stats " + e);
                 }
             }
-        } catch (IllegalStateException e) {
+        } catch (IllegalStateException | ServiceSpecificException e) {
             Log.wtf(TAG, "problem reading network stats", e);
         }
         return stats;
diff --git a/service/Android.bp b/service/Android.bp
index 499af25..7dcc888 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -225,12 +225,27 @@
     // This library combines system server jars that have access to different bootclasspath jars.
     // Lower SDK service jars must not depend on higher SDK jars as that would let them
     // transitively depend on the wrong bootclasspath jars. Sources also cannot be added here as
-    // they would transitively depend on bootclasspath jars that may not be available.
+    // they would depend on bootclasspath jars that may not be available.
     static_libs: [
         "service-connectivity-pre-jarjar",
         "service-connectivity-tiramisu-pre-jarjar",
         "service-nearby-pre-jarjar",
     ],
+    // The below libraries are not actually needed to build since no source is compiled
+    // (only combining prebuilt static_libs), but they are necessary so that R8 has the right
+    // references to optimize the code. Without these, there will be missing class warnings and
+    // code may be wrongly optimized.
+    // R8 runs after jarjar, so the framework-X libraries need to be the post-jarjar artifacts
+    // (.impl), if they are not just stubs, so that the name of jarjared classes match.
+    libs: [
+        "androidx.annotation_annotation",
+        "framework-annotations-lib",
+        "framework-connectivity.impl",
+        "framework-connectivity-t.impl",
+        "framework-tethering.stubs.module_lib",
+        "framework-wifi.stubs.module_lib",
+        "libprotobuf-java-nano",
+    ],
     jarjar_rules: ":connectivity-jarjar-rules",
     apex_available: [
         "com.android.tethering",
diff --git a/service/jni/com_android_server_TestNetworkService.cpp b/service/jni/com_android_server_TestNetworkService.cpp
index 9c7a761..a1d0310 100644
--- a/service/jni/com_android_server_TestNetworkService.cpp
+++ b/service/jni/com_android_server_TestNetworkService.cpp
@@ -76,27 +76,47 @@
         setTunTapCarrierEnabledImpl(env, iface, tun.get(), hasCarrier);
     }
 
-    // Activate interface using an unconnected datagram socket.
-    base::unique_fd inet6CtrlSock(socket(AF_INET6, SOCK_DGRAM, 0));
-    ifr.ifr_flags = IFF_UP;
     // Mark TAP interfaces as supporting multicast
-    if (!isTun) ifr.ifr_flags |= IFF_MULTICAST;
+    if (!isTun) {
+        base::unique_fd inet6CtrlSock(socket(AF_INET6, SOCK_DGRAM, 0));
+        ifr.ifr_flags = IFF_MULTICAST;
 
-    if (ioctl(inet6CtrlSock.get(), SIOCSIFFLAGS, &ifr)) {
-        throwException(env, errno, "activating", ifr.ifr_name);
-        return -1;
+        if (ioctl(inet6CtrlSock.get(), SIOCSIFFLAGS, &ifr)) {
+            throwException(env, errno, "set IFF_MULTICAST", ifr.ifr_name);
+            return -1;
+        }
     }
 
     return tun.release();
 }
 
+static void bringUpInterfaceImpl(JNIEnv* env, const char* iface) {
+    // Activate interface using an unconnected datagram socket.
+    base::unique_fd inet6CtrlSock(socket(AF_INET6, SOCK_DGRAM, 0));
+
+    ifreq ifr{};
+    strlcpy(ifr.ifr_name, iface, IFNAMSIZ);
+    if (ioctl(inet6CtrlSock.get(), SIOCGIFFLAGS, &ifr)) {
+        throwException(env, errno, "read flags", iface);
+        return;
+    }
+    ifr.ifr_flags |= IFF_UP;
+    if (ioctl(inet6CtrlSock.get(), SIOCSIFFLAGS, &ifr)) {
+        throwException(env, errno, "set IFF_UP", iface);
+        return;
+    }
+}
+
 //------------------------------------------------------------------------------
 
+
+
 static void setTunTapCarrierEnabled(JNIEnv* env, jclass /* clazz */, jstring
                                     jIface, jint tunFd, jboolean enabled) {
     ScopedUtfChars iface(env, jIface);
     if (!iface.c_str()) {
         jniThrowNullPointerException(env, "iface");
+        return;
     }
     setTunTapCarrierEnabledImpl(env, iface.c_str(), tunFd, enabled);
 }
@@ -112,11 +132,21 @@
     return createTunTapImpl(env, isTun, hasCarrier, iface.c_str());
 }
 
+static void bringUpInterface(JNIEnv* env, jclass /* clazz */, jstring jIface) {
+    ScopedUtfChars iface(env, jIface);
+    if (!iface.c_str()) {
+        jniThrowNullPointerException(env, "iface");
+        return;
+    }
+    bringUpInterfaceImpl(env, iface.c_str());
+}
+
 //------------------------------------------------------------------------------
 
 static const JNINativeMethod gMethods[] = {
     {"nativeSetTunTapCarrierEnabled", "(Ljava/lang/String;IZ)V", (void*)setTunTapCarrierEnabled},
     {"nativeCreateTunTap", "(ZZLjava/lang/String;)I", (void*)createTunTap},
+    {"nativeBringUpInterface", "(Ljava/lang/String;)V", (void*)bringUpInterface},
 };
 
 int register_com_android_server_TestNetworkService(JNIEnv* env) {
diff --git a/service/src/com/android/server/TestNetworkService.java b/service/src/com/android/server/TestNetworkService.java
index 1209579..15d9f13 100644
--- a/service/src/com/android/server/TestNetworkService.java
+++ b/service/src/com/android/server/TestNetworkService.java
@@ -47,7 +47,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.net.module.util.NetdUtils;
 import com.android.net.module.util.NetworkStackConstants;
 
 import java.io.IOException;
@@ -83,6 +82,8 @@
     private static native void nativeSetTunTapCarrierEnabled(@NonNull String iface, int tunFd,
             boolean enabled);
 
+    private static native void nativeBringUpInterface(String iface);
+
     @VisibleForTesting
     protected TestNetworkService(@NonNull Context context) {
         mHandlerThread = new HandlerThread("TestNetworkServiceThread");
@@ -120,7 +121,7 @@
      */
     @Override
     public TestNetworkInterface createInterface(boolean isTun, boolean hasCarrier, boolean bringUp,
-            LinkAddress[] linkAddrs, @Nullable String iface) {
+            boolean disableIpv6ProvisioningDelay, LinkAddress[] linkAddrs, @Nullable String iface) {
         enforceTestNetworkPermissions(mContext);
 
         Objects.requireNonNull(linkAddrs, "missing linkAddrs");
@@ -137,6 +138,14 @@
         try {
             ParcelFileDescriptor tunIntf = ParcelFileDescriptor.adoptFd(
                     nativeCreateTunTap(isTun, hasCarrier, interfaceName));
+
+            // Disable DAD and remove router_solicitation_delay before assigning link addresses.
+            if (disableIpv6ProvisioningDelay) {
+                mNetd.setProcSysNet(
+                        INetd.IPV6, INetd.CONF, interfaceName, "router_solicitation_delay", "0");
+                mNetd.setProcSysNet(INetd.IPV6, INetd.CONF, interfaceName, "dad_transmits", "0");
+            }
+
             for (LinkAddress addr : linkAddrs) {
                 mNetd.interfaceAddAddress(
                         interfaceName,
@@ -145,7 +154,7 @@
             }
 
             if (bringUp) {
-                NetdUtils.setInterfaceUp(mNetd, interfaceName);
+                nativeBringUpInterface(interfaceName);
             }
 
             return new TestNetworkInterface(tunIntf, interfaceName);
diff --git a/service/src/com/android/server/connectivity/DscpPolicyTracker.java b/service/src/com/android/server/connectivity/DscpPolicyTracker.java
index 7829d1a..8cb3213 100644
--- a/service/src/com/android/server/connectivity/DscpPolicyTracker.java
+++ b/service/src/com/android/server/connectivity/DscpPolicyTracker.java
@@ -52,12 +52,12 @@
 
     private static final String TAG = DscpPolicyTracker.class.getSimpleName();
     private static final String PROG_PATH =
-            "/sys/fs/bpf/net_shared/prog_dscp_policy_schedcls_set_dscp";
+            "/sys/fs/bpf/net_shared/prog_dscpPolicy_schedcls_set_dscp_ether";
     // Name is "map + *.o + map_name + map". Can probably shorten this
     private static final String IPV4_POLICY_MAP_PATH = makeMapPath(
-            "dscp_policy_ipv4_dscp_policies");
+            "dscpPolicy_ipv4_dscp_policies");
     private static final String IPV6_POLICY_MAP_PATH = makeMapPath(
-            "dscp_policy_ipv6_dscp_policies");
+            "dscpPolicy_ipv6_dscp_policies");
     private static final int MAX_POLICIES = 16;
 
     private static String makeMapPath(String which) {
@@ -212,8 +212,17 @@
         return DSCP_POLICY_STATUS_SUCCESS;
     }
 
+    private boolean isEthernet(String iface) {
+        try {
+            return TcUtils.isEthernet(iface);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to check ether type", e);
+        }
+        return false;
+    }
+
     /**
-     * Add the provided DSCP policy to the bpf map. Attach bpf program dscp_policy to iface
+     * Add the provided DSCP policy to the bpf map. Attach bpf program dscpPolicy to iface
      * if not already attached. Response will be sent back to nai with status.
      *
      * DSCP_POLICY_STATUS_SUCCESS - if policy was added successfully
@@ -221,13 +230,17 @@
      * DSCP_POLICY_STATUS_REQUEST_DECLINED - Interface index was invalid
      */
     public void addDscpPolicy(NetworkAgentInfo nai, DscpPolicy policy) {
-        if (!mAttachedIfaces.contains(nai.linkProperties.getInterfaceName())) {
-            if (!attachProgram(nai.linkProperties.getInterfaceName())) {
-                Log.e(TAG, "Unable to attach program");
-                sendStatus(nai, policy.getPolicyId(),
-                        DSCP_POLICY_STATUS_INSUFFICIENT_PROCESSING_RESOURCES);
-                return;
-            }
+        String iface = nai.linkProperties.getInterfaceName();
+        if (!isEthernet(iface)) {
+            Log.e(TAG, "DSCP policies are not supported on raw IP interfaces.");
+            sendStatus(nai, policy.getPolicyId(), DSCP_POLICY_STATUS_REQUEST_DECLINED);
+            return;
+        }
+        if (!mAttachedIfaces.contains(iface) && !attachProgram(iface)) {
+            Log.e(TAG, "Unable to attach program");
+            sendStatus(nai, policy.getPolicyId(),
+                    DSCP_POLICY_STATUS_INSUFFICIENT_PROCESSING_RESOURCES);
+            return;
         }
 
         final int ifIndex = getIfaceIndex(nai);
@@ -314,10 +327,8 @@
     private boolean attachProgram(@NonNull String iface) {
         try {
             NetworkInterface netIface = NetworkInterface.getByName(iface);
-            boolean isEth = TcUtils.isEthernet(iface);
-            String path = PROG_PATH + (isEth ? "_ether" : "_raw_ip");
             TcUtils.tcFilterAddDevBpf(netIface.getIndex(), false, PRIO_DSCP, (short) ETH_P_ALL,
-                    path);
+                    PROG_PATH);
         } catch (IOException e) {
             Log.e(TAG, "Unable to attach to TC on " + iface + ": " + e);
             return false;
diff --git a/tests/common/java/android/net/NetworkProviderTest.kt b/tests/common/java/android/net/NetworkProviderTest.kt
index 3ceacf8..c0e7f61 100644
--- a/tests/common/java/android/net/NetworkProviderTest.kt
+++ b/tests/common/java/android/net/NetworkProviderTest.kt
@@ -30,6 +30,7 @@
 import android.os.Looper
 import android.util.Log
 import androidx.test.InstrumentationRegistry
+import com.android.modules.utils.build.SdkLevel.isAtLeastS
 import com.android.net.module.util.ArrayTrackRecord
 import com.android.testutils.CompatUtil
 import com.android.testutils.ConnectivityModuleTest
@@ -38,7 +39,6 @@
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
 import com.android.testutils.DevSdkIgnoreRunner
 import com.android.testutils.TestableNetworkOfferCallback
-import com.android.testutils.isDevSdkInRange
 import org.junit.After
 import org.junit.Before
 import org.junit.Rule
@@ -376,7 +376,7 @@
         doReturn(mCm).`when`(mockContext).getSystemService(Context.CONNECTIVITY_SERVICE)
         val provider = createNetworkProvider(mockContext)
         // ConnectivityManager not required at creation time after R
-        if (!isDevSdkInRange(0, Build.VERSION_CODES.R)) {
+        if (isAtLeastS()) {
             verifyNoMoreInteractions(mockContext)
         }
 
diff --git a/tests/cts/hostside/aidl/com/android/cts/net/hostside/IRemoteSocketFactory.aidl b/tests/cts/hostside/aidl/com/android/cts/net/hostside/IRemoteSocketFactory.aidl
index 68176ad..6986e7e 100644
--- a/tests/cts/hostside/aidl/com/android/cts/net/hostside/IRemoteSocketFactory.aidl
+++ b/tests/cts/hostside/aidl/com/android/cts/net/hostside/IRemoteSocketFactory.aidl
@@ -20,6 +20,7 @@
 
 interface IRemoteSocketFactory {
     ParcelFileDescriptor openSocketFd(String host, int port, int timeoutMs);
+    ParcelFileDescriptor openDatagramSocketFd();
     String getPackageName();
     int getUid();
 }
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/RemoteSocketFactoryClient.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/RemoteSocketFactoryClient.java
index 80f99b6..01fbd66 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/RemoteSocketFactoryClient.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/RemoteSocketFactoryClient.java
@@ -83,9 +83,19 @@
     public FileDescriptor openSocketFd(String host, int port, int timeoutMs)
             throws RemoteException, ErrnoException, IOException {
         // Dup the filedescriptor so ParcelFileDescriptor's finalizer doesn't garbage collect it
-        // and cause our fd to become invalid. http://b/35927643 .
-        ParcelFileDescriptor pfd = mService.openSocketFd(host, port, timeoutMs);
-        FileDescriptor fd = Os.dup(pfd.getFileDescriptor());
+        // and cause fd to become invalid. http://b/35927643.
+        final ParcelFileDescriptor pfd = mService.openSocketFd(host, port, timeoutMs);
+        final FileDescriptor fd = Os.dup(pfd.getFileDescriptor());
+        pfd.close();
+        return fd;
+    }
+
+    public FileDescriptor openDatagramSocketFd()
+            throws RemoteException, ErrnoException, IOException {
+        // Dup the filedescriptor so ParcelFileDescriptor's finalizer doesn't garbage collect it
+        // and cause fd to become invalid. http://b/35927643.
+        final ParcelFileDescriptor pfd = mService.openDatagramSocketFd();
+        final FileDescriptor fd = Os.dup(pfd.getFileDescriptor());
         pfd.close();
         return fd;
     }
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
index dd8b523..5f032be 100755
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -16,6 +16,7 @@
 
 package com.android.cts.net.hostside;
 
+import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
 import static android.Manifest.permission.NETWORK_SETTINGS;
 import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
 import static android.content.pm.PackageManager.FEATURE_WIFI;
@@ -35,6 +36,8 @@
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_LOCKDOWN_VPN;
+import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_NONE;
 import static com.android.testutils.Cleanup.testAndCleanup;
 import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
 
@@ -59,12 +62,15 @@
 import android.database.Cursor;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
+import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
 import android.net.Proxy;
 import android.net.ProxyInfo;
+import android.net.TestNetworkInterface;
+import android.net.TestNetworkManager;
 import android.net.TransportInfo;
 import android.net.Uri;
 import android.net.VpnManager;
@@ -72,6 +78,7 @@
 import android.net.VpnTransportInfo;
 import android.net.cts.util.CtsNetUtils;
 import android.net.wifi.WifiManager;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.ParcelFileDescriptor;
@@ -90,11 +97,13 @@
 import android.test.MoreAsserts;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Range;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.compatibility.common.util.BlockingBroadcastReceiver;
 import com.android.modules.utils.build.SdkLevel;
+import com.android.net.module.util.PacketBuilder;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
 import com.android.testutils.RecorderCallback;
@@ -113,12 +122,14 @@
 import java.io.OutputStream;
 import java.net.DatagramPacket;
 import java.net.DatagramSocket;
+import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
@@ -164,6 +175,12 @@
     private static final String PRIVATE_DNS_SPECIFIER_SETTING = "private_dns_specifier";
     private static final int NETWORK_CALLBACK_TIMEOUT_MS = 30_000;
 
+    private static final LinkAddress TEST_IP4_DST_ADDR = new LinkAddress("198.51.100.1/24");
+    private static final LinkAddress TEST_IP4_SRC_ADDR = new LinkAddress("198.51.100.2/24");
+    private static final LinkAddress TEST_IP6_DST_ADDR = new LinkAddress("2001:db8:1:3::1/64");
+    private static final LinkAddress TEST_IP6_SRC_ADDR = new LinkAddress("2001:db8:1:3::2/64");
+    private static final short TEST_SRC_PORT = 5555;
+
     public static String TAG = "VpnTest";
     public static int TIMEOUT_MS = 3 * 1000;
     public static int SOCKET_TIMEOUT_MS = 100;
@@ -1572,4 +1589,180 @@
             return future.get(timeout, unit);
         }
     }
+
+    private static final boolean EXPECT_PASS = false;
+    private static final boolean EXPECT_BLOCK = true;
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+    public void testBlockIncomingPackets() throws Exception {
+        assumeTrue(supportedHardware());
+        final Network network = mCM.getActiveNetwork();
+        assertNotNull("Requires a working Internet connection", network);
+
+        final int remoteUid = mRemoteSocketFactoryClient.getUid();
+        final List<Range<Integer>> lockdownRange = List.of(new Range<>(remoteUid, remoteUid));
+        final DetailedBlockedStatusCallback remoteUidCallback = new DetailedBlockedStatusCallback();
+
+        // Create a TUN interface
+        final FileDescriptor tunFd = runWithShellPermissionIdentity(() -> {
+            final TestNetworkManager tnm = getInstrumentation().getContext().getSystemService(
+                    TestNetworkManager.class);
+            final TestNetworkInterface iface = tnm.createTunInterface(List.of(
+                    TEST_IP4_DST_ADDR, TEST_IP6_DST_ADDR));
+            return iface.getFileDescriptor().getFileDescriptor();
+        }, MANAGE_TEST_NETWORKS);
+
+        // Create a remote UDP socket
+        final FileDescriptor remoteUdpFd = mRemoteSocketFactoryClient.openDatagramSocketFd();
+
+        testAndCleanup(() -> {
+            runWithShellPermissionIdentity(() -> {
+                mCM.registerDefaultNetworkCallbackForUid(remoteUid, remoteUidCallback,
+                        new Handler(Looper.getMainLooper()));
+            }, NETWORK_SETTINGS);
+            remoteUidCallback.expectAvailableCallbacks(network);
+
+            // The remote UDP socket can receive packets coming from the TUN interface
+            checkBlockIncomingPacket(tunFd, remoteUdpFd, EXPECT_PASS);
+
+            // Lockdown uid that has the remote UDP socket
+            runWithShellPermissionIdentity(() -> {
+                mCM.setRequireVpnForUids(true /* requireVpn */, lockdownRange);
+            }, NETWORK_SETTINGS);
+
+            // setRequireVpnForUids setup a lockdown rule asynchronously. So it needs to wait for
+            // BlockedStatusCallback to be fired before checking the blocking status of incoming
+            // packets.
+            remoteUidCallback.expectBlockedStatusCallback(network, BLOCKED_REASON_LOCKDOWN_VPN);
+
+            if (SdkLevel.isAtLeastT()) {
+                // On T and above, lockdown rule drop packets not coming from lo regardless of the
+                // VPN connectivity.
+                checkBlockIncomingPacket(tunFd, remoteUdpFd, EXPECT_BLOCK);
+            }
+
+            // Start the VPN that has default routes. This VPN should have interface filtering rule
+            // for incoming packet and drop packets not coming from lo nor the VPN interface.
+            final String allowedApps =
+                    mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
+            startVpn(new String[]{"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
+                    new String[]{"0.0.0.0/0", "::/0"}, allowedApps, "" /* disallowedApplications */,
+                    null /* proxyInfo */, null /* underlyingNetworks */,
+                    false /* isAlwaysMetered */);
+
+            checkBlockIncomingPacket(tunFd, remoteUdpFd, EXPECT_BLOCK);
+        }, /* cleanup */ () -> {
+                mCM.unregisterNetworkCallback(remoteUidCallback);
+            }, /* cleanup */ () -> {
+                Os.close(tunFd);
+            }, /* cleanup */ () -> {
+                Os.close(remoteUdpFd);
+            }, /* cleanup */ () -> {
+                runWithShellPermissionIdentity(() -> {
+                    mCM.setRequireVpnForUids(false /* requireVpn */, lockdownRange);
+                }, NETWORK_SETTINGS);
+            });
+    }
+
+    private ByteBuffer buildIpv4UdpPacket(final Inet4Address dstAddr, final Inet4Address srcAddr,
+            final short dstPort, final short srcPort, final byte[] payload) throws IOException {
+
+        final ByteBuffer buffer = PacketBuilder.allocate(false /* hasEther */,
+                OsConstants.IPPROTO_IP, OsConstants.IPPROTO_UDP, payload.length);
+        final PacketBuilder packetBuilder = new PacketBuilder(buffer);
+
+        packetBuilder.writeIpv4Header(
+                (byte) 0 /* TOS */,
+                (short) 27149 /* ID */,
+                (short) 0x4000 /* flags=DF, offset=0 */,
+                (byte) 64 /* TTL */,
+                (byte) OsConstants.IPPROTO_UDP,
+                srcAddr,
+                dstAddr);
+        packetBuilder.writeUdpHeader(srcPort, dstPort);
+        buffer.put(payload);
+
+        return packetBuilder.finalizePacket();
+    }
+
+    private ByteBuffer buildIpv6UdpPacket(final Inet6Address dstAddr, final Inet6Address srcAddr,
+            final short dstPort, final short srcPort, final byte[] payload) throws IOException {
+
+        final ByteBuffer buffer = PacketBuilder.allocate(false /* hasEther */,
+                OsConstants.IPPROTO_IPV6, OsConstants.IPPROTO_UDP, payload.length);
+        final PacketBuilder packetBuilder = new PacketBuilder(buffer);
+
+        packetBuilder.writeIpv6Header(
+                0x60000000 /* version=6, traffic class=0, flow label=0 */,
+                (byte) OsConstants.IPPROTO_UDP,
+                (short) 64 /* hop limit */,
+                srcAddr,
+                dstAddr);
+        packetBuilder.writeUdpHeader(srcPort, dstPort);
+        buffer.put(payload);
+
+        return packetBuilder.finalizePacket();
+    }
+
+    private void checkBlockUdp(
+            final FileDescriptor srcTunFd,
+            final FileDescriptor dstUdpFd,
+            final boolean ipv6,
+            final boolean expectBlock) throws Exception {
+        final Random random = new Random();
+        final byte[] sendData = new byte[100];
+        random.nextBytes(sendData);
+        final short dstPort = (short) ((InetSocketAddress) Os.getsockname(dstUdpFd)).getPort();
+
+        ByteBuffer buf;
+        if (ipv6) {
+            buf = buildIpv6UdpPacket(
+                    (Inet6Address) TEST_IP6_DST_ADDR.getAddress(),
+                    (Inet6Address) TEST_IP6_SRC_ADDR.getAddress(),
+                    dstPort, TEST_SRC_PORT, sendData);
+        } else {
+            buf = buildIpv4UdpPacket(
+                    (Inet4Address) TEST_IP4_DST_ADDR.getAddress(),
+                    (Inet4Address) TEST_IP4_SRC_ADDR.getAddress(),
+                    dstPort, TEST_SRC_PORT, sendData);
+        }
+
+        Os.write(srcTunFd, buf);
+
+        final StructPollfd pollfd = new StructPollfd();
+        pollfd.events = (short) POLLIN;
+        pollfd.fd = dstUdpFd;
+        final int ret = Os.poll(new StructPollfd[]{pollfd}, SOCKET_TIMEOUT_MS);
+
+        if (expectBlock) {
+            assertEquals("Expect not to receive a packet but received a packet", 0, ret);
+        } else {
+            assertEquals("Expect to receive a packet but did not receive a packet", 1, ret);
+            final byte[] recvData = new byte[sendData.length];
+            final int readSize = Os.read(dstUdpFd, recvData, 0 /* byteOffset */, recvData.length);
+            assertEquals(recvData.length, readSize);
+            MoreAsserts.assertEquals(sendData, recvData);
+        }
+    }
+
+    private void checkBlockIncomingPacket(
+            final FileDescriptor srcTunFd,
+            final FileDescriptor dstUdpFd,
+            final boolean expectBlock) throws Exception {
+        checkBlockUdp(srcTunFd, dstUdpFd, false /* ipv6 */, expectBlock);
+        checkBlockUdp(srcTunFd, dstUdpFd, true /* ipv6 */, expectBlock);
+    }
+
+    private class DetailedBlockedStatusCallback extends TestableNetworkCallback {
+        public void expectAvailableCallbacks(Network network) {
+            super.expectAvailableCallbacks(network, false /* suspended */, true /* validated */,
+                    BLOCKED_REASON_NONE, NETWORK_CALLBACK_TIMEOUT_MS);
+        }
+        public void expectBlockedStatusCallback(Network network, int blockedStatus) {
+            super.expectBlockedStatusCallback(blockedStatus, network, NETWORK_CALLBACK_TIMEOUT_MS);
+        }
+        public void onBlockedStatusChanged(Network network, int blockedReasons) {
+            getHistory().add(new CallbackEntry.BlockedStatusInt(network, blockedReasons));
+        }
+    }
 }
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/RemoteSocketFactoryService.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/RemoteSocketFactoryService.java
index b1b7d77..fb6d16f 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/RemoteSocketFactoryService.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/RemoteSocketFactoryService.java
@@ -17,16 +17,17 @@
 package com.android.cts.net.hostside.app2;
 
 import android.app.Service;
-import android.content.Context;
 import android.content.Intent;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
-import android.util.Log;
 
 import com.android.cts.net.hostside.IRemoteSocketFactory;
 
+import java.io.UncheckedIOException;
+import java.net.DatagramSocket;
 import java.net.Socket;
+import java.net.SocketException;
 
 
 public class RemoteSocketFactoryService extends Service {
@@ -54,6 +55,16 @@
         public int getUid() {
             return Process.myUid();
         }
+
+        @Override
+        public ParcelFileDescriptor openDatagramSocketFd() {
+            try {
+                final DatagramSocket s = new DatagramSocket();
+                return ParcelFileDescriptor.fromDatagramSocket(s);
+            } catch (SocketException e) {
+                throw new UncheckedIOException(e);
+            }
+        }
     };
 
     @Override
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
index 3821f87..4d90a4a 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
@@ -116,4 +116,8 @@
     public void testInterleavedRoutes() throws Exception {
         runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testInterleavedRoutes");
     }
+
+    public void testBlockIncomingPackets() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testBlockIncomingPackets");
+    }
 }
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index 042c7fd..62f37bb 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -72,9 +72,9 @@
 android_test {
     name: "CtsNetTestCases",
     defaults: ["CtsNetTestCasesDefaults", "ConnectivityNextEnableDefaults"],
-    // TODO: CTS should not depend on the entirety of the networkstack code.
     static_libs: [
-        "NetworkStackApiCurrentLib",
+        "DhcpPacketLib",
+        "NetworkStackApiCurrentShims",
     ],
     test_suites: [
         "cts",
@@ -86,7 +86,8 @@
     name: "CtsNetTestCasesApiStableDefaults",
     // TODO: CTS should not depend on the entirety of the networkstack code.
     static_libs: [
-        "NetworkStackApiStableLib",
+        "DhcpPacketLib",
+        "NetworkStackApiStableShims",
     ],
     jni_uses_sdk_apis: true,
     min_sdk_version: "29",
diff --git a/tests/cts/net/native/src/BpfCompatTest.cpp b/tests/cts/net/native/src/BpfCompatTest.cpp
index e52533b..5c02b0d 100644
--- a/tests/cts/net/native/src/BpfCompatTest.cpp
+++ b/tests/cts/net/native/src/BpfCompatTest.cpp
@@ -31,7 +31,10 @@
   std::ifstream elfFile(elfPath, std::ios::in | std::ios::binary);
   ASSERT_TRUE(elfFile.is_open());
 
-  if (android::modules::sdklevel::IsAtLeastT()) {
+  if (android::modules::sdklevel::IsAtLeastU()) {
+    EXPECT_EQ(120, readSectionUint("size_of_bpf_map_def", elfFile, 0));
+    EXPECT_EQ(92, readSectionUint("size_of_bpf_prog_def", elfFile, 0));
+  } else if (android::modules::sdklevel::IsAtLeastT()) {
     EXPECT_EQ(116, readSectionUint("size_of_bpf_map_def", elfFile, 0));
     EXPECT_EQ(92, readSectionUint("size_of_bpf_prog_def", elfFile, 0));
   } else {
@@ -47,8 +50,13 @@
 }
 
 TEST(BpfTest, bpfStructSizeTest) {
-  doBpfStructSizeTest("/system/etc/bpf/gpu_mem.o");
-  doBpfStructSizeTest("/system/etc/bpf/time_in_state.o");
+  if (android::modules::sdklevel::IsAtLeastU()) {
+      doBpfStructSizeTest("/system/etc/bpf/gpuMem.o");
+      doBpfStructSizeTest("/system/etc/bpf/timeInState.o");
+  } else {
+      doBpfStructSizeTest("/system/etc/bpf/gpu_mem.o");
+      doBpfStructSizeTest("/system/etc/bpf/time_in_state.o");
+  }
 }
 
 int main(int argc, char **argv) {
diff --git a/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt b/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
index 1b77d5f..aad8804 100644
--- a/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
+++ b/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
@@ -37,9 +37,8 @@
 import android.net.cts.NetworkValidationTestUtil.setHttpsUrlDeviceConfig
 import android.net.cts.NetworkValidationTestUtil.setUrlExpirationDeviceConfig
 import android.net.cts.util.CtsNetUtils
-import android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTPS_URL
-import android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTP_URL
-import android.os.Build
+import com.android.net.module.util.NetworkStackConstants.TEST_CAPTIVE_PORTAL_HTTPS_URL
+import com.android.net.module.util.NetworkStackConstants.TEST_CAPTIVE_PORTAL_HTTP_URL
 import android.platform.test.annotations.AppModeFull
 import android.provider.DeviceConfig
 import android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY
@@ -47,11 +46,11 @@
 import android.util.Log
 import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
 import androidx.test.runner.AndroidJUnit4
+import com.android.modules.utils.build.SdkLevel.isAtLeastR
 import com.android.testutils.RecorderCallback
 import com.android.testutils.TestHttpServer
 import com.android.testutils.TestHttpServer.Request
 import com.android.testutils.TestableNetworkCallback
-import com.android.testutils.isDevSdkInRange
 import com.android.testutils.runAsShell
 import fi.iki.elonen.NanoHTTPD.Response.Status
 import junit.framework.AssertionFailedError
@@ -196,8 +195,8 @@
             assertNotEquals(network, cm.activeNetwork, wifiDefaultMessage)
 
             val startPortalAppPermission =
-                    if (isDevSdkInRange(0, Build.VERSION_CODES.Q)) CONNECTIVITY_INTERNAL
-                    else NETWORK_SETTINGS
+                    if (isAtLeastR()) NETWORK_SETTINGS
+                    else CONNECTIVITY_INTERNAL
             runAsShell(startPortalAppPermission) { cm.startCaptivePortalApp(network) }
 
             // Expect the portal content to be fetched at some point after detecting the portal.
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 5a56e2b..64238b3 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -37,9 +37,14 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.ConnectivityManager.EXTRA_NETWORK;
 import static android.net.ConnectivityManager.EXTRA_NETWORK_REQUEST;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY;
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_1;
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_2;
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_3;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY;
 import static android.net.ConnectivityManager.FIREWALL_RULE_ALLOW;
 import static android.net.ConnectivityManager.FIREWALL_RULE_DENY;
 import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
@@ -75,8 +80,6 @@
 import static android.net.cts.util.CtsNetUtils.TEST_HOST;
 import static android.net.cts.util.CtsNetUtils.TestNetworkCallback;
 import static android.net.cts.util.CtsTetheringUtils.TestTetheringEventCallback;
-import static android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTPS_URL;
-import static android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTP_URL;
 import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
 import static android.os.Process.INVALID_UID;
 import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
@@ -88,6 +91,8 @@
 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
 import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
+import static com.android.net.module.util.NetworkStackConstants.TEST_CAPTIVE_PORTAL_HTTPS_URL;
+import static com.android.net.module.util.NetworkStackConstants.TEST_CAPTIVE_PORTAL_HTTP_URL;
 import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_LOCKDOWN_VPN;
 import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_NONE;
 import static com.android.testutils.Cleanup.testAndCleanup;
@@ -186,7 +191,6 @@
 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.DeviceInfoUtils;
 import com.android.testutils.DumpTestUtils;
 import com.android.testutils.RecorderCallback.CallbackEntry;
@@ -331,11 +335,10 @@
         mCtsNetUtils = new CtsNetUtils(mContext);
         mTm = mContext.getSystemService(TelephonyManager.class);
 
-        if (DevSdkIgnoreRuleKt.isDevSdkInRange(null /* minExclusive */,
-                Build.VERSION_CODES.R /* maxInclusive */)) {
-            addLegacySupportedNetworkTypes();
-        } else {
+        if (isAtLeastS()) {
             addSupportedNetworkTypes();
+        } else {
+            addLegacySupportedNetworkTypes();
         }
 
         mUiAutomation = mInstrumentation.getUiAutomation();
@@ -2485,13 +2488,11 @@
         final CtsTetheringUtils tetherUtils = new CtsTetheringUtils(mContext);
         try {
             tetherEventCallback = tetherUtils.registerTetheringEventCallback();
-            // Adopt for NETWORK_SETTINGS permission.
-            mUiAutomation.adoptShellPermissionIdentity();
             // start tethering
             tetherEventCallback.assumeWifiTetheringSupported(mContext);
             tetherUtils.startWifiTethering(tetherEventCallback);
             // Update setting to verify the behavior.
-            mCm.setAirplaneMode(true);
+            setAirplaneMode(true);
             ConnectivitySettingsManager.setPrivateDnsMode(mContext,
                     ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF);
             ConnectivitySettingsManager.setNetworkAvoidBadWifi(mContext,
@@ -2499,7 +2500,7 @@
             assertEquals(AIRPLANE_MODE_ON, Settings.Global.getInt(
                     mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON));
             // Verify factoryReset
-            mCm.factoryReset();
+            runAsShell(NETWORK_SETTINGS, () -> mCm.factoryReset());
             verifySettings(AIRPLANE_MODE_OFF,
                     ConnectivitySettingsManager.PRIVATE_DNS_MODE_OPPORTUNISTIC,
                     ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI_PROMPT);
@@ -2507,17 +2508,20 @@
             tetherEventCallback.expectNoTetheringActive();
         } finally {
             // Restore settings.
-            mCm.setAirplaneMode(false);
+            setAirplaneMode(false);
             ConnectivitySettingsManager.setNetworkAvoidBadWifi(mContext, curAvoidBadWifi);
             ConnectivitySettingsManager.setPrivateDnsMode(mContext, curPrivateDnsMode);
             if (tetherEventCallback != null) {
                 tetherUtils.unregisterTetheringEventCallback(tetherEventCallback);
             }
             tetherUtils.stopAllTethering();
-            mUiAutomation.dropShellPermissionIdentity();
         }
     }
 
+    private void setAirplaneMode(boolean enable) {
+        runAsShell(NETWORK_SETTINGS, () -> mCm.setAirplaneMode(enable));
+    }
+
     /**
      * Verify that {@link ConnectivityManager#setProfileNetworkPreference} cannot be called
      * without required NETWORK_STACK permissions.
@@ -3298,84 +3302,76 @@
 
     private static final boolean EXPECT_PASS = false;
     private static final boolean EXPECT_BLOCK = true;
+    private static final boolean ALLOWLIST = true;
+    private static final boolean DENYLIST = false;
 
-    private void doTestFirewallBlockingDenyRule(final int chain) {
+    private void doTestFirewallBlocking(final int chain, final boolean isAllowList) {
+        final int myUid = Process.myUid();
+        final int ruleToAddMatch = isAllowList ? FIREWALL_RULE_ALLOW : FIREWALL_RULE_DENY;
+        final int ruleToRemoveMatch = isAllowList ? FIREWALL_RULE_DENY : FIREWALL_RULE_ALLOW;
+
         runWithShellPermissionIdentity(() -> {
-            try (DatagramSocket srcSock = new DatagramSocket();
-                 DatagramSocket dstSock = new DatagramSocket()) {
+            // Firewall chain status will be restored after the test.
+            final boolean wasChainEnabled = mCm.getFirewallChainEnabled(chain);
+            final DatagramSocket srcSock = new DatagramSocket();
+            final DatagramSocket dstSock = new DatagramSocket();
+            testAndCleanup(() -> {
+                if (wasChainEnabled) {
+                    mCm.setFirewallChainEnabled(chain, false /* enable */);
+                }
                 dstSock.setSoTimeout(SOCKET_TIMEOUT_MS);
 
-                // No global config, No uid config
+                // Chain disabled, UID not on chain.
                 checkFirewallBlocking(srcSock, dstSock, EXPECT_PASS);
 
-                // Has global config, No uid config
+                // Chain enabled, UID not on chain.
                 mCm.setFirewallChainEnabled(chain, true /* enable */);
-                checkFirewallBlocking(srcSock, dstSock, EXPECT_PASS);
+                assertTrue(mCm.getFirewallChainEnabled(chain));
+                checkFirewallBlocking(srcSock, dstSock, isAllowList ? EXPECT_BLOCK : EXPECT_PASS);
 
-                // Has global config, Has uid config
-                mCm.setUidFirewallRule(chain, Process.myUid(), FIREWALL_RULE_DENY);
-                checkFirewallBlocking(srcSock, dstSock, EXPECT_BLOCK);
+                // Chain enabled, UID on chain.
+                mCm.setUidFirewallRule(chain, myUid, ruleToAddMatch);
+                checkFirewallBlocking(srcSock, dstSock, isAllowList ?  EXPECT_PASS : EXPECT_BLOCK);
 
-                // No global config, Has uid config
+                // Chain disabled, UID on chain.
                 mCm.setFirewallChainEnabled(chain, false /* enable */);
+                assertFalse(mCm.getFirewallChainEnabled(chain));
                 checkFirewallBlocking(srcSock, dstSock, EXPECT_PASS);
 
-                // No global config, No uid config
-                mCm.setUidFirewallRule(chain, Process.myUid(), FIREWALL_RULE_ALLOW);
+                // Chain disabled, UID not on chain.
+                mCm.setUidFirewallRule(chain, myUid, ruleToRemoveMatch);
                 checkFirewallBlocking(srcSock, dstSock, EXPECT_PASS);
-            } finally {
-                mCm.setFirewallChainEnabled(chain, false /* enable */);
-                mCm.setUidFirewallRule(chain, Process.myUid(), FIREWALL_RULE_ALLOW);
-            }
-        }, NETWORK_SETTINGS);
-    }
-
-    private void doTestFirewallBlockingAllowRule(final int chain) {
-        runWithShellPermissionIdentity(() -> {
-            try (DatagramSocket srcSock = new DatagramSocket();
-                 DatagramSocket dstSock = new DatagramSocket()) {
-                dstSock.setSoTimeout(SOCKET_TIMEOUT_MS);
-
-                // No global config, No uid config
-                checkFirewallBlocking(srcSock, dstSock, EXPECT_PASS);
-
-                // Has global config, No uid config
-                mCm.setFirewallChainEnabled(chain, true /* enable */);
-                checkFirewallBlocking(srcSock, dstSock, EXPECT_BLOCK);
-
-                // Has global config, Has uid config
-                mCm.setUidFirewallRule(chain, Process.myUid(), FIREWALL_RULE_ALLOW);
-                checkFirewallBlocking(srcSock, dstSock, EXPECT_PASS);
-
-                // No global config, Has uid config
-                mCm.setFirewallChainEnabled(chain, false /* enable */);
-                checkFirewallBlocking(srcSock, dstSock, EXPECT_PASS);
-
-                // No global config, No uid config
-                mCm.setUidFirewallRule(chain, Process.myUid(), FIREWALL_RULE_DENY);
-                checkFirewallBlocking(srcSock, dstSock, EXPECT_PASS);
-            } finally {
-                mCm.setFirewallChainEnabled(chain, false /* enable */);
-                mCm.setUidFirewallRule(chain, Process.myUid(), FIREWALL_RULE_DENY);
-            }
+            }, /* cleanup */ () -> {
+                    srcSock.close();
+                    dstSock.close();
+                }, /* cleanup */ () -> {
+                    // Restore the global chain status
+                    mCm.setFirewallChainEnabled(chain, wasChainEnabled);
+                }, /* cleanup */ () -> {
+                    try {
+                        mCm.setUidFirewallRule(chain, myUid, ruleToRemoveMatch);
+                    } catch (IllegalStateException ignored) {
+                        // Removing match causes an exception when the rule entry for the uid does
+                        // not exist. But this is fine and can be ignored.
+                    }
+                });
         }, NETWORK_SETTINGS);
     }
 
     @Test @IgnoreUpTo(SC_V2)
     @AppModeFull(reason = "Socket cannot bind in instant app mode")
     public void testFirewallBlocking() {
-        // Following tests affect the actual state of networking on the device after the test.
-        // This might cause unexpected behaviour of the device. So, we skip them for now.
-        // We will enable following tests after adding the logic of firewall state restoring.
-        // doTestFirewallBlockingAllowRule(FIREWALL_CHAIN_DOZABLE);
-        // doTestFirewallBlockingAllowRule(FIREWALL_CHAIN_POWERSAVE);
-        // doTestFirewallBlockingAllowRule(FIREWALL_CHAIN_RESTRICTED);
-        // doTestFirewallBlockingAllowRule(FIREWALL_CHAIN_LOW_POWER_STANDBY);
+        // ALLOWLIST means the firewall denies all by default, uids must be explicitly allowed
+        doTestFirewallBlocking(FIREWALL_CHAIN_DOZABLE, ALLOWLIST);
+        doTestFirewallBlocking(FIREWALL_CHAIN_POWERSAVE, ALLOWLIST);
+        doTestFirewallBlocking(FIREWALL_CHAIN_RESTRICTED, ALLOWLIST);
+        doTestFirewallBlocking(FIREWALL_CHAIN_LOW_POWER_STANDBY, ALLOWLIST);
 
-        // doTestFirewallBlockingDenyRule(FIREWALL_CHAIN_STANDBY);
-        doTestFirewallBlockingDenyRule(FIREWALL_CHAIN_OEM_DENY_1);
-        doTestFirewallBlockingDenyRule(FIREWALL_CHAIN_OEM_DENY_2);
-        doTestFirewallBlockingDenyRule(FIREWALL_CHAIN_OEM_DENY_3);
+        // DENYLIST means the firewall allows all by default, uids must be explicitly denyed
+        doTestFirewallBlocking(FIREWALL_CHAIN_STANDBY, DENYLIST);
+        doTestFirewallBlocking(FIREWALL_CHAIN_OEM_DENY_1, DENYLIST);
+        doTestFirewallBlocking(FIREWALL_CHAIN_OEM_DENY_2, DENYLIST);
+        doTestFirewallBlocking(FIREWALL_CHAIN_OEM_DENY_3, DENYLIST);
     }
 
     private void assumeTestSApis() {
diff --git a/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt b/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
index 621b743..b68d3bf 100644
--- a/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
+++ b/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
@@ -27,6 +27,8 @@
 import android.net.IpPrefix
 import android.net.LinkAddress
 import android.net.LinkProperties
+import android.net.Network
+import android.net.MacAddress
 import android.net.NetworkAgent
 import android.net.NetworkAgent.DSCP_POLICY_STATUS_DELETED
 import android.net.NetworkAgent.DSCP_POLICY_STATUS_SUCCESS
@@ -45,10 +47,13 @@
 import android.net.TestNetworkManager
 import android.net.RouteInfo
 import android.os.HandlerThread
+import android.os.SystemClock
 import android.platform.test.annotations.AppModeFull
+import android.system.ErrnoException
 import android.system.Os
 import android.system.OsConstants.AF_INET
 import android.system.OsConstants.AF_INET6
+import android.system.OsConstants.ENETUNREACH
 import android.system.OsConstants.IPPROTO_UDP
 import android.system.OsConstants.SOCK_DGRAM
 import android.system.OsConstants.SOCK_NONBLOCK
@@ -56,9 +61,15 @@
 import android.util.Range
 import androidx.test.InstrumentationRegistry
 import androidx.test.runner.AndroidJUnit4
+import com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV4
+import com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV6
+import com.android.net.module.util.Struct
+import com.android.net.module.util.structs.EthernetHeader
+import com.android.testutils.ArpResponder
 import com.android.testutils.CompatUtil
 import com.android.testutils.DevSdkIgnoreRule
 import com.android.testutils.assertParcelingIsLossless
+import com.android.testutils.RouterAdvertisementResponder
 import com.android.testutils.runAsShell
 import com.android.testutils.SC_V2
 import com.android.testutils.TapPacketReader
@@ -74,7 +85,7 @@
 import org.junit.runner.RunWith
 import java.net.Inet4Address
 import java.net.Inet6Address
-import java.net.InetAddress
+import java.net.InetSocketAddress
 import java.nio.ByteBuffer
 import java.nio.ByteOrder
 import java.util.regex.Pattern
@@ -103,10 +114,12 @@
 
     private val LOCAL_IPV4_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.1")
     private val TEST_TARGET_IPV4_ADDR =
-            InetAddresses.parseNumericAddress("8.8.8.8") as Inet4Address
-    private val LOCAL_IPV6_ADDRESS = InetAddresses.parseNumericAddress("2001:db8::1")
+            InetAddresses.parseNumericAddress("203.0.113.1") as Inet4Address
     private val TEST_TARGET_IPV6_ADDR =
-            InetAddresses.parseNumericAddress("2001:4860:4860::8888") as Inet6Address
+        InetAddresses.parseNumericAddress("2001:4860:4860::8888") as Inet6Address
+    private val TEST_ROUTER_IPV6_ADDR =
+        InetAddresses.parseNumericAddress("fe80::1234") as Inet6Address
+    private val TEST_TARGET_MAC_ADDR = MacAddress.fromString("12:34:56:78:9a:bc")
 
     private val realContext = InstrumentationRegistry.getContext()
     private val cm = realContext.getSystemService(ConnectivityManager::class.java)
@@ -116,9 +129,12 @@
 
     private val handlerThread = HandlerThread(DscpPolicyTest::class.java.simpleName)
 
+    private lateinit var srcAddressV6: Inet6Address
     private lateinit var iface: TestNetworkInterface
     private lateinit var tunNetworkCallback: TestNetworkCallback
     private lateinit var reader: TapPacketReader
+    private lateinit var arpResponder: ArpResponder
+    private lateinit var raResponder: RouterAdvertisementResponder
 
     private fun getKernelVersion(): IntArray {
         // Example:
@@ -129,6 +145,7 @@
         return intArrayOf(Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2)))
     }
 
+    // TODO: replace with DeviceInfoUtils#isKernelVersionAtLeast
     private fun kernelIsAtLeast(major: Int, minor: Int): Boolean {
         val version = getKernelVersion()
         return (version.get(0) > major || (version.get(0) == major && version.get(1) >= minor))
@@ -142,9 +159,10 @@
         runAsShell(MANAGE_TEST_NETWORKS) {
             val tnm = realContext.getSystemService(TestNetworkManager::class.java)
 
-            iface = tnm.createTunInterface(arrayOf(
-                    LinkAddress(LOCAL_IPV4_ADDRESS, IP4_PREFIX_LEN),
-                    LinkAddress(LOCAL_IPV6_ADDRESS, IP6_PREFIX_LEN)))
+            // Only statically configure the IPv4 address; for IPv6, use the SLAAC generated
+            // address.
+            iface = tnm.createTapInterface(true /* disableIpv6ProvisioningDelay */,
+                    arrayOf(LinkAddress(LOCAL_IPV4_ADDRESS, IP4_PREFIX_LEN)))
             assertNotNull(iface)
         }
 
@@ -154,21 +172,30 @@
                 iface.fileDescriptor.fileDescriptor,
                 MAX_PACKET_LENGTH)
         reader.startAsyncForTest()
+
+        arpResponder = ArpResponder(reader, mapOf(TEST_TARGET_IPV4_ADDR to TEST_TARGET_MAC_ADDR))
+        arpResponder.start()
+        raResponder = RouterAdvertisementResponder(reader)
+        raResponder.addRouterEntry(TEST_TARGET_MAC_ADDR, TEST_ROUTER_IPV6_ADDR)
+        raResponder.start()
     }
 
     @After
     fun tearDown() {
         if (!kernelIsAtLeast(5, 15)) {
-            return;
+            return
         }
+        raResponder.stop()
+        arpResponder.stop()
+
         agentsToCleanUp.forEach { it.unregister() }
         callbacksToCleanUp.forEach { cm.unregisterNetworkCallback(it) }
 
         // reader.stop() cleans up tun fd
         reader.handler.post { reader.stop() }
-        if (iface.fileDescriptor.fileDescriptor != null)
-            Os.close(iface.fileDescriptor.fileDescriptor)
+        // quitSafely processes all events in the queue, except delayed messages.
         handlerThread.quitSafely()
+        handlerThread.join()
     }
 
     private fun requestNetwork(request: NetworkRequest, callback: TestableNetworkCallback) {
@@ -189,6 +216,39 @@
                 .build()
     }
 
+    private fun waitForGlobalIpv6Address(network: Network): Inet6Address {
+        // Wait for global IPv6 address to be available
+        val sock = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)
+        network.bindSocket(sock)
+
+        var inet6Addr: Inet6Address? = null
+        val timeout = SystemClock.elapsedRealtime() + PACKET_TIMEOUT_MS
+        while (timeout > SystemClock.elapsedRealtime()) {
+            try {
+                // Pick any arbitrary port
+                Os.connect(sock, TEST_TARGET_IPV6_ADDR, 12345)
+                val sockAddr = Os.getsockname(sock) as InetSocketAddress
+
+                // TODO: make RouterAdvertisementResponder.SLAAC_PREFIX public and use it here,
+                // or make it configurable and configure it here.
+                if (IpPrefix("2001:db8::/64").contains(sockAddr.address)) {
+                    inet6Addr = sockAddr.address as Inet6Address
+                    break
+                }
+            } catch (e: ErrnoException) {
+                // ignore ENETUNREACH -- there may not be an address available yet.
+                if (e.errno != ENETUNREACH) {
+                    Os.close(sock)
+                    throw e
+                }
+            }
+            SystemClock.sleep(10 /* ms */)
+        }
+        Os.close(sock)
+        assertNotNull(inet6Addr)
+        return inet6Addr!!
+    }
+
     private fun createConnectedNetworkAgent(
         context: Context = realContext,
         specifier: String? = iface.getInterfaceName()
@@ -211,9 +271,8 @@
         }
         val lp = LinkProperties().apply {
             addLinkAddress(LinkAddress(LOCAL_IPV4_ADDRESS, IP4_PREFIX_LEN))
-            addLinkAddress(LinkAddress(LOCAL_IPV6_ADDRESS, IP6_PREFIX_LEN))
             addRoute(RouteInfo(IpPrefix("0.0.0.0/0"), null, null))
-            addRoute(RouteInfo(InetAddress.getByName("fe80::1234")))
+            addRoute(RouteInfo(IpPrefix("::/0"), TEST_ROUTER_IPV6_ADDR))
             setInterfaceName(specifier)
         }
         val config = NetworkAgentConfig.Builder().build()
@@ -226,7 +285,9 @@
         agent.expectCallback<OnNetworkCreated>()
         agent.expectSignalStrengths(intArrayOf())
         agent.expectValidationBypassedStatus()
+
         val network = agent.network ?: fail("Expected a non-null network")
+        srcAddressV6 = waitForGlobalIpv6Address(network)
         return agent to callback
     }
 
@@ -237,7 +298,7 @@
     fun sendPacket(
         agent: TestableNetworkAgent,
         sendV6: Boolean,
-        dstPort: Int = 0,
+        dstPort: Int = 0
     ) {
         val testString = "test string"
         val testPacket = ByteBuffer.wrap(testString.toByteArray(Charsets.UTF_8))
@@ -249,11 +310,11 @@
 
         val originalPacket = testPacket.readAsArray()
         Os.sendto(socket, originalPacket, 0 /* bytesOffset */, originalPacket.size, 0 /* flags */,
-                if(sendV6) TEST_TARGET_IPV6_ADDR else TEST_TARGET_IPV4_ADDR, dstPort)
+                if (sendV6) TEST_TARGET_IPV6_ADDR else TEST_TARGET_IPV4_ADDR, dstPort)
         Os.close(socket)
     }
 
-    fun parseV4PacketDscp(buffer : ByteBuffer) : Int {
+    fun parseV4PacketDscp(buffer: ByteBuffer): Int {
         val ip_ver = buffer.get()
         val tos = buffer.get()
         val length = buffer.getShort()
@@ -265,7 +326,7 @@
         return tos.toInt().shr(2)
     }
 
-    fun parseV6PacketDscp(buffer : ByteBuffer) : Int {
+    fun parseV6PacketDscp(buffer: ByteBuffer): Int {
         val ip_ver = buffer.get()
         val tc = buffer.get()
         val fl = buffer.getShort()
@@ -279,9 +340,9 @@
     }
 
     fun parsePacketIp(
-        buffer : ByteBuffer,
-        sendV6 : Boolean,
-    ) : Boolean {
+        buffer: ByteBuffer,
+        sendV6: Boolean
+    ): Boolean {
         val ipAddr = if (sendV6) ByteArray(16) else ByteArray(4)
         buffer.get(ipAddr)
         val srcIp = if (sendV6) Inet6Address.getByAddress(ipAddr)
@@ -292,20 +353,20 @@
 
         Log.e(TAG, "IP Src:" + srcIp + " dst: " + dstIp)
 
-        if ((sendV6 && srcIp == LOCAL_IPV6_ADDRESS && dstIp == TEST_TARGET_IPV6_ADDR) ||
+        if ((sendV6 && srcIp == srcAddressV6 && dstIp == TEST_TARGET_IPV6_ADDR) ||
                 (!sendV6 && srcIp == LOCAL_IPV4_ADDRESS && dstIp == TEST_TARGET_IPV4_ADDR)) {
-            Log.e(TAG, "IP return true");
+            Log.e(TAG, "IP return true")
             return true
         }
-        Log.e(TAG, "IP return false");
+        Log.e(TAG, "IP return false")
         return false
     }
 
     fun parsePacketPort(
-        buffer : ByteBuffer,
-        srcPort : Int,
-        dstPort : Int
-    ) : Boolean {
+        buffer: ByteBuffer,
+        srcPort: Int,
+        dstPort: Int
+    ): Boolean {
         if (srcPort == 0 && dstPort == 0) return true
 
         val packetSrcPort = buffer.getShort().toInt()
@@ -315,26 +376,33 @@
 
         if ((srcPort == 0 || (srcPort != 0 && srcPort == packetSrcPort)) &&
                 (dstPort == 0 || (dstPort != 0 && dstPort == packetDstPort))) {
-            Log.e(TAG, "Port return true");
+            Log.e(TAG, "Port return true")
             return true
         }
-        Log.e(TAG, "Port return false");
+        Log.e(TAG, "Port return false")
         return false
     }
 
     fun validatePacket(
-        agent : TestableNetworkAgent,
-        sendV6 : Boolean = false,
-        dscpValue : Int = 0,
-        dstPort : Int = 0,
+        agent: TestableNetworkAgent,
+        sendV6: Boolean = false,
+        dscpValue: Int = 0,
+        dstPort: Int = 0
     ) {
-        var packetFound = false;
+        var packetFound = false
         sendPacket(agent, sendV6, dstPort)
         // TODO: grab source port from socket in sendPacket
 
         Log.e(TAG, "find DSCP value:" + dscpValue)
-        generateSequence { reader.poll(PACKET_TIMEOUT_MS) }.forEach { packet ->
+        val packets = generateSequence { reader.poll(PACKET_TIMEOUT_MS) }
+        for (packet in packets) {
             val buffer = ByteBuffer.wrap(packet, 0, packet.size).order(ByteOrder.BIG_ENDIAN)
+            // TODO: consider using Struct.parse for all packet parsing.
+            val etherHdr = Struct.parse(EthernetHeader::class.java, buffer)
+            val expectedType = if (sendV6) ETHER_TYPE_IPV6 else ETHER_TYPE_IPV4
+            if (etherHdr.etherType != expectedType) {
+                continue
+            }
             val dscp = if (sendV6) parseV6PacketDscp(buffer) else parseV4PacketDscp(buffer)
             Log.e(TAG, "DSCP value:" + dscp)
 
@@ -420,7 +488,7 @@
         val policy2 = DscpPolicy.Builder(1, 4)
                 .setDestinationPortRange(Range(5555, 5555))
                 .setDestinationAddress(TEST_TARGET_IPV6_ADDR)
-                .setSourceAddress(LOCAL_IPV6_ADDRESS)
+                .setSourceAddress(srcAddressV6)
                 .setProtocol(IPPROTO_UDP).build()
         agent.sendAddDscpPolicy(policy2)
         agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
diff --git a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
index 1748612..89b107e 100644
--- a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
@@ -21,6 +21,8 @@
 import android.content.Context
 import android.net.ConnectivityManager
 import android.net.EthernetManager
+import android.net.EthernetManager.ETHERNET_STATE_DISABLED
+import android.net.EthernetManager.ETHERNET_STATE_ENABLED
 import android.net.EthernetManager.InterfaceStateListener
 import android.net.EthernetManager.ROLE_CLIENT
 import android.net.EthernetManager.ROLE_NONE
@@ -35,15 +37,19 @@
 import android.net.EthernetNetworkUpdateRequest
 import android.net.InetAddresses
 import android.net.IpConfiguration
+import android.net.LinkAddress
 import android.net.MacAddress
 import android.net.Network
 import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED
 import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
 import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
 import android.net.NetworkCapabilities.TRANSPORT_TEST
 import android.net.NetworkRequest
+import android.net.StaticIpConfiguration
 import android.net.TestNetworkInterface
 import android.net.TestNetworkManager
+import android.net.cts.EthernetManagerTest.EthernetStateListener.CallbackEntry.EthernetStateChanged
 import android.net.cts.EthernetManagerTest.EthernetStateListener.CallbackEntry.InterfaceStateChanged
 import android.os.Build
 import android.os.Handler
@@ -54,16 +60,17 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.net.module.util.ArrayTrackRecord
 import com.android.net.module.util.TrackRecord
-import com.android.testutils.anyNetwork
 import com.android.testutils.ConnectivityModuleTest
-import com.android.testutils.DeviceInfoUtils.isKernelVersionAtLeast
 import com.android.testutils.DevSdkIgnoreRule
 import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.DeviceInfoUtils.isKernelVersionAtLeast
 import com.android.testutils.RecorderCallback.CallbackEntry.Available
+import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
 import com.android.testutils.RecorderCallback.CallbackEntry.Lost
 import com.android.testutils.RouterAdvertisementResponder
 import com.android.testutils.TapPacketReader
 import com.android.testutils.TestableNetworkCallback
+import com.android.testutils.anyNetwork
 import com.android.testutils.runAsShell
 import com.android.testutils.waitForIdle
 import org.junit.After
@@ -74,8 +81,9 @@
 import java.net.Inet6Address
 import java.util.concurrent.CompletableFuture
 import java.util.concurrent.ExecutionException
-import java.util.concurrent.TimeoutException
 import java.util.concurrent.TimeUnit
+import java.util.concurrent.TimeoutException
+import java.util.function.IntConsumer
 import kotlin.test.assertEquals
 import kotlin.test.assertFailsWith
 import kotlin.test.assertFalse
@@ -84,17 +92,25 @@
 import kotlin.test.assertTrue
 import kotlin.test.fail
 
+private const val TAG = "EthernetManagerTest"
 // TODO: try to lower this timeout in the future. Currently, ethernet tests are still flaky because
 // the interface is not ready fast enough (mostly due to the up / up / down / up issue).
 private const val TIMEOUT_MS = 2000L
-private const val NO_CALLBACK_TIMEOUT_MS = 200L
+// Timeout used to confirm no callbacks matching given criteria are received. Must be long enough to
+// process all callbacks including ip provisioning when using the updateConfiguration API.
+private const val NO_CALLBACK_TIMEOUT_MS = 500L
+
 private val DEFAULT_IP_CONFIGURATION = IpConfiguration(IpConfiguration.IpAssignment.DHCP,
-    IpConfiguration.ProxySettings.NONE, null, null)
+        IpConfiguration.ProxySettings.NONE, null, null)
 private val ETH_REQUEST: NetworkRequest = NetworkRequest.Builder()
-    .addTransportType(TRANSPORT_TEST)
-    .addTransportType(TRANSPORT_ETHERNET)
-    .removeCapability(NET_CAPABILITY_TRUSTED)
-    .build()
+        .addTransportType(TRANSPORT_TEST)
+        .addTransportType(TRANSPORT_ETHERNET)
+        .removeCapability(NET_CAPABILITY_TRUSTED)
+        .build()
+private val STATIC_IP_CONFIGURATION = IpConfiguration.Builder()
+        .setStaticIpConfiguration(StaticIpConfiguration.Builder()
+                .setIpAddress(LinkAddress("192.0.2.1/30")).build())
+        .build()
 
 @AppModeFull(reason = "Instant apps can't access EthernetManager")
 // EthernetManager is not updatable before T, so tests do not need to be backwards compatible.
@@ -161,7 +177,7 @@
 
     private open class EthernetStateListener private constructor(
         private val history: ArrayTrackRecord<CallbackEntry>
-    ) : InterfaceStateListener,
+    ) : InterfaceStateListener, IntConsumer,
                 TrackRecord<EthernetStateListener.CallbackEntry> by history {
         constructor() : this(ArrayTrackRecord())
 
@@ -174,6 +190,8 @@
                 val role: Int,
                 val configuration: IpConfiguration?
             ) : CallbackEntry()
+
+            data class EthernetStateChanged(val state: Int) : CallbackEntry()
         }
 
         override fun onInterfaceStateChanged(
@@ -185,6 +203,10 @@
             add(InterfaceStateChanged(iface, state, role, cfg))
         }
 
+        override fun accept(state: Int) {
+            add(EthernetStateChanged(state))
+        }
+
         fun <T : CallbackEntry> expectCallback(expected: T): T {
             val event = pollForNextCallback()
             assertEquals(expected, event)
@@ -195,6 +217,10 @@
             expectCallback(createChangeEvent(iface.name, state, role))
         }
 
+        fun expectCallback(state: Int) {
+            expectCallback(EthernetStateChanged(state))
+        }
+
         fun createChangeEvent(iface: String, state: Int, role: Int) =
                 InterfaceStateChanged(iface, state, role,
                         if (state != STATE_ABSENT) DEFAULT_IP_CONFIGURATION else null)
@@ -209,6 +235,10 @@
             assertNotNull(eventuallyExpect(createChangeEvent(iface.name, state, role)))
         }
 
+        fun eventuallyExpect(state: Int) {
+            assertNotNull(eventuallyExpect(EthernetStateChanged(state)))
+        }
+
         fun assertNoCallback() {
             val cb = events.poll(NO_CALLBACK_TIMEOUT_MS)
             assertNull(cb, "Expected no callback but got $cb")
@@ -280,11 +310,17 @@
 
     @After
     fun tearDown() {
-        setIncludeTestInterfaces(false)
+        // Reenable ethernet, so ABSENT callbacks are received.
+        setEthernetEnabled(true)
+
         for (iface in createdIfaces) {
             iface.destroy()
             ifaceListener.eventuallyExpect(iface, STATE_ABSENT, ROLE_NONE)
         }
+
+        // After test interfaces are removed, disable tracking.
+        setIncludeTestInterfaces(false)
+
         for (listener in addedListeners) {
             em.removeInterfaceStateListener(listener)
         }
@@ -391,6 +427,19 @@
         }
     }
 
+    private fun setEthernetEnabled(enabled: Boolean) {
+        runAsShell(NETWORK_SETTINGS) { em.setEthernetEnabled(enabled) }
+
+        val listener = EthernetStateListener()
+        em.addEthernetStateListener(handler::post, listener)
+        try {
+            listener.eventuallyExpect(
+                    if (enabled) ETHERNET_STATE_ENABLED else ETHERNET_STATE_DISABLED)
+        } finally {
+            em.removeEthernetStateListener(listener)
+        }
+    }
+
     // NetworkRequest.Builder does not create a copy of the passed NetworkRequest, so in order to
     // keep ETH_REQUEST as it is, a defensive copy is created here.
     private fun NetworkRequest.createCopyWithEthernetSpecifier(ifaceName: String) =
@@ -399,7 +448,10 @@
 
     // It can take multiple seconds for the network to become available.
     private fun TestableNetworkCallback.expectAvailable() =
-        expectCallback<Available>(anyNetwork(), 5000 /* ms timeout */).network
+            expectCallback<Available>(anyNetwork(), 5000 /* ms timeout */).network
+
+    private fun TestableNetworkCallback.expectLost(n: Network = anyNetwork()) =
+            expectCallback<Lost>(n, 5000 /* ms timeout */)
 
     // b/233534110: eventuallyExpect<Lost>() does not advance ReadHead, use
     // eventuallyExpect(Lost::class) instead.
@@ -407,7 +459,9 @@
         eventuallyExpect(Lost::class, TIMEOUT_MS) { n?.equals(it.network) ?: true }
 
     private fun TestableNetworkCallback.assertNeverLost(n: Network? = null) =
-        assertNoCallbackThat() { it is Lost && (n?.equals(it.network) ?: true) }
+        assertNoCallbackThat(NO_CALLBACK_TIMEOUT_MS) {
+            it is Lost && (n?.equals(it.network) ?: true)
+        }
 
     private fun TestableNetworkCallback.assertNeverAvailable(n: Network? = null) =
         assertNoCallbackThat() { it is Available && (n?.equals(it.network) ?: true) }
@@ -417,6 +471,18 @@
             it.networkSpecifier == EthernetNetworkSpecifier(name)
         }
 
+    private fun TestableNetworkCallback.expectCapabilitiesWithCapability(cap: Int) =
+        expectCapabilitiesThat(anyNetwork(), TIMEOUT_MS) {
+            it.hasCapability(cap)
+        }
+
+    private fun TestableNetworkCallback.expectLinkPropertiesWithLinkAddress(addr: LinkAddress) =
+        expectLinkPropertiesThat(anyNetwork(), TIMEOUT_MS) {
+            // LinkAddress.equals isn't possible as the system changes the LinkAddress.flags value.
+            // any() must be used since the interface may also have a link-local address.
+            it.linkAddresses.any { x -> x.isSameAddressAs(addr) }
+        }
+
     @Test
     fun testCallbacks() {
         // If an interface exists when the callback is registered, it is reported on registration.
@@ -681,4 +747,80 @@
         releaseTetheredInterface()
         listener.assertNoCallback()
     }
+
+    @Test
+    fun testEnableDisableInterface_withActiveRequest() {
+        val iface = createInterface()
+        val cb = requestNetwork(ETH_REQUEST)
+        cb.expectAvailable()
+        cb.assertNeverLost()
+
+        disableInterface(iface).expectResult(iface.name)
+        cb.eventuallyExpectLost()
+
+        enableInterface(iface).expectResult(iface.name)
+        cb.expectAvailable()
+    }
+
+    @Test
+    fun testUpdateConfiguration_forBothIpConfigAndCapabilities() {
+        val iface = createInterface()
+        val cb = requestNetwork(ETH_REQUEST.createCopyWithEthernetSpecifier(iface.name))
+        val network = cb.expectAvailable()
+        cb.assertNeverLost()
+
+        val testCapability = NET_CAPABILITY_TEMPORARILY_NOT_METERED
+        val nc = NetworkCapabilities
+                .Builder(ETH_REQUEST.networkCapabilities)
+                .addCapability(testCapability)
+                .build()
+        updateConfiguration(iface, STATIC_IP_CONFIGURATION, nc)
+
+        // UpdateConfiguration() currently does a restarts on the ethernet interface therefore lost
+        // will be expected first before available, as part of the restart.
+        cb.expectLost(network)
+        cb.expectAvailable()
+        cb.expectCapabilitiesWithCapability(testCapability)
+        cb.expectLinkPropertiesWithLinkAddress(
+                STATIC_IP_CONFIGURATION.staticIpConfiguration.ipAddress!!)
+    }
+
+    @Test
+    fun testUpdateConfiguration_forOnlyIpConfig() {
+        val iface: EthernetTestInterface = createInterface()
+        val cb = requestNetwork(ETH_REQUEST.createCopyWithEthernetSpecifier(iface.name))
+        val network = cb.expectAvailable()
+        cb.assertNeverLost()
+
+        updateConfiguration(iface, STATIC_IP_CONFIGURATION)
+
+        // UpdateConfiguration() currently does a restarts on the ethernet interface therefore lost
+        // will be expected first before available, as part of the restart.
+        cb.expectLost(network)
+        cb.expectAvailable()
+        cb.expectCallback<CapabilitiesChanged>()
+        cb.expectLinkPropertiesWithLinkAddress(
+                STATIC_IP_CONFIGURATION.staticIpConfiguration.ipAddress!!)
+    }
+
+    @Test
+    fun testUpdateConfiguration_forOnlyCapabilities() {
+        val iface: EthernetTestInterface = createInterface()
+        val cb = requestNetwork(ETH_REQUEST.createCopyWithEthernetSpecifier(iface.name))
+        val network = cb.expectAvailable()
+        cb.assertNeverLost()
+
+        val testCapability = NET_CAPABILITY_TEMPORARILY_NOT_METERED
+        val nc = NetworkCapabilities
+                .Builder(ETH_REQUEST.networkCapabilities)
+                .addCapability(testCapability)
+                .build()
+        updateConfiguration(iface, capabilities = nc)
+
+        // UpdateConfiguration() currently does a restarts on the ethernet interface therefore lost
+        // will be expected first before available, as part of the restart.
+        cb.expectLost(network)
+        cb.expectAvailable()
+        cb.expectCapabilitiesWithCapability(testCapability)
+    }
 }
diff --git a/tests/cts/net/src/android/net/cts/NetworkValidationTestUtil.kt b/tests/cts/net/src/android/net/cts/NetworkValidationTestUtil.kt
index 462c8a3..375bfb8 100644
--- a/tests/cts/net/src/android/net/cts/NetworkValidationTestUtil.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkValidationTestUtil.kt
@@ -17,9 +17,9 @@
 package android.net.cts
 
 import android.Manifest.permission.WRITE_DEVICE_CONFIG
-import android.net.util.NetworkStackUtils
 import android.provider.DeviceConfig
 import android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY
+import com.android.net.module.util.NetworkStackConstants
 import com.android.testutils.runAsShell
 
 /**
@@ -35,41 +35,41 @@
     @JvmStatic fun clearValidationTestUrlsDeviceConfig() {
         runAsShell(WRITE_DEVICE_CONFIG) {
             DeviceConfig.setProperty(NAMESPACE_CONNECTIVITY,
-                    NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTPS_URL, null, false)
+                    NetworkStackConstants.TEST_CAPTIVE_PORTAL_HTTPS_URL, null, false)
             DeviceConfig.setProperty(NAMESPACE_CONNECTIVITY,
-                    NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTP_URL, null, false)
+                    NetworkStackConstants.TEST_CAPTIVE_PORTAL_HTTP_URL, null, false)
             DeviceConfig.setProperty(NAMESPACE_CONNECTIVITY,
-                    NetworkStackUtils.TEST_URL_EXPIRATION_TIME, null, false)
+                    NetworkStackConstants.TEST_URL_EXPIRATION_TIME, null, false)
         }
     }
 
     /**
      * Set the test validation HTTPS URL.
      *
-     * @see NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTPS_URL
+     * @see NetworkStackConstants.TEST_CAPTIVE_PORTAL_HTTPS_URL
      */
     @JvmStatic
     fun setHttpsUrlDeviceConfig(rule: DeviceConfigRule, url: String?) =
             rule.setConfig(NAMESPACE_CONNECTIVITY,
-                NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTPS_URL, url)
+                NetworkStackConstants.TEST_CAPTIVE_PORTAL_HTTPS_URL, url)
 
     /**
      * Set the test validation HTTP URL.
      *
-     * @see NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTP_URL
+     * @see NetworkStackConstants.TEST_CAPTIVE_PORTAL_HTTP_URL
      */
     @JvmStatic
     fun setHttpUrlDeviceConfig(rule: DeviceConfigRule, url: String?) =
             rule.setConfig(NAMESPACE_CONNECTIVITY,
-                NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTP_URL, url)
+                NetworkStackConstants.TEST_CAPTIVE_PORTAL_HTTP_URL, url)
 
     /**
      * Set the test validation URL expiration.
      *
-     * @see NetworkStackUtils.TEST_URL_EXPIRATION_TIME
+     * @see NetworkStackConstants.TEST_URL_EXPIRATION_TIME
      */
     @JvmStatic
     fun setUrlExpirationDeviceConfig(rule: DeviceConfigRule, timestamp: Long?) =
             rule.setConfig(NAMESPACE_CONNECTIVITY,
-                NetworkStackUtils.TEST_URL_EXPIRATION_TIME, timestamp?.toString())
+                NetworkStackConstants.TEST_URL_EXPIRATION_TIME, timestamp?.toString())
 }
diff --git a/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java b/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
index 8c5372d..d5e9c9e 100644
--- a/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
@@ -16,12 +16,18 @@
 
 package android.net.cts.util;
 
+import static android.Manifest.permission.ACCESS_NETWORK_STATE;
+import static android.Manifest.permission.ACCESS_WIFI_STATE;
+import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.Manifest.permission.TETHER_PRIVILEGED;
 import static android.net.TetheringManager.TETHERING_WIFI;
 import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED;
 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED;
 
+import static com.android.testutils.TestPermissionUtil.runAsShell;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
@@ -396,9 +402,14 @@
         }
     }
 
+    private static boolean isWifiEnabled(final WifiManager wm) {
+        return runAsShell(ACCESS_WIFI_STATE, () -> wm.isWifiEnabled());
+
+    }
+
     private static void waitForWifiEnabled(final Context ctx) throws Exception {
         WifiManager wm = ctx.getSystemService(WifiManager.class);
-        if (wm.isWifiEnabled()) return;
+        if (isWifiEnabled(wm)) return;
 
         final ConditionVariable mWaiting = new ConditionVariable();
         final BroadcastReceiver receiver = new BroadcastReceiver() {
@@ -406,7 +417,7 @@
             public void onReceive(Context context, Intent intent) {
                 String action = intent.getAction();
                 if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
-                    if (wm.isWifiEnabled()) mWaiting.open();
+                    if (isWifiEnabled(wm)) mWaiting.open();
                 }
             }
         };
@@ -414,7 +425,7 @@
             ctx.registerReceiver(receiver, new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION));
             if (!mWaiting.block(DEFAULT_TIMEOUT_MS)) {
                 assertTrue("Wifi did not become enabled after " + DEFAULT_TIMEOUT_MS + "ms",
-                        wm.isWifiEnabled());
+                        isWifiEnabled(wm));
             }
         } finally {
             ctx.unregisterReceiver(receiver);
@@ -425,14 +436,16 @@
         final TestTetheringEventCallback tetherEventCallback =
                 new TestTetheringEventCallback();
 
-        mTm.registerTetheringEventCallback(c -> c.run() /* executor */, tetherEventCallback);
-        tetherEventCallback.expectCallbackStarted();
+        runAsShell(ACCESS_NETWORK_STATE, NETWORK_SETTINGS, () -> {
+            mTm.registerTetheringEventCallback(c -> c.run() /* executor */, tetherEventCallback);
+            tetherEventCallback.expectCallbackStarted();
+        });
 
         return tetherEventCallback;
     }
 
     public void unregisterTetheringEventCallback(final TestTetheringEventCallback callback) {
-        mTm.unregisterTetheringEventCallback(callback);
+        runAsShell(ACCESS_NETWORK_STATE, () -> mTm.unregisterTetheringEventCallback(callback));
     }
 
     private static List<String> getWifiTetherableInterfaceRegexps(
@@ -446,11 +459,11 @@
         if (!pm.hasSystemFeature(PackageManager.FEATURE_WIFI)) return false;
         final WifiManager wm = ctx.getSystemService(WifiManager.class);
         // Wifi feature flags only work when wifi is on.
-        final boolean previousWifiEnabledState = wm.isWifiEnabled();
+        final boolean previousWifiEnabledState = isWifiEnabled(wm);
         try {
             if (!previousWifiEnabledState) SystemUtil.runShellCommand("svc wifi enable");
             waitForWifiEnabled(ctx);
-            return wm.isPortableHotspotSupported();
+            return runAsShell(ACCESS_WIFI_STATE, () -> wm.isPortableHotspotSupported());
         } finally {
             if (!previousWifiEnabledState) SystemUtil.runShellCommand("svc wifi disable");
         }
@@ -463,17 +476,20 @@
         final StartTetheringCallback startTetheringCallback = new StartTetheringCallback();
         final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI)
                 .setShouldShowEntitlementUi(false).build();
-        mTm.startTethering(request, c -> c.run() /* executor */, startTetheringCallback);
-        startTetheringCallback.verifyTetheringStarted();
 
-        final TetheringInterface iface =
-                callback.expectTetheredInterfacesChanged(wifiRegexs, TETHERING_WIFI);
+        return runAsShell(TETHER_PRIVILEGED, () -> {
+            mTm.startTethering(request, c -> c.run() /* executor */, startTetheringCallback);
+            startTetheringCallback.verifyTetheringStarted();
 
-        callback.expectOneOfOffloadStatusChanged(
-                TETHER_HARDWARE_OFFLOAD_STARTED,
-                TETHER_HARDWARE_OFFLOAD_FAILED);
+            final TetheringInterface iface =
+                    callback.expectTetheredInterfacesChanged(wifiRegexs, TETHERING_WIFI);
 
-        return iface;
+            callback.expectOneOfOffloadStatusChanged(
+                    TETHER_HARDWARE_OFFLOAD_STARTED,
+                    TETHER_HARDWARE_OFFLOAD_FAILED);
+
+            return iface;
+        });
     }
 
     private static class StopSoftApCallback implements SoftApCallback {
@@ -501,23 +517,33 @@
     public void expectSoftApDisabled() {
         final StopSoftApCallback callback = new StopSoftApCallback();
         try {
-            mWm.registerSoftApCallback(c -> c.run(), callback);
+            runAsShell(NETWORK_SETTINGS, () -> mWm.registerSoftApCallback(c -> c.run(), callback));
             // registerSoftApCallback will immediately call the callback with the current state, so
             // this callback will fire even if softAp is already disabled.
             callback.waitForSoftApStopped();
         } finally {
-            mWm.unregisterSoftApCallback(callback);
+            runAsShell(NETWORK_SETTINGS, () -> mWm.unregisterSoftApCallback(callback));
         }
     }
 
     public void stopWifiTethering(final TestTetheringEventCallback callback) {
-        mTm.stopTethering(TETHERING_WIFI);
+        runAsShell(TETHER_PRIVILEGED, () -> {
+            mTm.stopTethering(TETHERING_WIFI);
+            callback.expectNoTetheringActive();
+            callback.expectOneOfOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
+        });
         expectSoftApDisabled();
-        callback.expectNoTetheringActive();
-        callback.expectOneOfOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
     }
 
     public void stopAllTethering() {
-        mTm.stopAllTethering();
+        final TestTetheringEventCallback callback = registerTetheringEventCallback();
+        try {
+            runAsShell(TETHER_PRIVILEGED, () -> {
+                mTm.stopAllTethering();
+                callback.expectNoTetheringActive();
+            });
+        } finally {
+            unregisterTetheringEventCallback(callback);
+        }
     }
 }
diff --git a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
index bd1b74a..274596f 100644
--- a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
+++ b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
@@ -15,6 +15,8 @@
  */
 package android.tethering.test;
 
+import static android.Manifest.permission.MODIFY_PHONE_STATE;
+import static android.Manifest.permission.TETHER_PRIVILEGED;
 import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
@@ -28,6 +30,8 @@
 import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
 import static android.net.cts.util.CtsTetheringUtils.isAnyIfaceMatch;
 
+import static com.android.testutils.TestPermissionUtil.runAsShell;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -37,7 +41,6 @@
 import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
-import android.app.UiAutomation;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -97,21 +100,8 @@
 
     private static final int DEFAULT_TIMEOUT_MS = 60_000;
 
-    private void adoptShellPermissionIdentity() {
-        final UiAutomation uiAutomation =
-                InstrumentationRegistry.getInstrumentation().getUiAutomation();
-        uiAutomation.adoptShellPermissionIdentity();
-    }
-
-    private void dropShellPermissionIdentity() {
-        final UiAutomation uiAutomation =
-                InstrumentationRegistry.getInstrumentation().getUiAutomation();
-        uiAutomation.dropShellPermissionIdentity();
-    }
-
     @Before
     public void setUp() throws Exception {
-        adoptShellPermissionIdentity();
         mContext = InstrumentationRegistry.getContext();
         mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
         mTM = (TetheringManager) mContext.getSystemService(Context.TETHERING_SERVICE);
@@ -128,9 +118,8 @@
 
     @After
     public void tearDown() throws Exception {
-        mTM.stopAllTethering();
+        mCtsTetheringUtils.stopAllTethering();
         mContext.unregisterReceiver(mTetherChangeReceiver);
-        dropShellPermissionIdentity();
     }
 
     private class TetherChangeReceiver extends BroadcastReceiver {
@@ -208,22 +197,19 @@
                 mCtsTetheringUtils.registerTetheringEventCallback();
         try {
             tetherEventCallback.assumeWifiTetheringSupported(mContext);
+            tetherEventCallback.expectNoTetheringActive();
+
+            final String[] wifiRegexs = mTM.getTetherableWifiRegexs();
+            mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
+
+            mTetherChangeReceiver.expectTethering(true /* active */, wifiRegexs);
+
+            mCtsTetheringUtils.stopWifiTethering(tetherEventCallback);
+            mTetherChangeReceiver.expectTethering(false /* active */, wifiRegexs);
         } finally {
             mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
         }
 
-        final String[] wifiRegexs = mTM.getTetherableWifiRegexs();
-        final StartTetheringCallback startTetheringCallback = new StartTetheringCallback();
-        final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI)
-                .setShouldShowEntitlementUi(false).build();
-        mTM.startTethering(request, c -> c.run() /* executor */, startTetheringCallback);
-        startTetheringCallback.verifyTetheringStarted();
-
-        mTetherChangeReceiver.expectTethering(true /* active */, wifiRegexs);
-
-        mTM.stopTethering(TETHERING_WIFI);
-        mCtsTetheringUtils.expectSoftApDisabled();
-        mTetherChangeReceiver.expectTethering(false /* active */, wifiRegexs);
     }
 
     @Test
@@ -267,7 +253,7 @@
             mCtsTetheringUtils.stopWifiTethering(tetherEventCallback);
 
             try {
-                final int ret = mTM.tether(wifiTetheringIface);
+                final int ret = runAsShell(TETHER_PRIVILEGED, () -> mTM.tether(wifiTetheringIface));
                 // There is no guarantee that the wifi interface will be available after disabling
                 // the hotspot, so don't fail the test if the call to tether() fails.
                 if (ret == TETHER_ERROR_NO_ERROR) {
@@ -277,7 +263,7 @@
                             new TetheringInterface(TETHERING_WIFI, wifiTetheringIface));
                 }
             } finally {
-                mTM.untether(wifiTetheringIface);
+                runAsShell(TETHER_PRIVILEGED, () -> mTM.untether(wifiTetheringIface));
             }
         } finally {
             mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
@@ -320,7 +306,7 @@
 
             mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
 
-            mTM.stopAllTethering();
+            mCtsTetheringUtils.stopAllTethering();
             tetherEventCallback.expectNoTetheringActive();
         } finally {
             mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
@@ -329,7 +315,6 @@
 
     @Test
     public void testEnableTetheringPermission() throws Exception {
-        dropShellPermissionIdentity();
         final StartTetheringCallback startTetheringCallback = new StartTetheringCallback();
         mTM.startTethering(new TetheringRequest.Builder(TETHERING_WIFI).build(),
                 c -> c.run() /* executor */, startTetheringCallback);
@@ -352,15 +337,21 @@
 
     private void assertEntitlementResult(final Consumer<EntitlementResultListener> functor,
             final int expect) throws Exception {
-        final EntitlementResultListener listener = new EntitlementResultListener();
-        functor.accept(listener);
+        runAsShell(TETHER_PRIVILEGED, () -> {
+            final EntitlementResultListener listener = new EntitlementResultListener();
+            functor.accept(listener);
 
-        assertEquals(expect, listener.get(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+            assertEquals(expect, listener.get(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        });
+    }
+
+    private boolean isTetheringSupported() {
+        return runAsShell(TETHER_PRIVILEGED, () -> mTM.isTetheringSupported());
     }
 
     @Test
     public void testRequestLatestEntitlementResult() throws Exception {
-        assumeTrue(mTM.isTetheringSupported());
+        assumeTrue(isTetheringSupported());
         assumeTrue(mPm.hasSystemFeature(FEATURE_TELEPHONY));
         // Verify that requestLatestTetheringEntitlementResult() can get entitlement
         // result(TETHER_ERROR_ENTITLEMENT_UNKNOWN due to invalid downstream type) via listener.
@@ -407,7 +398,13 @@
         final CarrierConfigManager configManager = (CarrierConfigManager) mContext
                 .getSystemService(Context.CARRIER_CONFIG_SERVICE);
         final int subId = SubscriptionManager.getDefaultSubscriptionId();
-        configManager.overrideConfig(subId, bundle);
+        runAsShell(MODIFY_PHONE_STATE, () -> configManager.overrideConfig(subId, bundle));
+    }
+
+    private boolean isTetheringApnRequired() {
+        final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+        return runAsShell(MODIFY_PHONE_STATE, () -> tm.isTetheringApnRequired());
+
     }
 
     @Test
@@ -447,10 +444,8 @@
 
             mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
 
-            final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
-                    Context.TELEPHONY_SERVICE);
-            final boolean dunRequired = telephonyManager.isTetheringApnRequired();
-            final int expectedCap = dunRequired ? NET_CAPABILITY_DUN : NET_CAPABILITY_INTERNET;
+            final int expectedCap = isTetheringApnRequired()
+                    ? NET_CAPABILITY_DUN : NET_CAPABILITY_INTERNET;
             final Network network = tetherEventCallback.getCurrentValidUpstream();
             final NetworkCapabilities netCap = mCm.getNetworkCapabilities(network);
             assertTrue(netCap.hasTransport(TRANSPORT_CELLULAR));
diff --git a/tests/mts/bpf_existence_test.cpp b/tests/mts/bpf_existence_test.cpp
index 2613363..67b4f42 100644
--- a/tests/mts/bpf_existence_test.cpp
+++ b/tests/mts/bpf_existence_test.cpp
@@ -87,13 +87,13 @@
     SHARED "map_block_blocked_ports_map",
     SHARED "map_clatd_clat_egress4_map",
     SHARED "map_clatd_clat_ingress6_map",
-    SHARED "map_dscp_policy_ipv4_dscp_policies_map",
-    SHARED "map_dscp_policy_ipv4_socket_to_policies_map_A",
-    SHARED "map_dscp_policy_ipv4_socket_to_policies_map_B",
-    SHARED "map_dscp_policy_ipv6_dscp_policies_map",
-    SHARED "map_dscp_policy_ipv6_socket_to_policies_map_A",
-    SHARED "map_dscp_policy_ipv6_socket_to_policies_map_B",
-    SHARED "map_dscp_policy_switch_comp_map",
+    SHARED "map_dscpPolicy_ipv4_dscp_policies_map",
+    SHARED "map_dscpPolicy_ipv4_socket_to_policies_map_A",
+    SHARED "map_dscpPolicy_ipv4_socket_to_policies_map_B",
+    SHARED "map_dscpPolicy_ipv6_dscp_policies_map",
+    SHARED "map_dscpPolicy_ipv6_socket_to_policies_map_A",
+    SHARED "map_dscpPolicy_ipv6_socket_to_policies_map_B",
+    SHARED "map_dscpPolicy_switch_comp_map",
     NETD "map_netd_app_uid_stats_map",
     NETD "map_netd_configuration_map",
     NETD "map_netd_cookie_tag_map",
@@ -126,8 +126,8 @@
 
 // Provided by *current* mainline module for T+ devices with 5.15+ kernels
 static const set<string> MAINLINE_FOR_T_5_15_PLUS = {
-    SHARED "prog_dscp_policy_schedcls_set_dscp_ether",
-    SHARED "prog_dscp_policy_schedcls_set_dscp_raw_ip",
+    SHARED "prog_dscpPolicy_schedcls_set_dscp_ether",
+    SHARED "prog_dscpPolicy_schedcls_set_dscp_raw_ip",
 };
 
 void addAll(set<string>* a, const set<string>& b) {
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index 58d002a..07884cf 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server;
 
+import static android.net.nsd.NsdManager.FAILURE_INTERNAL_ERROR;
+
 import static libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
 import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
 
@@ -44,11 +46,15 @@
 import android.net.mdns.aidl.DiscoveryInfo;
 import android.net.mdns.aidl.GetAddressInfo;
 import android.net.mdns.aidl.IMDnsEventListener;
+import android.net.mdns.aidl.RegistrationInfo;
 import android.net.mdns.aidl.ResolutionInfo;
 import android.net.nsd.INsdManagerCallback;
 import android.net.nsd.INsdServiceConnector;
 import android.net.nsd.MDnsManager;
 import android.net.nsd.NsdManager;
+import android.net.nsd.NsdManager.DiscoveryListener;
+import android.net.nsd.NsdManager.RegistrationListener;
+import android.net.nsd.NsdManager.ResolveListener;
 import android.net.nsd.NsdServiceInfo;
 import android.os.Binder;
 import android.os.Build;
@@ -226,7 +232,7 @@
         request.setPort(PORT);
 
         // Client registration request
-        NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
+        final RegistrationListener listener1 = mock(RegistrationListener.class);
         client.registerService(request, PROTOCOL, listener1);
         waitForIdle();
         verify(mMockMDnsM).registerEventListener(any());
@@ -235,13 +241,13 @@
                 eq(2), eq(SERVICE_NAME), eq(SERVICE_TYPE), eq(PORT), any(), eq(IFACE_IDX_ANY));
 
         // Client discovery request
-        NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class);
+        final DiscoveryListener listener2 = mock(DiscoveryListener.class);
         client.discoverServices(SERVICE_TYPE, PROTOCOL, listener2);
         waitForIdle();
         verify(mMockMDnsM).discover(3 /* id */, SERVICE_TYPE, IFACE_IDX_ANY);
 
         // Client resolve request
-        NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class);
+        final ResolveListener listener3 = mock(ResolveListener.class);
         client.resolveService(request, listener3);
         waitForIdle();
         verify(mMockMDnsM).resolve(
@@ -263,7 +269,7 @@
 
         final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
         request.setPort(PORT);
-        NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
+        final RegistrationListener listener1 = mock(RegistrationListener.class);
         client.registerService(request, PROTOCOL, listener1);
         waitForIdle();
         verify(mMockMDnsM).registerEventListener(any());
@@ -285,17 +291,22 @@
         verify(mMockMDnsM, never()).stopDaemon();
     }
 
+    private IMDnsEventListener getEventListener() {
+        final ArgumentCaptor<IMDnsEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(IMDnsEventListener.class);
+        verify(mMockMDnsM).registerEventListener(listenerCaptor.capture());
+        return listenerCaptor.getValue();
+    }
+
     @Test
     public void testDiscoverOnTetheringDownstream() throws Exception {
         final NsdManager client = connectClient(mService);
         final int interfaceIdx = 123;
-        final NsdManager.DiscoveryListener discListener = mock(NsdManager.DiscoveryListener.class);
+        final DiscoveryListener discListener = mock(DiscoveryListener.class);
         client.discoverServices(SERVICE_TYPE, PROTOCOL, discListener);
         waitForIdle();
 
-        final ArgumentCaptor<IMDnsEventListener> listenerCaptor =
-                ArgumentCaptor.forClass(IMDnsEventListener.class);
-        verify(mMockMDnsM).registerEventListener(listenerCaptor.capture());
+        final IMDnsEventListener eventListener = getEventListener();
         final ArgumentCaptor<Integer> discIdCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mMockMDnsM).discover(discIdCaptor.capture(), eq(SERVICE_TYPE),
                 eq(0) /* interfaceIdx */);
@@ -311,7 +322,6 @@
                 DOMAIN_NAME,
                 interfaceIdx,
                 INetd.LOCAL_NET_ID); // LOCAL_NET_ID (99) used on tethering downstreams
-        final IMDnsEventListener eventListener = listenerCaptor.getValue();
         eventListener.onServiceDiscoveryStatus(discoveryInfo);
         waitForIdle();
 
@@ -326,7 +336,7 @@
         assertEquals(interfaceIdx, foundInfo.getInterfaceIndex());
 
         // After discovering the service, verify resolving it
-        final NsdManager.ResolveListener resolveListener = mock(NsdManager.ResolveListener.class);
+        final ResolveListener resolveListener = mock(ResolveListener.class);
         client.resolveService(foundInfo, resolveListener);
         waitForIdle();
 
@@ -378,6 +388,154 @@
         assertEquals(interfaceIdx, resolvedService.getInterfaceIndex());
     }
 
+    @Test
+    public void testServiceRegistrationSuccessfulAndFailed() throws Exception {
+        final NsdManager client = connectClient(mService);
+        final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
+        request.setPort(PORT);
+        final RegistrationListener regListener = mock(RegistrationListener.class);
+        client.registerService(request, PROTOCOL, regListener);
+        waitForIdle();
+
+        final IMDnsEventListener eventListener = getEventListener();
+        final ArgumentCaptor<Integer> regIdCaptor = ArgumentCaptor.forClass(Integer.class);
+        verify(mMockMDnsM).registerService(regIdCaptor.capture(),
+                eq(SERVICE_NAME), eq(SERVICE_TYPE), eq(PORT), any(), eq(IFACE_IDX_ANY));
+
+        // Register service successfully.
+        final RegistrationInfo registrationInfo = new RegistrationInfo(
+                regIdCaptor.getValue(),
+                IMDnsEventListener.SERVICE_REGISTERED,
+                SERVICE_NAME,
+                SERVICE_TYPE,
+                PORT,
+                new byte[0] /* txtRecord */,
+                IFACE_IDX_ANY);
+        eventListener.onServiceRegistrationStatus(registrationInfo);
+
+        final ArgumentCaptor<NsdServiceInfo> registeredInfoCaptor =
+                ArgumentCaptor.forClass(NsdServiceInfo.class);
+        verify(regListener, timeout(TIMEOUT_MS))
+                .onServiceRegistered(registeredInfoCaptor.capture());
+        final NsdServiceInfo registeredInfo = registeredInfoCaptor.getValue();
+        assertEquals(SERVICE_NAME, registeredInfo.getServiceName());
+
+        // Fail to register service.
+        final RegistrationInfo registrationFailedInfo = new RegistrationInfo(
+                regIdCaptor.getValue(),
+                IMDnsEventListener.SERVICE_REGISTRATION_FAILED,
+                null /* serviceName */,
+                null /* registrationType */,
+                0 /* port */,
+                new byte[0] /* txtRecord */,
+                IFACE_IDX_ANY);
+        eventListener.onServiceRegistrationStatus(registrationFailedInfo);
+        verify(regListener, timeout(TIMEOUT_MS))
+                .onRegistrationFailed(any(), eq(FAILURE_INTERNAL_ERROR));
+    }
+
+    @Test
+    public void testServiceDiscoveryFailed() throws Exception {
+        final NsdManager client = connectClient(mService);
+        final DiscoveryListener discListener = mock(DiscoveryListener.class);
+        client.discoverServices(SERVICE_TYPE, PROTOCOL, discListener);
+        waitForIdle();
+
+        final IMDnsEventListener eventListener = getEventListener();
+        final ArgumentCaptor<Integer> discIdCaptor = ArgumentCaptor.forClass(Integer.class);
+        verify(mMockMDnsM).discover(discIdCaptor.capture(), eq(SERVICE_TYPE), eq(IFACE_IDX_ANY));
+        verify(discListener, timeout(TIMEOUT_MS)).onDiscoveryStarted(SERVICE_TYPE);
+
+        // Fail to discover service.
+        final DiscoveryInfo discoveryFailedInfo = new DiscoveryInfo(
+                discIdCaptor.getValue(),
+                IMDnsEventListener.SERVICE_DISCOVERY_FAILED,
+                null /* serviceName */,
+                null /* registrationType */,
+                null /* domainName */,
+                IFACE_IDX_ANY,
+                0 /* netId */);
+        eventListener.onServiceDiscoveryStatus(discoveryFailedInfo);
+        verify(discListener, timeout(TIMEOUT_MS))
+                .onStartDiscoveryFailed(SERVICE_TYPE, FAILURE_INTERNAL_ERROR);
+    }
+
+    @Test
+    public void testServiceResolutionFailed() throws Exception {
+        final NsdManager client = connectClient(mService);
+        final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
+        final ResolveListener resolveListener = mock(ResolveListener.class);
+        client.resolveService(request, resolveListener);
+        waitForIdle();
+
+        final IMDnsEventListener eventListener = getEventListener();
+        final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class);
+        verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE),
+                eq("local.") /* domain */, eq(IFACE_IDX_ANY));
+
+        // Fail to resolve service.
+        final ResolutionInfo resolutionFailedInfo = new ResolutionInfo(
+                resolvIdCaptor.getValue(),
+                IMDnsEventListener.SERVICE_RESOLUTION_FAILED,
+                null /* serviceName */,
+                null /* serviceType */,
+                null /* domain */,
+                null /* serviceFullName */,
+                null /* domainName */,
+                0 /* port */,
+                new byte[0] /* txtRecord */,
+                IFACE_IDX_ANY);
+        eventListener.onServiceResolutionStatus(resolutionFailedInfo);
+        verify(resolveListener, timeout(TIMEOUT_MS))
+                .onResolveFailed(any(), eq(FAILURE_INTERNAL_ERROR));
+    }
+
+    @Test
+    public void testGettingAddressFailed() throws Exception {
+        final NsdManager client = connectClient(mService);
+        final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
+        final ResolveListener resolveListener = mock(ResolveListener.class);
+        client.resolveService(request, resolveListener);
+        waitForIdle();
+
+        final IMDnsEventListener eventListener = getEventListener();
+        final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class);
+        verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE),
+                eq("local.") /* domain */, eq(IFACE_IDX_ANY));
+
+        // Resolve service successfully.
+        final ResolutionInfo resolutionInfo = new ResolutionInfo(
+                resolvIdCaptor.getValue(),
+                IMDnsEventListener.SERVICE_RESOLVED,
+                null /* serviceName */,
+                null /* serviceType */,
+                null /* domain */,
+                SERVICE_FULL_NAME,
+                DOMAIN_NAME,
+                PORT,
+                new byte[0] /* txtRecord */,
+                IFACE_IDX_ANY);
+        doReturn(true).when(mMockMDnsM).getServiceAddress(anyInt(), any(), anyInt());
+        eventListener.onServiceResolutionStatus(resolutionInfo);
+        waitForIdle();
+
+        final ArgumentCaptor<Integer> getAddrIdCaptor = ArgumentCaptor.forClass(Integer.class);
+        verify(mMockMDnsM).getServiceAddress(getAddrIdCaptor.capture(), eq(DOMAIN_NAME),
+                eq(IFACE_IDX_ANY));
+
+        // Fail to get service address.
+        final GetAddressInfo gettingAddrFailedInfo = new GetAddressInfo(
+                getAddrIdCaptor.getValue(),
+                IMDnsEventListener.SERVICE_GET_ADDR_FAILED,
+                null /* hostname */,
+                null /* address */,
+                IFACE_IDX_ANY,
+                0 /* netId */);
+        eventListener.onGettingServiceAddressStatus(gettingAddrFailedInfo);
+        verify(resolveListener, timeout(TIMEOUT_MS))
+                .onResolveFailed(any(), eq(FAILURE_INTERNAL_ERROR));
+    }
+
     private void waitForIdle() {
         HandlerUtils.waitForIdle(mHandler, TIMEOUT_MS);
     }
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index 03f2589..6f25d1b 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -1614,6 +1614,30 @@
         assertEquals(LegacyVpnInfo.STATE_FAILED, vpn.getLegacyVpnInfo().state);
     }
 
+    @Test
+    public void testVpnManagerEventWillNotBeSentToSettingsVpn() throws Exception {
+        startLegacyVpn(createVpn(PRIMARY_USER.id), mVpnProfile);
+        triggerOnAvailableAndGetCallback();
+
+        verifyInterfaceSetCfgWithFlags(IF_STATE_UP);
+
+        final IkeNonProtocolException exception = mock(IkeNonProtocolException.class);
+        final IkeTimeoutException ikeTimeoutException =
+                new IkeTimeoutException("IkeTimeoutException");
+        when(exception.getCause()).thenReturn(ikeTimeoutException);
+
+        final ArgumentCaptor<IkeSessionCallback> captor =
+                ArgumentCaptor.forClass(IkeSessionCallback.class);
+        verify(mIkev2SessionCreator, timeout(TEST_TIMEOUT_MS))
+                .createIkeSession(any(), any(), any(), any(), captor.capture(), any());
+        final IkeSessionCallback ikeCb = captor.getValue();
+        ikeCb.onClosedWithException(exception);
+
+        final Context userContext =
+                mContext.createContextAsUser(UserHandle.of(PRIMARY_USER.id), 0 /* flags */);
+        verify(userContext, never()).startService(any());
+    }
+
     private void setAndVerifyAlwaysOnPackage(Vpn vpn, int uid, boolean lockdownEnabled) {
         assertTrue(vpn.setAlwaysOnPackage(TEST_VPN_PKG, lockdownEnabled, null));
 
diff --git a/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
index 8a18ee7..503d920 100644
--- a/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
+++ b/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
@@ -313,21 +313,28 @@
 
         assertTrue(ret);
         verify(mIpClient).shutdown();
-        assertEquals(listener.expectOnResult(), TEST_IFACE);
+        assertEquals(TEST_IFACE, listener.expectOnResult());
     }
 
     @Test
     public void testUpdateInterfaceLinkStateForProvisionedInterface() throws Exception {
         initEthernetNetworkFactory();
         createAndVerifyProvisionedInterface(TEST_IFACE);
-        final TestNetworkManagementListener listener = new TestNetworkManagementListener();
+        final TestNetworkManagementListener listenerDown = new TestNetworkManagementListener();
+        final TestNetworkManagementListener listenerUp = new TestNetworkManagementListener();
 
-        final boolean ret =
-                mNetFactory.updateInterfaceLinkState(TEST_IFACE, false /* up */, listener);
+        final boolean retDown =
+                mNetFactory.updateInterfaceLinkState(TEST_IFACE, false /* up */, listenerDown);
 
-        assertTrue(ret);
+        assertTrue(retDown);
         verifyStop();
-        assertEquals(listener.expectOnResult(), TEST_IFACE);
+        assertEquals(TEST_IFACE, listenerDown.expectOnResult());
+
+        final boolean retUp =
+                mNetFactory.updateInterfaceLinkState(TEST_IFACE, true /* up */, listenerUp);
+
+        assertTrue(retUp);
+        assertEquals(TEST_IFACE, listenerUp.expectOnResult());
     }
 
     @Test
@@ -344,7 +351,7 @@
         verify(mDeps, never()).makeIpClient(any(), any(), any());
         verify(mDeps, never())
                 .makeEthernetNetworkAgent(any(), any(), any(), any(), any(), any(), any());
-        assertEquals(listener.expectOnResult(), TEST_IFACE);
+        assertEquals(TEST_IFACE, listener.expectOnResult());
     }
 
     @Test
@@ -609,7 +616,7 @@
         mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener);
         triggerOnProvisioningSuccess();
 
-        assertEquals(listener.expectOnResult(), TEST_IFACE);
+        assertEquals(TEST_IFACE, listener.expectOnResult());
     }
 
     @Test
@@ -655,6 +662,7 @@
                 });
 
         assertEquals(successfulListener.expectOnResult(), TEST_IFACE);
+        assertEquals(TEST_IFACE, successfulListener.expectOnResult());
     }
 
     private void verifyNetworkManagementCallIsAbortedWhenInterrupted(
@@ -683,7 +691,7 @@
         mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener);
         triggerOnProvisioningSuccess();
 
-        assertEquals(listener.expectOnResult(), TEST_IFACE);
+        assertEquals(TEST_IFACE, listener.expectOnResult());
         verify(mDeps).makeEthernetNetworkAgent(any(), any(),
                 eq(capabilities), any(), any(), any(), any());
         verifyRestart(ipConfiguration);