Merge "Make a defensive copy when sending NetworkInfo change"
diff --git a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
index 844efde..adc95ab 100644
--- a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
+++ b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
@@ -34,7 +34,6 @@
 import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
 
 import static com.android.networkstack.apishim.ConstantsShim.ACTION_TETHER_UNSUPPORTED_CARRIER_UI;
-import static com.android.networkstack.apishim.ConstantsShim.KEY_CARRIER_SUPPORTS_TETHERING_BOOL;
 
 import android.app.AlarmManager;
 import android.app.PendingIntent;
@@ -48,12 +47,10 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Parcel;
-import android.os.PersistableBundle;
 import android.os.ResultReceiver;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.provider.Settings;
-import android.telephony.CarrierConfigManager;
 import android.util.SparseIntArray;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -307,13 +304,13 @@
         if (SystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)) {
             return TETHERING_PROVISIONING_NOT_REQUIRED;
         }
-        // TODO: Find a way to avoid get carrier config twice.
-        if (carrierConfigAffirmsCarrierNotSupport(config)) {
+
+        if (!config.isCarrierSupportTethering) {
             // To block tethering, behave as if running provisioning check and failed.
             return TETHERING_PROVISIONING_CARRIER_UNSUPPORT;
         }
 
-        if (carrierConfigAffirmsEntitlementCheckNotRequired(config)) {
+        if (!config.isCarrierConfigAffirmsEntitlementCheckRequired) {
             return TETHERING_PROVISIONING_NOT_REQUIRED;
         }
         return (config.provisioningApp.length == 2)
@@ -380,57 +377,6 @@
     }
 
     /**
-     * Get carrier configuration bundle.
-     * @param config an object that encapsulates the various tethering configuration elements.
-     * */
-    public PersistableBundle getCarrierConfig(final TetheringConfiguration config) {
-        final CarrierConfigManager configManager = mContext
-                .getSystemService(CarrierConfigManager.class);
-        if (configManager == null) return null;
-
-        final PersistableBundle carrierConfig = configManager.getConfigForSubId(
-                config.activeDataSubId);
-
-        if (CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfig)) {
-            return carrierConfig;
-        }
-
-        return null;
-    }
-
-    // The logic here is aimed solely at confirming that a CarrierConfig exists
-    // and affirms that entitlement checks are not required.
-    //
-    // TODO: find a better way to express this, or alter the checking process
-    // entirely so that this is more intuitive.
-    // TODO: Find a way to avoid using getCarrierConfig everytime.
-    private boolean carrierConfigAffirmsEntitlementCheckNotRequired(
-            final TetheringConfiguration config) {
-        // Check carrier config for entitlement checks
-        final PersistableBundle carrierConfig = getCarrierConfig(config);
-        if (carrierConfig == null) return false;
-
-        // A CarrierConfigManager was found and it has a config.
-        final boolean isEntitlementCheckRequired = carrierConfig.getBoolean(
-                CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
-        return !isEntitlementCheckRequired;
-    }
-
-    private boolean carrierConfigAffirmsCarrierNotSupport(final TetheringConfiguration config) {
-        if (!SdkLevel.isAtLeastT()) {
-            return false;
-        }
-        // Check carrier config for entitlement checks
-        final PersistableBundle carrierConfig = getCarrierConfig(config);
-        if (carrierConfig == null) return false;
-
-        // A CarrierConfigManager was found and it has a config.
-        final boolean mIsCarrierSupport = carrierConfig.getBoolean(
-                KEY_CARRIER_SUPPORTS_TETHERING_BOOL, true);
-        return !mIsCarrierSupport;
-    }
-
-    /**
      * Run no UI tethering provisioning check.
      * @param type tethering type from TetheringManager.TETHERING_{@code *}
      * @param subId default data subscription ID.
@@ -479,7 +425,7 @@
 
     private void runTetheringProvisioning(
             boolean showProvisioningUi, int downstreamType, final TetheringConfiguration config) {
-        if (carrierConfigAffirmsCarrierNotSupport(config)) {
+        if (!config.isCarrierSupportTethering) {
             mListener.onTetherProvisioningFailed(downstreamType, "Carrier does not support.");
             if (showProvisioningUi) {
                 showCarrierUnsupportedDialog();
@@ -497,7 +443,7 @@
     }
 
     private void showCarrierUnsupportedDialog() {
-        // This is only used when carrierConfigAffirmsCarrierNotSupport() is true.
+        // This is only used when TetheringConfiguration.isCarrierSupportTethering is false.
         if (!SdkLevel.isAtLeastT()) {
             return;
         }
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 4504829..44935fc 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -482,7 +482,7 @@
             // To avoid launching unexpected provisioning checks, ignore re-provisioning
             // when no CarrierConfig loaded yet. Assume reevaluateSimCardProvisioning()
             // will be triggered again when CarrierConfig is loaded.
-            if (mEntitlementMgr.getCarrierConfig(mConfig) != null) {
+            if (TetheringConfiguration.getCarrierConfig(mContext, subId) != null) {
                 mEntitlementMgr.reevaluateSimCardProvisioning(mConfig);
             } else {
                 mLog.log("IGNORED reevaluate provisioning, no carrier config loaded");
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index f9f3ed9..7c36054 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -24,14 +24,17 @@
 import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
 
 import static com.android.net.module.util.DeviceConfigUtils.TETHERING_MODULE_NAME;
+import static com.android.networkstack.apishim.ConstantsShim.KEY_CARRIER_SUPPORTS_TETHERING_BOOL;
 
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
 import android.net.TetheringConfigurationParcel;
 import android.net.util.SharedLog;
+import android.os.PersistableBundle;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
@@ -142,6 +145,9 @@
     public final int provisioningCheckPeriod;
     public final String provisioningResponse;
 
+    public final boolean isCarrierSupportTethering;
+    public final boolean isCarrierConfigAffirmsEntitlementCheckRequired;
+
     public final int activeDataSubId;
 
     private final boolean mEnableLegacyDhcpServer;
@@ -207,6 +213,11 @@
         provisioningResponse = getResourceString(res,
                 R.string.config_mobile_hotspot_provision_response);
 
+        PersistableBundle carrierConfigs = getCarrierConfig(ctx, activeDataSubId);
+        isCarrierSupportTethering = carrierConfigAffirmsCarrierSupport(carrierConfigs);
+        isCarrierConfigAffirmsEntitlementCheckRequired =
+                carrierConfigAffirmsEntitlementCheckRequired(carrierConfigs);
+
         mOffloadPollInterval = getResourceInteger(res,
                 R.integer.config_tether_offload_poll_interval,
                 DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
@@ -329,6 +340,10 @@
         pw.print("provisioningAppNoUi: ");
         pw.println(provisioningAppNoUi);
 
+        pw.println("isCarrierSupportTethering: " + isCarrierSupportTethering);
+        pw.println("isCarrierConfigAffirmsEntitlementCheckRequired: "
+                + isCarrierConfigAffirmsEntitlementCheckRequired);
+
         pw.print("enableBpfOffload: ");
         pw.println(mEnableBpfOffload);
 
@@ -361,6 +376,9 @@
                 toIntArray(preferredUpstreamIfaceTypes)));
         sj.add(String.format("provisioningApp:%s", makeString(provisioningApp)));
         sj.add(String.format("provisioningAppNoUi:%s", provisioningAppNoUi));
+        sj.add(String.format("isCarrierSupportTethering:%s", isCarrierSupportTethering));
+        sj.add(String.format("isCarrierConfigAffirmsEntitlementCheckRequired:%s",
+                isCarrierConfigAffirmsEntitlementCheckRequired));
         sj.add(String.format("enableBpfOffload:%s", mEnableBpfOffload));
         sj.add(String.format("enableLegacyDhcpServer:%s", mEnableLegacyDhcpServer));
         return String.format("TetheringConfiguration{%s}", sj.toString());
@@ -596,6 +614,39 @@
         return result;
     }
 
+    private static boolean carrierConfigAffirmsEntitlementCheckRequired(
+            PersistableBundle carrierConfig) {
+        if (carrierConfig == null) {
+            return true;
+        }
+        return carrierConfig.getBoolean(
+                CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
+    }
+
+    private static boolean carrierConfigAffirmsCarrierSupport(PersistableBundle carrierConfig) {
+        if (!SdkLevel.isAtLeastT() || carrierConfig == null) {
+            return true;
+        }
+        return carrierConfig.getBoolean(KEY_CARRIER_SUPPORTS_TETHERING_BOOL, true);
+    }
+
+    /**
+     * Get carrier configuration bundle.
+     */
+    public static PersistableBundle getCarrierConfig(Context context, int activeDataSubId) {
+        final CarrierConfigManager configManager =
+                context.getSystemService(CarrierConfigManager.class);
+        if (configManager == null) {
+            return null;
+        }
+
+        final PersistableBundle carrierConfig = configManager.getConfigForSubId(activeDataSubId);
+        if (CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfig)) {
+            return carrierConfig;
+        }
+        return null;
+    }
+
     /**
      * Convert this TetheringConfiguration to a TetheringConfigurationParcel.
      */
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
index 690ff71..01d7b4b 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
@@ -304,33 +304,6 @@
     }
 
     @Test
