Merge "Rename dscp_policy.o to dscpPolicy.o"
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/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/src/com/android/server/TestNetworkService.java b/service/src/com/android/server/TestNetworkService.java
index 1209579..dc922f4 100644
--- a/service/src/com/android/server/TestNetworkService.java
+++ b/service/src/com/android/server/TestNetworkService.java
@@ -120,7 +120,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 +137,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,
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index a6ed762..75dc50e 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 3aec4c7..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 {
diff --git a/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt b/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
index 1b77d5f..b5d2a52 100644
--- a/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
+++ b/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
@@ -37,8 +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 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.os.Build
 import android.platform.test.annotations.AppModeFull
 import android.provider.DeviceConfig
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 766d62f..bb92a23 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -75,8 +75,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 +86,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;
@@ -2092,25 +2092,15 @@
 
         try {
             // Verify we cannot set Airplane Mode without correct permission:
-            try {
-                setAndVerifyAirplaneMode(true);
-                fail("SecurityException should have been thrown when setAirplaneMode was called"
-                        + "without holding permission NETWORK_AIRPLANE_MODE.");
-            } catch (SecurityException expected) {}
+            assertThrows(SecurityException.class, () -> setAndVerifyAirplaneMode(true));
 
             // disable airplane mode again to reach a known state
             runShellCommand("cmd connectivity airplane-mode disable");
 
-            // adopt shell permission which holds NETWORK_AIRPLANE_MODE
-            mUiAutomation.adoptShellPermissionIdentity();
+            // Verify we can enable Airplane Mode with correct permission.
+            // TODO: test that NETWORK_AIRPLANE_MODE works as well, once the shell has it.
+            runAsShell(NETWORK_SETTINGS, () -> setAndVerifyAirplaneMode(true));
 
-            // Verify we can enable Airplane Mode with correct permission:
-            try {
-                setAndVerifyAirplaneMode(true);
-            } catch (SecurityException e) {
-                fail("SecurityException should not have been thrown when setAirplaneMode(true) was"
-                        + "called whilst holding the NETWORK_AIRPLANE_MODE permission.");
-            }
             // Verify that the enabling airplane mode takes effect as expected to prevent flakiness
             // caused by fast airplane mode switches. Ensure network lost before turning off
             // airplane mode.
@@ -2118,12 +2108,8 @@
             if (supportTelephony) waitForLost(telephonyCb);
 
             // Verify we can disable Airplane Mode with correct permission:
-            try {
-                setAndVerifyAirplaneMode(false);
-            } catch (SecurityException e) {
-                fail("SecurityException should not have been thrown when setAirplaneMode(false) was"
-                        + "called whilst holding the NETWORK_AIRPLANE_MODE permission.");
-            }
+            runAsShell(NETWORK_SETTINGS, () -> setAndVerifyAirplaneMode(false));
+
             // Verify that turning airplane mode off takes effect as expected.
             // connectToCell only registers a request, it cannot / does not need to be called twice
             mCtsNetUtils.ensureWifiConnected();
@@ -2133,7 +2119,6 @@
             // Restore the previous state of airplane mode and permissions:
             runShellCommand("cmd connectivity airplane-mode "
                     + (isAirplaneModeEnabled ? "enable" : "disable"));
-            mUiAutomation.dropShellPermissionIdentity();
         }
     }
 
diff --git a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
index 1748612..1a3b01e 100644
--- a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
@@ -22,6 +22,8 @@
 import android.net.ConnectivityManager
 import android.net.EthernetManager
 import android.net.EthernetManager.InterfaceStateListener
+import android.net.EthernetManager.ETHERNET_STATE_DISABLED
+import android.net.EthernetManager.ETHERNET_STATE_ENABLED
 import android.net.EthernetManager.ROLE_CLIENT
 import android.net.EthernetManager.ROLE_NONE
 import android.net.EthernetManager.ROLE_SERVER
@@ -44,6 +46,7 @@
 import android.net.NetworkRequest
 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
@@ -76,6 +79,7 @@
 import java.util.concurrent.ExecutionException
 import java.util.concurrent.TimeoutException
 import java.util.concurrent.TimeUnit
+import java.util.function.IntConsumer
 import kotlin.test.assertEquals
 import kotlin.test.assertFailsWith
 import kotlin.test.assertFalse
@@ -161,7 +165,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 +178,8 @@
                 val role: Int,
                 val configuration: IpConfiguration?
             ) : CallbackEntry()
+
+            data class EthernetStateChanged(val state: Int) : CallbackEntry()
         }
 
         override fun onInterfaceStateChanged(
@@ -185,6 +191,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 +205,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 +223,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 +298,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 +415,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) =
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/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));