-    public void toleratesCarrierConfigManagerMissing() {
-        setupForRequiredProvisioning();
-        mockService(Context.CARRIER_CONFIG_SERVICE, CarrierConfigManager.class, null);
-        mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
-        // Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
-        // Therefore provisioning still be required.
-        assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig));
-    }
-
-    @Test
-    public void toleratesCarrierConfigMissing() {
-        setupForRequiredProvisioning();
-        when(mCarrierConfigManager.getConfig()).thenReturn(null);
-        mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
-        // We still have a provisioning app configured, so still require provisioning.
-        assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig));
-    }
-
-    @Test
-    public void toleratesCarrierConfigNotLoaded() {
-        setupForRequiredProvisioning();
-        mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, false);
-        // We still have a provisioning app configured, so still require provisioning.
-        assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig));
-    }
-
-    @Test
     public void provisioningNotRequiredWhenAppNotFound() {
         setupForRequiredProvisioning();
         when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
@@ -706,8 +679,8 @@
     @IgnoreUpTo(SC_V2)
     public void requestLatestTetheringEntitlementResult_carrierDoesNotSupport_noProvisionCount()
             throws Exception {
-        setupForRequiredProvisioning();
         setupCarrierConfig(false);
+        setupForRequiredProvisioning();
         mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
         ResultReceiver receiver = new ResultReceiver(null) {
             @Override
@@ -735,6 +708,7 @@
         mEnMgr.notifyUpstream(false);
         mLooper.dispatchAll();
         setupCarrierConfig(false);
+        mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
         mEnMgr.reevaluateSimCardProvisioning(mConfig);
 
         // Turn on upstream.
@@ -749,8 +723,8 @@
     @IgnoreUpTo(SC_V2)
     public void startProvisioningIfNeeded_carrierUnsupport()
             throws Exception {
-        setupForRequiredProvisioning();
         setupCarrierConfig(false);
+        setupForRequiredProvisioning();
         mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
         verify(mTetherProvisioningFailedListener, never())
                 .onTetherProvisioningFailed(TETHERING_WIFI, "Carrier does not support.");
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
index 7fcf2b2..3190f35 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
@@ -22,10 +22,13 @@
 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
+import static android.telephony.CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL;
+import static android.telephony.CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.networkstack.apishim.ConstantsShim.KEY_CARRIER_SUPPORTS_TETHERING_BOOL;
 import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_FORCE_USB_FUNCTIONS;
 import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_USB_NCM_FUNCTION;
 import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_USB_RNDIS_FUNCTION;
@@ -46,8 +49,10 @@
 import android.content.res.Resources;
 import android.net.util.SharedLog;
 import android.os.Build;
+import android.os.PersistableBundle;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyManager;
 import android.test.mock.MockContentResolver;
 
@@ -56,6 +61,7 @@
 
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.modules.utils.build.SdkLevel;
 import com.android.net.module.util.DeviceConfigUtils;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
@@ -88,6 +94,7 @@
     private static final long TEST_PACKAGE_VERSION = 1234L;
     @Mock private ApplicationInfo mApplicationInfo;
     @Mock private Context mContext;
+    @Mock private CarrierConfigManager mCarrierConfigManager;
     @Mock private TelephonyManager mTelephonyManager;
     @Mock private Resources mResources;
     @Mock private Resources mResourcesForSubId;
@@ -97,6 +104,7 @@
     private boolean mHasTelephonyManager;
     private MockitoSession mMockingSession;
     private MockContentResolver mContentResolver;
+    private final PersistableBundle mCarrierConfig = new PersistableBundle();
 
     private class MockTetheringConfiguration extends TetheringConfiguration {
         MockTetheringConfiguration(Context ctx, SharedLog log, int id) {
@@ -474,6 +482,56 @@
                 PROVISIONING_APP_RESPONSE);
     }
 
+    private <T> void mockService(String serviceName, Class<T> serviceClass, T service) {
+        when(mMockContext.getSystemServiceName(serviceClass)).thenReturn(serviceName);
+        when(mMockContext.getSystemService(serviceName)).thenReturn(service);
+    }
+
+    @Test
+    public void testGetCarrierConfigBySubId_noCarrierConfigManager_configsAreDefault() {
+        // Act like the CarrierConfigManager is present and ready unless told otherwise.
+        mockService(Context.CARRIER_CONFIG_SERVICE,
+                CarrierConfigManager.class, null);
+        final TetheringConfiguration cfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+
+        assertTrue(cfg.isCarrierSupportTethering);
+        assertTrue(cfg.isCarrierConfigAffirmsEntitlementCheckRequired);
+    }
+
+    @Test
+    public void testGetCarrierConfigBySubId_carrierConfigMissing_configsAreDefault() {
+        // Act like the CarrierConfigManager is present and ready unless told otherwise.
+        mockService(Context.CARRIER_CONFIG_SERVICE,
+                CarrierConfigManager.class, mCarrierConfigManager);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(null);
+        final TetheringConfiguration cfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+
+        assertTrue(cfg.isCarrierSupportTethering);
+        assertTrue(cfg.isCarrierConfigAffirmsEntitlementCheckRequired);
+    }
+
+    @Test
+    public void testGetCarrierConfigBySubId_hasConfigs_carrierUnsupportAndCheckNotRequired() {
+        mockService(Context.CARRIER_CONFIG_SERVICE,
+                CarrierConfigManager.class, mCarrierConfigManager);
+        mCarrierConfig.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
+        mCarrierConfig.putBoolean(KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, false);
+        mCarrierConfig.putBoolean(KEY_CARRIER_SUPPORTS_TETHERING_BOOL, false);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mCarrierConfig);
+        final TetheringConfiguration cfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+
+        if (SdkLevel.isAtLeastT()) {
+            assertFalse(cfg.isCarrierSupportTethering);
+        } else {
+            assertTrue(cfg.isCarrierSupportTethering);
+        }
+        assertFalse(cfg.isCarrierConfigAffirmsEntitlementCheckRequired);
+
+    }
+
     @Test
     public void testEnableLegacyWifiP2PAddress() throws Exception {
         final TetheringConfiguration defaultCfg = new TetheringConfiguration(
diff --git a/bpf_progs/bpf_shared.h b/bpf_progs/bpf_shared.h
index 2ddc7b8..a6e78b6 100644
--- a/bpf_progs/bpf_shared.h
+++ b/bpf_progs/bpf_shared.h
@@ -98,7 +98,7 @@
 static const int CONFIGURATION_MAP_SIZE = 2;
 static const int UID_OWNER_MAP_SIZE = 2000;
 
-#define BPF_PATH "/sys/fs/bpf/"
+#define BPF_PATH "/sys/fs/bpf/net_shared/"
 
 #define BPF_EGRESS_PROG_PATH BPF_PATH "prog_netd_cgroupskb_egress_stats"
 #define BPF_INGRESS_PROG_PATH BPF_PATH "prog_netd_cgroupskb_ingress_stats"
diff --git a/framework/src/android/net/ITestNetworkManager.aidl b/framework/src/android/net/ITestNetworkManager.aidl
index 847f14e..27d13c1 100644
--- a/framework/src/android/net/ITestNetworkManager.aidl
+++ b/framework/src/android/net/ITestNetworkManager.aidl
@@ -29,7 +29,8 @@
  */
 interface ITestNetworkManager
 {
-    TestNetworkInterface createInterface(boolean isTun, boolean bringUp, in LinkAddress[] addrs);
+    TestNetworkInterface createInterface(boolean isTun, boolean bringUp, in LinkAddress[] addrs,
+            in @nullable String iface);
 
     void setupTestNetwork(in String iface, in LinkProperties lp, in boolean isMetered,
             in int[] administratorUids, in IBinder binder);
diff --git a/framework/src/android/net/TestNetworkManager.java b/framework/src/android/net/TestNetworkManager.java
index 280e497..4e78823 100644
--- a/framework/src/android/net/TestNetworkManager.java
+++ b/framework/src/android/net/TestNetworkManager.java
@@ -45,6 +45,12 @@
      */
     public static final String TEST_TAP_PREFIX = "testtap";
 
+    /**
+     * Prefix for clat interfaces.
+     * @hide
+     */
+    public static final String CLAT_INTERFACE_PREFIX = "v4-";
+
     @NonNull private static final String TAG = TestNetworkManager.class.getSimpleName();
 
     @NonNull private final ITestNetworkManager mService;
@@ -160,7 +166,8 @@
     public TestNetworkInterface createTunInterface(@NonNull Collection<LinkAddress> linkAddrs) {
         try {
             final LinkAddress[] arr = new LinkAddress[linkAddrs.size()];
-            return mService.createInterface(TUN, BRING_UP, linkAddrs.toArray(arr));
+            return mService.createInterface(TUN, BRING_UP, linkAddrs.toArray(arr),
+                    null /* iface */);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -178,7 +185,7 @@
     @NonNull
     public TestNetworkInterface createTapInterface() {
         try {
-            return mService.createInterface(TAP, BRING_UP, NO_ADDRS);
+            return mService.createInterface(TAP, BRING_UP, NO_ADDRS, null /* iface */);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -197,7 +204,29 @@
     @NonNull
     public TestNetworkInterface createTapInterface(boolean bringUp) {
         try {
-            return mService.createInterface(TAP, bringUp, NO_ADDRS);
+            return mService.createInterface(TAP, bringUp, NO_ADDRS, null /* iface */);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Create a tap interface with a given interface name for testing purposes
+     *
+     * @param bringUp whether to bring up the interface before returning it.
+     * @param iface interface name to be assigned, so far only interface name which starts with
+     *              "v4-testtap" or "v4-testtun" is allowed to be created. If it's null, then use
+     *              the default name(e.g. testtap or testtun).
+     *
+     * @return A ParcelFileDescriptor of the underlying TAP interface. Close this to tear down the
+     *     TAP interface.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_TEST_NETWORKS)
+    @NonNull
+    public TestNetworkInterface createTapInterface(boolean bringUp, @NonNull String iface) {
+        try {
+            return mService.createInterface(TAP, bringUp, NO_ADDRS, 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 eb22f78..fe27335 100644
--- a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
+++ b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
-import android.content.res.Resources;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityResources;
 import android.net.EthernetManager;
@@ -71,8 +70,6 @@
 
     private final static int NETWORK_SCORE = 70;
     private static final String NETWORK_TYPE = "Ethernet";
-    private static final String LEGACY_TCP_BUFFER_SIZES =
-            "524288,1048576,3145728,524288,1048576,2097152";
 
     private final ConcurrentHashMap<String, NetworkInterfaceState> mTrackingInterfaces =
             new ConcurrentHashMap<>();
@@ -99,25 +96,9 @@
             return InterfaceParams.getByName(name);
         }
 
-        // TODO: remove legacy resource fallback after migrating its overlays.
-        private String getPlatformTcpBufferSizes(Context context) {
-            final Resources r = context.getResources();
-            final int resId = r.getIdentifier("config_ethernet_tcp_buffers", "string",
-                    context.getPackageName());
-            return r.getString(resId);
-        }
-
         public String getTcpBufferSizesFromResource(Context context) {
-            final String tcpBufferSizes;
-            final String platformTcpBufferSizes = getPlatformTcpBufferSizes(context);
-            if (!LEGACY_TCP_BUFFER_SIZES.equals(platformTcpBufferSizes)) {
-                // Platform resource is not the historical default: use the overlay.
-                tcpBufferSizes = platformTcpBufferSizes;
-            } else {
-                final ConnectivityResources resources = new ConnectivityResources(context);
-                tcpBufferSizes = resources.get().getString(R.string.config_ethernet_tcp_buffers);
-            }
-            return tcpBufferSizes;
+            final ConnectivityResources resources = new ConnectivityResources(context);
+            return resources.get().getString(R.string.config_ethernet_tcp_buffers);
         }
     }
 
diff --git a/service-t/src/com/android/server/ethernet/EthernetTracker.java b/service-t/src/com/android/server/ethernet/EthernetTracker.java
index 693d91a..e9053dd 100644
--- a/service-t/src/com/android/server/ethernet/EthernetTracker.java
+++ b/service-t/src/com/android/server/ethernet/EthernetTracker.java
@@ -25,7 +25,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
-import android.content.res.Resources;
 import android.net.ConnectivityResources;
 import android.net.EthernetManager;
 import android.net.IEthernetServiceListener;
@@ -86,7 +85,6 @@
     private static final boolean DBG = EthernetNetworkFactory.DBG;
 
     private static final String TEST_IFACE_REGEXP = TEST_TAP_PREFIX + "\\d+";
-    private static final String LEGACY_IFACE_REGEXP = "eth\\d";
 
     /**
      * Interface names we track. This is a product-dependent regular expression, plus,
@@ -134,48 +132,16 @@
     }
 
     public static class Dependencies {
-        // TODO: remove legacy resource fallback after migrating its overlays.
-        private String getPlatformRegexResource(Context context) {
-            final Resources r = context.getResources();
-            final int resId =
-                r.getIdentifier("config_ethernet_iface_regex", "string", context.getPackageName());
-            return r.getString(resId);
-        }
-
-        // TODO: remove legacy resource fallback after migrating its overlays.
-        private String[] getPlatformInterfaceConfigs(Context context) {
-            final Resources r = context.getResources();
-            final int resId = r.getIdentifier("config_ethernet_interfaces", "array",
-                    context.getPackageName());
-            return r.getStringArray(resId);
-        }
-
         public String getInterfaceRegexFromResource(Context context) {
-            final String platformRegex = getPlatformRegexResource(context);
-            final String match;
-            if (!LEGACY_IFACE_REGEXP.equals(platformRegex)) {
-                // Platform resource is not the historical default: use the overlay
-                match = platformRegex;
-            } else {
-                final ConnectivityResources resources = new ConnectivityResources(context);
-                match = resources.get().getString(
-                        com.android.connectivity.resources.R.string.config_ethernet_iface_regex);
-            }
-            return match;
+            final ConnectivityResources resources = new ConnectivityResources(context);
+            return resources.get().getString(
+                    com.android.connectivity.resources.R.string.config_ethernet_iface_regex);
         }
 
         public String[] getInterfaceConfigFromResource(Context context) {
-            final String[] platformInterfaceConfigs = getPlatformInterfaceConfigs(context);
-            final String[] interfaceConfigs;
-            if (platformInterfaceConfigs.length != 0) {
-                // Platform resource is not the historical default: use the overlay
-                interfaceConfigs = platformInterfaceConfigs;
-            } else {
-                final ConnectivityResources resources = new ConnectivityResources(context);
-                interfaceConfigs = resources.get().getStringArray(
-                        com.android.connectivity.resources.R.array.config_ethernet_interfaces);
-            }
-            return interfaceConfigs;
+            final ConnectivityResources resources = new ConnectivityResources(context);
+            return resources.get().getStringArray(
+                    com.android.connectivity.resources.R.array.config_ethernet_interfaces);
         }
     }
 
diff --git a/service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java b/service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java
index 25c88eb..5011dec 100644
--- a/service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java
+++ b/service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java
@@ -38,7 +38,7 @@
     private static final String TAG = BpfInterfaceMapUpdater.class.getSimpleName();
     // This is current path but may be changed soon.
     private static final String IFACE_INDEX_NAME_MAP_PATH =
-            "/sys/fs/bpf/map_netd_iface_index_name_map";
+            "/sys/fs/bpf/net_shared/map_netd_iface_index_name_map";
     private final IBpfMap<U32, InterfaceMapValue> mBpfMap;
     private final INetd mNetd;
     private final Handler mHandler;
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index e3794e4..82b1fb5 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -218,17 +218,16 @@
     private static final String NETSTATS_COMBINE_SUBTYPE_ENABLED =
             "netstats_combine_subtype_enabled";
 
-    // This is current path but may be changed soon.
     private static final String UID_COUNTERSET_MAP_PATH =
-            "/sys/fs/bpf/map_netd_uid_counterset_map";
+            "/sys/fs/bpf/net_shared/map_netd_uid_counterset_map";
     private static final String COOKIE_TAG_MAP_PATH =
-            "/sys/fs/bpf/map_netd_cookie_tag_map";
+            "/sys/fs/bpf/net_shared/map_netd_cookie_tag_map";
     private static final String APP_UID_STATS_MAP_PATH =
-            "/sys/fs/bpf/map_netd_app_uid_stats_map";
+            "/sys/fs/bpf/net_shared/map_netd_app_uid_stats_map";
     private static final String STATS_MAP_A_PATH =
-            "/sys/fs/bpf/map_netd_stats_map_A";
+            "/sys/fs/bpf/net_shared/map_netd_stats_map_A";
     private static final String STATS_MAP_B_PATH =
-            "/sys/fs/bpf/map_netd_stats_map_B";
+            "/sys/fs/bpf/net_shared/map_netd_stats_map_B";
 
     private final Context mContext;
     private final NetworkStatsFactory mStatsFactory;
diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
index 500c696..ba836b2 100644
--- a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -314,7 +314,11 @@
     }
 
     // TODO: use android::base::ScopeGuard.
-    if (int ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_USEVFORK)) {
+    if (int ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_USEVFORK
+#ifdef POSIX_SPAWN_CLOEXEC_DEFAULT
+                                           | POSIX_SPAWN_CLOEXEC_DEFAULT
+#endif
+                                           )) {
         posix_spawnattr_destroy(&attr);
         throwIOException(env, "posix_spawnattr_setflags failed", ret);
         return -1;
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 4835438..6414497 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -258,6 +258,7 @@
 import com.android.net.module.util.netlink.InetDiagMessage;
 import com.android.server.connectivity.AutodestructReference;
 import com.android.server.connectivity.CarrierPrivilegeAuthenticator;
+import com.android.server.connectivity.ClatCoordinator;
 import com.android.server.connectivity.ConnectivityFlags;
 import com.android.server.connectivity.DnsManager;
 import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
@@ -753,7 +754,7 @@
      * The BPF program attached to the tc-police hook to account for to-be-dropped traffic.
      */
     private static final String TC_POLICE_BPF_PROG_PATH =
-            "/sys/fs/bpf/prog_netd_schedact_ingress_account";
+            "/sys/fs/bpf/net_shared/prog_netd_schedact_ingress_account";
 
     private static String eventName(int what) {
         return sMagicDecoderRing.get(what, Integer.toString(what));
@@ -1405,6 +1406,19 @@
         }
 
         /**
+         * @see ClatCoordinator
+         */
+        public ClatCoordinator getClatCoordinator(INetd netd) {
+            return new ClatCoordinator(
+                new ClatCoordinator.Dependencies() {
+                    @NonNull
+                    public INetd getNetd() {
+                        return netd;
+                    }
+                });
+        }
+
+        /**
          * Wraps {@link TcUtils#tcFilterAddDevIngressPolice}
          */
         public void enableIngressRateLimit(String iface, long rateInBytesPerSecond) {
@@ -2865,7 +2879,7 @@
 
     private void enforceNetworkFactoryPermission() {
         // TODO: Check for the BLUETOOTH_STACK permission once that is in the API surface.
-        if (getCallingUid() == Process.BLUETOOTH_UID) return;
+        if (UserHandle.getAppId(getCallingUid()) == Process.BLUETOOTH_UID) return;
         enforceAnyPermissionOf(
                 android.Manifest.permission.NETWORK_FACTORY,
                 NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
@@ -2873,7 +2887,7 @@
 
     private void enforceNetworkFactoryOrSettingsPermission() {
         // TODO: Check for the BLUETOOTH_STACK permission once that is in the API surface.
-        if (getCallingUid() == Process.BLUETOOTH_UID) return;
+        if (UserHandle.getAppId(getCallingUid()) == Process.BLUETOOTH_UID) return;
         enforceAnyPermissionOf(
                 android.Manifest.permission.NETWORK_SETTINGS,
                 android.Manifest.permission.NETWORK_FACTORY,
@@ -2882,7 +2896,7 @@
 
     private void enforceNetworkFactoryOrTestNetworksPermission() {
         // TODO: Check for the BLUETOOTH_STACK permission once that is in the API surface.
-        if (getCallingUid() == Process.BLUETOOTH_UID) return;
+        if (UserHandle.getAppId(getCallingUid()) == Process.BLUETOOTH_UID) return;
         enforceAnyPermissionOf(
                 android.Manifest.permission.MANAGE_TEST_NETWORKS,
                 android.Manifest.permission.NETWORK_FACTORY,
@@ -2896,7 +2910,7 @@
                 android.Manifest.permission.NETWORK_SETTINGS, pid, uid)
                 || PERMISSION_GRANTED == mContext.checkPermission(
                 NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, pid, uid)
-                || uid == Process.BLUETOOTH_UID;
+                || UserHandle.getAppId(uid) == Process.BLUETOOTH_UID;
     }
 
     private boolean checkSettingsPermission() {
@@ -3417,6 +3431,10 @@
             pw.increaseIndent();
             nai.dumpInactivityTimers(pw);
             pw.decreaseIndent();
+            pw.println("Nat464Xlat:");
+            pw.increaseIndent();
+            nai.dumpNat464Xlat(pw);
+            pw.decreaseIndent();
             pw.decreaseIndent();
         }
     }
diff --git a/service/src/com/android/server/TestNetworkService.java b/service/src/com/android/server/TestNetworkService.java
index ccc2776..e12190c 100644
--- a/service/src/com/android/server/TestNetworkService.java
+++ b/service/src/com/android/server/TestNetworkService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import static android.net.TestNetworkManager.CLAT_INTERFACE_PREFIX;
 import static android.net.TestNetworkManager.TEST_TAP_PREFIX;
 import static android.net.TestNetworkManager.TEST_TUN_PREFIX;
 
@@ -98,6 +99,14 @@
         }
     }
 
+    // TODO: find a way to allow the caller to pass in non-clat interface names, ensuring that
+    // those names do not conflict with names created by callers that do not pass in an interface
+    // name.
+    private static boolean isValidInterfaceName(@NonNull final String iface) {
+        return iface.startsWith(CLAT_INTERFACE_PREFIX + TEST_TUN_PREFIX)
+                || iface.startsWith(CLAT_INTERFACE_PREFIX + TEST_TAP_PREFIX);
+    }
+
     /**
      * Create a TUN or TAP interface with the specified parameters.
      *
@@ -106,29 +115,35 @@
      */
     @Override
     public TestNetworkInterface createInterface(boolean isTun, boolean bringUp,
-            LinkAddress[] linkAddrs) {
+            LinkAddress[] linkAddrs, @Nullable String iface) {
         enforceTestNetworkPermissions(mContext);
 
         Objects.requireNonNull(linkAddrs, "missing linkAddrs");
 
-        String ifacePrefix = isTun ? TEST_TUN_PREFIX : TEST_TAP_PREFIX;
-        String iface = ifacePrefix + sTestTunIndex.getAndIncrement();
+        String interfaceName = iface;
+        if (iface == null) {
+            String ifacePrefix = isTun ? TEST_TUN_PREFIX : TEST_TAP_PREFIX;
+            interfaceName = ifacePrefix + sTestTunIndex.getAndIncrement();
+        } else if (!isValidInterfaceName(iface)) {
+            throw new IllegalArgumentException("invalid interface name requested: " + iface);
+        }
+
         final long token = Binder.clearCallingIdentity();
         try {
             ParcelFileDescriptor tunIntf =
-                    ParcelFileDescriptor.adoptFd(jniCreateTunTap(isTun, iface));
+                    ParcelFileDescriptor.adoptFd(jniCreateTunTap(isTun, interfaceName));
             for (LinkAddress addr : linkAddrs) {
                 mNetd.interfaceAddAddress(
-                        iface,
+                        interfaceName,
                         addr.getAddress().getHostAddress(),
                         addr.getPrefixLength());
             }
 
             if (bringUp) {
-                NetdUtils.setInterfaceUp(mNetd, iface);
+                NetdUtils.setInterfaceUp(mNetd, interfaceName);
             }
 
-            return new TestNetworkInterface(tunIntf, iface);
+            return new TestNetworkInterface(tunIntf, interfaceName);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } finally {
diff --git a/service/src/com/android/server/connectivity/ClatCoordinator.java b/service/src/com/android/server/connectivity/ClatCoordinator.java
index 8aa5990..4a7c77a 100644
--- a/service/src/com/android/server/connectivity/ClatCoordinator.java
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -36,6 +36,7 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.modules.utils.build.SdkLevel;
 import com.android.net.module.util.BpfMap;
 import com.android.net.module.util.IBpfMap;
@@ -100,11 +101,11 @@
     private static final String CLAT_INGRESS6_MAP_PATH = makeMapPath("ingress6");
 
     private static String makeMapPath(String which) {
-        return "/sys/fs/bpf/map_clatd_clat_" + which + "_map";
+        return "/sys/fs/bpf/net_shared/map_clatd_clat_" + which + "_map";
     }
 
     private static String makeProgPath(boolean ingress, boolean ether) {
-        String path = "/sys/fs/bpf/prog_clatd_schedcls_"
+        String path = "/sys/fs/bpf/net_shared/prog_clatd_schedcls_"
                 + (ingress ? "ingress6" : "egress4")
                 + "_clat_"
                 + (ether ? "ether" : "rawip");
@@ -122,8 +123,12 @@
     @Nullable
     private ClatdTracker mClatdTracker = null;
 
+    /**
+     * Dependencies of ClatCoordinator which makes ConnectivityService injection
+     * in tests.
+     */
     @VisibleForTesting
-    abstract static class Dependencies {
+    public abstract static class Dependencies {
         /**
           * Get netd.
           */
@@ -738,6 +743,69 @@
         mClatdTracker = null;
     }
 
+    private void dumpBpfIngress(@NonNull IndentingPrintWriter pw) {
+        if (mIngressMap == null) {
+            pw.println("No BPF ingress6 map");
+            return;
+        }
+
+        try {
+            if (mIngressMap.isEmpty()) {
+                pw.println("<empty>");
+            }
+            pw.println("BPF ingress map: iif nat64Prefix v6Addr -> v4Addr oif");
+            pw.increaseIndent();
+            mIngressMap.forEach((k, v) -> {
+                // TODO: print interface name
+                pw.println(String.format("%d %s/96 %s -> %s %d", k.iif, k.pfx96, k.local6,
+                        v.local4, v.oif));
+            });
+            pw.decreaseIndent();
+        } catch (ErrnoException e) {
+            pw.println("Error dumping BPF ingress6 map: " + e);
+        }
+    }
+
+    private void dumpBpfEgress(@NonNull IndentingPrintWriter pw) {
+        if (mEgressMap == null) {
+            pw.println("No BPF egress4 map");
+            return;
+        }
+
+        try {
+            if (mEgressMap.isEmpty()) {
+                pw.println("<empty>");
+            }
+            pw.println("BPF egress map: iif v4Addr -> v6Addr nat64Prefix oif");
+            pw.increaseIndent();
+            mEgressMap.forEach((k, v) -> {
+                // TODO: print interface name
+                pw.println(String.format("%d %s -> %s %s/96 %d %s", k.iif, k.local4, v.local6,
+                        v.pfx96, v.oif, v.oifIsEthernet != 0 ? "ether" : "rawip"));
+            });
+            pw.decreaseIndent();
+        } catch (ErrnoException e) {
+            pw.println("Error dumping BPF egress4 map: " + e);
+        }
+    }
+
+    /**
+     * Dump the cordinator information.
+     *
+     * @param pw print writer.
+     */
+    public void dump(@NonNull IndentingPrintWriter pw) {
+        // TODO: dump ClatdTracker
+        // TODO: move map dump to a global place to avoid duplicate dump while there are two or
+        // more IPv6 only networks.
+        pw.println("Forwarding rules:");
+        pw.increaseIndent();
+        dumpBpfIngress(pw);
+        dumpBpfEgress(pw);
+        pw.decreaseIndent();
+        pw.println();
+    }
+
     /**
      * Get clatd tracker. For test only.
      */
diff --git a/service/src/com/android/server/connectivity/ConnectivityNativeService.java b/service/src/com/android/server/connectivity/ConnectivityNativeService.java
index cde6ea7..c1ba40e 100644
--- a/service/src/com/android/server/connectivity/ConnectivityNativeService.java
+++ b/service/src/com/android/server/connectivity/ConnectivityNativeService.java
@@ -47,10 +47,11 @@
     private static final String TAG = ConnectivityNativeService.class.getSimpleName();
     private static final String CGROUP_PATH = "/sys/fs/cgroup";
     private static final String V4_PROG_PATH =
-            "/sys/fs/bpf/prog_block_bind4_block_port";
+            "/sys/fs/bpf/net_shared/prog_block_bind4_block_port";
     private static final String V6_PROG_PATH =
-            "/sys/fs/bpf/prog_block_bind6_block_port";
-    private static final String BLOCKED_PORTS_MAP_PATH = "/sys/fs/bpf/map_block_blocked_ports_map";
+            "/sys/fs/bpf/net_shared/prog_block_bind6_block_port";
+    private static final String BLOCKED_PORTS_MAP_PATH =
+            "/sys/fs/bpf/net_shared/map_block_blocked_ports_map";
 
     private final Context mContext;
 
diff --git a/service/src/com/android/server/connectivity/DscpPolicyTracker.java b/service/src/com/android/server/connectivity/DscpPolicyTracker.java
index de9dfe3..7829d1a 100644
--- a/service/src/com/android/server/connectivity/DscpPolicyTracker.java
+++ b/service/src/com/android/server/connectivity/DscpPolicyTracker.java
@@ -52,7 +52,7 @@
 
     private static final String TAG = DscpPolicyTracker.class.getSimpleName();
     private static final String PROG_PATH =
-            "/sys/fs/bpf/prog_dscp_policy_schedcls_set_dscp";
+            "/sys/fs/bpf/net_shared/prog_dscp_policy_schedcls_set_dscp";
     // 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");
@@ -61,7 +61,7 @@
     private static final int MAX_POLICIES = 16;
 
     private static String makeMapPath(String which) {
-        return "/sys/fs/bpf/map_" + which + "_map";
+        return "/sys/fs/bpf/net_shared/map_" + which + "_map";
     }
 
     private Set<String> mAttachedIfaces;
diff --git a/service/src/com/android/server/connectivity/Nat464Xlat.java b/service/src/com/android/server/connectivity/Nat464Xlat.java
index 7b06682..e8fc06d 100644
--- a/service/src/com/android/server/connectivity/Nat464Xlat.java
+++ b/service/src/com/android/server/connectivity/Nat464Xlat.java
@@ -36,9 +36,12 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.modules.utils.build.SdkLevel;
 import com.android.net.module.util.NetworkStackConstants;
 import com.android.server.ConnectivityService;
 
+import java.io.IOException;
 import java.net.Inet6Address;
 import java.util.Objects;
 
@@ -96,6 +99,7 @@
     private String mIface;
     private Inet6Address mIPv6Address;
     private State mState = State.IDLE;
+    private ClatCoordinator mClatCoordinator;
 
     private boolean mEnableClatOnCellular;
     private boolean mPrefixDiscoveryRunning;
@@ -106,6 +110,7 @@
         mNetd = netd;
         mNetwork = nai;
         mEnableClatOnCellular = deps.getCellular464XlatEnabled();
+        mClatCoordinator = deps.getClatCoordinator(mNetd);
     }
 
     /**
@@ -179,10 +184,18 @@
     private void enterStartingState(String baseIface) {
         mNat64PrefixInUse = selectNat64Prefix();
         String addrStr = null;
-        try {
-            addrStr = mNetd.clatdStart(baseIface, mNat64PrefixInUse.toString());
-        } catch (RemoteException | ServiceSpecificException e) {
-            Log.e(TAG, "Error starting clatd on " + baseIface + ": " + e);
+        if (SdkLevel.isAtLeastT()) {
+            try {
+                addrStr = mClatCoordinator.clatStart(baseIface, getNetId(), mNat64PrefixInUse);
+            } catch (IOException e) {
+                Log.e(TAG, "Error starting clatd on " + baseIface + ": " + e);
+            }
+        } else {
+            try {
+                addrStr = mNetd.clatdStart(baseIface, mNat64PrefixInUse.toString());
+            } catch (RemoteException | ServiceSpecificException e) {
+                Log.e(TAG, "Error starting clatd on " + baseIface + ": " + e);
+            }
         }
         mIface = CLAT_PREFIX + baseIface;
         mBaseIface = baseIface;
@@ -256,10 +269,18 @@
         }
 
         Log.i(TAG, "Stopping clatd on " + mBaseIface);
-        try {
-            mNetd.clatdStop(mBaseIface);
-        } catch (RemoteException | ServiceSpecificException e) {
-            Log.e(TAG, "Error stopping clatd on " + mBaseIface + ": " + e);
+        if (SdkLevel.isAtLeastT()) {
+            try {
+                mClatCoordinator.clatStop();
+            } catch (IOException e) {
+                Log.e(TAG, "Error stopping clatd on " + mBaseIface + ": " + e);
+            }
+        } else {
+            try {
+                mNetd.clatdStop(mBaseIface);
+            } catch (RemoteException | ServiceSpecificException e) {
+                Log.e(TAG, "Error stopping clatd on " + mBaseIface + ": " + e);
+            }
         }
 
         String iface = mIface;
@@ -506,6 +527,24 @@
         mNetwork.handler().post(() -> handleInterfaceRemoved(iface));
     }
 
+    /**
+     * Dump the NAT64 xlat information.
+     *
+     * @param pw print writer.
+     */
+    public void dump(IndentingPrintWriter pw) {
+        if (SdkLevel.isAtLeastT()) {
+            if (isStarted()) {
+                pw.println("ClatCoordinator:");
+                pw.increaseIndent();
+                mClatCoordinator.dump(pw);
+                pw.decreaseIndent();
+            } else {
+                pw.println("<not start>");
+            }
+        }
+    }
+
     @Override
     public String toString() {
         return "mBaseIface: " + mBaseIface + ", mIface: " + mIface + ", mState: " + mState;
diff --git a/service/src/com/android/server/connectivity/NetworkAgentInfo.java b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
index 1fc5a8f..323888a 100644
--- a/service/src/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
@@ -59,6 +59,7 @@
 import android.util.Pair;
 import android.util.SparseArray;
 
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.WakeupMessage;
 import com.android.modules.utils.build.SdkLevel;
 import com.android.server.ConnectivityService;
@@ -1186,6 +1187,15 @@
     }
 
     /**
+     * Dump the NAT64 xlat information.
+     *
+     * @param pw print writer.
+     */
+    public void dumpNat464Xlat(IndentingPrintWriter pw) {
+        clatd.dump(pw);
+    }
+
+    /**
      * Sets the most recent ConnectivityReport for this network.
      *
      * <p>This should only be called from the ConnectivityService thread.
diff --git a/service/src/com/android/server/connectivity/PermissionMonitor.java b/service/src/com/android/server/connectivity/PermissionMonitor.java
index 62b3add..c02d9cf 100755
--- a/service/src/com/android/server/connectivity/PermissionMonitor.java
+++ b/service/src/com/android/server/connectivity/PermissionMonitor.java
@@ -134,7 +134,9 @@
 
     // Store appIds traffic permissions for each user.
     // Keys are users, Values are SparseArrays where each entry maps an appId to the permissions
-    // that appId has within that user.
+    // that appId has within that user. The permissions are a bitmask of PERMISSION_INTERNET and
+    // PERMISSION_UPDATE_DEVICE_STATS, or 0 (PERMISSION_NONE) if the app has neither of those
+    // permissions. They can never be PERMISSION_UNINSTALLED.
     @GuardedBy("this")
     private final Map<UserHandle, SparseIntArray> mUsersTrafficPermissions = new ArrayMap<>();
 
@@ -545,17 +547,21 @@
 
         // Remove appIds traffic permission that belongs to the user
         final SparseIntArray removedUserAppIds = mUsersTrafficPermissions.remove(user);
-        // Generate appIds from left users.
+        // Generate appIds from the remaining users.
         final SparseIntArray appIds = makeAppIdsTrafficPermForAllUsers();
+
+        if (removedUserAppIds == null) {
+            Log.wtf(TAG, "onUserRemoved: Receive unknown user=" + user);
+            return;
+        }
+
         // Clear permission on those appIds belong to this user only, set the permission to
         // PERMISSION_UNINSTALLED.
-        if (removedUserAppIds != null) {
-            for (int i = 0; i < removedUserAppIds.size(); i++) {
-                final int appId = removedUserAppIds.keyAt(i);
-                // Need to clear permission if the removed appId is not found in the array.
-                if (appIds.indexOfKey(appId) < 0) {
-                    appIds.put(appId, PERMISSION_UNINSTALLED);
-                }
+        for (int i = 0; i < removedUserAppIds.size(); i++) {
+            final int appId = removedUserAppIds.keyAt(i);
+            // Need to clear permission if the removed appId is not found in the array.
+            if (appIds.indexOfKey(appId) < 0) {
+                appIds.put(appId, PERMISSION_UNINSTALLED);
             }
         }
         sendAppIdsTrafficPermission(appIds);
@@ -650,7 +656,6 @@
     }
 
     private synchronized void updateAppIdTrafficPermission(int uid) {
-        final int appId = UserHandle.getAppId(uid);
         final int uidTrafficPerm = getTrafficPermissionForUid(uid);
         final SparseIntArray userTrafficPerms =
                 mUsersTrafficPermissions.get(UserHandle.getUserHandleForUid(uid));
@@ -661,6 +666,7 @@
         // Do not put PERMISSION_UNINSTALLED into the array. If no package left on the uid
         // (PERMISSION_UNINSTALLED), remove the appId from the array. Otherwise, update the latest
         // permission to the appId.
+        final int appId = UserHandle.getAppId(uid);
         if (uidTrafficPerm == PERMISSION_UNINSTALLED) {
             userTrafficPerms.delete(appId);
         } else {
@@ -984,10 +990,6 @@
      */
     @VisibleForTesting
     void sendAppIdsTrafficPermission(SparseIntArray netdPermissionsAppIds) {
-        if (mNetd == null) {
-            Log.e(TAG, "Failed to get the netd service");
-            return;
-        }
         final ArrayList<Integer> allPermissionAppIds = new ArrayList<>();
         final ArrayList<Integer> internetPermissionAppIds = new ArrayList<>();
         final ArrayList<Integer> updateStatsPermissionAppIds = new ArrayList<>();
diff --git a/tests/common/AndroidTest_Coverage.xml b/tests/common/AndroidTest_Coverage.xml
index d4898b2..48d26b8 100644
--- a/tests/common/AndroidTest_Coverage.xml
+++ b/tests/common/AndroidTest_Coverage.xml
@@ -14,7 +14,8 @@
 -->
 <configuration description="Runs coverage tests for Connectivity">
     <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
-        <option name="test-file-name" value="ConnectivityCoverageTests.apk" />
+      <option name="test-file-name" value="ConnectivityCoverageTests.apk" />
+      <option name="install-arg" value="-t" />
     </target_preparer>
 
     <option name="test-tag" value="ConnectivityCoverageTests" />
diff --git a/tests/cts/hostside/TEST_MAPPING b/tests/cts/hostside/TEST_MAPPING
index fcec483..ab6de82 100644
--- a/tests/cts/hostside/TEST_MAPPING
+++ b/tests/cts/hostside/TEST_MAPPING
@@ -4,9 +4,6 @@
       "name": "CtsHostsideNetworkTests",
       "options": [
         {
-          "include-filter": "com.android.cts.net.HostsideRestrictBackgroundNetworkTests"
-        },
-        {
           "exclude-annotation": "androidx.test.filters.FlakyTest"
         },
         {
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index 96ce65f..524bd65 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -898,7 +898,7 @@
         final Intent intent = new Intent();
         if (type == TYPE_COMPONENT_ACTIVTIY) {
             intent.setComponent(new ComponentName(TEST_APP2_PKG, TEST_APP2_ACTIVITY_CLASS))
-                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
         } else if (type == TYPE_COMPONENT_FOREGROUND_SERVICE) {
             intent.setComponent(new ComponentName(TEST_APP2_PKG, TEST_APP2_SERVICE_CLASS))
                     .setFlags(1);
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java
new file mode 100644
index 0000000..098f295
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getUiDevice;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.net.hostside.Property.APP_STANDBY_MODE;
+import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.DOZE_MODE;
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+
+import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+@RequiredProperties({NON_METERED_NETWORK})
+public class ConnOnActivityStartTest extends AbstractRestrictBackgroundNetworkTestCase {
+    private static final int TEST_ITERATION_COUNT = 5;
+
+    @Before
+    public final void setUp() throws Exception {
+        super.setUp();
+        resetDeviceState();
+    }
+
+    @After
+    public final void tearDown() throws Exception {
+        super.tearDown();
+        resetDeviceState();
+    }
+
+    private void resetDeviceState() throws Exception {
+        resetBatteryState();
+        setBatterySaverMode(false);
+        setRestrictBackground(false);
+        setAppIdle(false);
+        setDozeMode(false);
+    }
+
+
+    @Test
+    @RequiredProperties({BATTERY_SAVER_MODE})
+    public void testStartActivity_batterySaver() throws Exception {
+        setBatterySaverMode(true);
+        assertLaunchedActivityHasNetworkAccess("testStartActivity_batterySaver");
+    }
+
+    @Test
+    @RequiredProperties({DATA_SAVER_MODE, METERED_NETWORK})
+    public void testStartActivity_dataSaver() throws Exception {
+        setRestrictBackground(true);
+        assertLaunchedActivityHasNetworkAccess("testStartActivity_dataSaver");
+    }
+
+    @Test
+    @RequiredProperties({DOZE_MODE})
+    public void testStartActivity_doze() throws Exception {
+        setDozeMode(true);
+        assertLaunchedActivityHasNetworkAccess("testStartActivity_doze");
+    }
+
+    @Test
+    @RequiredProperties({APP_STANDBY_MODE})
+    public void testStartActivity_appStandby() throws Exception {
+        turnBatteryOn();
+        setAppIdle(true);
+        assertLaunchedActivityHasNetworkAccess("testStartActivity_appStandby");
+    }
+
+    private void assertLaunchedActivityHasNetworkAccess(String testName) throws Exception {
+        for (int i = 0; i < TEST_ITERATION_COUNT; ++i) {
+            Log.i(TAG, testName + " start #" + i);
+            launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
+            getUiDevice().pressHome();
+            assertBackgroundState();
+            Log.i(TAG, testName + " end #" + i);
+        }
+    }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
index 56be3e3..c53276b 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
@@ -57,6 +57,7 @@
 import android.util.Log;
 
 import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
 
 import com.android.compatibility.common.util.AppStandbyUtils;
 import com.android.compatibility.common.util.BatteryUtils;
@@ -438,6 +439,10 @@
         return InstrumentationRegistry.getInstrumentation();
     }
 
+    public static UiDevice getUiDevice() {
+        return UiDevice.getInstance(getInstrumentation());
+    }
+
     // When power saver mode or restrict background enabled or adding any white/black list into
     // those modes, NetworkPolicy may need to take some time to update the rules of uids. So having
     // this function and using PollingCheck to try to make sure the uid has updated and reduce the
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
index 9fdb9c9..08cdea7 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
@@ -43,15 +43,6 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         Log.d(TAG, "MyActivity.onCreate()");
-        Common.notifyNetworkStateObserver(this, getIntent(), TYPE_COMPONENT_ACTIVTY);
-        finishCommandReceiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                Log.d(TAG, "Finishing MyActivity");
-                MyActivity.this.finish();
-            }
-        };
-        registerReceiver(finishCommandReceiver, new IntentFilter(ACTION_FINISH_ACTIVITY));
     }
 
     @Override
@@ -69,6 +60,28 @@
     }
 
     @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        Log.d(TAG, "MyActivity.onNewIntent()");
+        setIntent(intent);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        Log.d(TAG, "MyActivity.onResume(): " + getIntent());
+        Common.notifyNetworkStateObserver(this, getIntent(), TYPE_COMPONENT_ACTIVTY);
+        finishCommandReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                Log.d(TAG, "Finishing MyActivity");
+                MyActivity.this.finish();
+            }
+        };
+        registerReceiver(finishCommandReceiver, new IntentFilter(ACTION_FINISH_ACTIVITY));
+    }
+
+    @Override
     protected void onDestroy() {
         Log.d(TAG, "MyActivity.onDestroy()");
         super.onDestroy();
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java b/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java
new file mode 100644
index 0000000..3387fd7
--- /dev/null
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net;
+
+public class HostsideConnOnActivityStartTest extends HostsideNetworkTestCase {
+    private static final String TEST_CLASS = TEST_PKG + ".ConnOnActivityStartTest";
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        uninstallPackage(TEST_APP2_PKG, false);
+        installPackage(TEST_APP2_APK);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+
+        uninstallPackage(TEST_APP2_PKG, true);
+    }
+
+    public void testStartActivity_batterySaver() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_CLASS, "testStartActivity_batterySaver");
+    }
+
+    public void testStartActivity_dataSaver() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_CLASS, "testStartActivity_dataSaver");
+    }
+
+    public void testStartActivity_doze() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_CLASS, "testStartActivity_doze");
+    }
+
+    public void testStartActivity_appStandby() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_CLASS, "testStartActivity_appStandby");
+    }
+}
diff --git a/tests/cts/net/src/android/net/cts/DnsResolverTest.java b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
index c6fc38f..0c53411 100644
--- a/tests/cts/net/src/android/net/cts/DnsResolverTest.java
+++ b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
@@ -49,6 +49,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.platform.test.annotations.AppModeFull;
+import android.provider.Settings;
 import android.system.ErrnoException;
 import android.util.Log;
 
@@ -727,6 +728,18 @@
 
     @Test
     public void testPrivateDnsBypass() throws InterruptedException {
+        final String dataStallSetting = Settings.Global.getString(mCR,
+                Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK);
+        Settings.Global.putInt(mCR, Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 0);
+        try {
+            doTestPrivateDnsBypass();
+        } finally {
+            Settings.Global.putString(mCR, Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK,
+                    dataStallSetting);
+        }
+    }
+
+    private void doTestPrivateDnsBypass() throws InterruptedException {
         final Network[] testNetworks = getTestableNetworks();
 
         // Set an invalid private DNS server
diff --git a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
index 30e0015..f7a2421 100644
--- a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
@@ -177,7 +177,10 @@
     }
 
     private fun createInterface(): EthernetTestInterface {
-        return EthernetTestInterface(context, Handler(Looper.getMainLooper()))
+        return EthernetTestInterface(
+            context,
+            Handler(Looper.getMainLooper())
+        ).also { createdIfaces.add(it) }
     }
 
     private fun setIncludeTestInterfaces(value: Boolean) {
diff --git a/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java b/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java
index 9590f88..7286bf6 100644
--- a/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java
+++ b/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java
@@ -20,8 +20,6 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.cts.util.CtsNetUtils.TestNetworkCallback;
-import static android.net.cts.util.IkeSessionTestUtils.CHILD_PARAMS;
-import static android.net.cts.util.IkeSessionTestUtils.IKE_PARAMS;
 
 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
 import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
@@ -51,6 +49,7 @@
 import android.net.TestNetworkInterface;
 import android.net.VpnManager;
 import android.net.cts.util.CtsNetUtils;
+import android.net.cts.util.IkeSessionTestUtils;
 import android.net.ipsec.ike.IkeTunnelConnectionParams;
 import android.os.Build;
 import android.os.Process;
@@ -87,6 +86,7 @@
 import java.security.KeyPairGenerator;
 import java.security.PrivateKey;
 import java.security.cert.X509Certificate;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
@@ -196,6 +196,7 @@
 
     private final X509Certificate mServerRootCa;
     private final CertificateAndKey mUserCertKey;
+    private final List<TestableNetworkCallback> mCallbacksToUnregister = new ArrayList<>();
 
     public Ikev2VpnTest() throws Exception {
         // Build certificates
@@ -205,6 +206,9 @@
 
     @After
     public void tearDown() {
+        for (TestableNetworkCallback callback : mCallbacksToUnregister) {
+            sCM.unregisterNetworkCallback(callback);
+        }
         setAppop(AppOpsManager.OP_ACTIVATE_VPN, false);
         setAppop(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN, false);
     }
@@ -247,6 +251,28 @@
         return builder.build();
     }
 
+    private Ikev2VpnProfile buildIkev2VpnProfileIkeTunConnParams(
+            final boolean isRestrictedToTestNetworks, final boolean requiresValidation,
+            final boolean testIpv6) throws Exception {
+        final IkeTunnelConnectionParams params =
+                new IkeTunnelConnectionParams(testIpv6
+                        ? IkeSessionTestUtils.IKE_PARAMS_V6 : IkeSessionTestUtils.IKE_PARAMS_V4,
+                        IkeSessionTestUtils.CHILD_PARAMS);
+
+        final Ikev2VpnProfileBuilderShim builderShim =
+                Ikev2VpnProfileBuilderShimImpl.newInstance(null, null, params)
+                        .setRequiresInternetValidation(requiresValidation)
+                        .setProxy(TEST_PROXY_INFO)
+                        .setMaxMtu(TEST_MTU)
+                        .setMetered(false);
+
+        final Ikev2VpnProfile.Builder builder = (Ikev2VpnProfile.Builder) builderShim.getBuilder();
+        if (isRestrictedToTestNetworks) {
+            builder.restrictToTestNetworks();
+        }
+        return builder.build();
+    }
+
     private Ikev2VpnProfile buildIkev2VpnProfilePsk(@NonNull String remote,
             boolean isRestrictedToTestNetworks, boolean requiresValidation) throws Exception {
         final Ikev2VpnProfileBuilderShim builder =
@@ -320,8 +346,8 @@
         assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
         assumeTrue(TestUtils.shouldTestTApis());
 
-        final IkeTunnelConnectionParams expectedParams =
-                new IkeTunnelConnectionParams(IKE_PARAMS, CHILD_PARAMS);
+        final IkeTunnelConnectionParams expectedParams = new IkeTunnelConnectionParams(
+                IkeSessionTestUtils.IKE_PARAMS_V6, IkeSessionTestUtils.CHILD_PARAMS);
         final Ikev2VpnProfileBuilderShim ikeProfileBuilder =
                 Ikev2VpnProfileBuilderShimImpl.newInstance(null, null, expectedParams);
         // Verify the other Ike options could not be set with IkeTunnelConnectionParams.
@@ -467,7 +493,8 @@
     }
 
     private void checkStartStopVpnProfileBuildsNetworks(@NonNull IkeTunUtils tunUtils,
-            boolean testIpv6, boolean requiresValidation, boolean testSessionKey)
+            boolean testIpv6, boolean requiresValidation, boolean testSessionKey,
+            boolean testIkeTunConnParams)
             throws Exception {
         String serverAddr = testIpv6 ? TEST_SERVER_ADDR_V6 : TEST_SERVER_ADDR_V4;
         String initResp = testIpv6 ? SUCCESSFUL_IKE_INIT_RESP_V6 : SUCCESSFUL_IKE_INIT_RESP_V4;
@@ -477,14 +504,17 @@
         // Requires MANAGE_TEST_NETWORKS to provision a test-mode profile.
         mCtsNetUtils.setAppopPrivileged(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN, true);
 
-        final Ikev2VpnProfile profile = buildIkev2VpnProfilePsk(serverAddr,
-                true /* isRestrictedToTestNetworks */, requiresValidation);
+        final Ikev2VpnProfile profile = testIkeTunConnParams
+                ? buildIkev2VpnProfileIkeTunConnParams(true /* isRestrictedToTestNetworks */,
+                        requiresValidation, testIpv6)
+                : buildIkev2VpnProfilePsk(serverAddr, true /* isRestrictedToTestNetworks */,
+                        requiresValidation);
         assertNull(sVpnMgr.provisionVpnProfile(profile));
 
         final TestableNetworkCallback cb = new TestableNetworkCallback(TIMEOUT_MS);
         final NetworkRequest nr = new NetworkRequest.Builder()
                 .clearCapabilities().addTransportType(TRANSPORT_VPN).build();
-        sCM.registerNetworkCallback(nr, cb);
+        registerNetworkCallback(nr, cb);
 
         if (testSessionKey) {
             // testSessionKey will never be true if running on <T
@@ -520,7 +550,8 @@
             assertFalse(profileState.isLockdownEnabled());
         }
 
-        cb.expectCapabilitiesThat(vpnNetwork, TIMEOUT_MS, caps -> caps.hasTransport(TRANSPORT_VPN)
+        cb.expectCapabilitiesThat(vpnNetwork, TIMEOUT_MS,
+                caps -> caps.hasTransport(TRANSPORT_VPN)
                 && caps.hasCapability(NET_CAPABILITY_INTERNET)
                 && !caps.hasCapability(NET_CAPABILITY_VALIDATED)
                 && Process.myUid() == caps.getOwnerUid());
@@ -533,8 +564,10 @@
         // but unexpectedly sends this callback, expecting LOST below will fail because the next
         // callback will be the validated capabilities instead.
         // In S and below, |requiresValidation| is ignored, so this callback is always sent
-        // regardless of its value.
-        if (!requiresValidation || !TestUtils.shouldTestTApis()) {
+        // regardless of its value. However, there is a race in Vpn(see b/228574221) that VPN may
+        // misuse VPN network itself as the underlying network. The fix is not available without
+        // SDK > T platform. Thus, verify this only on T+ platform.
+        if (!requiresValidation && TestUtils.shouldTestTApis()) {
             cb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED, TIMEOUT_MS,
                     entry -> ((CallbackEntry.CapabilitiesChanged) entry).getCaps()
                             .hasCapability(NET_CAPABILITY_VALIDATED));
@@ -547,10 +580,16 @@
                 lost -> vpnNetwork.equals(lost.getNetwork()));
     }
 
+    private void registerNetworkCallback(NetworkRequest request, TestableNetworkCallback callback) {
+        sCM.registerNetworkCallback(request, callback);
+        mCallbacksToUnregister.add(callback);
+    }
+
     private class VerifyStartStopVpnProfileTest implements TestNetworkRunnable.Test {
         private final boolean mTestIpv6Only;
         private final boolean mRequiresValidation;
         private final boolean mTestSessionKey;
+        private final boolean mTestIkeTunConnParams;
 
         /**
          * Constructs the test
@@ -560,10 +599,11 @@
          * @param testSessionKey if true, start VPN by calling startProvisionedVpnProfileSession()
          */
         VerifyStartStopVpnProfileTest(boolean testIpv6Only, boolean requiresValidation,
-                boolean testSessionKey) {
+                boolean testSessionKey, boolean testIkeTunConnParams) {
             mTestIpv6Only = testIpv6Only;
             mRequiresValidation = requiresValidation;
             mTestSessionKey = testSessionKey;
+            mTestIkeTunConnParams = testIkeTunConnParams;
         }
 
         @Override
@@ -571,8 +611,8 @@
                 throws Exception {
             final IkeTunUtils tunUtils = new IkeTunUtils(testIface.getFileDescriptor());
 
-            checkStartStopVpnProfileBuildsNetworks(
-                    tunUtils, mTestIpv6Only, mRequiresValidation, mTestSessionKey);
+            checkStartStopVpnProfileBuildsNetworks(tunUtils, mTestIpv6Only, mRequiresValidation,
+                    mTestSessionKey, mTestIkeTunConnParams);
         }
 
         @Override
@@ -590,53 +630,83 @@
         }
     }
 
-    @Test
-    public void testStartStopVpnProfileV4() throws Exception {
+    private void doTestStartStopVpnProfile(boolean testIpv6Only, boolean requiresValidation,
+            boolean testSessionKey, boolean testIkeTunConnParams) throws Exception {
         assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
-
         // Requires shell permission to update appops.
         runWithShellPermissionIdentity(
                 new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(
-                        false /* testIpv6Only */, false /* requiresValidation */,
-                        false /* testSessionKey */)));
+                        testIpv6Only, requiresValidation, testSessionKey , testIkeTunConnParams)));
+    }
 
-        runWithShellPermissionIdentity(
-                new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(
-                        false /* testIpv6Only */, true /* requiresValidation */,
-                        false /* testSessionKey */)));
+    @Test
+    public void testStartStopVpnProfileV4() throws Exception {
+        doTestStartStopVpnProfile(false /* testIpv6Only */, false /* requiresValidation */,
+                false /* testSessionKey */, false /* testIkeTunConnParams */);
+    }
+
+    @Test @IgnoreUpTo(SC_V2)
+    public void testStartStopVpnProfileV4WithValidation() throws Exception {
+        assumeTrue(TestUtils.shouldTestTApis());
+        doTestStartStopVpnProfile(false /* testIpv6Only */, true /* requiresValidation */,
+                false /* testSessionKey */, false /* testIkeTunConnParams */);
     }
 
     @Test
     public void testStartStopVpnProfileV6() throws Exception {
-        assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
+        doTestStartStopVpnProfile(true /* testIpv6Only */, false /* requiresValidation */,
+                false /* testSessionKey */, false /* testIkeTunConnParams */);
+    }
 
-        // Requires shell permission to update appops.
-        runWithShellPermissionIdentity(
-                new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(
-                        true /* testIpv6Only */, false /* requiresValidation */,
-                        false /* testSessionKey */)));
-        runWithShellPermissionIdentity(
-                new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(
-                        true /* testIpv6Only */, true /* requiresValidation */,
-                        false /* testSessionKey */)));
+    @Test @IgnoreUpTo(SC_V2)
+    public void testStartStopVpnProfileV6WithValidation() throws Exception {
+        assumeTrue(TestUtils.shouldTestTApis());
+        doTestStartStopVpnProfile(true /* testIpv6Only */, true /* requiresValidation */,
+                false /* testSessionKey */, false /* testIkeTunConnParams */);
+    }
+
+    @Test @IgnoreUpTo(SC_V2)
+    public void testStartStopVpnProfileIkeTunConnParamsV4() throws Exception {
+        assumeTrue(TestUtils.shouldTestTApis());
+        doTestStartStopVpnProfile(false /* testIpv6Only */, false /* requiresValidation */,
+                false /* testSessionKey */, true /* testIkeTunConnParams */);
+    }
+
+    @Test @IgnoreUpTo(SC_V2)
+    public void testStartStopVpnProfileIkeTunConnParamsV4WithValidation() throws Exception {
+        assumeTrue(TestUtils.shouldTestTApis());
+        doTestStartStopVpnProfile(false /* testIpv6Only */, true /* requiresValidation */,
+                false /* testSessionKey */, true /* testIkeTunConnParams */);
+    }
+
+    @Test @IgnoreUpTo(SC_V2)
+    public void testStartStopVpnProfileIkeTunConnParamsV6() throws Exception {
+        assumeTrue(TestUtils.shouldTestTApis());
+        doTestStartStopVpnProfile(true /* testIpv6Only */, false /* requiresValidation */,
+                false /* testSessionKey */, true /* testIkeTunConnParams */);
+    }
+
+    @Test @IgnoreUpTo(SC_V2)
+    public void testStartStopVpnProfileIkeTunConnParamsV6WithValidation() throws Exception {
+        assumeTrue(TestUtils.shouldTestTApis());
+        doTestStartStopVpnProfile(true /* testIpv6Only */, true /* requiresValidation */,
+                false /* testSessionKey */, true /* testIkeTunConnParams */);
     }
 
     @IgnoreUpTo(SC_V2)
     @Test
-    public void testStartProvisionedVpnProfileSession() throws Exception {
-        assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
+    public void testStartProvisionedVpnV4ProfileSession() throws Exception {
         assumeTrue(TestUtils.shouldTestTApis());
+        doTestStartStopVpnProfile(false /* testIpv6Only */, false /* requiresValidation */,
+                true /* testSessionKey */, false /* testIkeTunConnParams */);
+    }
 
-        // Requires shell permission to update appops.
-        runWithShellPermissionIdentity(
-                new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(
-                        false /* testIpv6Only */, false /* requiresValidation */,
-                        true /* testSessionKey */)));
-
-        runWithShellPermissionIdentity(
-                new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(
-                        true /* testIpv6Only */, false /* requiresValidation */,
-                        true /* testSessionKey */)));
+    @IgnoreUpTo(SC_V2)
+    @Test
+    public void testStartProvisionedVpnV6ProfileSession() throws Exception {
+        assumeTrue(TestUtils.shouldTestTApis());
+        doTestStartStopVpnProfile(true /* testIpv6Only */, false /* requiresValidation */,
+                true /* testSessionKey */, false /* testIkeTunConnParams */);
     }
 
     private static class CertificateAndKey {
diff --git a/tests/cts/net/util/java/android/net/cts/util/IkeSessionTestUtils.java b/tests/cts/net/util/java/android/net/cts/util/IkeSessionTestUtils.java
index b4ebcdb..244bfc5 100644
--- a/tests/cts/net/util/java/android/net/cts/util/IkeSessionTestUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/IkeSessionTestUtils.java
@@ -16,44 +16,73 @@
 
 package android.net.cts.util;
 
+import static android.net.ipsec.ike.SaProposal.DH_GROUP_4096_BIT_MODP;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CBC;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128;
 import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_128;
-import static android.net.ipsec.ike.SaProposal.KEY_LEN_UNUSED;
+import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_256;
+import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC;
 
+import android.net.InetAddresses;
 import android.net.ipsec.ike.ChildSaProposal;
 import android.net.ipsec.ike.IkeFqdnIdentification;
+import android.net.ipsec.ike.IkeIpv4AddrIdentification;
+import android.net.ipsec.ike.IkeIpv6AddrIdentification;
 import android.net.ipsec.ike.IkeSaProposal;
 import android.net.ipsec.ike.IkeSessionParams;
-import android.net.ipsec.ike.SaProposal;
 import android.net.ipsec.ike.TunnelModeChildSessionParams;
 
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+
 /** Shared testing parameters and util methods for testing IKE */
 public class IkeSessionTestUtils {
-    private static final String TEST_CLIENT_ADDR = "test.client.com";
-    private static final String TEST_SERVER_ADDR = "test.server.com";
-    private static final String TEST_SERVER = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
+    private static final String TEST_SERVER_ADDR_V4 = "192.0.2.2";
+    private static final String TEST_SERVER_ADDR_V6 = "2001:db8::2";
+    private static final String TEST_IDENTITY = "client.cts.android.com";
+    private static final byte[] TEST_PSK = "ikeAndroidPsk".getBytes();
+    public static final IkeSessionParams IKE_PARAMS_V4 = getTestIkeSessionParams(false);
+    public static final IkeSessionParams IKE_PARAMS_V6 = getTestIkeSessionParams(true);
 
-    public static final IkeSaProposal SA_PROPOSAL = new IkeSaProposal.Builder()
-            .addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES, KEY_LEN_UNUSED)
-            .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96)
-            .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC)
-            .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP)
-            .build();
-    public static final ChildSaProposal CHILD_PROPOSAL = new ChildSaProposal.Builder()
-            .addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128)
-            .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_NONE)
-            .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP)
-            .build();
+    public static final TunnelModeChildSessionParams CHILD_PARAMS = getChildSessionParams();
 
-    public static final IkeSessionParams IKE_PARAMS =
-            new IkeSessionParams.Builder()
-                    .setServerHostname(TEST_SERVER)
-                    .addSaProposal(SA_PROPOSAL)
-                    .setLocalIdentification(new IkeFqdnIdentification(TEST_CLIENT_ADDR))
-                    .setRemoteIdentification(new IkeFqdnIdentification(TEST_SERVER_ADDR))
-                    .setAuthPsk("psk".getBytes())
-                    .build();
-    public static final TunnelModeChildSessionParams CHILD_PARAMS =
-            new TunnelModeChildSessionParams.Builder()
-                    .addSaProposal(CHILD_PROPOSAL)
-                    .build();
+    private static TunnelModeChildSessionParams getChildSessionParams() {
+        final TunnelModeChildSessionParams.Builder childOptionsBuilder =
+                new TunnelModeChildSessionParams.Builder()
+                        .addSaProposal(getChildSaProposals());
+
+        return childOptionsBuilder.build();
+    }
+
+    private static IkeSessionParams getTestIkeSessionParams(boolean testIpv6) {
+        final String testServer = testIpv6 ? TEST_SERVER_ADDR_V6 : TEST_SERVER_ADDR_V4;
+        final InetAddress addr = InetAddresses.parseNumericAddress(testServer);
+        final IkeSessionParams.Builder ikeOptionsBuilder =
+                new IkeSessionParams.Builder()
+                        .setServerHostname(testServer)
+                        .setLocalIdentification(new IkeFqdnIdentification(TEST_IDENTITY))
+                        .setRemoteIdentification(testIpv6
+                                ? new IkeIpv6AddrIdentification((Inet6Address) addr)
+                                : new IkeIpv4AddrIdentification((Inet4Address) addr))
+                        .setAuthPsk(TEST_PSK)
+                        .addSaProposal(getIkeSaProposals());
+
+        return ikeOptionsBuilder.build();
+    }
+
+    private static IkeSaProposal getIkeSaProposals() {
+        return new IkeSaProposal.Builder()
+                .addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256)
+                .addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128)
+                .addDhGroup(DH_GROUP_4096_BIT_MODP)
+                .addPseudorandomFunction(PSEUDORANDOM_FUNCTION_AES128_XCBC).build();
+    }
+
+    private static ChildSaProposal getChildSaProposals() {
+        return new ChildSaProposal.Builder()
+                .addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128)
+                .build();
+    }
 }
diff --git a/tests/mts/bpf_existence_test.cpp b/tests/mts/bpf_existence_test.cpp
index 2bba282..25694d7 100644
--- a/tests/mts/bpf_existence_test.cpp
+++ b/tests/mts/bpf_existence_test.cpp
@@ -42,6 +42,7 @@
 
 #define PLATFORM "/sys/fs/bpf/"
 #define TETHERING "/sys/fs/bpf/tethering/"
+#define SHARED "/sys/fs/bpf/net_shared/"
 
 class BpfExistenceTest : public ::testing::Test {
 };
@@ -84,6 +85,42 @@
 };
 
 static const set<string> INTRODUCED_T = {
+    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_netd_app_uid_stats_map",
+    SHARED "map_netd_configuration_map",
+    SHARED "map_netd_cookie_tag_map",
+    SHARED "map_netd_iface_index_name_map",
+    SHARED "map_netd_iface_stats_map",
+    SHARED "map_netd_stats_map_A",
+    SHARED "map_netd_stats_map_B",
+    SHARED "map_netd_uid_counterset_map",
+    SHARED "map_netd_uid_owner_map",
+    SHARED "map_netd_uid_permission_map",
+    SHARED "prog_block_bind4_block_port",
+    SHARED "prog_block_bind6_block_port",
+    SHARED "prog_clatd_schedcls_egress4_clat_ether",
+    SHARED "prog_clatd_schedcls_egress4_clat_rawip",
+    SHARED "prog_clatd_schedcls_ingress6_clat_ether",
+    SHARED "prog_clatd_schedcls_ingress6_clat_rawip",
+    SHARED "prog_dscp_policy_schedcls_set_dscp_ether",
+    SHARED "prog_dscp_policy_schedcls_set_dscp_raw_ip",
+    SHARED "prog_netd_cgroupskb_egress_stats",
+    SHARED "prog_netd_cgroupskb_ingress_stats",
+    SHARED "prog_netd_cgroupsock_inet_create",
+    SHARED "prog_netd_schedact_ingress_account",
+    SHARED "prog_netd_skfilter_allowlist_xtbpf",
+    SHARED "prog_netd_skfilter_denylist_xtbpf",
+    SHARED "prog_netd_skfilter_egress_xtbpf",
+    SHARED "prog_netd_skfilter_ingress_xtbpf",
 };
 
 static const set<string> REMOVED_T = {
diff --git a/tests/unit/AndroidManifest.xml b/tests/unit/AndroidManifest.xml
index 887f171..54e1cd0 100644
--- a/tests/unit/AndroidManifest.xml
+++ b/tests/unit/AndroidManifest.xml
@@ -50,7 +50,7 @@
     <uses-permission android:name="android.permission.NETWORK_STATS_PROVIDER" />
     <uses-permission android:name="android.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE" />
 
-    <application>
+    <application android:testOnly="true">
         <uses-library android:name="android.test.runner" />
         <uses-library android:name="android.net.ipsec.ike" />
         <activity
diff --git a/tests/unit/AndroidTest.xml b/tests/unit/AndroidTest.xml
index 939ae49..2d32e55 100644
--- a/tests/unit/AndroidTest.xml
+++ b/tests/unit/AndroidTest.xml
@@ -15,7 +15,8 @@
 -->
 <configuration description="Runs Frameworks Networking Tests.">
     <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
-        <option name="test-file-name" value="FrameworksNetTests.apk" />
+      <option name="test-file-name" value="FrameworksNetTests.apk" />
+      <option name="install-arg" value="-t" />
     </target_preparer>
 
     <option name="test-suite-tag" value="apct" />
diff --git a/tests/unit/java/android/net/Ikev2VpnProfileTest.java b/tests/unit/java/android/net/Ikev2VpnProfileTest.java
index 8222ca1..5cb014f 100644
--- a/tests/unit/java/android/net/Ikev2VpnProfileTest.java
+++ b/tests/unit/java/android/net/Ikev2VpnProfileTest.java
@@ -17,7 +17,7 @@
 package android.net;
 
 import static android.net.cts.util.IkeSessionTestUtils.CHILD_PARAMS;
-import static android.net.cts.util.IkeSessionTestUtils.IKE_PARAMS;
+import static android.net.cts.util.IkeSessionTestUtils.IKE_PARAMS_V6;
 
 import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
 
@@ -448,7 +448,7 @@
     @Test
     public void testConversionIsLosslessWithIkeTunConnParams() throws Exception {
         final IkeTunnelConnectionParams tunnelParams =
-                new IkeTunnelConnectionParams(IKE_PARAMS, CHILD_PARAMS);
+                new IkeTunnelConnectionParams(IKE_PARAMS_V6, CHILD_PARAMS);
         // Config authentication related fields is not required while building with
         // IkeTunnelConnectionParams.
         final Ikev2VpnProfile ikeProfile = new Ikev2VpnProfile.Builder(tunnelParams).build();
@@ -464,9 +464,9 @@
 
         // Verify building with IkeTunnelConnectionParams
         final IkeTunnelConnectionParams tunnelParams =
-                new IkeTunnelConnectionParams(IKE_PARAMS, CHILD_PARAMS);
+                new IkeTunnelConnectionParams(IKE_PARAMS_V6, CHILD_PARAMS);
         final IkeTunnelConnectionParams tunnelParams2 =
-                new IkeTunnelConnectionParams(IKE_PARAMS, CHILD_PARAMS);
+                new IkeTunnelConnectionParams(IKE_PARAMS_V6, CHILD_PARAMS);
         assertEquals(new Ikev2VpnProfile.Builder(tunnelParams).build(),
                 new Ikev2VpnProfile.Builder(tunnelParams2).build());
     }
diff --git a/tests/unit/java/com/android/internal/net/VpnProfileTest.java b/tests/unit/java/com/android/internal/net/VpnProfileTest.java
index 360390d..0a6d2f2 100644
--- a/tests/unit/java/com/android/internal/net/VpnProfileTest.java
+++ b/tests/unit/java/com/android/internal/net/VpnProfileTest.java
@@ -17,7 +17,7 @@
 package com.android.internal.net;
 
 import static android.net.cts.util.IkeSessionTestUtils.CHILD_PARAMS;
-import static android.net.cts.util.IkeSessionTestUtils.IKE_PARAMS;
+import static android.net.cts.util.IkeSessionTestUtils.IKE_PARAMS_V4;
 
 import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
 import static com.android.testutils.ParcelUtils.assertParcelSane;
@@ -128,7 +128,7 @@
     private VpnProfile getSampleIkev2ProfileWithIkeTunConnParams(String key) {
         final VpnProfile p = new VpnProfile(key, true /* isRestrictedToTestNetworks */,
                 false /* excludesLocalRoutes */, true /* requiresPlatformValidation */,
-                new IkeTunnelConnectionParams(IKE_PARAMS, CHILD_PARAMS));
+                new IkeTunnelConnectionParams(IKE_PARAMS_V4, CHILD_PARAMS));
 
         p.name = "foo";
         p.server = "bar";
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 4c76803..37df4eb 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -159,8 +159,8 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
 import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
 import static org.mockito.AdditionalMatchers.aryEq;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyLong;
@@ -342,6 +342,7 @@
 import com.android.server.ConnectivityService.NetworkRequestInfo;
 import com.android.server.ConnectivityServiceTest.ConnectivityServiceDependencies.ReportedInterfaces;
 import com.android.server.connectivity.CarrierPrivilegeAuthenticator;
+import com.android.server.connectivity.ClatCoordinator;
 import com.android.server.connectivity.ConnectivityFlags;
 import com.android.server.connectivity.MockableSystemProperties;
 import com.android.server.connectivity.Nat464Xlat;
@@ -540,6 +541,7 @@
     @Mock VpnProfileStore mVpnProfileStore;
     @Mock SystemConfigManager mSystemConfigManager;
     @Mock Resources mResources;
+    @Mock ClatCoordinator mClatCoordinator;
     @Mock PacProxyManager mPacProxyManager;
     @Mock BpfNetMaps mBpfNetMaps;
     @Mock CarrierPrivilegeAuthenticator mCarrierPrivilegeAuthenticator;
@@ -2004,6 +2006,11 @@
             return mBpfNetMaps;
         }
 
+        @Override
+        public ClatCoordinator getClatCoordinator(INetd netd) {
+            return mClatCoordinator;
+        }
+
         final ArrayTrackRecord<Pair<String, Long>> mRateLimitHistory = new ArrayTrackRecord<>();
         final Map<String, Long> mActiveRateLimit = new HashMap<>();
 
@@ -9569,6 +9576,59 @@
         return event;
     }
 
+    private <T> T verifyWithOrder(@Nullable InOrder inOrder, @NonNull T t) {
+        if (inOrder != null) {
+            return inOrder.verify(t);
+        } else {
+            return verify(t);
+        }
+    }
+
+    private <T> T verifyNeverWithOrder(@Nullable InOrder inOrder, @NonNull T t) {
+        if (inOrder != null) {
+            return inOrder.verify(t, never());
+        } else {
+            return verify(t, never());
+        }
+    }
+
+    private void verifyClatdStart(@Nullable InOrder inOrder, @NonNull String iface, int netId,
+            @NonNull String nat64Prefix) throws Exception {
+        if (SdkLevel.isAtLeastT()) {
+            verifyWithOrder(inOrder, mClatCoordinator)
+                .clatStart(eq(iface), eq(netId), eq(new IpPrefix(nat64Prefix)));
+        } else {
+            verifyWithOrder(inOrder, mMockNetd).clatdStart(eq(iface), eq(nat64Prefix));
+        }
+    }
+
+    private void verifyNeverClatdStart(@Nullable InOrder inOrder, @NonNull String iface)
+            throws Exception {
+        if (SdkLevel.isAtLeastT()) {
+            verifyNeverWithOrder(inOrder, mClatCoordinator).clatStart(eq(iface), anyInt(), any());
+        } else {
+            verifyNeverWithOrder(inOrder, mMockNetd).clatdStart(eq(iface), anyString());
+        }
+    }
+
+    private void verifyClatdStop(@Nullable InOrder inOrder, @NonNull String iface)
+            throws Exception {
+        if (SdkLevel.isAtLeastT()) {
+            verifyWithOrder(inOrder, mClatCoordinator).clatStop();
+        } else {
+            verifyWithOrder(inOrder, mMockNetd).clatdStop(eq(iface));
+        }
+    }
+
+    private void verifyNeverClatdStop(@Nullable InOrder inOrder, @NonNull String iface)
+            throws Exception {
+        if (SdkLevel.isAtLeastT()) {
+            verifyNeverWithOrder(inOrder, mClatCoordinator).clatStop();
+        } else {
+            verifyNeverWithOrder(inOrder, mMockNetd).clatdStop(eq(iface));
+        }
+    }
+
     @Test
     public void testStackedLinkProperties() throws Exception {
         final LinkAddress myIpv4 = new LinkAddress("1.2.3.4/24");
@@ -9601,6 +9661,7 @@
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
         reset(mMockDnsResolver);
         reset(mMockNetd);
+        reset(mClatCoordinator);
 
         // Connect with ipv6 link properties. Expect prefix discovery to be started.
         mCellNetworkAgent.connect(true);
@@ -9639,8 +9700,10 @@
                 && ri.iface != null && ri.iface.startsWith("v4-")));
 
         verifyNoMoreInteractions(mMockNetd);
+        verifyNoMoreInteractions(mClatCoordinator);
         verifyNoMoreInteractions(mMockDnsResolver);
         reset(mMockNetd);
+        reset(mClatCoordinator);
         reset(mMockDnsResolver);
         doReturn(getClatInterfaceConfigParcel(myIpv4)).when(mMockNetd)
                 .interfaceGetCfg(CLAT_MOBILE_IFNAME);
@@ -9661,7 +9724,7 @@
                 CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent).getLp();
         assertEquals(0, lpBeforeClat.getStackedLinks().size());
         assertEquals(kNat64Prefix, lpBeforeClat.getNat64Prefix());
-        verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString());
+        verifyClatdStart(null /* inOrder */, MOBILE_IFNAME, cellNetId, kNat64Prefix.toString());
 
         // Clat iface comes up. Expect stacked link to be added.
         clat.interfaceLinkStateChanged(CLAT_MOBILE_IFNAME, true);
@@ -9693,6 +9756,7 @@
                     new int[] { TRANSPORT_CELLULAR })));
         }
         reset(mMockNetd);
+        reset(mClatCoordinator);
         doReturn(getClatInterfaceConfigParcel(myIpv4)).when(mMockNetd)
                 .interfaceGetCfg(CLAT_MOBILE_IFNAME);
         // Change the NAT64 prefix without first removing it.
@@ -9701,11 +9765,12 @@
                 cellNetId, PREFIX_OPERATION_ADDED, kOtherNat64PrefixString, 96));
         networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
                 (lp) -> lp.getStackedLinks().size() == 0);
-        verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
+        verifyClatdStop(null /* inOrder */, MOBILE_IFNAME);
         assertRoutesRemoved(cellNetId, stackedDefault);
         verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_MOBILE_IFNAME);
 
-        verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kOtherNat64Prefix.toString());
+        verifyClatdStart(null /* inOrder */, MOBILE_IFNAME, cellNetId,
+                kOtherNat64Prefix.toString());
         networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
                 (lp) -> lp.getNat64Prefix().equals(kOtherNat64Prefix));
         clat.interfaceLinkStateChanged(CLAT_MOBILE_IFNAME, true);
@@ -9714,6 +9779,7 @@
         assertRoutesAdded(cellNetId, stackedDefault);
         verify(mMockNetd, times(1)).networkAddInterface(cellNetId, CLAT_MOBILE_IFNAME);
         reset(mMockNetd);
+        reset(mClatCoordinator);
 
         // Add ipv4 address, expect that clatd and prefix discovery are stopped and stacked
         // linkproperties are cleaned up.
@@ -9722,7 +9788,7 @@
         mCellNetworkAgent.sendLinkProperties(cellLp);
         networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
         assertRoutesAdded(cellNetId, ipv4Subnet);
-        verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
+        verifyClatdStop(null /* inOrder */, MOBILE_IFNAME);
         verify(mMockDnsResolver, times(1)).stopPrefix64Discovery(cellNetId);
 
         // As soon as stop is called, the linkproperties lose the stacked interface.
@@ -9739,8 +9805,10 @@
         networkCallback.assertNoCallback();
         verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_MOBILE_IFNAME);
         verifyNoMoreInteractions(mMockNetd);
+        verifyNoMoreInteractions(mClatCoordinator);
         verifyNoMoreInteractions(mMockDnsResolver);
         reset(mMockNetd);
+        reset(mClatCoordinator);
         reset(mMockDnsResolver);
         doReturn(getClatInterfaceConfigParcel(myIpv4)).when(mMockNetd)
                 .interfaceGetCfg(CLAT_MOBILE_IFNAME);
@@ -9762,7 +9830,7 @@
         mService.mResolverUnsolEventCallback.onNat64PrefixEvent(makeNat64PrefixEvent(
                 cellNetId, PREFIX_OPERATION_ADDED, kNat64PrefixString, 96));
         networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
-        verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString());
+        verifyClatdStart(null /* inOrder */, MOBILE_IFNAME, cellNetId, kNat64Prefix.toString());
 
         // Clat iface comes up. Expect stacked link to be added.
         clat.interfaceLinkStateChanged(CLAT_MOBILE_IFNAME, true);
@@ -9779,7 +9847,7 @@
         assertRoutesRemoved(cellNetId, ipv4Subnet, stackedDefault);
 
         // Stop has no effect because clat is already stopped.
-        verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
+        verifyClatdStop(null /* inOrder */, MOBILE_IFNAME);
         networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
                 (lp) -> lp.getStackedLinks().size() == 0);
         verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_MOBILE_IFNAME);
@@ -9792,7 +9860,9 @@
                 eq(Integer.toString(TRANSPORT_CELLULAR)));
         verify(mMockNetd).networkDestroy(cellNetId);
         verifyNoMoreInteractions(mMockNetd);
+        verifyNoMoreInteractions(mClatCoordinator);
         reset(mMockNetd);
+        reset(mClatCoordinator);
 
         // Test disconnecting a network that is running 464xlat.
 
@@ -9809,7 +9879,7 @@
         assertRoutesAdded(cellNetId, ipv6Subnet, ipv6Default);
 
         // Clatd is started and clat iface comes up. Expect stacked link to be added.
-        verify(mMockNetd).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString());
+        verifyClatdStart(null /* inOrder */, MOBILE_IFNAME, cellNetId, kNat64Prefix.toString());
         clat = getNat464Xlat(mCellNetworkAgent);
         clat.interfaceLinkStateChanged(CLAT_MOBILE_IFNAME, true /* up */);
         networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
@@ -9819,16 +9889,18 @@
         // assertRoutesAdded sees all calls since last mMockNetd reset, so expect IPv6 routes again.
         assertRoutesAdded(cellNetId, ipv6Subnet, ipv6Default, stackedDefault);
         reset(mMockNetd);
+        reset(mClatCoordinator);
 
         // Disconnect the network. clat is stopped and the network is destroyed.
         mCellNetworkAgent.disconnect();
         networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
         networkCallback.assertNoCallback();
-        verify(mMockNetd).clatdStop(MOBILE_IFNAME);
+        verifyClatdStop(null /* inOrder */, MOBILE_IFNAME);
         verify(mMockNetd).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(),
                 eq(Integer.toString(TRANSPORT_CELLULAR)));
         verify(mMockNetd).networkDestroy(cellNetId);
         verifyNoMoreInteractions(mMockNetd);
+        verifyNoMoreInteractions(mClatCoordinator);
 
         mCm.unregisterNetworkCallback(networkCallback);
     }
@@ -9859,7 +9931,7 @@
         baseLp.addDnsServer(InetAddress.getByName("2001:4860:4860::6464"));
 
         reset(mMockNetd, mMockDnsResolver);
-        InOrder inOrder = inOrder(mMockNetd, mMockDnsResolver);
+        InOrder inOrder = inOrder(mMockNetd, mMockDnsResolver, mClatCoordinator);
 
         // If a network already has a NAT64 prefix on connect, clatd is started immediately and
         // prefix discovery is never started.
@@ -9870,7 +9942,7 @@
         final Network network = mWiFiNetworkAgent.getNetwork();
         int netId = network.getNetId();
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
-        inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString());
+        verifyClatdStart(inOrder, iface, netId, pref64FromRa.toString());
         inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString());
         inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
         callback.assertNoCallback();
@@ -9880,7 +9952,7 @@
         lp.setNat64Prefix(null);
         mWiFiNetworkAgent.sendLinkProperties(lp);
         expectNat64PrefixChange(callback, mWiFiNetworkAgent, null);
-        inOrder.verify(mMockNetd).clatdStop(iface);
+        verifyClatdStop(inOrder, iface);
         inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
         inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
 
@@ -9889,7 +9961,7 @@
         lp.setNat64Prefix(pref64FromRa);
         mWiFiNetworkAgent.sendLinkProperties(lp);
         expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromRa);
-        inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString());
+        verifyClatdStart(inOrder, iface, netId, pref64FromRa.toString());
         inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
         inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString());
 
@@ -9898,22 +9970,22 @@
         lp.setNat64Prefix(null);
         mWiFiNetworkAgent.sendLinkProperties(lp);
         expectNat64PrefixChange(callback, mWiFiNetworkAgent, null);
-        inOrder.verify(mMockNetd).clatdStop(iface);
+        verifyClatdStop(inOrder, iface);
         inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
         inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
 
         mService.mResolverUnsolEventCallback.onNat64PrefixEvent(
                 makeNat64PrefixEvent(netId, PREFIX_OPERATION_ADDED, pref64FromDnsStr, 96));
         expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromDns);
-        inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString());
+        verifyClatdStart(inOrder, iface, netId, pref64FromDns.toString());
 
         // If an RA advertises the same prefix that was discovered by DNS, nothing happens: prefix
         // discovery is not stopped, and there are no callbacks.
         lp.setNat64Prefix(pref64FromDns);
         mWiFiNetworkAgent.sendLinkProperties(lp);
         callback.assertNoCallback();
-        inOrder.verify(mMockNetd, never()).clatdStop(iface);
-        inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
+        verifyNeverClatdStop(inOrder, iface);
+        verifyNeverClatdStart(inOrder, iface);
         inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
         inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
         inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
@@ -9922,8 +9994,8 @@
         lp.setNat64Prefix(null);
         mWiFiNetworkAgent.sendLinkProperties(lp);
         callback.assertNoCallback();
-        inOrder.verify(mMockNetd, never()).clatdStop(iface);
-        inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
+        verifyNeverClatdStop(inOrder, iface);
+        verifyNeverClatdStart(inOrder, iface);
         inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
         inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
         inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
@@ -9932,14 +10004,14 @@
         lp.setNat64Prefix(pref64FromRa);
         mWiFiNetworkAgent.sendLinkProperties(lp);
         expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromRa);
-        inOrder.verify(mMockNetd).clatdStop(iface);
+        verifyClatdStop(inOrder, iface);
         inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
 
         // Stopping prefix discovery results in a prefix removed notification.
         mService.mResolverUnsolEventCallback.onNat64PrefixEvent(
                 makeNat64PrefixEvent(netId, PREFIX_OPERATION_REMOVED, pref64FromDnsStr, 96));
 
-        inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString());
+        verifyClatdStart(inOrder, iface, netId, pref64FromRa.toString());
         inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString());
         inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
 
@@ -9947,9 +10019,9 @@
         lp.setNat64Prefix(newPref64FromRa);
         mWiFiNetworkAgent.sendLinkProperties(lp);
         expectNat64PrefixChange(callback, mWiFiNetworkAgent, newPref64FromRa);
-        inOrder.verify(mMockNetd).clatdStop(iface);
+        verifyClatdStop(inOrder, iface);
         inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
-        inOrder.verify(mMockNetd).clatdStart(iface, newPref64FromRa.toString());
+        verifyClatdStart(inOrder, iface, netId, newPref64FromRa.toString());
         inOrder.verify(mMockDnsResolver).setPrefix64(netId, newPref64FromRa.toString());
         inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
         inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
@@ -9959,8 +10031,8 @@
         mWiFiNetworkAgent.sendLinkProperties(lp);
         callback.assertNoCallback();
         assertEquals(newPref64FromRa, mCm.getLinkProperties(network).getNat64Prefix());
-        inOrder.verify(mMockNetd, never()).clatdStop(iface);
-        inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
+        verifyNeverClatdStop(inOrder, iface);
+        verifyNeverClatdStart(inOrder, iface);
         inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
         inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
         inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
@@ -9972,20 +10044,20 @@
         lp.setNat64Prefix(null);
         mWiFiNetworkAgent.sendLinkProperties(lp);
         expectNat64PrefixChange(callback, mWiFiNetworkAgent, null);
-        inOrder.verify(mMockNetd).clatdStop(iface);
+        verifyClatdStop(inOrder, iface);
         inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
         inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
         mService.mResolverUnsolEventCallback.onNat64PrefixEvent(
                 makeNat64PrefixEvent(netId, PREFIX_OPERATION_ADDED, pref64FromDnsStr, 96));
         expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromDns);
-        inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString());
+        verifyClatdStart(inOrder, iface, netId, pref64FromDns.toString());
         inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), any());
 
         lp.setNat64Prefix(pref64FromDns);
         mWiFiNetworkAgent.sendLinkProperties(lp);
         callback.assertNoCallback();
-        inOrder.verify(mMockNetd, never()).clatdStop(iface);
-        inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
+        verifyNeverClatdStop(inOrder, iface);
+        verifyNeverClatdStart(inOrder, iface);
         inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
         inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
         inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
@@ -9998,7 +10070,7 @@
         callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
         b.expectBroadcast();
 
-        inOrder.verify(mMockNetd).clatdStop(iface);
+        verifyClatdStop(inOrder, iface);
         inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
         inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
 
diff --git a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
index c3d64cb..f84d10f 100644
--- a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
@@ -109,9 +109,9 @@
             new FileDescriptor());
 
     private static final String EGRESS_PROG_PATH =
-            "/sys/fs/bpf/prog_clatd_schedcls_egress4_clat_rawip";
+            "/sys/fs/bpf/net_shared/prog_clatd_schedcls_egress4_clat_rawip";
     private static final String INGRESS_PROG_PATH =
-            "/sys/fs/bpf/prog_clatd_schedcls_ingress6_clat_ether";
+            "/sys/fs/bpf/net_shared/prog_clatd_schedcls_ingress6_clat_ether";
     private static final ClatEgress4Key EGRESS_KEY = new ClatEgress4Key(STACKED_IFINDEX,
             INET4_LOCAL4);
     private static final ClatEgress4Value EGRESS_VALUE = new ClatEgress4Value(BASE_IFINDEX,
diff --git a/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java
index aa4c4e3..06e0d6d 100644
--- a/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -21,6 +21,8 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.inOrder;
@@ -44,8 +46,11 @@
 import android.os.Handler;
 import android.os.test.TestLooper;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
 
+import com.android.modules.utils.build.SdkLevel;
 import com.android.server.ConnectivityService;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRunner;
@@ -75,13 +80,20 @@
     @Mock IDnsResolver mDnsResolver;
     @Mock INetd mNetd;
     @Mock NetworkAgentInfo mNai;
+    @Mock ClatCoordinator mClatCoordinator;
 
     TestLooper mLooper;
     Handler mHandler;
     NetworkAgentConfig mAgentConfig = new NetworkAgentConfig();
 
     Nat464Xlat makeNat464Xlat(boolean isCellular464XlatEnabled) {
-        return new Nat464Xlat(mNai, mNetd, mDnsResolver, new ConnectivityService.Dependencies()) {
+        final ConnectivityService.Dependencies deps = new ConnectivityService.Dependencies() {
+            @Override public ClatCoordinator getClatCoordinator(INetd netd) {
+                return mClatCoordinator;
+            }
+        };
+
+        return new Nat464Xlat(mNai, mNetd, mDnsResolver, deps) {
             @Override protected int getNetId() {
                 return NETID;
             }
@@ -208,6 +220,39 @@
         }
     }
 
+    private <T> T verifyWithOrder(@Nullable InOrder inOrder, @NonNull T t) {
+        if (inOrder != null) {
+            return inOrder.verify(t);
+        } else {
+            return verify(t);
+        }
+    }
+
+    private void verifyClatdStart(@Nullable InOrder inOrder) throws Exception {
+        if (SdkLevel.isAtLeastT()) {
+            verifyWithOrder(inOrder, mClatCoordinator)
+                .clatStart(eq(BASE_IFACE), eq(NETID), eq(new IpPrefix(NAT64_PREFIX)));
+        } else {
+            verifyWithOrder(inOrder, mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
+        }
+    }
+
+    private void verifyNeverClatdStart() throws Exception {
+        if (SdkLevel.isAtLeastT()) {
+            verify(mClatCoordinator, never()).clatStart(anyString(), anyInt(), any());
+        } else {
+            verify(mNetd, never()).clatdStart(anyString(), anyString());
+        }
+    }
+
+    private void verifyClatdStop(@Nullable InOrder inOrder) throws Exception {
+        if (SdkLevel.isAtLeastT()) {
+            verifyWithOrder(inOrder, mClatCoordinator).clatStop();
+        } else {
+            verifyWithOrder(inOrder, mNetd).clatdStop(eq(BASE_IFACE));
+        }
+    }
+
     private void checkNormalStartAndStop(boolean dueToDisconnect) throws Exception {
         Nat464Xlat nat = makeNat464Xlat(true);
         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
@@ -219,7 +264,7 @@
         // Start clat.
         nat.start();
 
-        verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
+        verifyClatdStart(null /* inOrder */);
 
         // Stacked interface up notification arrives.
         nat.interfaceLinkStateChanged(STACKED_IFACE, true);
@@ -235,7 +280,7 @@
         makeClatUnnecessary(dueToDisconnect);
         nat.stop();
 
-        verify(mNetd).clatdStop(eq(BASE_IFACE));
+        verifyClatdStop(null /* inOrder */);
         verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
         assertTrue(c.getValue().getStackedLinks().isEmpty());
         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
@@ -262,7 +307,7 @@
     private void checkStartStopStart(boolean interfaceRemovedFirst) throws Exception {
         Nat464Xlat nat = makeNat464Xlat(true);
         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
-        InOrder inOrder = inOrder(mNetd, mConnectivity);
+        InOrder inOrder = inOrder(mNetd, mConnectivity, mClatCoordinator);
 
         mNai.linkProperties.addLinkAddress(V6ADDR);
 
@@ -270,7 +315,7 @@
 
         nat.start();
 
-        inOrder.verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
+        verifyClatdStart(inOrder);
 
         // Stacked interface up notification arrives.
         nat.interfaceLinkStateChanged(STACKED_IFACE, true);
@@ -284,7 +329,7 @@
         // ConnectivityService stops clat (Network disconnects, IPv4 addr appears, ...).
         nat.stop();
 
-        inOrder.verify(mNetd).clatdStop(eq(BASE_IFACE));
+        verifyClatdStop(inOrder);
 
         inOrder.verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture());
         assertTrue(c.getValue().getStackedLinks().isEmpty());
@@ -306,7 +351,7 @@
 
         nat.start();
 
-        inOrder.verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
+        verifyClatdStart(inOrder);
 
         if (!interfaceRemovedFirst) {
             // Stacked interface removed notification arrives and is ignored.
@@ -328,7 +373,7 @@
         // ConnectivityService stops clat again.
         nat.stop();
 
-        inOrder.verify(mNetd).clatdStop(eq(BASE_IFACE));
+        verifyClatdStop(inOrder);
 
         inOrder.verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture());
         assertTrue(c.getValue().getStackedLinks().isEmpty());
@@ -357,7 +402,7 @@
 
         nat.start();
 
-        verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
+        verifyClatdStart(null /* inOrder */);
 
         // Stacked interface up notification arrives.
         nat.interfaceLinkStateChanged(STACKED_IFACE, true);
@@ -373,7 +418,7 @@
         nat.interfaceRemoved(STACKED_IFACE);
         mLooper.dispatchNext();
 
-        verify(mNetd).clatdStop(eq(BASE_IFACE));
+        verifyClatdStop(null /* inOrder */);
         verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
         verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
         assertTrue(c.getValue().getStackedLinks().isEmpty());
@@ -395,13 +440,13 @@
 
         nat.start();
 
-        verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
+        verifyClatdStart(null /* inOrder */);
 
         // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
         makeClatUnnecessary(dueToDisconnect);
         nat.stop();
 
-        verify(mNetd).clatdStop(eq(BASE_IFACE));
+        verifyClatdStop(null /* inOrder */);
         verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
         assertIdle(nat);
 
@@ -437,13 +482,13 @@
 
         nat.start();
 
-        verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
+        verifyClatdStart(null /* inOrder */);
 
         // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
         makeClatUnnecessary(dueToDisconnect);
         nat.stop();
 
-        verify(mNetd).clatdStop(eq(BASE_IFACE));
+        verifyClatdStop(null /* inOrder */);
         verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
         assertIdle(nat);
 
@@ -518,7 +563,7 @@
             mNai.linkProperties.setNat64Prefix(nat64Prefix);
             nat.setNat64PrefixFromRa(nat64Prefix);
             nat.update();
-            verify(mNetd, never()).clatdStart(anyString(), anyString());
+            verifyNeverClatdStart();
             assertIdle(nat);
         } else {
             // Prefix discovery is started.
@@ -529,7 +574,7 @@
             mNai.linkProperties.setNat64Prefix(nat64Prefix);
             nat.setNat64PrefixFromRa(nat64Prefix);
             nat.update();
-            verify(mNetd).clatdStart(BASE_IFACE, NAT64_PREFIX);
+            verifyClatdStart(null /* inOrder */);
             assertStarting(nat);
         }
     }